Przejdź do treści

Smart HTTP

Pitmaster komunikuje się z serwerami git przez HTTPS, używając protokołu smart HTTP. To podstawowy transport sieciowy. Korzysta z natywnych funkcji strumieni PHP (fopen z kontekstem strumienia) do żądań HTTP, dlatego allow_url_fopen musi być włączone.

SmartHttpClient

PHP
use Pitmaster\Protocol\SmartHttpClient;

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

Domyślny limit czasu to stała PITMASTER_HTTP_TIMEOUT (liczba całkowita), a jeśli nie jest zdefiniowana, wynosi 30 sekund.

Wykrywanie refów

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() wykonuje tę samą czynność dla git-receive-pack. Żądanie wykrycia refów jest wysyłane do:

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

Odpowiedź to zakodowana jako pkt-line lista refów, z możliwościami w pierwszym wierszu.

Wysyłanie packa (fetch)

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

Wysyła żądanie POST do <url>/git-upload-pack z treścią negocjacji want/have. Zwraca surową treść odpowiedzi (wiersze pkt-line, NAK i dane side-band). UploadPackClient buduje żądanie i wyodrębnia pack:

PHP
use Pitmaster\Protocol\UploadPackClient;

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

Odbieranie packa (push)

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

Wysyła żądanie POST do <url>/git-receive-pack z poleceniami aktualizacji refów i danymi packa.

Kodowanie PktLine

Protokół git używa ramkowania pkt-line do całej komunikacji. Każdy wiersz jest poprzedzony jego całkowitą długością (wraz z 4-bajtowym prefiksem długości), zakodowaną jako 4 cyfry szesnastkowe.

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)

Reguły długości

  • Minimalny wiersz danych: 0005 (ładunek 1 bajt)
  • Maksymalny wiersz danych: fff0 (ładunek 65516 bajtów)
  • Pakiet opróżniający: 0000
  • Pakiet separatora: 0001
  • Koniec odpowiedzi: 0002 (protokół v2; obsługiwany przez ProtocolV2::decode(), natomiast PktLine::decode() go odrzuca)
PHP
PktLine::MAX_PAYLOAD;  // 65516 (65520 - 4)

Wykrywanie refów

Klasa RefDiscovery analizuje ogłoszenie refów serwera.

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 ogłoszenia refów

Pierwszy wiersz ogłoszenia zawiera możliwości:

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

Możliwości

Klasa Capability analizuje możliwości protokołu i zarządza nimi.

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

Typowe możliwości

Możliwość Znaczenie
multi_ack Serwer obsługuje negocjacje multi-ack
thin-pack Serwer wysyła cienkie packi (mogą pomijać obiekty bazowe). Pitmaster żąda tej możliwości, ale nie potrafi jeszcze indeksować cienkich packów (#16)
side-band Serwer używa side-band dla danych packa (limit 1000 bajtów)
side-band-64k Serwer używa side-band 64K dla danych packa
ofs-delta Pack może używać obiektów OFS_DELTA
shallow Serwer obsługuje płytkie klonowanie
no-done Serwer nie wymaga wiersza „done”
symref=HEAD:refs/heads/main Symboliczny cel HEAD
agent=git/2.45.0 Wersja git serwera

Demultipleksowanie side-band

Gdy serwer używa side-band, dane packa są multipleksowane z komunikatami o postępie i błędach. Pierwszy bajt ładunku każdego wiersza pkt-line wskazuje kanał:

Kanał Znaczenie
1 Dane packa
2 Komunikaty o postępie (stderr)
3 Komunikaty o błędach krytycznych

PitmasterUploadPackClient usuwa bajty kanału i składa dane packa ponownie.

Wersje protokołu

v1 (domyślna)

Tradycyjny protokół. Wykrywanie refów przesyła wszystkie refy naraz. Pitmaster negocjuje w jednej rundzie: wysyła swoje wanty, każdy lokalny czubek refa jako have oraz done.

v2

Prostszy protokół o lepszej wydajności.

Najważniejsze różnice względem v1:

  • Wykrywanie refów to osobne polecenie ls-refs (może filtrować refy)
  • Fetch to pojedyncze polecenie fetch z uporządkowanymi parametrami
  • Używa pakietów separatora (0001) do oddzielania sekcji

clone() i fetch() zawsze używają v1. v2 jest dostępne, gdy wywołasz je jawnie:

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

Przebieg żądania HTTP

Klonowanie/pobieranie

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

Wysyłanie zmian

Kod
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

Obsługa błędów

Błędy sieciowe powodują wyjątek ProtocolException. Jeśli wymiana smart HTTP dla clone() lub fetch() nie powiedzie się, Pitmaster ponawia próbę przez dumb HTTP, a gdy ta również zawiedzie, ponownie zgłasza pierwotny błąd. Inne niepowodzenia (nieznany zdalny serwer, uchwyt bez procesu, pack, którego nie można indeksować) powodują inne typy wyjątków:

PHP
use Pitmaster\Exceptions\ProtocolException;

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

Typowe błędy:

  • Serwer nieosiągalny (przekroczono limit czasu)
  • Wymagane uwierzytelnienie lub nie znaleziono repozytorium (zgłaszane jako Unexpected HTTP status 401, 403 lub 404)
  • Nieoczekiwany Content-Type w odpowiedzi
  • Nieprawidłowe dane pkt-line
  • Komunikat z kanału błędów side-band