
# Smart HTTP

Pitmaster berkomunikasi dengan server git melalui HTTPS menggunakan protokol smart HTTP. Ini adalah transport jaringan utama. Protokol ini menggunakan fungsi stream native milik PHP (`fopen` dengan konteks stream) untuk permintaan HTTP, jadi `allow_url_fopen` harus diaktifkan.

## SmartHttpClient

```php
use Pitmaster\Protocol\SmartHttpClient;

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

Batas waktu default-nya adalah konstanta `PITMASTER_HTTP_TIMEOUT` (bilangan bulat), atau 30 detik jika konstanta tersebut tidak ditentukan.

### Temukan ref

```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()` melakukan hal yang sama untuk `git-receive-pack`. Permintaan discovery ref dikirim ke:
```
GET <url>/info/refs?service=git-upload-pack
```

Responsnya berupa daftar ref berkode pkt-line dengan kapabilitas pada baris pertama.

### Upload pack (fetch)

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

Mengirim POST ke `<url>/git-upload-pack` dengan isi permintaan negosiasi want/have. Mengembalikan isi respons mentah (pkt-lines, `NAK`, dan data side-band). `UploadPackClient` menyusun permintaan dan mengekstrak pack:

```php
use Pitmaster\Protocol\UploadPackClient;

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

### Receive pack (push)

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

Mengirim POST ke `<url>/git-receive-pack` dengan perintah pembaruan ref dan data pack.

## Encoding PktLine

Protokol git menggunakan framing pkt-line untuk semua komunikasi. Setiap baris diawali panjang totalnya (termasuk prefiks panjang 4 byte) yang dikodekan sebagai 4 digit heksadesimal.

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

### Aturan panjang

- Baris data minimum: `0005` (payload 1 byte)
- Baris data maksimum: `fff0` (payload 65516 byte)
- Paket flush: `0000`
- Paket delimiter: `0001`
- Akhir respons: `0002` (protokol v2; ditangani oleh `ProtocolV2::decode()`, sedangkan `PktLine::decode()` menolaknya)

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

## Discovery ref

Kelas `RefDiscovery` mengurai iklan ref dari server.

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

Baris pertama iklan tersebut mencakup kapabilitas:

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

## Kapabilitas

Kelas `Capability` mengurai dan mengelola kapabilitas protokol.

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

### Kapabilitas umum

| Kapabilitas | Arti |
|-----------|---------|
| `multi_ack` | Server mendukung negosiasi multi-ack |
| `thin-pack` | Server mengirim pack tipis (objek dasar dapat dihilangkan). Pitmaster memintanya, tetapi belum dapat mengindeks pack tipis ([#16](https://github.com/inline0/pitmaster/issues/16)) |
| `side-band` | Server menggunakan side-band untuk data pack (terbatas hingga 1000 byte) |
| `side-band-64k` | Server menggunakan side-band 64K untuk data pack |
| `ofs-delta` | Pack dapat menggunakan objek OFS_DELTA |
| `shallow` | Server mendukung clone dangkal |
| `no-done` | Server tidak memerlukan baris "done" |
| `symref=HEAD:refs/heads/main` | Target simbolis HEAD |
| `agent=git/2.45.0` | Versi git server |

## Demultipleks side-band

Saat server menggunakan side-band, data pack dimultipleks bersama pesan progres dan kesalahan. Byte pertama dari setiap payload pkt-line menunjukkan kanalnya:

| Kanal | Arti |
|---------|---------|
| 1 | Data pack |
| 2 | Pesan progres (stderr) |
| 3 | Pesan kesalahan fatal |

Pitmaster milik `UploadPackClient` menghapus byte kanal dan menyusun kembali data pack.

## Versi protokol

### v1 (default)

Protokol tradisional. Discovery ref mengirim semua ref sekaligus. Pitmaster bernegosiasi dalam satu putaran: mengirim wants-nya, setiap tip ref lokal sebagai `have`, dan `done`.

### v2

Protokol yang lebih sederhana dengan performa lebih baik.

Perbedaan utama dari v1:
- Discovery ref adalah perintah `ls-refs` terpisah (dapat memfilter ref)
- Fetch adalah satu perintah `fetch` dengan parameter terstruktur
- Menggunakan paket delimiter (`0001`) untuk memisahkan bagian

`clone()` dan `fetch()` selalu menggunakan v1. v2 tersedia jika Anda memanggilnya secara eksplisit:

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

## Alur permintaan HTTP

### Clone/Fetch

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

## Penanganan error

Kesalahan jaringan melempar `ProtocolException`. Jika pertukaran smart HTTP untuk `clone()` atau `fetch()` gagal, Pitmaster mencoba lagi melalui dumb HTTP dan melempar ulang kesalahan awal jika upaya itu juga gagal. Kegagalan lain (remote tidak dikenal, handle tanpa proses, pack yang tidak dapat diindeks) melempar jenis pengecualian lain:

```php
use Pitmaster\Exceptions\ProtocolException;

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

Kesalahan umum:
- Server tidak dapat dijangkau (batas waktu)
- Autentikasi diperlukan atau repositori tidak ditemukan (dilaporkan sebagai `Unexpected HTTP status 401`, `403`, atau `404`)
- `Content-Type` tak terduga dalam respons
- Data pkt-line tidak valid
- Pesan kanal kesalahan side-band
