---
id: 6e0bce9e22a4bf7d9672c59e67bcf5ec
author: Lemon Mint
title: 'gosuda.org/chromenet: Stiva de rețea a Chromium pentru Go, construită reproductibil dintr-o versiune fixată'
description: CGO_ENABLED=0 Un http.RoundTripper în Go care trimite cereri prin propria stivă //net a Chromium (HTTP/1.1, HTTP/2, QUIC/HTTP/3, BoringSSL) într-un worker HQQ izolat, cu sursa, toolchain-urile și șase binare încorporate fixate prin SHA-256 și construite în sandbox-uri gvx.
language: ro
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` este un `http.RoundTripper` pentru Go, care trimite cereri prin propria stivă de rețea a Chromium. Stiva este `//net` dintr-o versiune oficială Chromium: implementările sale HTTP/1.1, HTTP/2 și QUIC/HTTP/3, BoringSSL, configurația TLS a Chrome și Chrome Root Store. Aceasta rulează într-un proces worker separat, `chrome_net_server`, iar partea Go rămâne Go pur (`CGO_ENABLED=0`), comunicând cu worker-ul prin RPC cu memorie partajată [HQQ](https://gosuda.org/hqq).

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

## De ce să rulăm stiva de rețea a Chromium

Un program Go care comunică pe web cu `net/http` îi prezintă serverului un client Go. Acesta este comportamentul implicit corect pentru traficul serviciu-la-serviciu. Este însă greșit atunci când comportamentul serverului depinde de tipul de client pe care îl crede în fața sa: endpoint-uri care impun un browser, middleware anti-bot, CDN-uri care servesc versiuni diferite de protocol unor amprente TLS și HTTP/2 diferite, precum și teste care trebuie să reproducă ceea ce experimentează un browser real.

Biblioteca standard Go nu poate reproduce comportamentul pe fir al unui browser. `crypto/tls` are propria ordine a suitelor de cifruri și propriul set de extensii, iar `net/http` are propriile setări HTTP/2. Bibliotecile care imită un browser prin copierea octeților ClientHello ai acestuia continuă să copieze, lansare după lansare. `chromenet` urmează o altă cale: nu imită stiva de rețea Chromium, ci o rulează.

Măsurat pe motorul `darwin/arm64`, față de un serviciu public de amprentare (`tls.peet.ws`), configurația implicită raportează:

| Proprietate                         | Valoare observată                                                        |
| :---------------------------------- | :----------------------------------------------------------------------- |
| Protocol negociat                   | `h2`                                                                     |
| JA4                                 | `t13d1516h2_8daaf6152771_806a8c22fdea`                                   |
| Amprentă HTTP/2 (format Akamai)     | `1:65536;2:0;4:6291456;6:262144\|15663105\|0\|m,a,s,p`                   |
| User-Agent                          | Chrome desktop, de versiunea majoră a motorului, de exemplu `Chrome/157.0.0.0` |

Cu `ForceHTTP3`, cererile către `cloudflare-quic.com` și `www.google.com` au primit răspuns prin HTTP/3. Aceste valori provin de pe o singură platformă, dintr-o singură zi. Motorul este chiar stiva, deci valorile se schimbă atunci când se schimbă versiunea Chromium fixată.

## Utilizare

`NewTransport` returnează un `http.RoundTripper`. Folosiți-l oriunde ați folosi `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))
}
```

La prima cerere, modulul scrie binarul worker încorporat pentru platforma dumneavoastră în directorul cache al utilizatorului, verifică digestul său SHA-256 cu `gosuda.org/hqq/secureexec`, îl pornește cu o legătură de viață cu procesul părinte (`--parent-stdin`, astfel încât se oprește când se oprește procesul dumneavoastră) și deschide un pool de sesiuni. Nu este nevoie de nicio instalare.

Câteva comportamente urmează `http.Transport`, astfel încât `http.Client` să funcționeze neschimbat:

