
# Έξυπνο HTTP

Το Pitmaster επικοινωνεί με διακομιστές git μέσω HTTPS χρησιμοποιώντας το έξυπνο πρωτόκολλο 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, με τις δυνατότητες στην πρώτη γραμμή.

### Αποστολή pack (fetch)

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

Στέλνει ένα POST στο `<url>/git-upload-pack` με το σώμα διαπραγμάτευσης want/have. Επιστρέφει το ακατέργαστο σώμα απόκρισης (pkt-lines, `NAK` και δεδομένα side-band). Το `UploadPackClient` δημιουργεί το αίτημα και εξάγει το pack:

```php
use Pitmaster\Protocol\UploadPackClient;

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

### Λήψη pack (push)

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

Στέλνει ένα POST στο `<url>/git-receive-pack` με τις εντολές ενημέρωσης αναφορών και τα δεδομένα pack.

## Κωδικοποίηση PktLine

Το πρωτόκολλο git χρησιμοποιεί πλαισίωση pkt-line για όλη την επικοινωνία. Κάθε γραμμή έχει ως πρόθεμα το συνολικό μήκος της (μαζί με το πρόθεμα μήκους των 4 byte), κωδικοποιημένο ως 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 byte)
- Μέγιστη γραμμή δεδομένων: `fff0` (ωφέλιμο φορτίο 65516 byte)
- Πακέτο εκκαθάρισης: `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` | Ο διακομιστής στέλνει thin packs (ενδέχεται να παραλείπονται βασικά αντικείμενα). Το Pitmaster το ζητά, αλλά δεν μπορεί ακόμη να δημιουργήσει ευρετήριο για thin packs ([#16](https://github.com/inline0/pitmaster/issues/16)) |
| `side-band` | Ο διακομιστής χρησιμοποιεί side-band για δεδομένα pack (με όριο τα 1000 byte) |
| `side-band-64k` | Ο διακομιστής χρησιμοποιεί side-band 64K για δεδομένα pack |
| `ofs-delta` | Το pack μπορεί να χρησιμοποιεί αντικείμενα OFS_DELTA |
| `shallow` | Ο διακομιστής υποστηρίζει shallow clone |
| `no-done` | Ο διακομιστής δεν απαιτεί γραμμή "done" |
| `symref=HEAD:refs/heads/main` | Συμβολικός στόχος του HEAD |
| `agent=git/2.45.0` | Έκδοση git του διακομιστή |

## Διαχωρισμός καναλιών side-band

Όταν ο διακομιστής χρησιμοποιεί side-band, τα δεδομένα pack πολυπλέκονται με μηνύματα προόδου και σφάλματος. Το πρώτο byte του ωφέλιμου φορτίου κάθε pkt-line υποδεικνύει το κανάλι:

| Κανάλι | Σημασία |
|---------|---------|
| 1 | Δεδομένα pack |
| 2 | Μηνύματα προόδου (stderr) |
| 3 | Μηνύματα μοιραίου σφάλματος |

Το Pitmaster του `UploadPackClient` αφαιρεί τα byte καναλιού και ανασυνθέτει τα δεδομένα pack.

## Εκδόσεις πρωτοκόλλου

### 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`. Αν αποτύχει η ανταλλαγή έξυπνου HTTP για `clone()` ή `fetch()`, το Pitmaster επιχειρεί ξανά μέσω απλού HTTP και, αν αποτύχει κι αυτό, επαναφέρει το αρχικό σφάλμα. Άλλες αποτυχίες (άγνωστος απομακρυσμένος διακομιστής, λαβή χωρίς διεργασία, pack που δεν μπορεί να καταχωριστεί σε ευρετήριο) προκαλούν άλλους τύπους εξαιρέσεων:

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