
# Smart HTTP

Pitmaster comunica con i server git tramite HTTPS usando il protocollo smart HTTP. È il trasporto di rete principale. Usa le funzioni native di stream di PHP (`fopen` con un contesto stream) per le richieste HTTP, quindi `allow_url_fopen` deve essere abilitato.

## SmartHttpClient

```php
use Pitmaster\Protocol\SmartHttpClient;

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

Il timeout predefinito è la costante `PITMASTER_HTTP_TIMEOUT` (un intero), oppure 30 secondi se non è definita.

### Rilevare i riferimenti

```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()` fa lo stesso per `git-receive-pack`. La richiesta di rilevamento dei riferimenti viene inviata a:
```
GET <url>/info/refs?service=git-upload-pack
```

La risposta è un elenco di riferimenti codificato in pkt-line, con le funzionalità sulla prima riga.

### Upload pack (fetch)

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

Invia una POST a `<url>/git-upload-pack` con il corpo della negoziazione want/have. Restituisce il corpo grezzo della risposta (righe pkt-line, `NAK` e dati side-band). `UploadPackClient` crea la richiesta ed estrae il 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);
```

Invia una POST a `<url>/git-receive-pack` con i comandi di aggiornamento dei riferimenti e i dati del pack.

## Codifica PktLine

Il protocollo git usa la struttura pkt-line per tutte le comunicazioni. Ogni riga è preceduta dalla sua lunghezza totale (compreso il prefisso di 4 byte), codificata con 4 cifre esadecimali.

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

### Regole di lunghezza

- Riga dati minima: `0005` (payload di 1 byte)
- Riga dati massima: `fff0` (payload di 65516 byte)
- Pacchetto di flush: `0000`
- Pacchetto delimitatore: `0001`
- Fine risposta: `0002` (protocollo v2; gestita da `ProtocolV2::decode()`, mentre `PktLine::decode()` la rifiuta)

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

## Rilevamento dei riferimenti

La classe `RefDiscovery` analizza l'annuncio dei riferimenti del server.

```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 dell'annuncio dei riferimenti

La prima riga dell'annuncio include le funzionalità:

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

## Funzionalità

La classe `Capability` analizza e gestisce le funzionalità del protocollo.

```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']
```

### Funzionalità comuni

| Funzionalità | Significato |
|-----------|---------|
| `multi_ack` | Il server supporta la negoziazione multi-ack |
| `thin-pack` | Il server invia pack sottili (gli oggetti di base possono essere omessi). Pitmaster la richiede, ma al momento non può indicizzare pack sottili ([#16](https://github.com/inline0/pitmaster/issues/16)) |
| `side-band` | Il server usa side-band per i dati del pack (limite di 1000 byte) |
| `side-band-64k` | Il server usa side-band a 64K per i dati del pack |
| `ofs-delta` | Il pack può contenere oggetti OFS_DELTA |
| `shallow` | Il server supporta i cloni superficiali |
| `no-done` | Il server non richiede una riga «done» |
| `symref=HEAD:refs/heads/main` | Riferimento simbolico di destinazione di HEAD |
| `agent=git/2.45.0` | Versione di Git del server |

## Demultiplexing side-band

Quando il server usa side-band, i dati del pack sono multiplexati insieme ai messaggi di avanzamento e di errore. Il primo byte del payload di ogni riga pkt-line indica il canale:

| Canale | Significato |
|---------|---------|
| 1 | Dati del pack |
| 2 | Messaggi di avanzamento (stderr) |
| 3 | Messaggi di errore fatale |

Il `UploadPackClient` di Pitmaster rimuove i byte del canale e ricompone i dati del pack.

## Versioni del protocollo

### v1 (predefinito)

Il protocollo tradizionale. Il rilevamento dei riferimenti invia tutti i riferimenti in una volta. Pitmaster negozia in un unico passaggio: invia i propri want, ogni estremità dei riferimenti locali come `have` e `done`.

### v2

Un protocollo più semplice e dalle prestazioni migliori.

Differenze principali rispetto a v1:
- Il rilevamento dei riferimenti è un comando `ls-refs` separato (può filtrare i riferimenti)
- Il fetch è un unico comando `fetch` con parametri strutturati
- Usa pacchetti delimitatori (`0001`) per separare le sezioni

`clone()` e `fetch()` usano sempre v1. v2 è disponibile se lo richiamate esplicitamente:

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

## Flusso delle richieste 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
```

## Gestione degli errori

Gli errori di rete generano `ProtocolException`. Se lo scambio smart HTTP di `clone()` o `fetch()` fallisce, Pitmaster riprova con HTTP non smart e, in caso di un ulteriore fallimento, genera nuovamente l'errore originale. Gli altri errori (remote sconosciuto, handle senza processi, pack non indicizzabile) generano altri tipi di eccezione:

```php
use Pitmaster\Exceptions\ProtocolException;

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

Errori comuni:
- Server non raggiungibile (timeout)
- Autenticazione richiesta o repository non trovato (segnalato come `Unexpected HTTP status 401`, `403` o `404`)
- `Content-Type` imprevisto nella risposta
- Dati pkt-line non validi
- Messaggio sul canale di errore side-band
