
# Smart HTTP

Pitmaster kommunikerer med git-servere over HTTPS ved hjælp af smart HTTP-protokollen. Dette er den primære netværkstransport. Den bruger PHPs native stream-funktioner (`fopen` med en stream-kontekst) til HTTP-anmodninger, så `allow_url_fopen` skal være aktiveret.

## SmartHttpClient

```php
use Pitmaster\Protocol\SmartHttpClient;

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

Timeouten er som standard konstanten `PITMASTER_HTTP_TIMEOUT` (et heltal) eller 30 sekunder, hvis den ikke er defineret.

### Find referencer

```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()` gør det samme for `git-receive-pack`. Referenceopdagelsesanmodningen sendes til:
```
GET <url>/info/refs?service=git-upload-pack
```

Svaret er en pkt-line-kodet liste over referencer med funktioner på den første linje.

### Upload af pakke (fetch)

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

Sender en POST til `<url>/git-upload-pack` med kroppen, der indeholder want/have-forhandlingen. Returnerer den rå svarbody (pkt-lines, `NAK` og side-band-data). `UploadPackClient` opbygger anmodningen og udtrækker pakken:

```php
use Pitmaster\Protocol\UploadPackClient;

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

### Modtagelse af pakke (push)

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

Sender en POST til `<url>/git-receive-pack` med kommandoerne til referenceopdatering og pakkedataene.

## PktLine-kodning

git-protokollen bruger pkt-line-indramning til al kommunikation. Hver linje indledes med dens samlede længde (inklusive det 4-byte lange længdepræfiks), kodet som 4 hexadecimale cifre.

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

### Regler for længde

- Minimum for datalinje: `0005` (1 byte data)
- Maksimum for datalinje: `fff0` (65516 bytes data)
- Flush-pakke: `0000`
- Afgrænsningspakke: `0001`
- Svarafslutning: `0002` (protokol v2; håndteres af `ProtocolV2::decode()`, mens `PktLine::decode()` afviser den)

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

## Referenceopdagelse

Klassen `RefDiscovery` fortolker serverens referenceannoncering.

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

### Format for referenceannoncering

Den første linje i annonceringen indeholder funktioner:

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

## Funktioner

Klassen `Capability` fortolker og administrerer protokolfunktioner.

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

### Almindelige funktioner

| Funktion | Betydning |
|-----------|---------|
| `multi_ack` | Serveren understøtter multi-ack-forhandling |
| `thin-pack` | Serveren sender tynde pakker (basisobjekter kan være udeladt). Pitmaster anmoder om det, men kan endnu ikke indeksere tynde pakker ([#16](https://github.com/inline0/pitmaster/issues/16)) |
| `side-band` | Serveren bruger side-band til pakkedata (begrænset til 1000 bytes) |
| `side-band-64k` | Serveren bruger 64K side-band til pakkedata |
| `ofs-delta` | Pakken kan bruge OFS_DELTA-objekter |
| `shallow` | Serveren understøtter overfladisk kloning |
| `no-done` | Serveren kræver ikke en "done"-linje |
| `symref=HEAD:refs/heads/main` | HEADs symbolske mål |
| `agent=git/2.45.0` | Serverens Git-version |

## Demultipleksning af side-band

Når serveren bruger side-band, multiplexes pakkedata med status- og fejlmeddelelser. Den første byte i hver pkt-line-payload angiver kanalen:

| Kanal | Betydning |
|---------|---------|
| 1 | Pakkedata |
| 2 | Statusmeddelelser (stderr) |
| 3 | Kritiske fejlmeddelelser |

Pitmasters `UploadPackClient` fjerner kanalbytes og samler pakkedataene igen.

## Protokolversioner

### v1 (standard)

Den traditionelle protokol. Referenceopdagelsen sender alle referencer på én gang. Pitmaster forhandler i én runde: den sender sine wants, alle lokale reference-spidser som `have` samt `done`.

### v2

En enklere protokol med bedre ydeevne.

Vigtige forskelle fra v1:
- Referenceopdagelse er en separat kommando af typen `ls-refs` (kan filtrere referencer)
- Fetch er én kommando af typen `fetch` med strukturerede parametre
- Bruger afgrænsningspakker (`0001`) til at adskille sektioner

`clone()` og `fetch()` bruger altid v1. v2 er tilgængelig, når du kalder den eksplicit:

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

## Anmodningsforløb for 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
```

## Fejlhåndtering

Netværksfejl kaster `ProtocolException`. Hvis udvekslingen via smart HTTP under `clone()` eller `fetch()` mislykkes, forsøger Pitmaster igen via dumb HTTP og kaster den oprindelige fejl igen, hvis det også mislykkes. Andre fejl (ukendt fjernlager, handle uden proces, pakke, der ikke kan indekseres) kaster andre undtagelsestyper:

```php
use Pitmaster\Exceptions\ProtocolException;

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

Almindelige fejl:
- Serveren kan ikke nås (timeout)
- Godkendelse påkrævet, eller arkivet blev ikke fundet (rapporteres som `Unexpected HTTP status 401`, `403` eller `404`)
- Uventet `Content-Type` i svaret
- Ugyldige pkt-line-data
- Fejlmeddelelse fra side-band-fejlkanalen
