
# Smart HTTP

Pitmaster se comunica con servidores git mediante HTTPS usando el protocolo smart HTTP. Este es el transporte de red principal. Usa las funciones nativas de flujo de PHP (`fopen` con un contexto de flujo) para las solicitudes HTTP, por lo que debe estar habilitado `allow_url_fopen`.

## SmartHttpClient

```php
use Pitmaster\Protocol\SmartHttpClient;

$http = new SmartHttpClient();
// Or with custom timeout:
$http = new SmartHttpClient(timeout: 60);
```

El tiempo de espera predeterminado es la constante `PITMASTER_HTTP_TIMEOUT` (un entero) o 30 segundos si no está definida.

### Descubrir refs

```php
$discovery = $http->discoverRefs('https://github.com/user/repo.git');

foreach ($discovery->refs() as $name => $id) {
    echo "{$id->hex} {$name}\n";
}

// HEAD symref (e.g., 'refs/heads/main')
$defaultBranch = $discovery->headSymref();

// Specific ref
$mainId = $discovery->ref('refs/heads/main');

// Server capabilities (a Capability object, or null)
$caps = $discovery->capabilities();
```

`discoverReceivePackRefs()` hace lo mismo para `git-receive-pack`. La solicitud de descubrimiento de refs se envía a:
```
GET <url>/info/refs?service=git-upload-pack
```

La respuesta es una lista de refs codificada en pkt-line, con las capacidades en la primera línea.

### Enviar pack (fetch)

```php
$response = $http->uploadPack($url, $requestBody);
```

Envía un POST a `<url>/git-upload-pack` con el cuerpo de negociación want/have. Devuelve el cuerpo de respuesta sin procesar (pkt-lines, `NAK` y datos de banda lateral). `UploadPackClient` construye la solicitud y extrae el pack:

```php
use Pitmaster\Protocol\UploadPackClient;

$client = new UploadPackClient($http);
$packData = $client->fetch($url, $wants, $haves);   // ObjectId[] wants and haves
```

### Recibir pack (push)

```php
$response = $http->receivePack($url, $requestBody);
```

Envía un POST a `<url>/git-receive-pack` con los comandos de actualización de refs y los datos del pack.

## Codificación PktLine

El protocolo git usa el encuadre pkt-line para todas las comunicaciones. Cada línea lleva como prefijo su longitud total (incluido el prefijo de longitud de 4 bytes), codificada como 4 dígitos hexadecimales.

```php
use Pitmaster\Protocol\PktLine;

// Encode a single line
$encoded = PktLine::encode("want abc123def456\n");
// "0016want abc123def456\n"

// Flush packet (end of section)
$flush = PktLine::flush();
// "0000"

// Delimiter packet (protocol v2)
$delim = PktLine::delimiter();
// "0001"

// Decode a stream
$lines = PktLine::decode($rawData);
// Each element is: string (data), null (flush), or false (delimiter)
```

### Reglas de longitud

- Línea de datos mínima: `0005` (carga útil de 1 byte)
- Línea de datos máxima: `fff0` (carga útil de 65516 bytes)
- Paquete de vaciado: `0000`
- Paquete delimitador: `0001`
- Fin de respuesta: `0002` (protocolo v2; lo gestiona `ProtocolV2::decode()`, mientras que `PktLine::decode()` lo rechaza)

```php
PktLine::MAX_PAYLOAD;  // 65516 (65520 - 4)
```

## Descubrimiento de refs

La clase `RefDiscovery` analiza el anuncio de refs del servidor.

```php
use Pitmaster\Protocol\RefDiscovery;

$discovery = RefDiscovery::parse($pktLines);

// All refs
$refs = $discovery->refs();
// ['refs/heads/main' => ObjectId, 'refs/tags/v1.0' => ObjectId, ...]

// Single ref
$id = $discovery->ref('refs/heads/main');

// HEAD symbolic ref (from capabilities)
$symref = $discovery->headSymref();
// 'refs/heads/main' or null

// Server capabilities
$caps = $discovery->capabilities();
// Capability object (see below), or null
```

### Formato del anuncio de refs

La primera línea del anuncio incluye las capacidades:

```
<hash> HEAD\0<capability list>\n
<hash> refs/heads/main\n
<hash> refs/tags/v1.0\n
0000
```

