Nauczanie programu Go, aby mówił jak Chrome

Jeśli kiedykolwiek podróżowałeś za granicę i zamówiłeś kawę zdaniem wziętym wprost z podręcznika, znasz spojrzenie, jakim obdarza cię barista. Twoja gramatyka jest poprawna. Każde słowo figuruje w słowniku. A mimo to w ciągu pół sekundy wszyscy w kawiarni wiedzą, że przyjechałeś z zewnątrz.
Standardowy klient HTTP w Go ma nieco podobny problem. Jest świetnym rozwiązaniem domyślnym: niezawodny, czytelny i gotowy do poważnej pracy zaraz po instalacji. Gdy jednak Twój program nawiązuje połączenie z serwerem, jednoznacznie wygląda jak program napisany w Go. W większości przypadków nikomu to nie przeszkadza. Niektóre witryny, sieci CDN i konfiguracje testowe zwracają jednak uwagę na to, kto puka do drzwi, i zachowują się zupełnie inaczej w zależności od tego, z jakim rodzajem klienta mają do czynienia.
Dlatego stworzyliśmy gosuda.org/chromenet. Zamiast uczyć standardowego klienta Go udawania przeglądarki na przyzwoitym poziomie, chromenet pozwala programowi w Go komunikować się z siecią za pomocą tego samego silnika sieciowego, którego używa Google Chrome, czyli własnego kodu sieciowego Chromium. Niezależnie od tego, czy sprawdzasz, jak Twoje konfiguracje testowe traktują ruch przeglądarkowy, czy łączysz się z witryną za wymagającą siecią CDN, komunikacja przez silnik Chrome eliminuje całą warstwę domysłów.
Nie musisz przepisywać swojej aplikacji, aby z tego skorzystać. W kodzie działa ono jako bezpośredni zamiennik http.Transport. Nadal używasz http.Client tak jak zawsze, a chromenet obsługuje rozmowę w tle.
Aby wypróbować najnowsze wydanie, zainstaluj je poleceniem go get:
1go get gosuda.org/chromenet@v0.2.10157081033800
Następnie przekaż jego transport swojemu klientowi:
1client := http.Client{
2 Transport: chromenet.NewTransport(chromenet.Options{}),
3}
W tym wydaniu pełen zakres nowoczesnego ruchu sieciowego przechodzi przez silnik Chrome. Obejmuje on zwykłe żądania webowe, WebSocket oraz WebTransport. Wcześniejsze wersje obsługiwały poprawnie jedynie zwykłe żądania, podczas gdy WebSocket i WebTransport były namiastkami. Oznaczało to, że Twoje wstępne powitanie przy drzwiach brzmiało jak Chrome, ale po przejściu na inny styl rozmowy przykrywka mogła się zsunąć. To wydanie zastępuje te namiastki prawdziwymi rozwiązaniami.
Czy skoro korzystamy z własnego kodu sieciowego Chromium, to czy Twoja zwarta kompilacja w Go zamienia się w rozbudowany problem z kompilatorem C? Na szczęście nie. Kod Go pozostaje zwykłym kodem Go, bez cgo i bez potrzeby używania kompilatora C.
Zamiast wszywać kod przeglądarki bezpośrednio do pliku binarnego, chromenet uruchamia silnik jako osobny, mały program obok Twojego programu w Go. Wyobraź sobie zatrudnienie biegłego lokalnego tłumacza, który idzie obok Ciebie, zamiast próbować odtworzyć Twoje własne struny głosowe. Ponieważ silnik działa w tym osobnym programie, awaria w jego wnętrzu nie może zatrzymać Twojej usługi.

