Treci la conținut

Smart HTTP

Pitmaster comunică cu serverele git prin HTTPS, folosind protocolul smart HTTP. Acesta este transportul de rețea principal. Folosește funcțiile native de flux ale PHP (fopen cu un context de flux) pentru cereri HTTP, așadar allow_url_fopen trebuie să fie activat.

SmartHttpClient

PHP
use Pitmaster\Protocol\SmartHttpClient;

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

Valoarea implicită a timeoutului este constanta PITMASTER_HTTP_TIMEOUT (un număr întreg) sau 30 de secunde dacă aceasta nu este definită.

Descoperirea referințelor

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() face același lucru pentru git-receive-pack. Cererea de descoperire a referințelor este trimisă la:

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

Răspunsul este o listă de referințe codificată pkt-line, cu capabilitățile pe prima linie.

Încărcarea pachetului (fetch)

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

Trimite un POST la <url>/git-upload-pack cu corpul de negociere want/have. Returnează corpul brut al răspunsului (pkt-lines, NAK și date side-band). UploadPackClient construiește cererea și extrage pachetul:

PHP
use Pitmaster\Protocol\UploadPackClient;

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

Primirea pachetului (push)

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

Trimite un POST la <url>/git-receive-pack cu comenzile de actualizare a referințelor și datele pachetului.

Codificarea PktLine

Protocolul git folosește încadrarea pkt-line pentru toate comunicațiile. Fiecare linie este prefixată cu lungimea totală (inclusiv prefixul de lungime de 4 octeți), codificată ca 4 cifre hexazecimale.

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)

Reguli privind lungimea

  • Linia minimă de date: 0005 (1 octet de conținut)
  • Linia maximă de date: fff0 (65516 octeți de conținut)
  • Pachet flush: 0000
  • Pachet delimitator: 0001
  • Sfârșit de răspuns: 0002 (protocol v2; gestionat de ProtocolV2::decode(), în timp ce PktLine::decode() îl respinge)
PHP
PktLine::MAX_PAYLOAD;  // 65516 (65520 - 4)

Descoperirea referințelor

Clasa RefDiscovery analizează anunțul de referințe al serverului.

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

Formatul anunțului de referințe

Prima linie a anunțului include capabilitățile:

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

Capabilități

Clasa Capability analizează și gestionează capabilitățile protocolului.

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

Capabilități comune

Capabilitate Semnificație
multi_ack Serverul acceptă negocierea multi-ack
thin-pack Serverul trimite pachete subțiri (obiectele de bază pot fi omise). Pitmaster o solicită, dar încă nu poate indexa pachete subțiri (#16)
side-band Serverul folosește side-band pentru datele pachetului (limitat la 1000 octeți)
side-band-64k Serverul folosește side-band de 64K pentru datele pachetului
ofs-delta Pachetul poate folosi obiecte OFS_DELTA
shallow Serverul acceptă clone superficiale
no-done Serverul nu necesită o linie „done”
symref=HEAD:refs/heads/main Ținta simbolică a HEAD
agent=git/2.45.0 Versiunea git a serverului

Demultiplexarea side-band

Când serverul folosește side-band, datele pachetului sunt multiplexate cu mesajele de progres și de eroare. Primul octet din conținutul fiecărei linii pkt-line indică canalul:

Canal Semnificație
1 Datele pachetului
2 Mesaje de progres (stderr)
3 Mesaje de eroare fatală

Pitmaster al lui UploadPackClient elimină octeții canalului și reasamblează datele pachetului.

Versiuni de protocol

v1 (implicit)

Protocolul tradițional. Descoperirea referințelor trimite toate referințele simultan. Pitmaster negociază într-o singură rundă: trimite cererile sale, fiecare vârf de referință locală ca have și done.

v2

Un protocol mai simplu, cu performanțe mai bune.

Diferențe esențiale față de v1:

  • Descoperirea referințelor este o comandă ls-refs separată (poate filtra referințele)
  • Fetch este o singură comandă fetch cu parametri structurați
  • Folosește pachete delimitatoare (0001) pentru a separa secțiunile

clone() și fetch() folosesc întotdeauna v1. v2 este disponibil când îl apelați explicit:

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

Fluxul cererilor HTTP

Clone/Fetch

Cod
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

Cod
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

Tratarea erorilor

Erorile de rețea generează ProtocolException. Când schimbul smart HTTP pentru clone() sau fetch() eșuează, Pitmaster reîncearcă prin dumb HTTP și generează din nou eroarea inițială dacă și încercarea respectivă eșuează. Alte probleme (un server la distanță necunoscut, un handle fără proces, un pachet care nu poate fi indexat) generează alte tipuri de excepții:

PHP
use Pitmaster\Exceptions\ProtocolException;

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

Erori frecvente:

  • Server inaccesibil (timeout)
  • Este necesară autentificarea sau depozitul nu a fost găsit (raportat ca Unexpected HTTP status 401, 403 sau 404)
  • Content-Type neașteptat în răspuns
  • Date pkt-line nevalide
  • Mesaj pe canalul de eroare side-band