Smart HTTP
Pitmaster komunikuje se servery git přes HTTPS pomocí chytrého protokolu HTTP. Jde o hlavní síťový transport. Pro požadavky HTTP využívá nativní streamovací funkce PHP (fopen s kontextem streamu), takže musí být povolené allow_url_fopen.
SmartHttpClient
use Pitmaster\Protocol\SmartHttpClient;
$http = new SmartHttpClient();
// Or with custom timeout:
$http = new SmartHttpClient(timeout: 60);
Výchozí hodnota časového limitu je konstanta PITMASTER_HTTP_TIMEOUT (celé číslo), případně 30 sekund, pokud není definována.
Zjištění referencí
$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() provádí totéž pro git-receive-pack. Požadavek na zjištění referencí se odesílá na:
GET <url>/info/refs?service=git-upload-pack
Odpovědí je seznam referencí zakódovaný ve formátu pkt-line, přičemž první řádek obsahuje schopnosti.
Upload pack (fetch)
$response = $http->uploadPack($url, $requestBody);
Odesílá požadavek POST na <url>/git-upload-pack s tělem obsahujícím vyjednávání want/have. Vrací nezpracované tělo odpovědi (řádky pkt-line, NAK a data side-band). UploadPackClient sestaví požadavek a extrahuje pack:
use Pitmaster\Protocol\UploadPackClient;
$client = new UploadPackClient($http);
$packData = $client->fetch($url, $wants, $haves); // ObjectId[] wants and haves
Receive pack (push)
$response = $http->receivePack($url, $requestBody);
Odesílá požadavek POST na <url>/git-receive-pack s příkazy pro aktualizaci referencí a daty packu.
Kódování PktLine
Protokol git používá pro veškerou komunikaci rámcování pkt-line. Každému řádku předchází jeho celková délka (včetně čtyřbajtové předpony délky) zapsaná jako 4 hexadecimální číslice.
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)
Pravidla délky
- Minimální datový řádek:
0005(datová část o 1 bajtu) - Maximální datový řádek:
fff0(datová část o 65516 bajtech) - Paket flush:
0000 - Paket oddělovače:
0001 - Konec odpovědi:
0002(protokol v2; zpracovává hoProtocolV2::decode(), zatímcoPktLine::decode()ho odmítá)
PktLine::MAX_PAYLOAD; // 65516 (65520 - 4)
Zjišťování referencí
Třída RefDiscovery analyzuje inzerci referencí serveru.
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
Formát inzerce referencí
První řádek inzerce obsahuje schopnosti:
<hash> HEAD\0<capability list>\n
<hash> refs/heads/main\n
<hash> refs/tags/v1.0\n
0000
Schopnosti
Třída Capability analyzuje a spravuje schopnosti protokolu.
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']
Běžné schopnosti
| Schopnost | Význam |
|---|---|
multi_ack |
Server podporuje vyjednávání multi-ack |
thin-pack |
Server odesílá tenké packy (základní objekty mohou chybět). Pitmaster o to žádá, ale zatím nedokáže indexovat tenké packy (#16) |
side-band |
Server používá side-band pro data packu (omezeno na 1000 bajtů) |
side-band-64k |
Server používá side-band 64K pro data packu |
ofs-delta |
Pack může používat objekty OFS_DELTA |
shallow |
Server podporuje mělké klonování |
no-done |
Server nevyžaduje řádek „done“ |
symref=HEAD:refs/heads/main |
Symbolický cíl HEAD |
agent=git/2.45.0 |
Verze Git na serveru |
Demultiplexování Side-band
Když server používá side-band, data packu se multiplexují se zprávami o průběhu a chybách. První bajt datové části každého řádku pkt-line označuje kanál:
| Kanál | Význam |
|---|---|
| 1 | Data packu |
| 2 | Zprávy o průběhu (stderr) |
| 3 | Zprávy o kritických chybách |
Demultiplexování UploadPackClient v Pitmaster odstraní bajty kanálu a znovu sestaví data packu.
Verze protokolu
v1 (výchozí)
Tradiční protokol. Při zjišťování referencí se odešlou všechny reference najednou. Pitmaster vyjednává v jednom kole: odešle své požadavky, každý místní vrchol reference jako have a done.
v2
Jednodušší protokol s vyšším výkonem.
Hlavní rozdíly oproti v1:
- Zjišťování referencí je samostatný příkaz
ls-refs(lze filtrovat reference) - Fetch je jediný příkaz
fetchse strukturovanými parametry - K oddělení sekcí se používají oddělovací pakety (
0001)
clone() a fetch() vždy používají v1. v2 je dostupný při výslovném zavolání:
$discovery = $http->discoverRefsV2($url); // ls-refs
$packData = (new UploadPackClient($http))->fetchV2($url, $wants, $haves);
Průběh požadavku HTTP
Klonování a 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
Zpracování chyb
Síťové chyby vyvolávají ProtocolException. Pokud výměna přes smart HTTP při clone() nebo fetch() selže, Pitmaster zkusí znovu použít hloupý HTTP a při dalším selhání znovu vyvolá původní chybu. Jiné chyby (neznámý vzdálený server, handle bez procesu, pack, který nelze indexovat) vyvolávají jiné typy výjimek:
use Pitmaster\Exceptions\ProtocolException;
try {
$repo->fetch();
} catch (ProtocolException $e) {
echo "Network error: {$e->getMessage()}\n";
}
Běžné chyby:
- Server není dostupný (vypršel časový limit)
- Je vyžadováno ověření nebo nebyl nalezen repozitář (hlášeno jako
Unexpected HTTP status 401,403nebo404) - Neočekávaný
Content-Typev odpovědi - Neplatná data pkt-line
- Zpráva o chybě z kanálu side-band