Smart HTTP
Pitmaster comunica con i server git tramite HTTPS usando il protocollo smart HTTP. È il trasporto di rete principale. Usa le funzioni native di stream di PHP (fopen con un contesto stream) per le richieste HTTP, quindi allow_url_fopen deve essere abilitato.
SmartHttpClient
use Pitmaster\Protocol\SmartHttpClient;
$http = new SmartHttpClient();
// Or with custom timeout:
$http = new SmartHttpClient(timeout: 60);
Il timeout predefinito è la costante PITMASTER_HTTP_TIMEOUT (un intero), oppure 30 secondi se non è definita.
Rilevare i riferimenti
$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() fa lo stesso per git-receive-pack. La richiesta di rilevamento dei riferimenti viene inviata a:
GET <url>/info/refs?service=git-upload-pack
La risposta è un elenco di riferimenti codificato in pkt-line, con le funzionalità sulla prima riga.
Upload pack (fetch)
$response = $http->uploadPack($url, $requestBody);
Invia una POST a <url>/git-upload-pack con il corpo della negoziazione want/have. Restituisce il corpo grezzo della risposta (righe pkt-line, NAK e dati side-band). UploadPackClient crea la richiesta ed estrae il 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);
Invia una POST a <url>/git-receive-pack con i comandi di aggiornamento dei riferimenti e i dati del pack.
Codifica PktLine
Il protocollo git usa la struttura pkt-line per tutte le comunicazioni. Ogni riga è preceduta dalla sua lunghezza totale (compreso il prefisso di 4 byte), codificata con 4 cifre esadecimali.
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)
Regole di lunghezza
- Riga dati minima:
0005(payload di 1 byte) - Riga dati massima:
fff0(payload di 65516 byte) - Pacchetto di flush:
0000 - Pacchetto delimitatore:
0001 - Fine risposta:
0002(protocollo v2; gestita daProtocolV2::decode(), mentrePktLine::decode()la rifiuta)
PktLine::MAX_PAYLOAD; // 65516 (65520 - 4)
Rilevamento dei riferimenti
La classe RefDiscovery analizza l'annuncio dei riferimenti del server.
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
Formato dell'annuncio dei riferimenti
La prima riga dell'annuncio include le funzionalità:
<hash> HEAD\0<capability list>\n
<hash> refs/heads/main\n
<hash> refs/tags/v1.0\n
0000
Funzionalità
La classe Capability analizza e gestisce le funzionalità del protocollo.
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']
Funzionalità comuni
| Funzionalità | Significato |
|---|---|
multi_ack |
Il server supporta la negoziazione multi-ack |
thin-pack |
Il server invia pack sottili (gli oggetti di base possono essere omessi). Pitmaster la richiede, ma al momento non può indicizzare pack sottili (#16) |
side-band |
Il server usa side-band per i dati del pack (limite di 1000 byte) |
side-band-64k |
Il server usa side-band a 64K per i dati del pack |
ofs-delta |
Il pack può contenere oggetti OFS_DELTA |
shallow |
Il server supporta i cloni superficiali |
no-done |
Il server non richiede una riga «done» |
symref=HEAD:refs/heads/main |
Riferimento simbolico di destinazione di HEAD |
agent=git/2.45.0 |
Versione di Git del server |
Demultiplexing side-band
Quando il server usa side-band, i dati del pack sono multiplexati insieme ai messaggi di avanzamento e di errore. Il primo byte del payload di ogni riga pkt-line indica il canale:
| Canale | Significato |
|---|---|
| 1 | Dati del pack |
| 2 | Messaggi di avanzamento (stderr) |
| 3 | Messaggi di errore fatale |
Il UploadPackClient di Pitmaster rimuove i byte del canale e ricompone i dati del pack.
Versioni del protocollo
v1 (predefinito)
Il protocollo tradizionale. Il rilevamento dei riferimenti invia tutti i riferimenti in una volta. Pitmaster negozia in un unico passaggio: invia i propri want, ogni estremità dei riferimenti locali come have e done.
v2
Un protocollo più semplice e dalle prestazioni migliori.
Differenze principali rispetto a v1:
- Il rilevamento dei riferimenti è un comando
ls-refsseparato (può filtrare i riferimenti) - Il fetch è un unico comando
fetchcon parametri strutturati - Usa pacchetti delimitatori (
0001) per separare le sezioni
clone() e fetch() usano sempre v1. v2 è disponibile se lo richiamate esplicitamente:
$discovery = $http->discoverRefsV2($url); // ls-refs
$packData = (new UploadPackClient($http))->fetchV2($url, $wants, $haves);
Flusso delle richieste 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
Gestione degli errori
Gli errori di rete generano ProtocolException. Se lo scambio smart HTTP di clone() o fetch() fallisce, Pitmaster riprova con HTTP non smart e, in caso di un ulteriore fallimento, genera nuovamente l'errore originale. Gli altri errori (remote sconosciuto, handle senza processi, pack non indicizzabile) generano altri tipi di eccezione:
use Pitmaster\Exceptions\ProtocolException;
try {
$repo->fetch();
} catch (ProtocolException $e) {
echo "Network error: {$e->getMessage()}\n";
}
Errori comuni:
- Server non raggiungibile (timeout)
- Autenticazione richiesta o repository non trovato (segnalato come
Unexpected HTTP status 401,403o404) Content-Typeimprevisto nella risposta- Dati pkt-line non validi
- Messaggio sul canale di errore side-band