# Fikatia Lisans Kanalı → Socket.IO v3 (engine.io v3) İstemci Güncellemesi

## Amaç
Fikatia lisans kill-switch kanalını raw WebSocket'den Socket.IO v3 protocol'üne taşımak. Sunucu tarafı (`lisans_kontrol.go`) ZATEN TAMAMLANDI — `/isans.socket.io/` endpoint'i, `/isans` namespace'i, `merhaba`/`disconnect` olayları, `lisans-karar` olayıyla imzalı kararlar. Bu plan sadece **istemci** (`fikocad`) tarafını günceller.

## Sunucu Arayüzü (Değişmez — referans)
- **Endpoint:** `GET|POST /isans.socket.io/` (adaptor.HTTPHandler)
- **Namespace:** `/isans` (default `/` değil!)
- **Client olayları:** `merhaba` (makine bilgisi), `disconnect`
- **Server olayları:** `lisans-karar` → `{tip:"karar", seri, engelli, mesaj, zaman, imza}`
- **Transport:** polling + websocket + webtransport (sunucu `SetAllowUpgrades(true)`)
- **Ping:** 25s interval, 20s timeout (handshake'dan gelir)

## Wire Formatı (Doğrulandı — socket.io-go-parser v2.2.2 + engine.io-go-parser v1.2.2)

### Socket.IO EVENT packet'ı (namespace `/isans`)
```
2[/isans,merhaba,{"seri":"...","musteri":"..."}]
```
- `2` = EVENT tipi (byte '2')
- `/isans,` = namespace (`,` ile ayrılır, `/` default namespace için atlanır)
- `merhaba` = event adı
- `{"..."}` = args JSON array'i (tek nesne → array'e sarılır)

### engine.io v3 polling payload formatı
```
<length>:<packet>
```
- `<length>` = UTF-16 karakter sayısı (decimal)
- `<packet>` = socket.io packet'ı
- Örnek: `0:2[/isans,merhaba,{"seri":"..."}]`

### HTTP long-polling handshake akışı
1. `GET /isans.socket.io/?EIO=3&transport=polling`
   → `{"upgrades":["websocket","webtransport"],"pingInterval":25000,"pingTimeout":20000,"sid":"abcd1234"}`
2. Client CONNECT: `POST /isans.socket.io/?EIO=3&sid=abcd1234&transport=polling`
   → body: `0:2[/isans]` (CONNECT packet, namespace `/isans`)
3. Server: `0:2[/isans]` → namespace'e bağlanır (opsiyonel ack)
4. Client EVENT: `POST ...&transport=polling` → body: `0:2[/isans,merhaba,{"..."}]`
5. Server EVENT: `0:2[/isans,lisans-karar,{"tip":"karar",...}]`

### WebSocket transport
- Yükseltme: `GET /isans.socket.io/?EIO=3&transport=websocket&sid=abcd1234`
- Her frame: `2[/isans,merhaba,{"..."}]` (engine.io v3 packet: `2` = message)
- Frame formatı: socket.io EVENT packet'ı (polling'deki gibi, length prefix YOK)

## İstemci Stratejisi

### Transport Seçimi: Polling + WebSocket (önerilen)
- **Başlangıç:** HTTP long-polling ile handshake (NAT/geri çekilme dostu)
- **Yükseltme:** websocket'e (sunucu `SetAllowUpgrades(true)`)
- **Ping:** handshake'dan gelen `pingInterval` (25s) ile

### URL Formatı: Güncelle
- Mevcut: `wss://fikoyt.com/lisans-ws` (raw WS)
- Yeni: `https://fikoyt.com/isans.socket.io/` (HTTP(S) → Socket.IO)
- `.socket.io/` suffix'i otomatik eklenmez — kullanıcı URL'ye yazar
- `ws://`/`wss://` prefix'leri artık anlamsız (HTTP(S) kullanılır)

## Implementasyon Planı

### 1. `uygulama/fikocad/lisans_canli.cpp` — Yeni transport katmanı

#### 1.1 HTTP long-polling implementasyonu
```cpp
struct HttpPolling {
  HINTERNET oturum;
  std::string sid;
  DWORD pingIntervalMs = 25000;
  DWORD pingTimeoutMs = 20000;
  
  // GET ile handshake → sid + ping parametreleri
  bool baslat(const UrlParca& u);
  // POST ile packet gönder (length-prefixed)
  bool gonder(const std::string& packet);
  // GET ile yanıt al (birden fazla packet olabilir)
  std::string al();
};
```

#### 1.2 Packet encoder/decoder
- **Encoder (merhaba):** `merhabaMesajiUret()` → socket.io EVENT packet'ı → `2[/isans,merhaba,{...}]` → engine.io v3 payload → `0:2[/isans,merhaba,{...}]`
- **Decoder (yanıt):** engine.io v3 payload → `<length>:<packet>` → socket.io EVENT packet'ı → `2[/isans,lisans-karar,{...}]` → JSON ayrıştır → `lisans-karar` olayı

