
# Розумний HTTP

Pitmaster обмінюється даними із серверами git через HTTPS, використовуючи протокол smart HTTP. Це основний мережевий транспорт. Він використовує власні потокові функції PHP (`fopen` із контекстом потоку) для запитів HTTP, тому `allow_url_fopen` має бути ввімкнено.

## SmartHttpClient

```php
use Pitmaster\Protocol\SmartHttpClient;

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

Час очікування за замовчуванням дорівнює константі `PITMASTER_HTTP_TIMEOUT` (ціле число) або 30 секундам, якщо її не визначено.

### Виявлення посилань

```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()` виконує те саме для `git-receive-pack`. Запит на виявлення посилань надсилається до:
```
GET <url>/info/refs?service=git-upload-pack
```

Відповідь — це закодований у pkt-line список посилань із можливостями в першому рядку.

### Надсилання пакета (fetch)

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

Надсилає POST до `<url>/git-upload-pack` із тілом запиту для узгодження want/have. Повертає необроблене тіло відповіді (рядки pkt-line, `NAK` і дані side-band). `UploadPackClient` формує запит і витягує пакет:

```php
use Pitmaster\Protocol\UploadPackClient;

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

### Отримання пакета (push)

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

Надсилає POST до `<url>/git-receive-pack` із командами оновлення посилань і даними пакета.

## Кодування PktLine

Протокол git використовує кадрування pkt-line для всього обміну даними. Перед кожним рядком указується його загальна довжина (разом із 4-байтовим префіксом довжини), закодована як 4 шістнадцяткові цифри.

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

### Правила довжини

- Мінімальний рядок даних: `0005` (корисне навантаження 1 байт)
- Максимальний рядок даних: `fff0` (корисне навантаження 65516 байтів)
- Пакет скидання: `0000`
- Пакет-роздільник: `0001`
- Кінець відповіді: `0002` (протокол v2; обробляється `ProtocolV2::decode()`, тоді як `PktLine::decode()` його відхиляє)

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

## Виявлення посилань

Клас `RefDiscovery` аналізує оголошення посилань сервера.

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

### Формат оголошення посилань

Перший рядок оголошення містить можливості:

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

## Можливості

Клас `Capability` аналізує протокольні можливості та керує ними.

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

### Поширені можливості

| Можливість | Значення |
|-----------|---------|
| `multi_ack` | Сервер підтримує узгодження multi-ack |
| `thin-pack` | Сервер надсилає тонкі пакети (базові об’єкти можуть бути відсутні). Pitmaster запитує їх, але поки не може індексувати тонкі пакети ([#16](https://github.com/inline0/pitmaster/issues/16)) |
| `side-band` | Сервер використовує side-band для даних пакета (обмеження — 1000 байтів) |
| `side-band-64k` | Сервер використовує side-band 64K для даних пакета |
| `ofs-delta` | У пакеті можуть бути об’єкти OFS_DELTA |
| `shallow` | Сервер підтримує поверхневе клонування |
| `no-done` | Серверу не потрібен рядок "done" |
| `symref=HEAD:refs/heads/main` | Символьна ціль HEAD |
| `agent=git/2.45.0` | Версія git сервера |

## Демультиплексування side-band

Коли сервер використовує side-band, дані пакета мультиплексуються з повідомленнями про перебіг і помилки. Перший байт корисного навантаження кожного рядка pkt-line позначає канал:

| Канал | Значення |
|---------|---------|
| 1 | Дані пакета |
| 2 | Повідомлення про перебіг (stderr) |
| 3 | Повідомлення про критичні помилки |

Pitmaster видаляє байти каналів із `UploadPackClient` і повторно збирає дані пакета.

## Версії протоколу

### v1 (за замовчуванням)

Традиційний протокол. Під час виявлення посилань надсилаються всі посилання одразу. Pitmaster узгоджує дані за один цикл: надсилає потрібні йому об’єкти, кожну локальну вершину посилання як `have`, а також `done`.

### v2

Простіший протокол із кращою продуктивністю.

Основні відмінності від v1:
- Виявлення посилань — це окрема команда `ls-refs` (можна фільтрувати посилання)
- Fetch — це одна команда `fetch` зі структурованими параметрами
- Для розділення секцій використовуються пакети-роздільники (`0001`)

`clone()` і `fetch()` завжди використовують v1. v2 доступний, якщо викликати його явно:

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

## Потік запиту HTTP

### Клонування/отримання

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

### Надсилання

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

## Обробка помилок

Помилки мережі призводять до винятку `ProtocolException`. Якщо обмін через smart HTTP під час `clone()` або `fetch()` завершується помилкою, Pitmaster повторює спробу через dumb HTTP і, якщо вона теж не вдається, повторно викидає початкову помилку. Інші збої (невідомий віддалений репозиторій, дескриптор без процесу, пакет, який неможливо індексувати) спричиняють винятки інших типів:

```php
use Pitmaster\Exceptions\ProtocolException;

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

Поширені помилки:
- Сервер недоступний (час очікування вичерпано)
- Потрібна автентифікація або репозиторій не знайдено (повідомляється як `Unexpected HTTP status 401`, `403` або `404`)
- Неочікуваний `Content-Type` у відповіді
- Некоректні дані pkt-line
- Повідомлення про помилку в каналі side-band
