Aller au contenu

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 à :

Code
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 :

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

Code
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

Code
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