#### 1.3 JSON decoder (mevcut yardımcıları kullan)
- `jsonDizgeAl()`, `jsonSayiAl()`, `jsonBoolAl()` → socket.io EVENT packet'ının args JSON'ını çözümle
- Event adı çıkar (`[/isans,]` prefix'ini at) → `lisans-karar` ise `canliKararCoz()` ile doğrula

#### 1.4 WebSocket yükseltme (opsiyonel ama kullanıcı istedi)
- `GET /isans.socket.io/?EIO=3&transport=websocket&sid=...`
- WinHTTP websocket yükseltme (mevcut kodda var)
- Frame formatı: socket.io EVENT packet'ı (length prefix YOK)
- Frame decode: `2[/isans,lisans-karar,{...}]` → event adı + args JSON

#### 1.5 Ping/pong
- Handshake'dan gelen `pingInterval` ile periyodik ping
- engine.io v3 ping: `4` (engine.io message) → polling'de `0:4`
- PONG otomatik (sunucu handle eder)

### 2. `UrlParca` yapısını güncelle
- `wss://`/`ws://` prefix'lerini kaldır → `https://`/`http://`
- `port` varsayılan 443 (https) / 80 (http)
- `yol` → `/isans.socket.io/` (kullanıcı URL'ye yazar)

### 3. `merhabaMesajiUret()` — Değişmez
- Mevcut format sunucunun beklediğiyle birebir: `tip,seri,musteri,bilgisayar,surum,pid,makine[]`
- Sadece socket.io EVENT packet'ına sar

### 4. `canliIsParcacigi()` — Transport soyutlama
- Mevcut yeniden bağlanma mantığı (üstsel geri çekilme) korunur
- Sadece `wsOturumu()` → yeni transport implementasyonu

### 5. UI ipucu güncelle
- `uzakCanliAyarBolumu()` → yeni URL formatını göster: `https://fikoyt.com/isans.socket.io/`
- Açıklama: "Socket.IO v3 lisans kanalı"

## Test Stratejisi

### Derleme
- `cmake --build insa\Release --target fikocad` (sadece fikatia)
- Sunucu Go derleyicisi yok → kullanıcıya RUNTIME TEST notu

### Birim test (yeni)
- `motor/cekirdek/test/test_lisans_uzak.cpp` → Socket.IO EVENT packet'ı decoder testi
- Örnek: `2[/isans,lisans-karar,{"tip":"karar","seri":"FKC-2026-0001","engelli":true,"mesaj":"...","zaman":1756100000,"imza":"..."}]` → `canliKararCoz()` → `Gecerli`

### Runtime (kullanıcı)
1. fikocad'i başlat
2. URL'yi `https://<host>/isans.socket.io/` yap
3. `/lisans-kontrol` panelinde makinenin görünmesini bekle
4. "Engelle"/"İzin ver" tıkla → fikocad'de kararın uygulanmasını dene

## Riskler / Notlar

### engine.io v3 UTF-16 length hesaplaması
- `Utf16Count()` / `Utf16Len()` — ASCII için length = byte sayısı, Unicode için UTF-16 kod birimi sayısı
- Mesaj/bilgisayar adı ASCII → length = byte sayısı (basit)
- Unicode karakter varsa UTF-16 hesaplama gerekli

### Namespace handling
- `/isans` namespace'i için packet'ların önünde `/isans,` olmalı
- Default `/` namespace için prefix atlanır
- Client, connect EVENT'ında namespace'i belirtmeli: `0[/isans]`

### WebSocket frame formatı
- Polling'de `0:<packet>` (length prefix)
- WebSocket'de `<packet>` (length prefix YOK)
- Her iki transport da socket.io EVENT packet'ını kullanır

### Ping/pong timing
- Handshake'dan gelen `pingInterval` (25s) kullan
- `pingTimeout` (20s) içinde PONG gelmezse bağlantı kopar

## Kabul Kriterleri
- [ ] fikocad `https://<host>/isans.socket.io/` URL'sinden Socket.IO v3 sunucusuna bağlanır
- [ ] HTTP long-polling ile başlar, websocket'e yükseltir
- [ ] `merhaba` packet'ıyla makine bilgisi + seri gönderilir
- [ ] `lisans-karar` packet'ıyla sunucudan gelen imzalı karar `canliKararCoz()` ile doğrulanır
- [ ] Bağlantı koparsa üstsel geri çekilmeyle yeniden bağlanır
- [ ] `/lisans-kontrol` panelinde makine görünür
- [ ] URL formatı UI'da doğru gösterilir