- Transport-ul nu urmează redirecționările și nu păstrează cookie-uri. `http.Client` face ambele lucruri, la fel ca în cazul `http.Transport`.
- Răspunsurile comprimate cu gzip, deflate, Brotli sau zstd sosesc decodificate, iar `resp.Uncompressed` este true.
- O cerere ține o sesiune a motorului până când corpul răspunsului este închis. Închideți corpurile.
- Anularea contextului cererii anulează cererea în interiorul Chromium.
- Erorile de rețea sunt valori `*client.NetError`, care conțin codul și numele erorii Chromium:

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

### Opțiuni

| Câmp                          | Efect                                                                                                      |
| :---------------------------- | :--------------------------------------------------------------------------------------------------------- |
| `UserAgent`                   | Înlocuiește User-Agent-ul implicit Chrome. Un antet de cerere `User-Agent` are în continuare prioritate.   |
| `AcceptLanguage`              | Înlocuiește `en-US,en;q=0.9`.                                                                              |
| `Proxy`                       | Reguli de proxy Chromium, precum `http://127.0.0.1:8080` sau `socks5://127.0.0.1:1080`. Valoarea goală înseamnă conexiune directă. |
| `DisableHTTP2`, `DisableQUIC` | Dezactivează protocoalele.                                                                                 |
| `ForceHTTP3`                  | Folosește QUIC pentru fiecare origine, fără a aștepta un anunț `Alt-Svc`.                                  |
| `InsecureSkipVerify`          | Acceptă orice certificat de server.                                                                        |
| `Pool`                        | Sesiunile motorului de utilizat. Nil folosește `client.DefaultPool()`, care permite 64 de cereri concurente. |

Transporturile cu opțiuni egale partajează un singur Chromium `URLRequestContext`, astfel încât pool-urile de conexiuni, sesiunile HTTP/2 și sesiunile QUIC sunt reutilizate între cereri. Setarea `HQQ_CHROME_NET_SERVER=/path/to/chrome_net_server` înlocuiește worker-ul încorporat, de exemplu cu un build local.

## Cum funcționează

| Strat               | Ce rulează acolo                                                                                                                                                                              |
| :------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Programul dumneavoastră | `http.Client` și `chromenet.Transport`, Go pur.                                                                                                                                          |
| Granița de proces   | RPC cu memorie partajată HQQ. Interfața este `chrome_net.idl`, compilată de `hqq-bindgen` în clientul Go și în glue-ul serverului C.                                                       |
| `chrome_net_server` | Un strat C++ mic peste Chromium: un fir de rețea care deține fiecare obiect `//net` și fire de sesiune care blochează până când firul de rețea răspunde la o cerere.                        |
| Chromium `//net`    | `URLRequestContext`, `URLRequest`, sesiuni HTTP/2 și QUIC, BoringSSL.                                                                                                                         |

IDL-ul are șase operații esențiale: crearea unui motor din opțiuni `key=value`, pornirea unei cereri cu antetul său (`METHOD URL` plus liniile de antet) și lungimea corpului, scrierea fragmentelor din corpul cererii, așteptarea antetului răspunsului, citirea corpului decodificat și distrugerea handle-urilor. Un handle de cerere declară `@cancel`, astfel încât un apel abandonat întrerupe cererea în interiorul Chromium, în loc să o lase să ruleze până la capăt.

Izolarea stivei într-un proces worker este atât o proprietate de siguranță, cât și una de împachetare. Codul de rețea al Chromium analizează date ostile provenite din internetul deschis. Dacă se blochează, serviciul dumneavoastră Go nu se blochează odată cu el: apelul eșuează cu o eroare, sesiunea este eliminată, iar clientul pornește un worker nou data viitoare când are nevoie de unul.

## Construit reproductibil, fixat prin SHA-256

Un binar de 25 MB care conține Chromium este demn de încredere doar dacă puteți spune exact ce a intrat în el. `chromenet` urmărește Chromium Stable în același mod în care `gosuda.org/sqlite3` urmărește SQLite: sursa nu este niciodată vendorizată, doar fixată.

