@@ -3,19 +3,26 @@ package httpproxy
33import (
44 "crypto/tls"
55 "encoding/base64"
6+ "fmt"
7+ "io"
8+ "net"
69 "net/http"
10+ "regexp"
711 "strings"
12+ "sync"
813 "sync/atomic"
914)
1015
1116type ConnectAction int
1217
1318const (
14- ConnectAccept = ConnectAction (iota )
15- ConnectReject
19+ ConnectNone = ConnectAction (iota )
20+ ConnectOk
1621 ConnectMitm
1722)
1823
24+ var hasPort = regexp .MustCompile (`:\d+$` )
25+
1926type Proxy struct {
2027 SessionNo int64
2128 Rt http.RoundTripper
@@ -80,6 +87,77 @@ func doAuth(ctx *Context, w http.ResponseWriter, r *http.Request) bool {
8087 return true
8188}
8289
90+ func doConnect (ctx * Context , w http.ResponseWriter , r * http.Request ) (w2 http.ResponseWriter , r2 * http.Request ) {
91+ if r .Method != "CONNECT" {
92+ w2 , r2 = w , r
93+ return
94+ }
95+ hij , ok := w .(http.Hijacker )
96+ if ! ok {
97+ if r .Close {
98+ defer r .Body .Close ()
99+ }
100+ err := fmt .Errorf ("httpserver does not support hijacking" )
101+ doError (ctx , err )
102+ return
103+ }
104+ hijConn , _ , err := hij .Hijack ()
105+ if err != nil {
106+ if r .Close {
107+ defer r .Body .Close ()
108+ }
109+ doError (ctx , err )
110+ return
111+ }
112+ ctx .ConnectAction = ConnectOk
113+ host := r .URL .Host
114+ if ctx .Prx .OnConnect != nil {
115+ ctx .ConnectAction , host = ctx .Prx .OnConnect (ctx , host )
116+ if ctx .ConnectAction == ConnectNone {
117+ ctx .ConnectAction = ConnectOk
118+ }
119+ }
120+ if ! hasPort .MatchString (host ) {
121+ host += ":80"
122+ }
123+ switch ctx .ConnectAction {
124+ case ConnectOk :
125+ targetConn , err := net .Dial ("tcp" , host )
126+ if err != nil {
127+ hijConn .Close ()
128+ doError (ctx , err )
129+ return
130+ }
131+ _ , err = hijConn .Write ([]byte ("HTTP/1.0 200 OK\r \n \r \n " ))
132+ if err != nil {
133+ hijConn .Close ()
134+ targetConn .Close ()
135+ doError (ctx , err )
136+ return
137+ }
138+ var wg sync.WaitGroup
139+ wg .Add (2 )
140+ go func () {
141+ _ , err := io .Copy (targetConn , hijConn )
142+ if err != nil {
143+ doError (ctx , err )
144+ }
145+ wg .Done ()
146+ }()
147+ go func () {
148+ _ , err := io .Copy (hijConn , targetConn )
149+ if err != nil {
150+ doError (ctx , err )
151+ }
152+ wg .Done ()
153+ }()
154+ hijConn .Close ()
155+ targetConn .Close ()
156+ }
157+
158+ return
159+ }
160+
83161func doRequest (ctx * Context , w http.ResponseWriter , r * http.Request ) bool {
84162 r .RequestURI = ""
85163 if ! r .URL .IsAbs () {
@@ -148,15 +226,15 @@ func (prx *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
148226 }
149227 removeProxyHeaders (r )
150228
151- // doConnect
152-
153- req := r
154-
155- if doRequest (ctx , w , req ) {
229+ if w2 , r2 := doConnect (ctx , w , r ); r2 != nil {
230+ w , r = w2 , r2
231+ } else {
156232 return
157233 }
158234
159- if doResponse (ctx , w , req ) {
235+ if doRequest (ctx , w , r ) {
160236 return
161237 }
238+
239+ doResponse (ctx , w , r )
162240}
0 commit comments