
# Smart HTTP

Pitmaster communique avec les serveurs git via HTTPS au moyen du protocole smart HTTP. Il s’agit du transport réseau principal. Il utilise les fonctions de flux natives de PHP (`fopen` avec un contexte de flux) pour les requêtes HTTP ; `allow_url_fopen` doit donc être activé.

## SmartHttpClient

```php
use Pitmaster\Protocol\SmartHttpClient;

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

Le délai d’expiration est par défaut la constante `PITMASTER_HTTP_TIMEOUT` (un entier), ou 30 secondes si elle n’est pas définie.

### Découvrir les 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()` fait de même pour `git-receive-pack`. La requête de découverte des refs est envoyée à :
```
GET <url>/info/refs?service=git-upload-pack
```

La réponse est une liste de refs encodée en pkt-line, avec les fonctionnalités sur la première ligne.

### Envoyer le pack (fetch)

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

Envoie une requête POST à `<url>/git-upload-pack` avec le corps de négociation want/have. Renvoie le corps brut de la réponse (pkt-lines, `NAK` et données side-band). `UploadPackClient` construit la requête et extrait le pack :

```php
use Pitmaster\Protocol\UploadPackClient;

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

### Recevoir le pack (push)

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

Envoie une requête POST à `<url>/git-receive-pack` avec les commandes de mise à jour des refs et les données du pack.

## Encodage PktLine

Le protocole git utilise le cadrage pkt-line pour toutes les communications. Chaque ligne est préfixée par sa longueur totale (y compris le préfixe de longueur de 4 octets), encodée sur 4 chiffres hexadécimaux.

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

### Règles de longueur

- Ligne de données minimale : `0005` (charge utile de 1 octet)
- Ligne de données maximale : `fff0` (charge utile de 65516 octets)
- Paquet de vidage : `0000`
- Paquet délimiteur : `0001`
- Fin de réponse : `0002` (v2 du protocole ; pris en charge par `ProtocolV2::decode()`, tandis que `PktLine::decode()` le rejette)

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

## Découverte des refs

La classe `RefDiscovery` analyse l’annonce des refs du serveur.

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

### Format de l’annonce des refs

La première ligne de l’annonce inclut les fonctionnalités :

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

## Fonctionnalités

La classe `Capability` analyse et gère les fonctionnalités du protocole.

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

### Fonctionnalités courantes

| Fonctionnalité | Signification |
|-----------|---------|
| `multi_ack` | Le serveur prend en charge la négociation multi-ack |
| `thin-pack` | Le serveur envoie des packs minces (les objets de base peuvent être omis). Pitmaster le demande, mais ne peut pas encore indexer les packs minces ([#16](https://github.com/inline0/pitmaster/issues/16)) |
| `side-band` | Le serveur utilise side-band pour les données du pack (limitées à 1000 octets) |
| `side-band-64k` | Le serveur utilise side-band 64K pour les données du pack |
| `ofs-delta` | Le pack peut utiliser des objets OFS_DELTA |
| `shallow` | Le serveur prend en charge le clonage superficiel |
| `no-done` | Le serveur n’exige pas de ligne « done » |
| `symref=HEAD:refs/heads/main` | Cible symbolique de HEAD |
| `agent=git/2.45.0` | Version de git du serveur |

## Démultiplexage side-band

Lorsque le serveur utilise side-band, les données du pack sont multiplexées avec les messages de progression et d’erreur. Le premier octet de la charge utile de chaque pkt-line indique le canal :

| Canal | Signification |
|---------|---------|
| 1 | Données du pack |
| 2 | Messages de progression (stderr) |
| 3 | Messages d’erreur fatale |

Le `UploadPackClient` de Pitmaster supprime les octets de canal et reconstitue les données du pack.

## Versions du protocole

### v1 (par défaut)

Le protocole traditionnel. La découverte des refs envoie toutes les refs en une seule fois. Pitmaster négocie en un seul tour : il envoie ses wants, chaque pointe de ref locale comme `have`, puis `done`.

### v2

Un protocole plus simple et plus performant.

Principales différences par rapport à v1 :
- La découverte des refs est une commande `ls-refs` distincte (elle peut filtrer les refs)
- Fetch est une commande `fetch` unique avec des paramètres structurés
- Utilise des paquets délimiteurs (`0001`) pour séparer les sections

`clone()` et `fetch()` utilisent toujours v1. v2 est disponible lorsque vous l’appelez explicitement :

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

## Flux de requête HTTP

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

## Gestion des erreurs

Les erreurs réseau déclenchent `ProtocolException`. Si l’échange smart HTTP de `clone()` ou `fetch()` échoue, Pitmaster réessaie via dumb HTTP et relance l’erreur d’origine si cette tentative échoue aussi. Les autres échecs (remote inconnu, handle sans processus, pack impossible à indexer) déclenchent d’autres types d’exception :

```php
use Pitmaster\Exceptions\ProtocolException;

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

Erreurs courantes :
- Serveur inaccessible (délai d’expiration)
- Authentification requise ou dépôt introuvable (signalé par `Unexpected HTTP status 401`, `403` ou `404`)
- `Content-Type` inattendu dans la réponse
- Données pkt-line non valides
- Message du canal d’erreur side-band
