Lär ditt Go-program att tala som Chrome

Om du någon gång har rest utomlands och beställt kaffe med en fras som var tagen ur en lärobok, känner du till blicken som baristan ger dig. Din grammatik är korrekt. Varje ord finns i ordboken. Ändå vet alla i kaféet inom en halv sekund att du kommer från en annan stad.
Go:s standardklient för HTTP har en liten del av det problemet. Den är ett utmärkt förval: pålitlig, ren och redo för seriöst arbete direkt från start. När ditt program kontaktar en server ser det dock otvetydigt ut som ett Go-program. Oftast spelar det ingen roll. Ändå finns det webbplatser, CDN:er och testmiljöer som noggrant uppmärksammar vem som knackar på dörren, och som beter sig helt olika beroende på vilken typ av klient de tror sig kommunicera med.
Det var därför vi byggde gosuda.org/chromenet. I stället för att lära Go:s standardklient att göra ett godtagbart försök att efterlikna en webbläsare låter chromenet ett Go-program kommunicera med webben via samma nätverksmotor som Google Chrome använder, nämligen Chromiums egen nätverkskod. Oavsett om du vill undersöka hur dina egna testmiljöer behandlar webbläsartrafik eller kommunicerar med en webbplats bakom en kräsen CDN, tar kommunikation via Chromes motor bort ett helt lager av osäkerhet.
Du behöver inte skriva om din applikation för att använda den. I din kod fungerar den som ett direkt ersättningsalternativ för http.Transport. Du fortsätter att använda http.Client precis som tidigare, och chromenet sköter kommunikationen under ytan.
För att prova den senaste versionen installerar du den med go get:
1go get gosuda.org/chromenet@v0.2.10157081033800
Därefter överlämnar du dess transport till din klient:
1client := http.Client{
2 Transport: chromenet.NewTransport(chromenet.Options{}),
3}
Med den här versionen färdas hela spektrumet av modern webbtrafik genom Chromes motor. Det omfattar nu vanliga webbförfrågningar, WebSocket och WebTransport. Tidigare versioner hanterade endast vanliga förfrågningar på ett korrekt sätt, medan WebSocket och WebTransport var provisoriska ersättningar. Det innebar att din inledande hälsning vid ytterdörren lät som Chrome, men om du bytte till en annan typ av konversation kunde din täckmantel glida. Den här versionen ersätter dessa provisoriska lösningar med de riktiga.
Men om vi använder Chromiums egen nätverkskod, betyder det då att ditt tysta Go-bygge förvandlas till en omfattande huvudvärk med C-kompilatorn? Lyckligtvis nej. Din Go-kod förblir ren Go, utan cgo och utan något behov av en C-kompilator.
I stället för att svetsa in webbläsarkod direkt i din binärfil kör chromenet motorn som ett litet separat program bredvid ditt Go-program. Tänk på det som att anlita en flytande lokal tolk som går vid din sida i stället för att försöka bygga om dina egna stämband. Eftersom motorn körs i det separata programmet kan ett krasch i den inte slå ut din tjänst.

Ditt Go-program förblir rent Go medan ett litet följeprogram för trafiken genom Chromiums nätverksmotor.
Det finns inget extra att installera på din arbetsdator eller dina servrar heller. Motorns binärfiler för Linux, macOS och Windows, för både Intel/AMD och ARM, levereras direkt i Go-modulen och verifieras innan de körs.
Att leverera binärfiler i en modul väcker förståeligt nog en fråga för den som bryr sig om förtroende, och vi ville ge ett rakt svar. Binärfilerna byggs på ett reproducerbart sätt från en specifik Chromium-version, för närvarande Chromium 157, så vem som helst kan bygga om dem och få exakt samma bitar. Varje version bekräftas dessutom av GitHub. För att hålla jämna steg med webbläsarvärlden utan manuellt arbete följer ett schemalagt jobb Chromiums stabila kanal och uppdaterar versionen automatiskt när en ny Chrome lanseras.
Vad är det med uTLS?
Om du tidigare har försökt få en Go-klient att se ut som en webbläsare har du sannolikt stött på uTLS. Det är ett förtjänstfullt arbete: ett Go-bibliotek som låter dig klä det allra första meddelandet i en säker anslutning så att det liknar Chrome, Firefox eller vilket annat du föredrar. Det är litet, det är rent Go, och inget extra körs bredvid ditt program.
Haken ligger i hur långt förklädnaden sträcker sig. uTLS omfattar handskakningen, alltså de första orden i konversationen. Allt därefter måste du fortfarande bygga själv: HTTP/2-lagret måste kopplas upp och finjusteras för hand, HTTP/3 är ett separat äventyr, och WebSocket eller WebTransport innebär ännu mer limkod. Chrome-förinställningarna är dessutom kopior, så någon måste hålla dem aktuella varje gång Chrome ändrar sina vanor. Det liknar att härma en persons accent medan resten av kroppsspråket fortfarande avslöjar en.
chromenet väljer den motsatta vägen. Den imiterar inte Chrome utan låter Chromes egen motor sköta kommunikationen, så att handskakningen, hur anslutningar återanvänds, protokolluppgraderingarna och transporterna alla kommer från en och samma källa och förändras tillsammans när Chromium förändras.
Avvägningen är ärlig. uTLS är lättare och låter dig utforma vilken hälsning som helst du kan tänka dig, medan chromenet medför en hjälpprocess på 25 MB och ger dig Chromes beteende snarare än ett eget. Om du vill ha ett litet beroende och full kontroll över handskakningen är uTLS fortfarande ett utmärkt val.
Varje design har sina avvägningar, och vi vill vara öppna om begränsningarna här. Att paketera en riktig webbläsares nätverksmotor är inte litet: varje motorbinärfil är omkring 25 MB. På macOS är binärfilerna inte notariserade. Och på Linux måste ditt system ha glibc 2.31 eller nyare. Om dessa gränser passar din miljö får du riktig Chrome-nätverkning genom att byta ut ett enda transportfält.
Projektet finns på github.com/gosuda/chromenet. Om du känner till systerprojekten i gosuda:s öppna källkodsfamilj, gosuda.org/hqq och gosuda.org/sqlite3, kommer andemeningen att kännas igen: att ta kapabel, tung maskinering från den bredare mjukvarevärlden och göra den hemmastadd i ren Go.
Så om du tar till uTLS för att ditt Go-program behöver låta som en webbläsare, prova chromenet. Det kan vara ett bra alternativ till uTLS, särskilt när du behöver mer än HTTPS-förfrågningar: HTTP/2, HTTP/3, WebSocket och WebTransport färdas alla genom samma Chrome-motor, utan limkod på din sida.