| Fixare                                                                                                                   | Unde                           |
| :----------------------------------------------------------------------------------------------------------------------- | :----------------------------- |
| Versiunea Chromium și arhiva sa oficială de sursă `-lite`                                                                | `third_party/chromium/VERSION` |
| SHA-256 al arhivei și al fiecărui toolchain fixat de acea versiune: clang, Rust, GN, ninja, runtime-ul clang pentru Windows, xwin | `third_party/chromium/SHA256`  |
| Versiunile MSVC CRT și Windows SDK preluate prin xwin                                                                    | `third_party/chromium/WINSDK`  |
| Release-ul runtime-ului sandbox (`rungv`), digestul imaginii guest, data snapshot-ului Debian                            | `scripts/engine/BUILDENV`      |
| Runtime-ul C HQQ                                                                                                         | `go.mod` și `go.sum`           |

Fixarea curentă este Chromium `157.0.8103.38`. Un download care nu corespunde fixării sale oprește build-ul. Release-ul aplică exact un patch la Chromium: pe Linux, `chrome_net_server` este construit fără NSS și verifică certificatele serverelor doar față de Chrome Root Store, astfel încât binarul nu are nevoie de nimic în afara libc.

Motoarele Linux și Windows sunt construite în sandbox-uri [gvx](https://gosuda.org/blog/posts/gvx-cross-platform-linux-containers-z48916d01): procese neprivilegiate, cu o imagine guest fixată și fără daemon pe gazdă. Motoarele Windows sunt compilate încrucișat pe Linux, cu clang-cl și lld-link ale Chromium, împotriva Windows SDK pe care xwin îl preia de la Microsoft. Motoarele macOS se construiesc nativ pe un runner macOS, deoarece build-ul Chromium pentru macOS necesită Xcode.

Reconstruirea aceluiași commit a produs binare identice byte cu byte între rulări CI separate. Binarele de release sunt identice byte cu cele pe care o rulare CI anterioară le-a construit pentru commit-ul care a introdus motorul în monorepo, pe toate cele șase platforme.

| Platformă       | Dimensiune binar |
| :-------------- | ---------------: |
| `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 |

Motoarele Linux depind doar de `libc`, `libm`, `libdl`, `libpthread` și `libgcc_s` și necesită glibc 2.31 sau mai nou.

### Urmărirea Chromium și a release-urilor

Un workflow programat verifică Chromium Stable la fiecare șase ore. Pentru un release nou, rescrie fixările, construiește și testează toate cele șase motoare pe un branch `chromium/<version>`, atestă binarele și comite fixările împreună cu binarele încorporate, digesturile lor și textele licențelor pe `main`, într-un singur commit. În monorepo, fișierele `OWNERS` pe fiecare fișier acordă botului workflow-ului exact acele fișiere, astfel încât actualizarea intră fără un pas uman, în timp ce modificările rețetei de build în sine (fixarea Windows SDK și patch-ul) necesită în continuare un owner.

Fiecare binar de release are o atestare de proveniență a build-ului, emisă de workflow-ul care l-a construit:

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

Textele licențelor pentru fiecare componentă din binare (Chromium, BoringSSL, QUICHE și restul graficului de build) sunt generate de `tools/licenses/licenses.py` propriu al Chromium și livrate cu release-ul ca `NOTICE_<os>_<arch>.txt`.

## Limite

- `DialWebSocket` este un client RFC 6455 pur Go și nu folosește stiva Chromium.
- `DialWebTransport` este o implementare în proces. Nu ajunge la un server.
- Amprentele implicite JA4 și HTTP/2 sunt cele ale versiunii Chromium fixate. Chromium le modifică de la un release la altul, iar `chromenet` le urmează.
- Binarele macOS sunt semnate ad-hoc și nu sunt notarizate.
- Un proces worker consumă memorie. Fiecare binar de motor are 24 până la 28 MB pe disc, iar runtime-ul Chromium pornește un pool de fire de execuție și un fir de rețea.
- O sesiune a motorului este ținută până când corpul răspunsului este închis. Un program care scurge corpuri de răspuns va epuiza pool-ul.

## Sursă

Repository-ul este [github.com/gosuda/chromenet](https://github.com/gosuda/chromenet), oglindit din `//net/chromenet` din monorepo-ul gosuda. Rețeta de build se află în `scripts/engine`, glue-ul motorului în `engine/`, iar interfața în `chrome_net.idl`.
