Client:
func main() {
session := azuretls.NewSession()
defer session.Close()
session.SetTimeout(3 * time.Second)
// dupe settings: 6:262144,6:262144
if err := session.ApplyHTTP2("1:65536,2:0,3:1000,4:6291456,6:262144,6:262144|15663105|0|m,s,a,p"); err != nil {
panic(err)
}
resp, err := session.Do(&azuretls.Request{
Method: "GET",
Url: "https://localhost:8080",
})
if err != nil {
fmt.Printf("Request error: %v\n", err)
return
}
fmt.Println("Proto:", resp.HttpResponse.Proto)
}
Server:
func main() {
// Create a simple HTTP handler
handler := func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, World!\n"))
}
// Set up the server
server := &http.Server{
Addr: ":8080",
Handler: http.HandlerFunc(handler),
}
// Enable HTTP/2
http2.ConfigureServer(server, nil)
log.Println("Starting server on https://localhost:8080")
err := server.ListenAndServeTLS("cert.pem", "key.pem")
if err != nil {
log.Fatal(err)
}
}
Result on Client side:
Result on Server side:
Multiple errors flood like that (~150 lines/sec on localhost):
2025/11/13 10:01:58 http2: server connection error from 127.0.0.1:45838: connection error: PROTOCOL_ERROR
2025/11/13 10:01:58 http2: server connection error from 127.0.0.1:45850: connection error: PROTOCOL_ERROR
2025/11/13 10:01:58 http2: server connection error from 127.0.0.1:45858: connection error: PROTOCOL_ERROR
2025/11/13 10:01:58 http2: server connection error from 127.0.0.1:45874: connection error: PROTOCOL_ERROR
2025/11/13 10:01:58 http2: server connection error from 127.0.0.1:45876: connection error: PROTOCOL_ERROR
Same can be tested vs https://caddyserver.com/, it will hang until timeout.
Quickly observing, this PROTOCOL_ERROR coming from f.HasDuplicates() checking server.go#L1766, and then leads to server.go#L1560, which should send GoAway:
case ConnectionError:
if res.f != nil {
if id := res.f.Header().StreamID; id > sc.maxClientStreamID {
sc.maxClientStreamID = id
}
}
sc.logf("http2: server connection error from %v: %v", sc.conn.RemoteAddr(), ev)
sc.goAway(ErrCode(ev))
return true // goAway will handle shutdown
So I also capture connection to caddyserver.com and it looks like:

It is send GOAWAY's but looks like the client ignores it and tries to reconnect or something like that.
I guess the easiest fix will be to not allow SETTINGS ID duplication, but the https://tls.peet.ws/api/all is actually works with that and show two identical SETTINGS. Or client should respect GOAWAY's.
Client:
Server:
Result on Client side:
Result on Server side:
Multiple errors flood like that (~150 lines/sec on localhost):
Same can be tested vs
https://caddyserver.com/, it will hang until timeout.Quickly observing, this PROTOCOL_ERROR coming from f.HasDuplicates() checking server.go#L1766, and then leads to server.go#L1560, which should send GoAway:
So I also capture connection to caddyserver.com and it looks like:

It is send GOAWAY's but looks like the client ignores it and tries to reconnect or something like that.
I guess the easiest fix will be to not allow SETTINGS ID duplication, but the https://tls.peet.ws/api/all is actually works with that and show two identical SETTINGS. Or client should respect GOAWAY's.