Pereiti prie turinio

Išmanusis HTTP

Pitmaster palaiko ryšį su git serveriais per HTTPS, naudodamas išmaniojo HTTP protokolą. Tai pagrindinis tinklo transportas. HTTP užklausoms jis naudoja savąsias PHP srauto funkcijas (fopen su srauto kontekstu), todėl turi būti įjungtas allow_url_fopen.

SmartHttpClient

PHP
use Pitmaster\Protocol\SmartHttpClient;

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

Numatytoji skirtojo laiko reikšmė yra konstanta PITMASTER_HTTP_TIMEOUT (sveikasis skaičius) arba 30 sekundžių, jei ji neapibrėžta.

Aptikti nuorodas

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() tą patį atlieka su git-receive-pack. Nuorodų aptikimo užklausa siunčiama į:

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

Atsakymas yra pkt-line koduotas nuorodų sąrašas, kurio pirmoje eilutėje pateikiamos galimybės.

Įkelti paketą (gauti)

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

Siunčia POST užklausą į <url>/git-upload-pack su norimų ir turimų objektų suderinimo turiniu. Grąžina neapdorotą atsakymo turinį (pkt-lines, NAK ir šalutinės juostos duomenis). UploadPackClient sukuria užklausą ir ištraukia paketą:

PHP
use Pitmaster\Protocol\UploadPackClient;

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

Gauti paketą (siųsti)

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

Siunčia POST užklausą į <url>/git-receive-pack su nuorodų atnaujinimo komandomis ir paketo duomenimis.

PktLine kodavimas

git protokolas visam ryšiui naudoja pkt-line kadravimą. Prieš kiekvieną eilutę pateikiamas jos bendras ilgis (įskaitant 4 baitų ilgio prefiksą), užkoduotas kaip 4 šešioliktainiai skaitmenys.

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)

Ilgio taisyklės

  • Minimali duomenų eilutė: 0005 (1 baito naudingoji apkrova)
  • Maksimali duomenų eilutė: fff0 (65516 baitų naudingoji apkrova)
  • Išvalymo paketas: 0000
  • Skyriklio paketas: 0001
  • Atsakymo pabaiga: 0002 (protokolo v2; apdoroja ProtocolV2::decode(), o PktLine::decode() jį atmeta)
PHP
PktLine::MAX_PAYLOAD;  // 65516 (65520 - 4)

Nuorodų aptikimas

RefDiscovery klasė analizuoja serverio paskelbtas nuorodas.

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

Paskelbtų nuorodų formatas

Pirmoje paskelbimo eilutėje pateikiamos galimybės:

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

Galimybės

Capability klasė analizuoja ir tvarko protokolo galimybes.

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

Dažniausios galimybės

Galimybė Reikšmė
multi_ack Serveris palaiko kelių patvirtinimų suderinimą
thin-pack Serveris siunčia plonus paketus (bazinių objektų gali nebūti). Pitmaster jų prašo, bet kol kas negali indeksuoti plonų paketų (#16)
side-band Serveris paketų duomenims naudoja šalutinę juostą (iki 1000 baitų)
side-band-64k Serveris paketų duomenims naudoja 64K šalutinę juostą
ofs-delta Pakete gali būti OFS_DELTA objektų
shallow Serveris palaiko negilųjį klonavimą
no-done Serveris nereikalauja „done“ eilutės
symref=HEAD:refs/heads/main Simbolinis HEAD tikslas
agent=git/2.45.0 Serverio git versija

Šalutinės juostos demultipleksavimas

Kai serveris naudoja šalutinę juostą, paketo duomenys perduodami kartu su eigos ir klaidų pranešimais. Pirmasis kiekvienos pkt-line naudingosios apkrovos baitas nurodo kanalą:

Kanalas Reikšmė
1 Paketo duomenys
2 Eigos pranešimai (stderr)
3 Kritinių klaidų pranešimai

Pitmaster UploadPackClient pašalina kanalų baitus ir iš naujo surenka paketo duomenis.

Protokolo versijos

v1 (numatytoji)

Tradicinis protokolas. Aptinkant nuorodas, visos nuorodos siunčiamos iš karto. Pitmaster suderina vienu etapu: siunčia norimus objektus, kiekvienos vietinės nuorodos viršūnę kaip have ir done.

v2

Paprastesnis ir našesnis protokolas.

Pagrindiniai skirtumai nuo v1:

  • Nuorodų aptikimas yra atskira komanda ls-refs (galima filtruoti nuorodas)
  • Gavimas vykdomas viena komanda fetch su struktūrizuotais parametrais
  • Skyriklio paketais (0001) atskiriamos skiltys

clone() ir fetch() visada naudoja v1. v2 prieinamas, kai jį iškviečiate aiškiai:

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

HTTP užklausos eiga

Klonavimas / gavimas

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

Siuntimas

Kodas
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

Klaidų tvarkymas

Dėl tinklo klaidų išmetama ProtocolException. Jei išmaniojo HTTP apsikeitimas duomenimis vykdant clone() arba fetch() nepavyksta, Pitmaster pakartotinai bando naudoti paprastąjį HTTP ir, jei nepavyksta ir tada, išmeta pradinę klaidą. Dėl kitų nesėkmių (nežinomos nuotolinės saugyklos, procesų nepalaikančio deskriptoriaus, negalimo indeksuoti paketo) išmetamos kitokio tipo išimtys:

PHP
use Pitmaster\Exceptions\ProtocolException;

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

Dažnos klaidos:

  • Serveris nepasiekiamas (skirtojo laiko pabaiga)
  • Būtinas autentifikavimas arba saugykla nerasta (pranešama kaip Unexpected HTTP status 401, 403 arba 404)
  • Atsakyme netikėta Content-Type
  • Netinkami pkt-line duomenys
  • Šalutinės juostos klaidos kanalo pranešimas