
# Išmanusis HTTP

Pitmaster palaiko ryšį su git serveriais per HTTPS, naudodamas išmaniojo HTTP protokolą. Tai pagrindinis tinklo transportas. HTTP užklausoms jis naudoja savąsias PHP srauto funkcijas (`fopen` su srauto kontekstu), todėl turi būti įjungtas `allow_url_fopen`.

## SmartHttpClient

```php
use Pitmaster\Protocol\SmartHttpClient;

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

Numatytoji skirtojo laiko reikšmė yra konstanta `PITMASTER_HTTP_TIMEOUT` (sveikasis skaičius) arba 30 sekundžių, jei ji neapibrėžta.

### Aptikti nuorodas

```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()` tą patį atlieka su `git-receive-pack`. Nuorodų aptikimo užklausa siunčiama į:
```
GET <url>/info/refs?service=git-upload-pack
```

Atsakymas yra pkt-line koduotas nuorodų sąrašas, kurio pirmoje eilutėje pateikiamos galimybės.

### Įkelti paketą (gauti)

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

Siunčia POST užklausą į `<url>/git-upload-pack` su norimų ir turimų objektų suderinimo turiniu. Grąžina neapdorotą atsakymo turinį (pkt-lines, `NAK` ir šalutinės juostos duomenis). `UploadPackClient` sukuria užklausą ir ištraukia paketą:

```php
use Pitmaster\Protocol\UploadPackClient;

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

### Gauti paketą (siųsti)

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

Siunčia POST užklausą į `<url>/git-receive-pack` su nuorodų atnaujinimo komandomis ir paketo duomenimis.

## PktLine kodavimas

git protokolas visam ryšiui naudoja pkt-line kadravimą. Prieš kiekvieną eilutę pateikiamas jos bendras ilgis (įskaitant 4 baitų ilgio prefiksą), užkoduotas kaip 4 šešioliktainiai skaitmenys.

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

### Ilgio taisyklės

- Minimali duomenų eilutė: `0005` (1 baito naudingoji apkrova)
- Maksimali duomenų eilutė: `fff0` (65516 baitų naudingoji apkrova)
- Išvalymo paketas: `0000`
- Skyriklio paketas: `0001`
- Atsakymo pabaiga: `0002` (protokolo v2; apdoroja `ProtocolV2::decode()`, o `PktLine::decode()` jį atmeta)

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

## Nuorodų aptikimas

`RefDiscovery` klasė analizuoja serverio paskelbtas nuorodas.

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

### Paskelbtų nuorodų formatas

Pirmoje paskelbimo eilutėje pateikiamos galimybės:

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

## Galimybės

`Capability` klasė analizuoja ir tvarko protokolo galimybes.

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

### Dažniausios galimybės

| Galimybė | Reikšmė |
|-----------|---------|
| `multi_ack` | Serveris palaiko kelių patvirtinimų suderinimą |
| `thin-pack` | Serveris siunčia plonus paketus (bazinių objektų gali nebūti). Pitmaster jų prašo, bet kol kas negali indeksuoti plonų paketų ([#16](https://github.com/inline0/pitmaster/issues/16)) |
| `side-band` | Serveris paketų duomenims naudoja šalutinę juostą (iki 1000 baitų) |
| `side-band-64k` | Serveris paketų duomenims naudoja 64K šalutinę juostą |
| `ofs-delta` | Pakete gali būti OFS_DELTA objektų |
| `shallow` | Serveris palaiko negilųjį klonavimą |
| `no-done` | Serveris nereikalauja „done“ eilutės |
| `symref=HEAD:refs/heads/main` | Simbolinis HEAD tikslas |
| `agent=git/2.45.0` | Serverio git versija |

## Šalutinės juostos demultipleksavimas

Kai serveris naudoja šalutinę juostą, paketo duomenys perduodami kartu su eigos ir klaidų pranešimais. Pirmasis kiekvienos pkt-line naudingosios apkrovos baitas nurodo kanalą:

| Kanalas | Reikšmė |
|---------|---------|
| 1 | Paketo duomenys |
| 2 | Eigos pranešimai (stderr) |
| 3 | Kritinių klaidų pranešimai |

Pitmaster `UploadPackClient` pašalina kanalų baitus ir iš naujo surenka paketo duomenis.

## Protokolo versijos

### v1 (numatytoji)

Tradicinis protokolas. Aptinkant nuorodas, visos nuorodos siunčiamos iš karto. Pitmaster suderina vienu etapu: siunčia norimus objektus, kiekvienos vietinės nuorodos viršūnę kaip `have` ir `done`.

### v2

Paprastesnis ir našesnis protokolas.

Pagrindiniai skirtumai nuo v1:
- Nuorodų aptikimas yra atskira komanda `ls-refs` (galima filtruoti nuorodas)
- Gavimas vykdomas viena komanda `fetch` su struktūrizuotais parametrais
- Skyriklio paketais (`0001`) atskiriamos skiltys

`clone()` ir `fetch()` visada naudoja v1. v2 prieinamas, kai jį iškviečiate aiškiai:

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

## HTTP užklausos eiga

### Klonavimas / gavimas

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

### Siuntimas

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

## Klaidų tvarkymas

Dėl tinklo klaidų išmetama `ProtocolException`. Jei išmaniojo HTTP apsikeitimas duomenimis vykdant `clone()` arba `fetch()` nepavyksta, Pitmaster pakartotinai bando naudoti paprastąjį HTTP ir, jei nepavyksta ir tada, išmeta pradinę klaidą. Dėl kitų nesėkmių (nežinomos nuotolinės saugyklos, procesų nepalaikančio deskriptoriaus, negalimo indeksuoti paketo) išmetamos kitokio tipo išimtys:

```php
use Pitmaster\Exceptions\ProtocolException;

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

Dažnos klaidos:
- Serveris nepasiekiamas (skirtojo laiko pabaiga)
- Būtinas autentifikavimas arba saugykla nerasta (pranešama kaip `Unexpected HTTP status 401`, `403` arba `404`)
- Atsakyme netikėta `Content-Type`
- Netinkami pkt-line duomenys
- Šalutinės juostos klaidos kanalo pranešimas
