
# Älykäs HTTP

Pitmaster viestii git-palvelinten kanssa HTTPS-yhteyden kautta käyttäen älykästä HTTP-protokollaa. Tämä on ensisijainen verkkosiirtotapa. Siinä käytetään PHP-järjestelmän natiiveja virtatoimintoja (`fopen` ja virtakonteksti) HTTP-pyyntöihin, joten `allow_url_fopen` on otettava käyttöön.

## SmartHttpClient

```php
use Pitmaster\Protocol\SmartHttpClient;

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

Aikakatkaisun oletusarvo on vakio `PITMASTER_HTTP_TIMEOUT` (kokonaisluku) tai 30 sekuntia, jos sitä ei ole määritelty.

### Selvitä viitteet

```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()` tekee saman kohteelle `git-receive-pack`. Viitteiden selvityspyyntö lähetetään osoitteeseen:
```
GET <url>/info/refs?service=git-upload-pack
```

Vastaus on pkt-line-muotoon koodattu viiteluettelo, jonka ensimmäisellä rivillä ovat ominaisuudet.

### Lähetyspaketti (nouto)

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

Lähettää POST-pyynnön osoitteeseen `<url>/git-upload-pack`, jonka runko sisältää haluttujen ja olemassa olevien objektien neuvottelun. Palauttaa raakavastausrungon (pkt-line-rivit, `NAK` ja side-band-tiedot). `UploadPackClient` muodostaa pyynnön ja poimii paketin:

```php
use Pitmaster\Protocol\UploadPackClient;

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

### Vastaanottopaketti (lähetys)

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

Lähettää POST-pyynnön osoitteeseen `<url>/git-receive-pack`, jonka runko sisältää viitepäivityskomennot ja pakettitiedot.

## PktLine-koodaus

git-protokolla käyttää pkt-line-kehystystä kaikessa tiedonsiirrossa. Jokaisen rivin eteen lisätään rivin kokonaispituus (mukaan lukien 4 tavun pituusetuliite), joka koodataan neljänä heksadesimaalinumerona.

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

### Pituussäännöt

- Tietorivin vähimmäispituus: `0005` (hyötykuorma 1 tavu)
- Tietorivin enimmäispituus: `fff0` (65516 tavun hyötykuorma)
- Tyhjennyspaketti: `0000`
- Erotinpaketti: `0001`
- Vastauksen loppu: `0002` (protokolla v2; `ProtocolV2::decode()` käsittelee sen, mutta `PktLine::decode()` hylkää sen)

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

## Viitteiden selvitys

Luokka `RefDiscovery` jäsentää palvelimen viiteilmoituksen.

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

### Viiteilmoituksen muoto

Ilmoituksen ensimmäinen rivi sisältää ominaisuudet:

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

## Ominaisuudet

Luokka `Capability` jäsentää protokollan ominaisuudet ja hallinnoi niitä.

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

### Yleiset ominaisuudet

| Ominaisuus | Merkitys |
|-----------|---------|
| `multi_ack` | Palvelin tukee usean kuittauksen neuvottelua |
| `thin-pack` | Palvelin lähettää ohuita paketteja (perusobjekteja voidaan jättää pois). Pitmaster pyytää niitä, mutta ei vielä osaa indeksoida ohuita paketteja ([#16](https://github.com/inline0/pitmaster/issues/16)) |
| `side-band` | Palvelin käyttää side-bandia pakettitiedoille (enintään 1000 tavua) |
| `side-band-64k` | Palvelin käyttää 64K side-bandia pakettitiedoille |
| `ofs-delta` | Paketti voi käyttää OFS_DELTA-objekteja |
| `shallow` | Palvelin tukee vajaata kloonausta |
| `no-done` | Palvelin ei vaadi "done"-riviä |
| `symref=HEAD:refs/heads/main` | HEADin symbolinen kohde |
| `agent=git/2.45.0` | Palvelimen git-versio |

## Side-band-kanavien erottelu

Kun palvelin käyttää side-bandia, pakettitiedot lomitetaan edistymis- ja virheilmoitusten kanssa. Jokaisen pkt-line-hyötykuorman ensimmäinen tavu ilmaisee kanavan:

| Kanava | Merkitys |
|---------|---------|
| 1 | Pakettitiedot |
| 2 | Edistymisilmoitukset (stderr) |
| 3 | Vakavat virheilmoitukset |

Pitmaster-luokan `UploadPackClient` poistaa kanavatavut ja kokoaa pakettitiedot uudelleen.

## Protokollaversiot

### v1 (oletus)

Perinteinen protokolla. Viitteiden selvityksessä lähetetään kaikki viitteet kerralla. Pitmaster neuvottelee yhdellä kierroksella: se lähettää haluamansa objektit, jokaisen paikallisen viitteen kärjen muodossa `have` sekä `done`.

### v2

Yksinkertaisempi ja suorituskykyisempi protokolla.

Tärkeimmät erot protokollaan v1 verrattuna:
- Viitteiden selvitys on erillinen `ls-refs`-komento (viitteitä voi suodattaa)
- Nouto tehdään yhdellä `fetch`-komennolla, jolla on rakenteiset parametrit
- Osioiden erottamiseen käytetään erotinpaketteja (`0001`)

`clone()` ja `fetch()` käyttävät aina v1-protokollaa. v2 on käytettävissä, kun kutsut sitä erikseen:

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

## HTTP-pyynnön kulku

### Kloonaus/nouto

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

### Lähetys

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

## Virheenkäsittely

Verkkovirheet aiheuttavat poikkeuksen `ProtocolException`. Jos älykäs HTTP-pyyntö kohteelle `clone()` tai `fetch()` epäonnistuu, Pitmaster yrittää uudelleen tavallisella HTTP-pyynnöllä ja heittää alkuperäisen virheen uudelleen, jos sekin epäonnistuu. Muunlaiset virheet (tuntematon etärepositorio, prosessiton käsittelijä tai indeksoimaton paketti) aiheuttavat muuntyyppisiä poikkeuksia:

```php
use Pitmaster\Exceptions\ProtocolException;

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

Yleisiä virheitä:
- Palvelimeen ei saada yhteyttä (aikakatkaisu)
- Todennus vaaditaan tai repositoriota ei löydy (ilmoitetaan muodossa `Unexpected HTTP status 401`, `403` tai `404`)
- Vastauksessa on odottamaton `Content-Type`
- Virheellistä pkt-line-dataa
- Side-band-virhekanavan viesti
