go: net/http: http client regression building with js/wasm and running on Chrome: `net::ERR_H2_OR_QUIC_REQUIRED`
What version of Go are you using (go version
)?
$ go version go version go1.21.0 linux/amd64
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env
)?
go env
Output
$ go env GO111MODULE='' GOARCH='wasm' GOBIN='' GOCACHE='/home/h/.cache/go-build' GOENV='/home/h/.config/go/env' GOEXE='' GOEXPERIMENT='' GOFLAGS='' GOHOSTARCH='amd64' GOHOSTOS='linux' GOINSECURE='' GOMODCACHE='/home/h/go/pkg/mod' GONOPROXY='' GONOSUMDB='' GOOS='js' GOPATH='/home/h/go' GOPRIVATE='' GOPROXY='https://proxy.golang.org,direct' GOROOT='/usr/lib/go' GOSUMDB='sum.golang.org' GOTMPDIR='' GOTOOLCHAIN='auto' GOTOOLDIR='/usr/lib/go/pkg/tool/linux_amd64' GOVCS='' GOVERSION='go1.21.0' GCCGO='gccgo' GOWASM='' AR='ar' CC='gcc' CXX='g++' CGO_ENABLED='0' GOMOD='/home/h/code/pk/axesfull/go.mod' GOWORK='' CGO_CFLAGS='-O2 -g' CGO_CPPFLAGS='' CGO_CXXFLAGS='-O2 -g' CGO_FFLAGS='-O2 -g' CGO_LDFLAGS='-O2 -g' PKG_CONFIG='pkg-config' GOGCCFLAGS='-fPIC -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build4139731908=/tmp/go-build -gno-record-gcc-switches' uname -sr: Linux 6.4.8-arch1-1 LSB Version: n/a Distributor ID: Arch Description: Arch Linux Release: rolling Codename: n/a /usr/lib/libc.so.6: GNU C Library (GNU libc) stable release version 2.38.
What did you do?
NOTE: I only get these errors in Chromium based browsers, not in Firefox.
I have this following function:
type Account struct {
ID string `json:"id"`
}
func TestPersist(t *testing.T) {
fmt.Printf("runtime.Version(): %v\n", runtime.Version())
acc := Account{"5e7fd575-b5f5-4d7e-8fa7-70b5fef7662f"}
buf, _ := json.Marshal(acc)
r := bytes.NewBuffer(buf)
req, err := http.NewRequest("POST", "https://nk.axesfull.dev/v2/account/authenticate/device", r)
if err != nil {
t.Fatalf("could not create request: %v", err)
}
_, err = http.DefaultClient.Do(req)
if err != nil {
t.Fatalf("could not authenticate account: %v", err)
}
}
compiled with:
GOOS=js GOARCH=wasm go test -c -o test.wasm -run TestPersist
I ran this with the default wasm_exec.html
thats provided in $GOROOT/misc/wasm
with just adding test.v
to argv:
.
async function run() {
console.clear();
await go.run(inst);
go.arvc = ["js", "-test.v"]
inst = await WebAssembly.instantiate(mod, go.importObject); // reset instance
}
This works with go1.20, but not with go1.21.
Reproducible example available at https://github.com/jnowls/wasm-http-chrome - just run ./run.sh
What did you expect to see?
The browser successfully complete the request (as seen in Firefox and older versions of chrome)
What did you see instead?
Firefox go1.21 and go1.20:
Chrome go1.21 and go1.20:
About this issue
- Original URL
- State: closed
- Created a year ago
- Comments: 29 (17 by maintainers)
If I understand correctly, https://go.dev/cl/458395 which added support for streaming HTTP request bodies in wasm bulids, results requests failing when:
Also, it sounds like there is no workaround: Requests always fail when the above conditions are met.
Is that all correct?
If so, I agree that we should roll this change back. We should also backport the rollback to the next 1.21 minor release, as this is both a significant regression (you can’t send requests at all under these circumstances) and there is no workaround.
I see nobody disagree about revert, and submitted CL of revert! https://go-review.googlesource.com/c/go/+/522955
I agree that it should either be turned off entirely or opt-in, so that it always works as expected by default. The change that introduced this was https://go-review.googlesource.com/c/go/+/458395, so the first step would be to revert this (so that it’s working in tip) and then consider reintroducing it with an opt-in.
Thanks for the offer of contributing the fix @haruyama480 😃
I don’t see any quick and easy way to determine whether or not the browser will complete a HTTP request with H2 or H1 before it happens 😦. Just disabling request streaming for HTTP also probably isn’t a proper fix since POST requests with HTTPS+H1 (what OP describes) also fail.
If we do end up going with implementing it such that clients select whether to use the streaming API, that should probably not be done with a global variable, it will only make things more complicated to deal with, though I’m not sure what the best alternative to give the client the control is.
I personally think request streaming should be reverted for now, and revisited at a later point where the streaming API provides proper mechanisms to fallback in case of no support from the server.
That is perfectly okay with me! I was just excited that I found a technical way to do what we wanted!
I don’t think adding explicit type assertions into the code is the way to go. The problem with that is that it becomes impossible for users to wrap the transport transparently. I’d sooner see we allow the user to be more explicit about opting in. We already have a pattern for configuring the HTTP request via headers (See https://go.googlesource.com/go/+/refs/heads/master/src/net/http/roundtrip_js.go#26 and friends). We could probably just add another header that would opt into the behavior?
CC @hawkinsw. Please feel free to open a CL with a fix, sorry for the inconvenience!
@jnowls. Correct. Sorry, should have worded it better.