
# Smart HTTP

Pitmaster kommuniziert über HTTPS mit git-Servern und verwendet dabei das Smart-HTTP-Protokoll. Dies ist der primäre Netzwerktransport. Für HTTP-Anfragen nutzt es die nativen Stream-Funktionen von PHP (`fopen` mit einem Stream-Kontext), daher muss `allow_url_fopen` aktiviert sein.

## SmartHttpClient

```php
use Pitmaster\Protocol\SmartHttpClient;

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

Das Timeout entspricht standardmäßig der Konstante `PITMASTER_HTTP_TIMEOUT` (einem Integer) oder 30 Sekunden, wenn sie nicht definiert ist.

### Refs ermitteln

```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()` macht dasselbe für `git-receive-pack`. Die Anfrage zur Ref-Ermittlung geht an:
```
GET <url>/info/refs?service=git-upload-pack
```

Die Antwort ist eine pkt-line-kodierte Ref-Liste mit Fähigkeiten in der ersten Zeile.

### Upload pack (Abruf)

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

Sendet eine POST-Anfrage mit dem Want/Have-Aushandlungstext an `<url>/git-upload-pack`. Gibt den unverarbeiteten Antworttext zurück (pkt-lines, `NAK` und Side-Band-Daten). `UploadPackClient` erstellt die Anfrage und extrahiert das Pack:

```php
use Pitmaster\Protocol\UploadPackClient;

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

### Receive pack (Übertragung)

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

Sendet eine POST-Anfrage mit den Ref-Aktualisierungsbefehlen und Packdaten an `<url>/git-receive-pack`.

## PktLine-Kodierung

Das Protokoll git nutzt für die gesamte Kommunikation pkt-line-Framing. Jeder Zeile wird ihre Gesamtlänge (einschließlich des 4-Byte-Längenpräfixes) vorangestellt, codiert als 4 hexadezimale Ziffern.

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

### Längenregeln

- Minimale Datenzeile: `0005` (Nutzdaten von 1 Byte)
- Maximale Datenzeile: `fff0` (Nutzdaten von 65516 Bytes)
- Flush-Paket: `0000`
- Trennpaket: `0001`
- Antwortende: `0002` (Protokoll v2; verarbeitet von `ProtocolV2::decode()`, während `PktLine::decode()` es ablehnt)

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

## Ref-Ermittlung

Die Klasse `RefDiscovery` verarbeitet die Ref-Ankündigung des Servers.

```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 der Ref-Ankündigung

Die erste Zeile der Ankündigung enthält Fähigkeiten:

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

## Fähigkeiten

Die Klasse `Capability` verarbeitet und verwaltet Protokollfähigkeiten.

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

### Gängige Fähigkeiten

| Fähigkeit | Bedeutung |
|-----------|---------|
| `multi_ack` | Server unterstützt Multi-Ack-Aushandlung |
| `thin-pack` | Server sendet Thin Packs (Basisobjekte können fehlen). Pitmaster fordert sie an, kann Thin Packs aber noch nicht indizieren ([#16](https://github.com/inline0/pitmaster/issues/16)) |
| `side-band` | Server nutzt Side-Band für Packdaten (begrenzt auf 1000 Bytes) |
| `side-band-64k` | Server nutzt 64K-Side-Band für Packdaten |
| `ofs-delta` | Pack kann OFS_DELTA-Objekte verwenden |
| `shallow` | Server unterstützt Shallow-Klone |
| `no-done` | Server benötigt keine „done“-Zeile |
| `symref=HEAD:refs/heads/main` | Symbolisches Ziel von HEAD |
| `agent=git/2.45.0` | Git-Version des Servers |

## Side-Band-Demultiplexing

Wenn der Server Side-Band verwendet, werden Packdaten mit Fortschritts- und Fehlermeldungen multiplexiert. Das erste Byte der Nutzdaten jeder pkt-line gibt den Kanal an:

| Kanal | Bedeutung |
|---------|---------|
| 1 | Packdaten |
| 2 | Fortschrittsmeldungen (stderr) |
| 3 | Schwerwiegende Fehlermeldungen |

Pitmaster von `UploadPackClient` entfernt die Kanal-Bytes und setzt die Packdaten wieder zusammen.

## Protokollversionen

### v1 (Standard)

Das traditionelle Protokoll. Bei der Ref-Ermittlung werden alle Refs auf einmal gesendet. Pitmaster handelt in einer einzigen Runde aus: Es sendet seine Wants, jeden lokalen Ref-Tip als `have` und `done`.

### v2

Ein einfacheres Protokoll mit besserer Leistung.

Wichtige Unterschiede zu v1:
- Die Erkennung von Refs ist ein separater Befehl `ls-refs` (mit Filter für Refs)
- Das Abrufen erfolgt mit einem einzigen Befehl `fetch` und strukturierten Parametern
- Trennzeichenpakete (`0001`) trennen die Abschnitte

`clone()` und `fetch()` verwenden immer v1. v2 ist verfügbar, wenn du es ausdrücklich aufrufst:

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

## HTTP-Anfrageablauf

### Klonen/Abrufen

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

## Fehlerbehandlung

Netzwerkfehler lösen `ProtocolException` aus. Wenn der Austausch von `clone()` oder `fetch()` über das intelligente HTTP fehlschlägt, versucht Pitmaster es erneut über das einfache HTTP und löst den ursprünglichen Fehler erneut aus, falls auch das fehlschlägt. Andere Fehler (ein unbekanntes Remote, ein Handle ohne Prozess, ein nicht indizierbares Pack) lösen andere Ausnahmetypen aus:

```php
use Pitmaster\Exceptions\ProtocolException;

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

Häufige Fehler:
- Server nicht erreichbar (Zeitüberschreitung)
- Authentifizierung erforderlich oder Repository nicht gefunden (gemeldet als `Unexpected HTTP status 401`, `403` oder `404`)
- Unerwartetes `Content-Type` in der Antwort
- Ungültige pkt-line-Daten
- Fehlermeldung über den Sideband-Kanal
