add http2 support

Add http2 using the go-1.6 net/http built-in support.

net/http's http2 doesn't support hijacking, so instead of hijacking the
inital CONNECT tcp connection, tunnel proxied requests/responses over
the reader and writer streams of the CONNECT request.
This commit is contained in:
Barna Csorogi 2016-03-26 18:52:06 +01:00
parent 6dae9bda0c
commit a4a691b7b8
2 changed files with 81 additions and 12 deletions

74
proxy/mitmconn.go Normal file
View file

@ -0,0 +1,74 @@
package proxy
import (
"io"
"net"
"net/http"
"time"
)
type mitmConn struct {
w FlushWriter
r io.Reader
remoteAddr addr
closed chan struct{}
}
type FlushWriter interface {
io.Writer
http.Flusher
}
func newMitmConn(w FlushWriter, r io.Reader, remoteAddr string) *mitmConn {
return &mitmConn{
w: w,
r: r,
remoteAddr: addr(remoteAddr),
closed: make(chan struct{}),
}
}
func (c *mitmConn) Read(b []byte) (int, error) {
return c.r.Read(b)
}
func (c *mitmConn) Write(b []byte) (int, error) {
n, err := c.w.Write(b)
c.w.Flush()
return n, err
}
func (c *mitmConn) Close() error {
close(c.closed)
return nil
}
func (c *mitmConn) RemoteAddr() net.Addr {
return c.remoteAddr
}
func (c *mitmConn) LocalAddr() net.Addr {
panic("not implemented")
}
func (c *mitmConn) SetDeadline(t time.Time) error {
panic("not implemented")
}
func (c *mitmConn) SetReadDeadline(t time.Time) error {
panic("not implemented")
}
func (c *mitmConn) SetWriteDeadline(t time.Time) error {
panic("not implemented")
}
type addr string
func (a addr) String() string {
return string(a)
}
func (a addr) Network() string {
return "tcp"
}

View file

@ -53,7 +53,7 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
func (p *Proxy) handle(w http.ResponseWriter, r *http.Request) error { func (p *Proxy) handle(w http.ResponseWriter, r *http.Request) error {
if r.Method == "CONNECT" { if r.Method == "CONNECT" {
return p.handleConnect(w, r.Host) return p.handleConnect(w, r)
} }
resp, err := forward(r) resp, err := forward(r)
if err != nil { if err != nil {
@ -88,24 +88,19 @@ func (p *Proxy) proxyResponse(w *ResponseWriter, r *ResponseReader) error {
return nil return nil
} }
func (p *Proxy) handleConnect(w http.ResponseWriter, host string) error { func (p *Proxy) handleConnect(w http.ResponseWriter, r *http.Request) error {
if p.ml == nil { if p.ml == nil {
return fmt.Errorf("CONNECT received but mitm is not enabled") return fmt.Errorf("CONNECT received but mitm is not enabled")
} }
h, ok := w.(http.Hijacker)
if !ok {
return fmt.Errorf("connection cannot be hijacked")
}
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
conn, _, err := h.Hijack() fw := w.(FlushWriter)
fw.Flush()
conn := newMitmConn(fw, r.Body, r.RemoteAddr)
sconn, err := p.ml.Serve(conn, r.Host)
if err != nil { if err != nil {
return err return err
} }
sconn, err := p.ml.Serve(conn, host)
if err != nil {
conn.Close()
return err
}
sconn.Close() // TODO: reuse this connection for https requests sconn.Close() // TODO: reuse this connection for https requests
<-conn.closed
return nil return nil
} }