Vai al contenuto

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

PHP
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

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() fa lo stesso per git-receive-pack. La richiesta di rilevamento dei riferimenti viene inviata a:

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

PHP
$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:

PHP
use Pitmaster\Protocol\UploadPackClient;

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

Receive pack (push)

PHP
$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.

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)

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 da ProtocolV2::decode(), mentre PktLine::decode() la rifiuta)
PHP
PktLine::MAX_PAYLOAD;  // 65516 (65520 - 4)

Rilevamento dei riferimenti

La classe RefDiscovery analizza l'annuncio dei riferimenti del server.

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

Formato dell'annuncio dei riferimenti

La prima riga dell'annuncio include le funzionalità:

Codice
<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.

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

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-refs separato (può filtrare i riferimenti)
  • Il fetch è un unico comando fetch con parametri strutturati
  • Usa pacchetti delimitatori (0001) per separare le sezioni

clone() e fetch() usano sempre v1. v2 è disponibile se lo richiamate esplicitamente:

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

Flusso delle richieste HTTP

Clone/Fetch

Codice
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

Codice
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:

PHP
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, 403 o 404)
  • Content-Type imprevisto nella risposta
  • Dati pkt-line non validi
  • Messaggio sul canale di errore side-band