GoSuda

Je Go-programma leren spreken zoals Chrome

By Lemon Mint views ...

Als je ooit in het buitenland bent geweest en koffie hebt besteld met een zin uit het leerboek, ken je de blik die de barista je dan geeft. Je grammatica klopt. Elk woord staat in het woordenboek. Toch weet iedereen in het café binnen een halve seconde dat je van buiten de stad komt.

De standaard HTTP-client van Go heeft een beetje van dat probleem. Het is een uitstekende standaardkeuze: betrouwbaar, overzichtelijk en direct klaar voor serieus werk. Wanneer je programma echter contact opneemt met een server, herkent men het onmiskenbaar als een Go-programma. Meestal maakt dat niemand iets uit. Toch letten sommige websites, CDN's en testopstellingen nauwgezet op wie er aanklopt, en ze gedragen zich heel verschillend, afhankelijk van het type client waarvan zij denken dat ze ermee communiceren.

Daarom hebben we gosuda.org/chromenet gebouwd. In plaats van de standaardclient van Go te trainen om een redelijke browserimitatie te geven, laat chromenet een Go-programma met het web communiceren via dezelfde netwerkengine die Google Chrome gebruikt, namelijk de eigen netwerkcode van Chromium. Of je nu wilt nagaan hoe je eigen testopstellingen browserverkeer behandelen, of wilt communiceren met een site achter een kritische CDN, door te spreken via de engine van Chrome verdwijnt een hele laag van giswerk.

Je hoeft je applicatie niet te herschrijven om het te gebruiken. In jouw code werkt het als een directe vervanging voor http.Transport. Je blijft http.Client gebruiken zoals je altijd deed, en chromenet regelt de communicatie daaronder.

Om de nieuwste release uit te proberen, installeer je deze met go get:

1go get gosuda.org/chromenet@v0.2.10157081033800

Geef vervolgens de transport aan je client:

1client := http.Client{
2	Transport: chromenet.NewTransport(chromenet.Options{}),
3}

Met deze release verloopt het volledige bereik van moderne webverkeer via de engine van Chrome. Het omvat nu gewone webverzoeken, WebSocket en WebTransport. Eerdere versies verwerkten alleen gewone verzoeken goed, terwijl WebSocket en WebTransport slechts tijdelijke vervangingen waren. Dat betekende dat je eerste begroeting aan de voordeur wel als Chrome klonk, maar dat je dekmantel kon verschuiven zodra je overging op een andere vorm van communicatie. Deze release vervangt die tijdelijke oplossingen door de echte implementatie.

Maar als we de eigen netwerkcode van Chromium gebruiken, betekent dat dan dat je rustige Go-build verandert in een omvangrijke C-compilerhoofdpijn? Gelukkig niet. Je Go-code blijft gewone Go, zonder cgo en zonder dat er een C-compiler nodig is.

In plaats van browsercode rechtstreeks in je binary te verwerken, laat chromenet de engine draaien als een klein afzonderlijk programma naast je Go-programma. Denk hieraan als het inhuren van een vloeiend lokale tolk die naast je meeloopt, in plaats van te proberen je eigen stembanden opnieuw op te bouwen. Omdat de engine in dat afzonderlijke programma draait, kan een crash erin je service niet neerhalen.

Hoe chromenet werkt: je Go-programma vraagt een klein begeleidend programma, dat de netwerkengine van Chromium draait en met het web communiceert

Je Go-programma blijft gewone Go, terwijl een klein begeleidend programma het verkeer door de netwerkengine van Chromium leidt.

Er hoeft ook niets extra's geïnstalleerd te worden op je werkstation of je servers. De engine-binaries voor Linux, macOS en Windows, op zowel Intel/AMD als ARM, worden meegeleverd binnen de Go-module en worden geverifieerd voordat ze worden uitgevoerd.

Het meeleveren van binaries in een module roept begrijpelijkerwijs een vraag op voor iedereen die waarde hecht aan vertrouwen, en we wilden daarop een eenvoudig antwoord geven. De binaries worden op een reproduceerbare manier gebouwd uit een specifieke Chromium-release, momenteel Chromium 157, zodat iedereen ze opnieuw kan bouwen en exact dezelfde bytes krijgt, en elke release wordt door GitHub geattesteerd. Om gelijke tred te houden met de browserwereld zonder handmatig werk, volgt een geplande taak het stabiele kanaal van Chromium en werkt de versie automatisch bij zodra een nieuwe Chrome uitkomt.

Wat met uTLS?

Als je ooit hebt geprobeerd een Go-client als browser te laten lijken, ben je waarschijnlijk uTLS tegengekomen. Het is een mooi stuk werk: een Go-bibliotheek waarmee je het allereerste bericht van een beveiligde verbinding kunt laten lijken op Chrome, Firefox of wat je maar wilt. Het is klein, het is pure Go, en er draait niets extra naast je programma.

Het addertje is hoe ver het kostuum reikt. uTLS dekt de handshake, dat zijn de eerste woorden van het gesprek. Alles daarna moet je zelf bouwen: de HTTP/2-laag moet met de hand worden aangesloten en afgesteld, HTTP/3 is een apart avontuur, en WebSocket of WebTransport vragen nog meer maatwerk. De Chrome-presets zijn bovendien kopieën, en iemand moet ze telkens actueel houden wanneer Chrome zijn gewoonten verandert. Het lijkt een beetje op het nabootsen van iemands accent, terwijl de rest van zijn lichaamstaal je nog steeds verraadt.

chromenet kiest de tegenovergestelde weg. Het imiteert Chrome niet, maar laat de eigen engine van Chrome het gesprek voeren, zodat de handshake, de manier waarop verbindingen hergebruikt worden, de protocolupgrades en de transporten allemaal uit één bron komen en samen meebewegen wanneer Chromium verandert.

De afweging is eerlijk. uTLS is lichter en laat je elke denkbare begroeting ontwerpen, terwijl chromenet een helper van 25 MB meebrengt en je het gedrag van Chrome geeft in plaats van een eigen variant. Als je een kleine afhankelijkheid en volledige controle over de handshake wilt, blijft uTLS een uitstekende keuze.

Elk ontwerp kent afwegingen, en we willen hier eerlijk zijn over de beperkingen. Het meeleveren van een volwaardige browser-netwerkengine is niet klein: elke engine-binary is ongeveer 25 MB. Op macOS zijn de binaries niet genotariseerd. En op Linux moet je systeem glibc 2.31 of nieuwer hebben. Als die grenzen bij jouw opzet passen, krijg je echte Chrome-netwerking door één transportveld te vervangen.

Het project vind je op github.com/gosuda/chromenet. Als je de zusterprojecten in de open source-familie van gosuda kent, gosuda.org/hqq en gosuda.org/sqlite3, zal de geest je bekend voorkomen: capabele, zware techniek uit de bredere softwarewereld nemen en die laten aanvoelen alsof ze thuishoort in gewone Go.

Dus, als je uTLS pakt omdat je Go-programma als een browser moet klinken, probeer dan chromenet eens. Het kan een goed alternatief zijn voor uTLS, zeker wanneer je meer nodig hebt dan HTTPS-verzoeken: HTTP/2, HTTP/3, WebSocket en WebTransport lopen allemaal via dezelfde Chrome-engine, zonder lijmcode aan jouw kant.