
# Smart HTTP

Pitmaster comunică cu serverele git prin HTTPS, folosind protocolul smart HTTP. Acesta este transportul de rețea principal. Folosește funcțiile native de flux ale PHP (`fopen` cu un context de flux) pentru cereri HTTP, așadar `allow_url_fopen` trebuie să fie activat.

## SmartHttpClient

```php
use Pitmaster\Protocol\SmartHttpClient;

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

Valoarea implicită a timeoutului este constanta `PITMASTER_HTTP_TIMEOUT` (un număr întreg) sau 30 de secunde dacă aceasta nu este definită.

### Descoperirea referințelor

```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()` face același lucru pentru `git-receive-pack`. Cererea de descoperire a referințelor este trimisă la:
```
GET <url>/info/refs?service=git-upload-pack
```

Răspunsul este o listă de referințe codificată pkt-line, cu capabilitățile pe prima linie.

### Încărcarea pachetului (fetch)

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

Trimite un POST la `<url>/git-upload-pack` cu corpul de negociere want/have. Returnează corpul brut al răspunsului (pkt-lines, `NAK` și date side-band). `UploadPackClient` construiește cererea și extrage pachetul:

```php
use Pitmaster\Protocol\UploadPackClient;

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

### Primirea pachetului (push)

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

Trimite un POST la `<url>/git-receive-pack` cu comenzile de actualizare a referințelor și datele pachetului.

## Codificarea PktLine

Protocolul git folosește încadrarea pkt-line pentru toate comunicațiile. Fiecare linie este prefixată cu lungimea totală (inclusiv prefixul de lungime de 4 octeți), codificată ca 4 cifre hexazecimale.

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

### Reguli privind lungimea

- Linia minimă de date: `0005` (1 octet de conținut)
- Linia maximă de date: `fff0` (65516 octeți de conținut)
- Pachet flush: `0000`
- Pachet delimitator: `0001`
- Sfârșit de răspuns: `0002` (protocol v2; gestionat de `ProtocolV2::decode()`, în timp ce `PktLine::decode()` îl respinge)

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

## Descoperirea referințelor

Clasa `RefDiscovery` analizează anunțul de referințe al serverului.

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

### Formatul anunțului de referințe

Prima linie a anunțului include capabilitățile:

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

## Capabilități

Clasa `Capability` analizează și gestionează capabilitățile protocolului.

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

### Capabilități comune

| Capabilitate | Semnificație |
|-----------|---------|
| `multi_ack` | Serverul acceptă negocierea multi-ack |
| `thin-pack` | Serverul trimite pachete subțiri (obiectele de bază pot fi omise). Pitmaster o solicită, dar încă nu poate indexa pachete subțiri ([#16](https://github.com/inline0/pitmaster/issues/16)) |
| `side-band` | Serverul folosește side-band pentru datele pachetului (limitat la 1000 octeți) |
| `side-band-64k` | Serverul folosește side-band de 64K pentru datele pachetului |
| `ofs-delta` | Pachetul poate folosi obiecte OFS_DELTA |
| `shallow` | Serverul acceptă clone superficiale |
| `no-done` | Serverul nu necesită o linie „done” |
| `symref=HEAD:refs/heads/main` | Ținta simbolică a HEAD |
| `agent=git/2.45.0` | Versiunea git a serverului |

## Demultiplexarea side-band

Când serverul folosește side-band, datele pachetului sunt multiplexate cu mesajele de progres și de eroare. Primul octet din conținutul fiecărei linii pkt-line indică canalul:

| Canal | Semnificație |
|---------|---------|
| 1 | Datele pachetului |
| 2 | Mesaje de progres (stderr) |
| 3 | Mesaje de eroare fatală |

Pitmaster al lui `UploadPackClient` elimină octeții canalului și reasamblează datele pachetului.

## Versiuni de protocol

### v1 (implicit)

Protocolul tradițional. Descoperirea referințelor trimite toate referințele simultan. Pitmaster negociază într-o singură rundă: trimite cererile sale, fiecare vârf de referință locală ca `have` și `done`.

### v2

Un protocol mai simplu, cu performanțe mai bune.

Diferențe esențiale față de v1:
- Descoperirea referințelor este o comandă `ls-refs` separată (poate filtra referințele)
- Fetch este o singură comandă `fetch` cu parametri structurați
- Folosește pachete delimitatoare (`0001`) pentru a separa secțiunile

`clone()` și `fetch()` folosesc întotdeauna v1. v2 este disponibil când îl apelați explicit:

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

## Fluxul cererilor 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
```

## Tratarea erorilor

Erorile de rețea generează `ProtocolException`. Când schimbul smart HTTP pentru `clone()` sau `fetch()` eșuează, Pitmaster reîncearcă prin dumb HTTP și generează din nou eroarea inițială dacă și încercarea respectivă eșuează. Alte probleme (un server la distanță necunoscut, un handle fără proces, un pachet care nu poate fi indexat) generează alte tipuri de excepții:

```php
use Pitmaster\Exceptions\ProtocolException;

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

Erori frecvente:
- Server inaccesibil (timeout)
- Este necesară autentificarea sau depozitul nu a fost găsit (raportat ca `Unexpected HTTP status 401`, `403` sau `404`)
- `Content-Type` neașteptat în răspuns
- Date pkt-line nevalide
- Mesaj pe canalul de eroare side-band
