---
id: 6e0bce9e22a4bf7d9672c59e67bcf5ec
author: Lemon Mint
title: 'gosuda.org/chromenet: Chromiums nätverksstack för Go, byggd reproducerbart från en fastlagd version'
description: CGO_ENABLED=0 Go http.RoundTripper som skickar förfrågningar genom Chromiums egna //net-stack (HTTP/1.1, HTTP/2, QUIC/HTTP/3, BoringSSL) i en isolerad HQQ-worker, med källkod, toolchains och sex inbäddade binärfiler som är fästa via SHA-256 och byggda i gvx-sandboxes.
language: sv
date: 2026-11-09T00:00:00Z
path: /chromenet
go_package: gosuda.org/chromenet
go_repourl: https://github.com/gosuda/chromenet.git
category: network
---

`gosuda.org/chromenet` är en Go-implementation av `http.RoundTripper` som skickar förfrågningar genom Chromiums egen nätverksstack. Stacken är `//net` från en officiell Chromium-version: dess implementationer av HTTP/1.1, HTTP/2 och QUIC/HTTP/3, BoringSSL, Chromes TLS-konfiguration och Chrome Root Store. Den körs i en separat arbetsprocess, `chrome_net_server`, och Go-sidan förblir ren Go (`CGO_ENABLED=0`) och kommunicerar med arbetsprocessen via [HQQ](https://gosuda.org/hqq) shared-memory RPC.

```bash
go get gosuda.org/chromenet@v0.1.10157081033800
```

## Varför köra Chromiums nätverksstack

Ett Go-program som kommunicerar med webben via `net/http` framstår för servern som en Go-klient. Det är ett rimligt standardval för tjänst-till-tjänst-trafik. Det är däremot ett olämpligt val när serverns beteende beror på vilken typ av klient den tror sig kommunicera med: webbläsarspärrade slutpunkter, middleware mot botar, CDN:er som levererar olika protokollversioner till olika TLS- och HTTP/2-fingeravtryck, samt tester som måste återskapa vad en verklig webbläsare upplever.

Go:s standardbibliotek kan inte återge en webbläsares beteende på nätverksnivå. `crypto/tls` har en egen ordning för chiffersviter och en egen uppsättning tillägg, och `net/http` har egna HTTP/2-inställningar. Bibliotek som efterliknar en webbläsare genom att kopiera dess ClientHello-byte måste fortsätta kopiera, version efter version. `chromenet` tar en annan väg: den efterliknar inte Chromiums nätverksstack utan kör den.

Vid mätning på `darwin/arm64`-motorn mot en offentlig fingeravtryckstjänst (`tls.peet.ws`) rapporterar standardkonfigurationen följande:

| Egenskap                           | Observerat värde                                                      |
| :--------------------------------- | :-------------------------------------------------------------------- |
| Förhandlat protokoll               | `h2`                                                                  |
| JA4                                | `t13d1516h2_8daaf6152771_806a8c22fdea`                                |
| HTTP/2-fingeravtryck (Akamai-format) | `1:65536;2:0;4:6291456;6:262144\|15663105\|0\|m,a,s,p`                |
| User-Agent                         | Chrome för skrivbordet med motorns huvudversion, t.ex. `Chrome/157.0.0.0` |

Med `ForceHTTP3` besvarades förfrågningar till `cloudflare-quic.com` och `www.google.com` över HTTP/3. Dessa värden kommer från en plattform vid ett tillfälle. Motorn är själva stacken, så värdena ändras när den fastlagda Chromium-versionen ändras.

## Användning

`NewTransport` returnerar en `http.RoundTripper`. Använd den överallt där du annars skulle använda `http.Transport`:

```go
package main

import (
	"fmt"
	"io"
	"net/http"

	"gosuda.org/chromenet"
)

func main() {
	client := &http.Client{Transport: chromenet.NewTransport(chromenet.Options{})}

	resp, err := client.Get("https://example.com/")
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	fmt.Println(resp.Proto, resp.StatusCode, len(body))
}
```

Vid den första förfrågan skriver modulen den inbäddade arbetsbinären för din plattform till användarens cachekatalog, verifierar dess SHA-256-digest med `gosuda.org/hqq/secureexec`, startar den med en överordnad livlina (`--parent-stdin`, så att den avslutas när ditt program avslutas) och öppnar en sessionspool. Ingenting behöver installeras.

Några beteenden följer `http.Transport` så att `http.Client` fungerar oförändrat:

- Transporten följer inte omdirigeringar och lagrar inga cookies. `http.Client` gör båda, precis som med `http.Transport`.
- Svar komprimerade med gzip, deflate, Brotli eller zstd levereras avkodade, och `resp.Uncompressed` är true.
- En förfrågan håller en motorsession tills dess svarskropp stängs. Stäng kropparna.
- Att avbryta förfrågans kontext avbryter förfrågan inne i Chromium.
- Nätverksfel är `*client.NetError`-värden som innehåller Chromiums felkod och felnamn:

```go
var ne *client.NetError
if errors.As(err, &ne) {
	fmt.Println(ne.Code, ne.Name) // -105 ERR_NAME_NOT_RESOLVED
}
```

### Alternativ

| Fält                          | Effekt                                                                                                 |
| :---------------------------- | :----------------------------------------------------------------------------------------------------- |
| `UserAgent`                   | Ersätter den standardmässiga Chrome User-Agent. En `User-Agent`-förfrågningshuvud har fortfarande företräde. |
| `AcceptLanguage`              | Ersätter `en-US,en;q=0.9`.                                                                             |
| `Proxy`                       | Chromiums proxyregler, till exempel `http://127.0.0.1:8080` eller `socks5://127.0.0.1:1080`. Tomt värde innebär direkt anslutning. |
| `DisableHTTP2`, `DisableQUIC` | Stänger av protokollen.                                                                                |
| `ForceHTTP3`                  | Använder QUIC för alla origins utan att vänta på en `Alt-Svc`-annonsering.                             |
| `InsecureSkipVerify`          | Accepterar alla servercertifikat.                                                                      |
| `Pool`                        | Motorsessioner som ska användas. Nil använder `client.DefaultPool()`, som tillåter 64 samtidiga förfrågningar. |

Transporter med lika alternativ delar en Chromium-`URLRequestContext`, så att anslutningspooler, HTTP/2-sessioner och QUIC-sessioner återanvänds mellan förfrågningar. Genom att ange `HQQ_CHROME_NET_SERVER=/path/to/chrome_net_server` ersätts den inbäddade arbetsbinären, till exempel med en lokal build.

## Så fungerar det

| Lager               | Vad som körs där                                                                                                                                                       |
| :------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Ditt program        | `http.Client` och `chromenet.Transport`, ren Go.                                                                                                                       |
| Processgräns        | HQQ shared-memory RPC. Gränssnittet är `chrome_net.idl`, som kompileras av `hqq-bindgen` till Go-klienten och C-serverlimmet.                                          |
| `chrome_net_server` | Ett litet C++-lager ovanpå Chromium: en nätverkstråd som äger alla `//net`-objekt, och sessionstrådar som blockerar på en förfrågan tills nätverkstråden svarar. |
| Chromium `//net`    | `URLRequestContext`, `URLRequest`, HTTP/2- och QUIC-sessioner, BoringSSL.                                                                                              |

IDL:en har sex operationer som är viktiga: skapa en motor från `key=value`-alternativ, starta en förfrågan med dess huvud (`METHOD URL` plus huvudrader) och kroppens längd, skriva delar av förfrågningskroppen, vänta på svarshuvudet, läsa den avkodade svarskroppen och förstöra handtagen. Ett förfrågningshandtag deklarerar `@cancel`, så att ett övergivet anrop avbryter förfrågan inne i Chromium i stället för att låta den köras till slut.

Att isolera stacken i en arbetsprocess är lika mycket en säkerhetsegenskap som en paketeringsfråga. Chromiums nätverkskod tolkar fientlig indata från det öppna internet. Om den kraschar kraschar inte din Go-tjänst med: anropet misslyckas med ett fel, sessionen kasseras och klienten startar en ny arbetsprocess nästa gång den behövs.

## Reproducerbart byggd, fastlagd med SHA-256

En 25 MB stor binär som innehåller Chromium är bara tillförlitlig om du exakt kan ange vad som ingick i den. `chromenet` följer Chromium Stable på samma sätt som `gosuda.org/sqlite3` följer SQLite: källkoden vendoreras aldrig, den bara fastlags.

| Fastlagt                                                                                                                 | Var                            |
| :----------------------------------------------------------------------------------------------------------------------- | :----------------------------- |
| Chromium-version och dess officiella `-lite`-källarkiv                                                                   | `third_party/chromium/VERSION` |
| SHA-256 för arkivet och för varje verktygskedja som versionen fastlägger: clang, Rust, GN, ninja, Windows clang-runtime, xwin | `third_party/chromium/SHA256`  |
| MSVC CRT- och Windows SDK-versioner som hämtas via xwin                                                                  | `third_party/chromium/WINSDK`  |
| Versionen av sandbox-körmiljön (`rungv`), gästavbildningens digest och Debian-snapshotdatumet                            | `scripts/engine/BUILDENV`      |
| HQQ:s C-körmiljö                                                                                                         | `go.mod` och `go.sum`          |

Den aktuella fastläggningen är Chromium `157.0.8103.38`. En nedladdning som inte stämmer med sin fastläggning stoppar bygget. Versionen tillämpar exakt en patch på Chromium: på Linux byggs `chrome_net_server` utan NSS, och servercertifikat verifieras enbart mot Chrome Root Store, så att binären inte behöver något utöver libc.

Linux- och Windows-motorerna byggs inuti [gvx](https://gosuda.org/blog/posts/gvx-cross-platform-linux-containers-z48916d01)-sandlådor: processer utan privilegier, med en fastlagd gästavbildning och ingen värddaemon. Windows-motorerna korskompileras på Linux med Chromiums clang-cl och lld-link mot Windows SDK som xwin hämtar från Microsoft. macOS-motorerna byggs nativt på en macOS-runner, eftersom Chromiums macOS-build kräver Xcode.

Ombyggnad av samma commit gav bitidentiska binärer över separata CI-körningar. Releasebinärerna är bitidentiska med de som en tidigare CI-körning byggde för den commit som införde motorn i monorepot, på alla sex plattformar.

| Plattform       | Binärstorlek |
| :-------------- | -----------: |
| `linux/amd64`   |      27,9 MB |
| `linux/arm64`   |      27,4 MB |
| `darwin/amd64`  |      26,2 MB |
| `darwin/arm64`  |      23,9 MB |
| `windows/amd64` |      27,3 MB |
| `windows/arm64` |      25,8 MB |

Linux-motorerna beror enbart på `libc`, `libm`, `libdl`, `libpthread` och `libgcc_s`, och kräver glibc 2.31 eller nyare.

### Följa Chromium och releaser

Ett schemalagt arbetsflöde kontrollerar Chromium Stable var sjätte timme. För en ny release skriver det om fastläggningarna, bygger och testar alla sex motorer på en gren `chromium/<version>`, attesterar binärerna och committar fastläggningarna tillsammans med de inbäddade binärerna, deras digester och licenstexterna till `main` i en enda commit. I monorepot ger filvisa `OWNERS`-filer arbetsflödets bot exakt dessa filer, så uppdateringen landar utan ett manuellt steg. Ändringar av själva byggreceptet (Windows SDK-fastläggningen och patchen) kräver fortfarande en ägare.

Varje releasebinär bär en byggprovenienattestation från arbetsflödet som byggde den:

```bash
gh attestation verify chrome_net_server_linux_amd64 --repo gosuda/chromenet
```

Licenstexterna för vad varje binär innehåller (Chromium, BoringSSL, QUICHE och resten av byggkedjan) genereras av Chromiums egna `tools/licenses/licenses.py` och levereras med releasen som `NOTICE_<os>_<arch>.txt`.

## Begränsningar

- `DialWebSocket` är en ren Go-klient för RFC 6455 och använder inte Chromium-stacken.
- `DialWebTransport` är en implementation i samma process. Den når inte en server.
- Standardfingeravtrycken för JA4 och HTTP/2 är de för den fastlagda Chromium-versionen. Chromium ändrar dem från version till version, och `chromenet` följer efter.
- macOS-binärerna är ad-hoc-signerade och inte notariserade.
- En arbetsprocess kostar minne. Varje motorbinär är 24 till 28 MB på disk, och Chromiums körmiljö startar en trådpool och en nätverkstråd.
- En motorsession hålls tills svarskroppen stängs. Ett program som läcker svarskroppar kommer att tömma poolen.

## Källa

Repositoryt finns på [github.com/gosuda/chromenet](https://github.com/gosuda/chromenet) och speglas från `//net/chromenet` i gosuda-monorepot. Byggreceptet finns i `scripts/engine`, motorlimmet i `engine/` och gränssnittet i `chrome_net.idl`.
