Skip to content

Instantly share code, notes, and snippets.

@gadelkareem
Last active March 14, 2019 00:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gadelkareem/3484fe05747f9fcc55b408cf23e92484 to your computer and use it in GitHub Desktop.
Save gadelkareem/3484fe05747f9fcc55b408cf23e92484 to your computer and use it in GitHub Desktop.
io.copy hangs on hijacked connection
package main
import (
"context"
"fmt"
"github.com/gadelkareem/go-helpers"
"io"
"net"
"net/http"
"sync"
"sync/atomic"
"time"
)
const Timeout = 5 * time.Second
var counter int64
func main() {
go func() {
for range time.Tick(Timeout) {
fmt.Printf("%d requests being processed\n", atomic.LoadInt64(&counter))
}
}()
s := &http.Server{
Addr: ":8888",
Handler: http.HandlerFunc(Serve),
}
s.ListenAndServe()
}
func Serve(w http.ResponseWriter, r *http.Request) {
atomic.AddInt64(&counter, 1)
// the fix
defer func() {
atomic.AddInt64(&counter, -1)
}()
if r.Method == http.MethodConnect {
// HTTPS
hij, ok := w.(http.Hijacker)
if !ok {
return
}
hj, bufrw, err := hij.Hijack()
h.PanicOnError(err)
bufrw.WriteString("HTTP/1.0 200 OK\r\n\r\n")
bufrw.Flush()
target, err := net.DialTimeout("tcp", r.URL.Host, Timeout)
h.PanicOnError(err)
// Not sure if this is needed since we add a context timeout
// Just leaving it for the example
dl := time.Now().Add(Timeout)
err = hj.SetDeadline(dl)
h.PanicOnError(err)
err = target.SetDeadline(dl)
h.PanicOnError(err)
var wg sync.WaitGroup
ctx, cancel := context.WithTimeout(r.Context(), Timeout)
defer cancel()
go func() {
for {
select {
case <-ctx.Done():
hj.Close()
target.Close()
println("context canceled")
return
}
}
}()
targetTCP, _ := target.(*net.TCPConn)
hjTCP, _ := hj.(*net.TCPConn)
wg.Add(2)
go copyAndClose(targetTCP, hjTCP, &wg)
go copyAndClose(hjTCP, targetTCP, &wg)
wg.Wait()
} else {
//ignore HTTP for this example
}
return
}
func copyAndClose(dst, src *net.TCPConn, wg *sync.WaitGroup) {
io.Copy(dst, src)
dst.CloseWrite()
src.CloseRead()
wg.Done()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment