
# Smart HTTP

Pitmaster se comunica com servidores git por HTTPS usando o protocolo smart HTTP. Este é o transporte de rede principal. Ele usa as funções nativas de stream de PHP (`fopen` com um contexto de stream) para solicitações HTTP, portanto `allow_url_fopen` precisa estar habilitado.

## SmartHttpClient

```php
use Pitmaster\Protocol\SmartHttpClient;

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

O tempo limite usa por padrão a constante `PITMASTER_HTTP_TIMEOUT` (um inteiro) ou 30 segundos quando ela não está definida.

### Descobrir 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()` faz o mesmo para `git-receive-pack`. A solicitação de descoberta de refs é enviada para:
```
GET <url>/info/refs?service=git-upload-pack
```

A resposta é uma lista de refs codificada em pkt-line, com recursos na primeira linha.

### Upload pack (fetch)

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

Envia um POST para `<url>/git-upload-pack` com o corpo da negociação want/have. Retorna o corpo bruto da resposta (pkt-lines, `NAK` e dados side-band). `UploadPackClient` cria a solicitação e extrai o pack:

```php
use Pitmaster\Protocol\UploadPackClient;

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

### Receive pack (push)

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

Envia um POST para `<url>/git-receive-pack` com os comandos de atualização de refs e os dados do pack.

## Codificação PktLine

O protocolo git usa enquadramento pkt-line em toda a comunicação. Cada linha tem como prefixo seu comprimento total (incluindo o prefixo de comprimento de 4 bytes), codificado como 4 dígitos hexadecimais.

```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)
```

### Regras de comprimento

- Linha de dados mínima: `0005` (carga útil de 1 byte)
- Linha de dados máxima: `fff0` (carga útil de 65516 bytes)
- Pacote de flush: `0000`
- Pacote delimitador: `0001`
- Fim da resposta: `0002` (protocolo v2; tratado por `ProtocolV2::decode()`, enquanto `PktLine::decode()` o rejeita)

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

## Descoberta de refs

A classe `RefDiscovery` analisa o anúncio de refs do 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 do anúncio de refs

A primeira linha do anúncio inclui recursos:

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

## Recursos

A classe `Capability` analisa e gerencia os recursos do 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']
```

### Recursos comuns

| Recurso | Significado |
|-----------|---------|
| `multi_ack` | O servidor oferece suporte à negociação multi-ack |
| `thin-pack` | O servidor envia packs finos (objetos-base podem ser omitidos). Pitmaster solicita esse recurso, mas ainda não pode indexar packs finos ([#16](https://github.com/inline0/pitmaster/issues/16)) |
| `side-band` | O servidor usa side-band para dados do pack (limitado a 1000 bytes) |
| `side-band-64k` | O servidor usa side-band de 64K para dados do pack |
| `ofs-delta` | O pack pode usar objetos OFS_DELTA |
| `shallow` | O servidor oferece suporte a clone raso |
| `no-done` | O servidor não exige uma linha "done" |
| `symref=HEAD:refs/heads/main` | Destino simbólico de HEAD |
| `agent=git/2.45.0` | Versão git do servidor |

## Demultiplexação side-band

Quando o servidor usa side-band, os dados do pack são multiplexados com mensagens de progresso e de erro. O primeiro byte da carga útil de cada pkt-line indica o canal:

| Canal | Significado |
|---------|---------|
| 1 | Dados do pack |
| 2 | Mensagens de progresso (stderr) |
| 3 | Mensagens de erro fatal |

Pitmaster de `UploadPackClient` remove os bytes do canal e remonta os dados do pack.

## Versões do protocolo

### v1 (padrão)

O protocolo tradicional. A descoberta de refs envia todas as refs de uma vez. Pitmaster negocia em uma única rodada: envia suas solicitações, cada ponta de ref local como `have` e `done`.

### v2

Um protocolo mais simples e com melhor desempenho.

Principais diferenças em relação a v1:
- A descoberta de refs é um comando `ls-refs` separado (pode filtrar refs)
- Fetch é um único comando `fetch` com parâmetros estruturados
- Usa pacotes delimitadores (`0001`) para separar seções

`clone()` e `fetch()` sempre usam v1. v2 está disponível quando chamado explicitamente:

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

## Fluxo de solicitação HTTP

### Clone/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)
```

### 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
```

## Tratamento de erros

Erros de rede lançam `ProtocolException`. Quando a troca smart HTTP de `clone()` ou `fetch()` falha, Pitmaster tenta novamente usando dumb HTTP e relança o erro original se isso também falhar. Outras falhas (remoto desconhecido, handle sem processo, pack que não pode ser indexado) lançam outros tipos de exceção:

```php
use Pitmaster\Exceptions\ProtocolException;

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

Erros comuns:
- Servidor inacessível (tempo limite)
- Autenticação necessária ou repositório não encontrado (reportado como `Unexpected HTTP status 401`, `403` ou `404`)
- `Content-Type` inesperado na resposta
- Dados pkt-line inválidos
- Mensagem de erro do canal side-band
