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
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
$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:
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)
$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:
use Pitmaster\Protocol\UploadPackClient;
$client = new UploadPackClient($http);
$packData = $client->fetch($url, $wants, $haves); // ObjectId[] wants and haves
Receive pack (Übertragung)
$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.
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 vonProtocolV2::decode(), währendPktLine::decode()es ablehnt)
PktLine::MAX_PAYLOAD; // 65516 (65520 - 4)
Ref-Ermittlung
Die Klasse RefDiscovery verarbeitet die Ref-Ankündigung des Servers.
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:
<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.
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
fetchund strukturierten Parametern - Trennzeichenpakete (
0001) trennen die Abschnitte
clone() und fetch() verwenden immer v1. v2 ist verfügbar, wenn du es ausdrücklich aufrufst:
$discovery = $http->discoverRefsV2($url); // ls-refs
$packData = (new UploadPackClient($http))->fetchV2($url, $wants, $haves);
HTTP-Anfrageablauf
Klonen/Abrufen
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
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:
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,403oder404) - Unerwartetes
Content-Typein der Antwort - Ungültige pkt-line-Daten
- Fehlermeldung über den Sideband-Kanal