mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-23 23:18:13 +02:00
Telecharger un fichier depuis ajean.link rendait du JSON
Le proxy chiffre (relay_e2e.go) reemballe TOUTE reponse en JSON avant de
la renvoyer : une reponse non-JSON est enveloppee en chaine. Du binaire
n'y survit pas — les octets non-UTF8 sont massacres — et le navigateur
telechargeait l'enveloppe JSON a la place du fichier. Le probleme est
structurel, pas propre au telechargement : il touchait aussi l'export
Markdown de conversation, qui revenait entre guillemets, echappements
compris.
handleChatFile prend donc deux formes supplementaires : `meta=1` rend une
fiche {name, size, e2e}, et `b64=1&offset=&len=` une tranche en base64.
Le client demande la fiche d'abord — minuscule, elle passe partout — et
en tire le transport a utiliser : binaire direct en local, tranches
base64 derriere le tunnel. Le client ne peut pas le deviner autrement, le
proxy lui rendant des reponses JSON parfaitement ordinaires.
Le decoupage en tranches de 8 Mo evite de tenir le fichier entier en
memoire pour le transporter, comme a l'envoi. Verifie sur 120 Mo : les
deux chemins rendent exactement les memes 125 829 120 octets, et le test
Go compare octet a octet sur des donnees non-UTF8, celles que le
reemballage JSON detruisait.
relay_e2e.go marque desormais les requetes venues du tunnel
(X-Ajean-E2E) : c'est le seul moyen pour un handler de savoir que sa
reponse sera reemballee.
This commit is contained in:
1 parent
e07051e1e3
commit
78423276e7
6 files changed
+261
-22
No files matched your search
@@ -1,8 +1,10 @@
|
||||
package ajean
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -231,3 +233,72 @@ func TestChatUploadUnknownSessionRefused(t *testing.T) {
|
||||
t.Fatalf("code %d, attendu 409 — resp %+v", code, resp)
|
||||
}
|
||||
}
|
||||
|
||||
// Le téléchargement via app.ajean.link : le proxy chiffré réemballe toute
|
||||
// réponse en JSON, où du binaire ne survit pas. Le handler doit donc annoncer
|
||||
// qu'on est derrière le tunnel, puis savoir servir des tranches base64.
|
||||
func TestChatFileB64ForE2E(t *testing.T) {
|
||||
dir, _ := uploadsDir()
|
||||
// Des octets NON-UTF8 : c'est exactement ce que le réemballage JSON massacre.
|
||||
raw := make([]byte, 5000)
|
||||
for i := range raw {
|
||||
raw[i] = byte(i % 256)
|
||||
}
|
||||
p := filepath.Join(dir, "binaire.bin")
|
||||
if err := os.WriteFile(p, raw, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(p)
|
||||
|
||||
// La fiche dit « tunnel » quand la requête vient du proxy, et pas autrement.
|
||||
meta := func(viaTunnel bool) map[string]any {
|
||||
req := httptest.NewRequest("GET", "/api/chat/file?meta=1&path=uploads/binaire.bin", nil)
|
||||
if viaTunnel {
|
||||
req.Header.Set(e2eInnerHeader, "1")
|
||||
}
|
||||
rec := httptest.NewRecorder()
|
||||
handleChatFile(rec, req)
|
||||
var m map[string]any
|
||||
_ = json.Unmarshal(rec.Body.Bytes(), &m)
|
||||
return m
|
||||
}
|
||||
if m := meta(true); m["e2e"] != true || m["size"].(float64) != 5000 {
|
||||
t.Fatalf("fiche via tunnel : %+v", m)
|
||||
}
|
||||
if m := meta(false); m["e2e"] != false {
|
||||
t.Fatalf("fiche en local annoncée comme tunnel : %+v", m)
|
||||
}
|
||||
|
||||
// Reconstitution par tranches : l'octet doit revenir intact.
|
||||
var got []byte
|
||||
for off, guard := 0, 0; ; guard++ {
|
||||
if guard > 50 {
|
||||
t.Fatal("boucle de tranches sans fin")
|
||||
}
|
||||
rec := httptest.NewRecorder()
|
||||
handleChatFile(rec, httptest.NewRequest("GET",
|
||||
fmt.Sprintf("/api/chat/file?b64=1&len=1024&offset=%d&path=uploads/binaire.bin", off), nil))
|
||||
if rec.Code != 200 {
|
||||
t.Fatalf("tranche à %d : code %d — %s", off, rec.Code, rec.Body.String())
|
||||
}
|
||||
var j struct {
|
||||
Data string `json:"data"`
|
||||
EOF bool `json:"eof"`
|
||||
}
|
||||
if err := json.Unmarshal(rec.Body.Bytes(), &j); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b, err := base64.StdEncoding.DecodeString(j.Data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got = append(got, b...)
|
||||
off += len(b)
|
||||
if j.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !bytes.Equal(got, raw) {
|
||||
t.Fatalf("fichier reconstitué : %d octets, attendu %d", len(got), len(raw))
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user