## Capacidades

La clase `Capability` analiza y gestiona las capacidades del protocolo.

```php
use Pitmaster\Protocol\Capability;

$caps = Capability::parse('thin-pack side-band-64k ofs-delta symref=HEAD:refs/heads/main');

$caps->has('thin-pack');           // true
$caps->get('symref');              // 'HEAD:refs/heads/main'
$caps->all();                      // ['thin-pack' => null, ..., 'symref' => 'HEAD:refs/heads/main']
```

### Capacidades comunes

| Capacidad | Significado |
|-----------|---------|
| `multi_ack` | El servidor admite la negociación multi-ack |
| `thin-pack` | El servidor envía packs delgados (pueden omitirse objetos base). Pitmaster lo solicita, pero aún no puede indexar packs delgados ([#16](https://github.com/inline0/pitmaster/issues/16)) |
| `side-band` | El servidor usa banda lateral para los datos del pack (límite de 1000 bytes) |
| `side-band-64k` | El servidor usa banda lateral de 64K para los datos del pack |
| `ofs-delta` | El pack puede usar objetos OFS_DELTA |
| `shallow` | El servidor admite clones superficiales |
| `no-done` | El servidor no requiere una línea «done» |
| `symref=HEAD:refs/heads/main` | Destino simbólico de HEAD |
| `agent=git/2.45.0` | Versión de git del servidor |

## Demultiplexación de banda lateral

Cuando el servidor usa banda lateral, los datos del pack se multiplexan con mensajes de progreso y error. El primer byte de la carga útil de cada pkt-line indica el canal:

| Canal | Significado |
|---------|---------|
| 1 | Datos del pack |
| 2 | Mensajes de progreso (stderr) |
| 3 | Mensajes de error fatal |

El `UploadPackClient` de Pitmaster elimina los bytes de canal y vuelve a ensamblar los datos del pack.

## Versiones del protocolo

### v1 (predeterminada)

El protocolo tradicional. El descubrimiento de refs envía todas las refs a la vez. Pitmaster negocia en una sola ronda: envía sus wants, cada punta de ref local como `have` y `done`.

### v2

Un protocolo más sencillo y de mejor rendimiento.

Diferencias clave respecto a v1:
- El descubrimiento de refs es un comando `ls-refs` independiente (puede filtrar refs)
- Fetch es un único comando `fetch` con parámetros estructurados
- Usa paquetes delimitadores (`0001`) para separar secciones

`clone()` y `fetch()` siempre usan v1. v2 está disponible si se invoca explícitamente:

```php
$discovery = $http->discoverRefsV2($url);       // ls-refs
$packData = (new UploadPackClient($http))->fetchV2($url, $wants, $haves);
```

## Flujo de solicitud HTTP

### Clonar/obtener (fetch)

```
1. GET  /info/refs?service=git-upload-pack
   Response: pkt-line encoded ref list

2. POST /git-upload-pack
   Request:  want <hash>\n ... have <hash>\n ... done\n
   Response: NAK\n + pack data (possibly side-band wrapped)
```

### Enviar (push)

```
1. GET  /info/refs?service=git-receive-pack
   Response: pkt-line encoded ref list

2. POST /git-receive-pack
   Request:  <old-hash> <new-hash> refs/heads/main\0<capabilities>\n
             0000
             PACK<pack data>
   Response: unpack ok\n + ref status lines
```

## Gestión de errores

Los errores de red generan `ProtocolException`. Si falla el intercambio smart HTTP de `clone()` o `fetch()`, Pitmaster vuelve a intentarlo mediante dumb HTTP y vuelve a lanzar el error original si también falla. Otros fallos (remoto desconocido, identificador sin proceso, pack que no se puede indexar) generan otros tipos de excepción:

```php
use Pitmaster\Exceptions\ProtocolException;

try {
    $repo->fetch();
} catch (ProtocolException $e) {
    echo "Network error: {$e->getMessage()}\n";
}
```

Errores comunes:
- Servidor inaccesible (tiempo de espera agotado)
- Se requiere autenticación o no se encuentra el repositorio (se informa como `Unexpected HTTP status 401`, `403` o `404`)
- `Content-Type` inesperado en la respuesta
- Datos pkt-line no válidos
- Mensaje del canal de error de banda lateral
