Zum Inhalt springen

Smart HTTP

Pitmaster kommuniziert über HTTPS mit git-Servern und verwendet dabei das Smart-HTTP-Protokoll. Dies ist der primäre Netzwerktransport. Für HTTP-Anfragen nutzt es die nativen Stream-Funktionen von PHP (fopen mit einem Stream-Kontext), daher muss allow_url_fopen aktiviert sein.

SmartHttpClient

PHP
use Pitmaster\Protocol\SmartHttpClient;

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

Das Timeout entspricht standardmäßig der Konstante PITMASTER_HTTP_TIMEOUT (einem Integer) oder 30 Sekunden, wenn sie nicht definiert ist.

Refs ermitteln

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() macht dasselbe für git-receive-pack. Die Anfrage zur Ref-Ermittlung geht an:

Code
GET <url>/info/refs?service=git-upload-pack

Die Antwort ist eine pkt-line-kodierte Ref-Liste mit Fähigkeiten in der ersten Zeile.

Upload pack (Abruf)

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

Sendet eine POST-Anfrage mit dem Want/Have-Aushandlungstext an <url>/git-upload-pack. Gibt den unverarbeiteten Antworttext zurück (pkt-lines, NAK und Side-Band-Daten). UploadPackClient erstellt die Anfrage und extrahiert das Pack:

PHP
use Pitmaster\Protocol\UploadPackClient;

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

Receive pack (Übertragung)

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

Sendet eine POST-Anfrage mit den Ref-Aktualisierungsbefehlen und Packdaten an <url>/git-receive-pack.

PktLine-Kodierung

Das Protokoll git nutzt für die gesamte Kommunikation pkt-line-Framing. Jeder Zeile wird ihre Gesamtlänge (einschließlich des 4-Byte-Längenpräfixes) vorangestellt, codiert als 4 hexadezimale Ziffern.

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)

Längenregeln

  • Minimale Datenzeile: 0005 (Nutzdaten von 1 Byte)
  • Maximale Datenzeile: fff0 (Nutzdaten von 65516 Bytes)
  • Flush-Paket: 0000
  • Trennpaket: 0001
  • Antwortende: 0002 (Protokoll v2; verarbeitet von ProtocolV2::decode(), während PktLine::decode() es ablehnt)
PHP
PktLine::MAX_PAYLOAD;  // 65516 (65520 - 4)

Ref-Ermittlung

Die Klasse RefDiscovery verarbeitet die Ref-Ankündigung des Servers.

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 der Ref-Ankündigung

Die erste Zeile der Ankündigung enthält Fähigkeiten:

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

Fähigkeiten

Die Klasse Capability verarbeitet und verwaltet Protokollfähigkeiten.

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

Gängige Fähigkeiten

Fähigkeit Bedeutung
multi_ack Server unterstützt Multi-Ack-Aushandlung
thin-pack Server sendet Thin Packs (Basisobjekte können fehlen). Pitmaster fordert sie an, kann Thin Packs aber noch nicht indizieren (#16)
side-band Server nutzt Side-Band für Packdaten (begrenzt auf 1000 Bytes)
side-band-64k Server nutzt 64K-Side-Band für Packdaten
ofs-delta Pack kann OFS_DELTA-Objekte verwenden
shallow Server unterstützt Shallow-Klone
no-done Server benötigt keine „done“-Zeile
symref=HEAD:refs/heads/main Symbolisches Ziel von HEAD
agent=git/2.45.0 Git-Version des Servers

Side-Band-Demultiplexing

Wenn der Server Side-Band verwendet, werden Packdaten mit Fortschritts- und Fehlermeldungen multiplexiert. Das erste Byte der Nutzdaten jeder pkt-line gibt den Kanal an:

Kanal Bedeutung
1 Packdaten
2 Fortschrittsmeldungen (stderr)
3 Schwerwiegende Fehlermeldungen

Pitmaster von UploadPackClient entfernt die Kanal-Bytes und setzt die Packdaten wieder zusammen.

Protokollversionen

v1 (Standard)

Das traditionelle Protokoll. Bei der Ref-Ermittlung werden alle Refs auf einmal gesendet. Pitmaster handelt in einer einzigen Runde aus: Es sendet seine Wants, jeden lokalen Ref-Tip als have und done.

v2

Ein einfacheres Protokoll mit besserer Leistung.

Wichtige Unterschiede zu v1:

  • Die Erkennung von Refs ist ein separater Befehl ls-refs (mit Filter für Refs)
  • Das Abrufen erfolgt mit einem einzigen Befehl fetch und strukturierten Parametern
  • Trennzeichenpakete (0001) trennen die Abschnitte

clone() und fetch() verwenden immer v1. v2 ist verfügbar, wenn du es ausdrücklich aufrufst:

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

HTTP-Anfrageablauf

Klonen/Abrufen

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

Fehlerbehandlung

Netzwerkfehler lösen ProtocolException aus. Wenn der Austausch von clone() oder fetch() über das intelligente HTTP fehlschlägt, versucht Pitmaster es erneut über das einfache HTTP und löst den ursprünglichen Fehler erneut aus, falls auch das fehlschlägt. Andere Fehler (ein unbekanntes Remote, ein Handle ohne Prozess, ein nicht indizierbares Pack) lösen andere Ausnahmetypen aus:

PHP
use Pitmaster\Exceptions\ProtocolException;

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

Häufige Fehler:

  • Server nicht erreichbar (Zeitüberschreitung)
  • Authentifizierung erforderlich oder Repository nicht gefunden (gemeldet als Unexpected HTTP status 401, 403 oder 404)
  • Unerwartetes Content-Type in der Antwort
  • Ungültige pkt-line-Daten
  • Fehlermeldung über den Sideband-Kanal