hashicorp / yamux

Golang connection multiplexing library
Mozilla Public License 2.0
2.25k stars 236 forks source link

http.Serve(session, nil) hangs in abortPendingRead() when hijack is used #57

Closed hagna closed 6 years ago

hagna commented 6 years ago

This may be a misuse of yamux instead of a yamux issue, but I just wanted to let you know that the following code hangs for me in hijack() (really abortPendingRead() in go/src/net/http/server.go:3341) on go version devel +3067376 Mon Feb 26 22:10:51 2018 +0000 linux/amd64.

package main

import (
    "context"
    "flag"
    "fmt"
    "github.com/hashicorp/yamux"
    "net"
    "net/http"
    "time"
)

var endpoint = flag.String("ep", "127.0.0.1:8000", "endpoint")

func client() {
    // Get a TCP connection
    conn, err := net.Dial("tcp", *endpoint)
    if err != nil {
        panic(err)
    }

    // Setup client side of yamux
    session, err := yamux.Client(conn, nil)
    if err != nil {
        panic(err)
    }
    http.Serve(session, nil)

}

func server() {
    // Accept a TCP connection
    listener, err := net.Listen("tcp", *endpoint)
    if err != nil {
        panic(err)
    }
    conn, err := listener.Accept()
    if err != nil {
        panic(err)
    }

    // Setup server side of yamux
    session, err := yamux.Server(conn, nil)
    if err != nil {
        panic(err)
    }
    tr := &http.Transport{
        DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
            return session.Open()
        },
    }
    client := &http.Client{Transport: tr}
    resp, err := client.Get("http://127.0.0.1:8000/works")
    if err != nil {
        panic(err)
    }
    fmt.Println(resp)
    fmt.Println("hanging in abortPendingRead()...")
    resp, err = client.Get("http://127.0.0.1:8000/bug")
    if err != nil {
        fmt.Println("we should not see this because it should hang in abortPendingRead()")
        fmt.Println(err)
    }
    fmt.Println(resp)

}

func main() {
    flag.Parse()
    http.HandleFunc("/works", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "this works")
    })
    http.HandleFunc("/bug", func(w http.ResponseWriter, r *http.Request) {
        h, ok := w.(http.Hijacker)
        if !ok {
            fmt.Printf("does not support hijack")
        }
        c, _, err := h.Hijack()
        if err != nil {
            panic(err)
        }
        fmt.Fprintf(c, "this works")
        c.Close()
    })

    go server()
    time.Sleep(90 * time.Millisecond)
    client()
}
hagna commented 6 years ago

go version go1.10 linux/amd64 works. go version devel +0e8b711 Tue Mar 6 18:37:19 2018 +0000 linux/amd64 also works.