
# Smart HTTP

Pitmaster komunicira s strežniki git prek HTTPS s protokolom smart HTTP. To je primarni omrežni način prenosa. Uporablja izvorne pretočne funkcije PHP (`fopen` s kontekstom toka) za zahteve HTTP, zato mora biti omogočen `allow_url_fopen`.

## SmartHttpClient

```php
use Pitmaster\Protocol\SmartHttpClient;

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

Privzeta vrednost časovne omejitve je konstanta `PITMASTER_HTTP_TIMEOUT` (celo število) oziroma 30 sekund, če ni določena.

### Odkrivanje referenc

```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()` enako naredi za `git-receive-pack`. Zahteva za odkrivanje referenc se pošlje na:
```
GET <url>/info/refs?service=git-upload-pack
```

Odgovor je seznam referenc, kodiran v pkt-line, z zmožnostmi v prvi vrstici.

### Pošiljanje paketa (pridobivanje)

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

Pošlje zahtevo POST na `<url>/git-upload-pack` s telesom za dogovarjanje want/have. Vrne neobdelano telo odgovora (pkt-lines, `NAK` in podatke stranskega pasu). `UploadPackClient` sestavi zahtevo in izlušči paket:

```php
use Pitmaster\Protocol\UploadPackClient;

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

### Prejem paketa (potiskanje)

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

Pošlje zahtevo POST na `<url>/git-receive-pack` z ukazi za posodobitev referenc in podatki paketa.

## Kodiranje PktLine

Protokol git za vso komunikacijo uporablja okvirjanje pkt-line. Pred vsako vrstico je zapisana njena skupna dolžina (vključno s 4-bajtno predpono dolžine), kodirana kot 4 šestnajstiške števke.

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

### Pravila dolžine

- Najkrajša podatkovna vrstica: `0005` (1 bajt koristnega tovora)
- Najdaljša podatkovna vrstica: `fff0` (65516 bajtov koristnega tovora)
- Paket za izpraznitev: `0000`
- Ločilni paket: `0001`
- Konec odgovora: `0002` (protokol v2; obravnava ga `ProtocolV2::decode()`, medtem ko ga `PktLine::decode()` zavrne)

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

## Odkrivanje referenc

Razred `RefDiscovery` razčleni oglas referenc strežnika.

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

### Oblika oglasa referenc

Prva vrstica oglasa vključuje zmožnosti:

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

## Zmožnosti

Razred `Capability` razčleni in upravlja zmožnosti protokola.

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

### Pogoste zmožnosti

| Zmožnost | Pomen |
|-----------|---------|
| `multi_ack` | Strežnik podpira pogajanje multi-ack |
| `thin-pack` | Strežnik pošilja tanke pakete (osnovni objekti so lahko izpuščeni). Pitmaster to zahteva, vendar še ne more indeksirati tankih paketov ([#16](https://github.com/inline0/pitmaster/issues/16)) |
| `side-band` | Strežnik za podatke paketa uporablja stranski pas (omejen na 1000 bajtov) |
| `side-band-64k` | Strežnik za podatke paketa uporablja stranski pas 64K |
| `ofs-delta` | Paket lahko uporablja objekte OFS_DELTA |
| `shallow` | Strežnik podpira plitvo kloniranje |
| `no-done` | Strežnik ne zahteva vrstice »done« |
| `symref=HEAD:refs/heads/main` | Simbolični cilj HEAD |
| `agent=git/2.45.0` | Različica git strežnika |

## Demultipleksiranje stranskega pasu

Ko strežnik uporablja stranski pas, se podatki paketa multipleksirajo s sporočili o napredku in napakah. Prvi bajt koristnega tovora vsake vrstice pkt-line označuje kanal:

| Kanal | Pomen |
|---------|---------|
| 1 | Podatki paketa |
| 2 | Sporočila o napredku (stderr) |
| 3 | Usodna sporočila o napaki |

Pitmaster`UploadPackClient` odstrani bajte kanala in znova sestavi podatke paketa.

## Različice protokola

### v1 (privzeto)

Tradicionalni protokol. Pri odkrivanju referenc se vse reference pošljejo hkrati. Pitmaster se pogaja v enem krogu: pošlje svoje zahteve, vsak lokalni vrh reference kot `have` in `done`.

### v2

Preprostejši protokol z boljšo zmogljivostjo.

Glavne razlike v primerjavi z v1:
- Odkrivanje referenc je ločen ukaz `ls-refs` (reference je mogoče filtrirati)
- Pridobivanje je en sam ukaz `fetch` s strukturiranimi parametri
- Za ločevanje razdelkov uporablja ločilne pakete (`0001`)

`clone()` in `fetch()` vedno uporabljata v1. v2 je na voljo, ko ga izrecno pokličete:

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

## Potek zahteve HTTP

### Kloniranje/pridobivanje

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

### Potiskanje

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

## Obravnava napak

Omrežne napake sprožijo `ProtocolException`. Če izmenjava smart HTTP za `clone()` ali `fetch()` spodleti, Pitmaster poskusi znova prek dumb HTTP in ob ponovnem neuspehu znova sproži prvotno napako. Druge napake (neznan oddaljeni repozitorij, ročica brez procesa, paketa ni mogoče indeksirati) sprožijo druge vrste izjem:

```php
use Pitmaster\Exceptions\ProtocolException;

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

Pogoste napake:
- Strežnik ni dosegljiv (časovna omejitev)
- Zahtevano preverjanje pristnosti ali repozitorij ni bil najden (sporočeno kot `Unexpected HTTP status 401`, `403` ali `404`)
- Nepričakovan `Content-Type` v odgovoru
- Neveljavni podatki pkt-line
- Sporočilo o napaki v kanalu stranskega pasu
