-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
x/net/http2: Above go1.19, the server cannot send packets to the client #56753
Comments
This code is doing something extremely complicated and unusual involving sending a request on a connection, hijacking the connection, and then starting a new HTTP/2 session on the hijacked connection. This could use a much more focused reproduction case to understand what might have changed. |
The client can work and run regardless of the go version, the main problem is the server (this problem will occur above 1.19) and I simplify the code : serverpackage main
import (
"fmt"
"net"
"net/http"
"time"
"golang.org/x/net/http2"
)
type B struct {
}
func (b *B) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
resp.Write([]byte("long"))
resp.(http.Flusher).Flush()
time.Sleep(2 * time.Second)
resp.Write([]byte("long end"))
resp.(http.Flusher).Flush()
return
}
type A struct {
}
func (a *A) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
conn, err := UpgradeServerTCP(resp, req)
if err != nil {
return
}
fmt.Println(resp)
go func() {
server := &http2.Server{}
server.ServeConn(conn, &http2.ServeConnOpts{
Handler: &B{},
})
}()
}
func UpgradeServerTCP(w http.ResponseWriter, req *http.Request) (c net.Conn, err error) {
hijacker, ok := w.(http.Hijacker)
if !ok {
return nil, fmt.Errorf("http upgrade failed")
}
h := w.Header()
h.Set("Content-Length", "2")
h.Set("Content-Type", "application/json")
w.WriteHeader(101)
w.Write([]byte("{}"))
conn, _, err := hijacker.Hijack()
if err != nil {
return nil, err
}
return conn, nil
}
func main() {
err := http.ListenAndServe("127.0.0.1:10001", &A{})
if err != nil {
panic(err)
}
} clientpackage main
import (
"bufio"
"bytes"
"context"
"fmt"
"io"
"net"
"net/http"
"golang.org/x/net/http2"
)
type readCloserWrap struct {
io.Reader
io.Closer
}
type CP struct {
client *http2.ClientConn
}
func (c *CP) GetClientConn(req *http.Request, addr string) (*http2.ClientConn, error) {
return c.client, nil
}
func (c *CP) MarkDead(conn *http2.ClientConn) {
conn.Close()
}
func UpgradeClientTCP(ctx context.Context, isProxy bool, conn net.Conn, req *http.Request) (c net.Conn, err error) {
req.Header.Set("Upgrade", "Tcp")
req.Header.Set("Connection", "Upgrade")
req.Header.Set("Content-Type", "application/json")
defer func() {
if err != nil {
conn.Close()
}
}()
err = req.Write(conn)
if err != nil {
return nil, err
}
br := bufio.NewReader(conn)
resp, err := http.ReadResponse(br, req)
if err != nil {
return nil, err
}
if resp.StatusCode != 101 {
return nil, fmt.Errorf("statusCode - %d", resp.StatusCode)
}
return &connWithBufReader{conn, br}, nil
}
type connWithBufReader struct {
net.Conn
br *bufio.Reader
}
func (p *connWithBufReader) Read(b []byte) (int, error) {
return p.br.Read(b)
}
func main() {
conn, _ := (&net.Dialer{}).Dial("tcp", "127.0.0.1:10001")
req, err := http.NewRequest("POST", "/", bytes.NewReader([]byte("{}")))
if err != nil {
conn.Close()
return
}
req.Header.Set("Content-Type", "application/json")
rawConn, err := UpgradeClientTCP(context.Background(), false, conn, req)
if err != nil {
return
}
pr, _ := io.Pipe()
mr := io.MultiReader(bytes.NewReader([]byte(`{"a":"a"}`)), pr)
req, _ = http.NewRequest("POST", "http://127.0.0.1:10001/long", &readCloserWrap{mr, pr})
req.Header.Set("Content-Type", "application/json")
cp := &CP{}
h2t := &http2.Transport{ConnPool: cp, AllowHTTP: true}
h2c, err := h2t.NewClientConn(rawConn)
if err != nil {
fmt.Println(err)
return
}
cp.client = h2c
cli := &http.Client{
Transport: h2t,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
resp, err := cli.Do(req)
if err != nil {
fmt.Println("err ", err)
return
}
defer resp.Body.Close()
data, _ := io.ReadAll(resp.Body)
fmt.Println(string(data))
} |
I know why this happened: The Just call writeHeader
And this writing is compatible with before 1.18.8.
|
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
From the packet capture point of view, the server does not send data packets to the client
server.go
client.go
What did you expect to see?
It is normal for versions below go 1.18.8
What did you see instead?
The text was updated successfully, but these errors were encountered: