
# Smart HTTP

Pitmaster communiceert met git-servers via HTTPS met het smart HTTP-protocol. Dit is het primaire netwerktransport. Het gebruikt de native streamfuncties van PHP (`fopen` met een streamcontext) voor HTTP-verzoeken, dus `allow_url_fopen` moet zijn ingeschakeld.

## SmartHttpClient

```php
use Pitmaster\Protocol\SmartHttpClient;

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

De time-out is standaard de constante `PITMASTER_HTTP_TIMEOUT` (een geheel getal), of 30 seconden als deze niet is gedefinieerd.

### Refs ontdekken

```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()` doet hetzelfde voor `git-receive-pack`. Het verzoek voor ref-discovery gaat naar:
```
GET <url>/info/refs?service=git-upload-pack
```

De reactie is een met pkt-line gecodeerde lijst met refs, met capabilities op de eerste regel.

### Upload pack (fetch)

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

Verstuurt een POST naar `<url>/git-upload-pack` met de body voor de want/have-onderhandeling. Geeft de ruwe responsebody terug (pkt-lines, `NAK` en side-band-data). `UploadPackClient` bouwt het verzoek op en haalt de pack eruit:

```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);
```

Verstuurt een POST naar `<url>/git-receive-pack` met de ref-updateopdrachten en pack-data.

## PktLine-codering

Het git-protocol gebruikt pkt-line-framing voor alle communicatie. Elke regel begint met de totale lengte (inclusief het voorvoegsel van 4 bytes), gecodeerd als 4 hexadecimale cijfers.

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

### Lengteregels

- Minimale gegevensregel: `0005` (payload van 1 byte)
- Maximale gegevensregel: `fff0` (payload van 65516 bytes)
- Flush-pakket: `0000`
- Scheidingstekenpakket: `0001`
- Einde van de response: `0002` (protocol v2; verwerkt door `ProtocolV2::decode()`, terwijl `PktLine::decode()` het weigert)

```php
PktLine::MAX_PAYLOAD;  // 65516 (65520 - 4)
```

## Ref-discovery

De klasse `RefDiscovery` parseert de ref-advertentie van de 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
```

### Indeling van de ref-advertentie

De eerste regel van de advertentie bevat capabilities:

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

## Capabilities

De klasse `Capability` parseert en beheert protocol-capabilities.

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

### Veelgebruikte capabilities

| Capability | Betekenis |
|-----------|-----------|
| `multi_ack` | De server ondersteunt multi-ack-onderhandeling |
| `thin-pack` | De server verstuurt thin packs (basisobjecten kunnen ontbreken). Pitmaster vraagt hierom, maar kan thin packs nog niet indexeren ([#16](https://github.com/inline0/pitmaster/issues/16)) |
| `side-band` | De server gebruikt side-band voor pack-data (beperkt tot 1000 bytes) |
| `side-band-64k` | De server gebruikt 64K side-band voor pack-data |
| `ofs-delta` | Pack kan OFS_DELTA-objecten gebruiken |
| `shallow` | De server ondersteunt shallow clone |
| `no-done` | De server vereist geen "done"-regel |
| `symref=HEAD:refs/heads/main` | Symbolisch doel van HEAD |
| `agent=git/2.45.0` | Git-versie van de server |

## Side-band-demultiplexing

Wanneer de server side-band gebruikt, wordt de pack-data gemultiplexed met voortgangs- en foutmeldingen. De eerste byte van de payload van elke pkt-line geeft het kanaal aan:

| Kanaal | Betekenis |
|---------|-----------|
| 1 | Pack-data |
| 2 | Voortgangsberichten (stderr) |
| 3 | Fatale foutmeldingen |

Pitmaster's `UploadPackClient` verwijdert de kanaalbytes en voegt de pack-data weer samen.

## Protocolversies

### v1 (standaard)

Het traditionele protocol. Ref-discovery verstuurt alle refs tegelijk. Pitmaster onderhandelt in één ronde: het verstuurt zijn wants, elke lokale ref-tip als een `have` en `done`.

### v2

Een eenvoudiger protocol met betere prestaties.

Belangrijkste verschillen met v1:
- Ref-discovery is een afzonderlijke opdracht `ls-refs` (kan refs filteren)
- Fetch is één opdracht `fetch` met gestructureerde parameters
- Gebruikt scheidingstekenpakketten (`0001`) om secties van elkaar te scheiden

`clone()` en `fetch()` gebruiken altijd v1. v2 is beschikbaar wanneer u het expliciet aanroept:

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

## Verzoekstroom van 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
```

## Foutafhandeling

Netwerkfouten gooien `ProtocolException`. Wanneer de smart HTTP-uitwisseling van `clone()` of `fetch()` mislukt, probeert Pitmaster het opnieuw via dumb HTTP en gooit het de oorspronkelijke fout opnieuw als ook dat mislukt. Andere fouten (een onbekende remote, een handle zonder proces, een pack die niet kan worden geïndexeerd) gooien andere uitzonderingstypen:

```php
use Pitmaster\Exceptions\ProtocolException;

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

Veelvoorkomende fouten:
- Server onbereikbaar (time-out)
- Authenticatie vereist of repository niet gevonden (gemeld als `Unexpected HTTP status 401`, `403` of `404`)
- Onverwachte `Content-Type` in de response
- Ongeldige pkt-line-data
- Foutmelding via side-band-kanaal
