
# Smart HTTP

Pitmaster komunikuje się z serwerami git przez HTTPS, używając protokołu smart HTTP. To podstawowy transport sieciowy. Korzysta z natywnych funkcji strumieni PHP (`fopen` z kontekstem strumienia) do żądań HTTP, dlatego `allow_url_fopen` musi być włączone.

## SmartHttpClient

```php
use Pitmaster\Protocol\SmartHttpClient;

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

Domyślny limit czasu to stała `PITMASTER_HTTP_TIMEOUT` (liczba całkowita), a jeśli nie jest zdefiniowana, wynosi 30 sekund.

### Wykrywanie refów

```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()` wykonuje tę samą czynność dla `git-receive-pack`. Żądanie wykrycia refów jest wysyłane do:
```
GET <url>/info/refs?service=git-upload-pack
```

Odpowiedź to zakodowana jako pkt-line lista refów, z możliwościami w pierwszym wierszu.

### Wysyłanie packa (fetch)

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

Wysyła żądanie POST do `<url>/git-upload-pack` z treścią negocjacji want/have. Zwraca surową treść odpowiedzi (wiersze pkt-line, `NAK` i dane side-band). `UploadPackClient` buduje żądanie i wyodrębnia pack:

```php
use Pitmaster\Protocol\UploadPackClient;

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

### Odbieranie packa (push)

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

Wysyła żądanie POST do `<url>/git-receive-pack` z poleceniami aktualizacji refów i danymi packa.

## Kodowanie PktLine

Protokół git używa ramkowania pkt-line do całej komunikacji. Każdy wiersz jest poprzedzony jego całkowitą długością (wraz z 4-bajtowym prefiksem długości), zakodowaną jako 4 cyfry szesnastkowe.

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

### Reguły długości

- Minimalny wiersz danych: `0005` (ładunek 1 bajt)
- Maksymalny wiersz danych: `fff0` (ładunek 65516 bajtów)
- Pakiet opróżniający: `0000`
- Pakiet separatora: `0001`
- Koniec odpowiedzi: `0002` (protokół v2; obsługiwany przez `ProtocolV2::decode()`, natomiast `PktLine::decode()` go odrzuca)

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

## Wykrywanie refów

Klasa `RefDiscovery` analizuje ogłoszenie refów serwera.

```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 ogłoszenia refów

Pierwszy wiersz ogłoszenia zawiera możliwości:

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

## Możliwości

Klasa `Capability` analizuje możliwości protokołu i zarządza nimi.

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

### Typowe możliwości

| Możliwość | Znaczenie |
|-----------|---------|
| `multi_ack` | Serwer obsługuje negocjacje multi-ack |
| `thin-pack` | Serwer wysyła cienkie packi (mogą pomijać obiekty bazowe). Pitmaster żąda tej możliwości, ale nie potrafi jeszcze indeksować cienkich packów ([#16](https://github.com/inline0/pitmaster/issues/16)) |
| `side-band` | Serwer używa side-band dla danych packa (limit 1000 bajtów) |
| `side-band-64k` | Serwer używa side-band 64K dla danych packa |
| `ofs-delta` | Pack może używać obiektów OFS_DELTA |
| `shallow` | Serwer obsługuje płytkie klonowanie |
| `no-done` | Serwer nie wymaga wiersza „done” |
| `symref=HEAD:refs/heads/main` | Symboliczny cel HEAD |
| `agent=git/2.45.0` | Wersja git serwera |

## Demultipleksowanie side-band

Gdy serwer używa side-band, dane packa są multipleksowane z komunikatami o postępie i błędach. Pierwszy bajt ładunku każdego wiersza pkt-line wskazuje kanał:

| Kanał | Znaczenie |
|---------|---------|
| 1 | Dane packa |
| 2 | Komunikaty o postępie (stderr) |
| 3 | Komunikaty o błędach krytycznych |

Pitmaster`UploadPackClient` usuwa bajty kanału i składa dane packa ponownie.

## Wersje protokołu

### v1 (domyślna)

Tradycyjny protokół. Wykrywanie refów przesyła wszystkie refy naraz. Pitmaster negocjuje w jednej rundzie: wysyła swoje wanty, każdy lokalny czubek refa jako `have` oraz `done`.

### v2

Prostszy protokół o lepszej wydajności.

Najważniejsze różnice względem v1:
- Wykrywanie refów to osobne polecenie `ls-refs` (może filtrować refy)
- Fetch to pojedyncze polecenie `fetch` z uporządkowanymi parametrami
- Używa pakietów separatora (`0001`) do oddzielania sekcji

`clone()` i `fetch()` zawsze używają v1. v2 jest dostępne, gdy wywołasz je jawnie:

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

## Przebieg żądania HTTP

### Klonowanie/pobieranie

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

### Wysyłanie zmian

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

## Obsługa błędów

Błędy sieciowe powodują wyjątek `ProtocolException`. Jeśli wymiana smart HTTP dla `clone()` lub `fetch()` nie powiedzie się, Pitmaster ponawia próbę przez dumb HTTP, a gdy ta również zawiedzie, ponownie zgłasza pierwotny błąd. Inne niepowodzenia (nieznany zdalny serwer, uchwyt bez procesu, pack, którego nie można indeksować) powodują inne typy wyjątków:

```php
use Pitmaster\Exceptions\ProtocolException;

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

Typowe błędy:
- Serwer nieosiągalny (przekroczono limit czasu)
- Wymagane uwierzytelnienie lub nie znaleziono repozytorium (zgłaszane jako `Unexpected HTTP status 401`, `403` lub `404`)
- Nieoczekiwany `Content-Type` w odpowiedzi
- Nieprawidłowe dane pkt-line
- Komunikat z kanału błędów side-band
