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
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
$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:
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)
$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:
use Pitmaster\Protocol\UploadPackClient;
$client = new UploadPackClient($http);
$packData = $client->fetch($url, $wants, $haves); // ObjectId[] wants and haves
Primirea pachetului (push)
$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.
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 deProtocolV2::decode(), în timp cePktLine::decode()îl respinge)
PktLine::MAX_PAYLOAD; // 65516 (65520 - 4)
Descoperirea referințelor
Clasa RefDiscovery analizează anunțul de referințe al serverului.
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:
<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.
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-refsseparată (poate filtra referințele) - Fetch este o singură comandă
fetchcu 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:
$discovery = $http->discoverRefsV2($url); // ls-refs
$packData = (new UploadPackClient($http))->fetchV2($url, $wants, $haves);
Fluxul cererilor HTTP
Clone/Fetch
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
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:
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,403sau404) Content-Typeneașteptat în răspuns- Date pkt-line nevalide
- Mesaj pe canalul de eroare side-band