Pular para o conteúdo

Smart HTTP

Pitmaster se comunica com servidores git por HTTPS usando o protocolo smart HTTP. Este é o transporte de rede principal. Ele usa as funções nativas de stream de PHP (fopen com um contexto de stream) para solicitações HTTP, portanto allow_url_fopen precisa estar habilitado.

SmartHttpClient

PHP
use Pitmaster\Protocol\SmartHttpClient;

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

O tempo limite usa por padrão a constante PITMASTER_HTTP_TIMEOUT (um inteiro) ou 30 segundos quando ela não está definida.

Descobrir refs

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() faz o mesmo para git-receive-pack. A solicitação de descoberta de refs é enviada para:

Código
GET <url>/info/refs?service=git-upload-pack

A resposta é uma lista de refs codificada em pkt-line, com recursos na primeira linha.

Upload pack (fetch)

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

Envia um POST para <url>/git-upload-pack com o corpo da negociação want/have. Retorna o corpo bruto da resposta (pkt-lines, NAK e dados side-band). UploadPackClient cria a solicitação e extrai o 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);

Envia um POST para <url>/git-receive-pack com os comandos de atualização de refs e os dados do pack.

Codificação PktLine

O protocolo git usa enquadramento pkt-line em toda a comunicação. Cada linha tem como prefixo seu comprimento total (incluindo o prefixo de comprimento de 4 bytes), codificado como 4 dígitos hexadecimais.

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)

Regras de comprimento

  • Linha de dados mínima: 0005 (carga útil de 1 byte)
  • Linha de dados máxima: fff0 (carga útil de 65516 bytes)
  • Pacote de flush: 0000
  • Pacote delimitador: 0001
  • Fim da resposta: 0002 (protocolo v2; tratado por ProtocolV2::decode(), enquanto PktLine::decode() o rejeita)
PHP
PktLine::MAX_PAYLOAD;  // 65516 (65520 - 4)

Descoberta de refs

A classe RefDiscovery analisa o anúncio de refs do servidor.

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 do anúncio de refs

A primeira linha do anúncio inclui recursos:

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

Recursos

A classe Capability analisa e gerencia os recursos do protocolo.

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

Recursos comuns

Recurso Significado
multi_ack O servidor oferece suporte à negociação multi-ack
thin-pack O servidor envia packs finos (objetos-base podem ser omitidos). Pitmaster solicita esse recurso, mas ainda não pode indexar packs finos (#16)
side-band O servidor usa side-band para dados do pack (limitado a 1000 bytes)
side-band-64k O servidor usa side-band de 64K para dados do pack
ofs-delta O pack pode usar objetos OFS_DELTA
shallow O servidor oferece suporte a clone raso
no-done O servidor não exige uma linha "done"
symref=HEAD:refs/heads/main Destino simbólico de HEAD
agent=git/2.45.0 Versão git do servidor

Demultiplexação side-band

Quando o servidor usa side-band, os dados do pack são multiplexados com mensagens de progresso e de erro. O primeiro byte da carga útil de cada pkt-line indica o canal:

Canal Significado
1 Dados do pack
2 Mensagens de progresso (stderr)
3 Mensagens de erro fatal

Pitmaster de UploadPackClient remove os bytes do canal e remonta os dados do pack.

Versões do protocolo

v1 (padrão)

O protocolo tradicional. A descoberta de refs envia todas as refs de uma vez. Pitmaster negocia em uma única rodada: envia suas solicitações, cada ponta de ref local como have e done.

v2

Um protocolo mais simples e com melhor desempenho.

Principais diferenças em relação a v1:

  • A descoberta de refs é um comando ls-refs separado (pode filtrar refs)
  • Fetch é um único comando fetch com parâmetros estruturados
  • Usa pacotes delimitadores (0001) para separar seções

clone() e fetch() sempre usam v1. v2 está disponível quando chamado explicitamente:

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

Fluxo de solicitação HTTP

Clone/Fetch

Código
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

Código
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

Tratamento de erros

Erros de rede lançam ProtocolException. Quando a troca smart HTTP de clone() ou fetch() falha, Pitmaster tenta novamente usando dumb HTTP e relança o erro original se isso também falhar. Outras falhas (remoto desconhecido, handle sem processo, pack que não pode ser indexado) lançam outros tipos de exceção:

PHP
use Pitmaster\Exceptions\ProtocolException;

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

Erros comuns:

  • Servidor inacessível (tempo limite)
  • Autenticação necessária ou repositório não encontrado (reportado como Unexpected HTTP status 401, 403 ou 404)
  • Content-Type inesperado na resposta
  • Dados pkt-line inválidos
  • Mensagem de erro do canal side-band