Twój program w Go pozostaje zwykłym kodem Go, podczas gdy mały program towarzyszący przenosi ruch przez silnik sieciowy Chromium.
Na Twojej stacji roboczej ani na serwerach nie trzeba też instalować niczego dodatkowego. Pliki binarne silnika dla systemów Linux, macOS i Windows, zarówno dla architektur Intel/AMD, jak i ARM, są dołączone bezpośrednio do modułu Go i są weryfikowane przed uruchomieniem.
Dołączanie plików binarnych do modułu zrozumiale rodzi pytanie dla każdego, komu zależy na zaufaniu, dlatego chcieliśmy udzielić jasnej odpowiedzi. Pliki binarne są budowane w sposób powtarzalny z konkretnego wydania Chromium, obecnie Chromium 157, więc każdy może je przebudować i otrzymać dokładnie te same bajty, a każde wydanie jest poświadczane przez GitHub. Aby nadążyć za światem przeglądarek bez ręcznej pracy, zaplanowane zadanie śledzi stabilny kanał Chromium i automatycznie aktualizuje wersję, gdy pojawia się nowy Chrome.
Co z uTLS?
Jeśli wcześniej próbowałeś sprawić, by klient Go wyglądał jak przeglądarka, prawdopodobnie spotkałeś uTLS. To znakomita praca: biblioteka w Go, która pozwala ubrać bardzo pierwszą wiadomość bezpiecznego połączenia tak, by przypominała Chrome, Firefox lub dowolną inną przeglądarkę. Jest mała, czysto napisana w Go i nic dodatkowego nie działa obok Twojego programu.
Haczyk tkwi w tym, jak daleko sięga ten kostium. uTLS obejmuje uzgadnianie połączenia, czyli pierwsze kilka słów rozmowy. Wszystko, co następuje później, nadal trzeba zbudować samodzielnie: warstwę HTTP/2 trzeba podłączyć i dostroić ręcznie, HTTP/3 to osobna historia, a WebSocket lub WebTransport wymagają jeszcze więcej kleju. Gotowe profile Chrome są również kopiami, więc ktoś musi je aktualizować za każdym razem, gdy Chrome zmienia swoje przyzwyczajenia. To trochę jak naśladowanie akcentu danej osoby, podczas gdy reszta jej mowy ciała wciąż ją zdradza.
chromenet idzie w przeciwnym kierunku. Nie imituje Chrome, lecz zleca rozmowę własnemu silnikowi Chrome, dzięki czemu uzgadnianie połączenia, sposób ponownego wykorzystania połączeń, przejścia na nowsze protokoły oraz transporty pochodzą z jednego miejsca i zmieniają się razem wraz z rozwojem Chromium.
Ten wybór ma swoje koszty, o czym mówimy otwarcie. uTLS jest lżejszy i pozwala skonstruować dowolne powitanie, jakie tylko sobie wyobrazisz, natomiast chromenet wnosi pomocnika o rozmiarze 25 MB i zapewnia zachowanie Chrome, a nie własne, niestandardowe. Jeśli zależy Ci na niewielkiej liczbie zależności i pełnej kontroli nad uzgadnianiem połączenia, uTLS nadal jest świetnym wyborem.
Każde rozwiązanie ma swoje kompromisy i chcemy o ograniczeniach tutaj powiedzieć wprost. Dołączenie prawdziwego silnika sieciowego przeglądarki nie jest niewielkie: każdy plik binarny silnika ma około 25 MB. Na macOS pliki binarne nie są notaryzowane. Na Linuxie system musi mieć glibc w wersji 2.31 lub nowszej. Jeśli te ograniczenia pasują do Twojej konfiguracji, uzyskujesz prawdziwą sieć Chrome, zmieniając jedno pole transportu.
Projekt znajduje się pod adresem github.com/gosuda/chromenet. Jeśli znasz projekty pokrewne z rodziny open source gosuda, czyli gosuda.org/hqq i gosuda.org/sqlite3, ich duch będzie Ci znajomy: zapożyczanie sprawnych, ciężkich narzędzi z szerszego świata oprogramowania i sprawianie, by czuły się jak u siebie w zwykłym Go.
Zatem jeśli sięgasz po uTLS, ponieważ potrzebujesz, by Twój program w Go brzmiał jak przeglądarka, wypróbuj chromenet. Może być dobrą alternatywą dla uTLS, zwłaszcza gdy potrzebujesz czegoś więcej niż żądań HTTPS: HTTP/2, HTTP/3, WebSocket i WebTransport przechodzą wszystkie przez ten sam silnik Chrome, bez kodu pośredniczącego po Twojej stronie.