Saltar al contenido

Smart HTTP

Pitmaster se comunica con servidores git mediante HTTPS usando el protocolo smart HTTP. Este es el transporte de red principal. Usa las funciones nativas de flujo de PHP (fopen con un contexto de flujo) para las solicitudes HTTP, por lo que debe estar habilitado allow_url_fopen.

SmartHttpClient

PHP
use Pitmaster\Protocol\SmartHttpClient;

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

El tiempo de espera predeterminado es la constante PITMASTER_HTTP_TIMEOUT (un entero) o 30 segundos si no está definida.

Descubrir 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() hace lo mismo para git-receive-pack. La solicitud de descubrimiento de refs se envía a:

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

La respuesta es una lista de refs codificada en pkt-line, con las capacidades en la primera línea.

Enviar pack (fetch)

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

Envía un POST a <url>/git-upload-pack con el cuerpo de negociación want/have. Devuelve el cuerpo de respuesta sin procesar (pkt-lines, NAK y datos de banda lateral). UploadPackClient construye la solicitud y extrae el pack:

PHP
use Pitmaster\Protocol\UploadPackClient;

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

Recibir pack (push)

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

Envía un POST a <url>/git-receive-pack con los comandos de actualización de refs y los datos del pack.

Codificación PktLine

El protocolo git usa el encuadre pkt-line para todas las comunicaciones. Cada línea lleva como prefijo su longitud total (incluido el prefijo de longitud de 4 bytes), codificada como 4 dígitos hexadecimales.

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)

Reglas de longitud

  • Línea de datos mínima: 0005 (carga útil de 1 byte)
  • Línea de datos máxima: fff0 (carga útil de 65516 bytes)
  • Paquete de vaciado: 0000
  • Paquete delimitador: 0001
  • Fin de respuesta: 0002 (protocolo v2; lo gestiona ProtocolV2::decode(), mientras que PktLine::decode() lo rechaza)
PHP
PktLine::MAX_PAYLOAD;  // 65516 (65520 - 4)

Descubrimiento de refs

La clase RefDiscovery analiza el anuncio de refs del 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 del anuncio de refs

La primera línea del anuncio incluye las capacidades:

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

Capacidades

La clase Capability analiza y gestiona las capacidades del 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']

Capacidades comunes

Capacidad Significado
multi_ack El servidor admite la negociación multi-ack
thin-pack El servidor envía packs delgados (pueden omitirse objetos base). Pitmaster lo solicita, pero aún no puede indexar packs delgados (#16)
side-band El servidor usa banda lateral para los datos del pack (límite de 1000 bytes)
side-band-64k El servidor usa banda lateral de 64K para los datos del pack
ofs-delta El pack puede usar objetos OFS_DELTA
shallow El servidor admite clones superficiales
no-done El servidor no requiere una línea «done»
symref=HEAD:refs/heads/main Destino simbólico de HEAD
agent=git/2.45.0 Versión de git del servidor

Demultiplexación de banda lateral

Cuando el servidor usa banda lateral, los datos del pack se multiplexan con mensajes de progreso y error. El primer byte de la carga útil de cada pkt-line indica el canal:

Canal Significado
1 Datos del pack
2 Mensajes de progreso (stderr)
3 Mensajes de error fatal

El UploadPackClient de Pitmaster elimina los bytes de canal y vuelve a ensamblar los datos del pack.

Versiones del protocolo

v1 (predeterminada)

El protocolo tradicional. El descubrimiento de refs envía todas las refs a la vez. Pitmaster negocia en una sola ronda: envía sus wants, cada punta de ref local como have y done.

v2

Un protocolo más sencillo y de mejor rendimiento.

Diferencias clave respecto a v1:

  • El descubrimiento de refs es un comando ls-refs independiente (puede filtrar refs)
  • Fetch es un único comando fetch con parámetros estructurados
  • Usa paquetes delimitadores (0001) para separar secciones

clone() y fetch() siempre usan v1. v2 está disponible si se invoca explícitamente:

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

Flujo de solicitud HTTP

Clonar/obtener (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)

Enviar (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

Gestión de errores

Los errores de red generan ProtocolException. Si falla el intercambio smart HTTP de clone() o fetch(), Pitmaster vuelve a intentarlo mediante dumb HTTP y vuelve a lanzar el error original si también falla. Otros fallos (remoto desconocido, identificador sin proceso, pack que no se puede indexar) generan otros tipos de excepción:

PHP
use Pitmaster\Exceptions\ProtocolException;

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

Errores comunes:

  • Servidor inaccesible (tiempo de espera agotado)
  • Se requiere autenticación o no se encuentra el repositorio (se informa como Unexpected HTTP status 401, 403 o 404)
  • Content-Type inesperado en la respuesta
  • Datos pkt-line no válidos
  • Mensaje del canal de error de banda lateral