-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathhttp_handler.go
More file actions
149 lines (130 loc) · 3.71 KB
/
Copy pathhttp_handler.go
File metadata and controls
149 lines (130 loc) · 3.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package socks
import (
"bytes"
"strings"
"fmt"
"io"
"net"
"net/http"
"net/http/httputil"
"net/url"
)
// HTTPProxy is an HTTP Handler that serve CONNECT method and
// route request to proxy server by Router.
type HTTPProxyHandler struct {
scheme string
forward Dialer
*httputil.ReverseProxy
}
// NewHTTPProxy constructs one HTTPProxy
func NewHTTPProxyHandler(scheme string, forward Dialer, transport http.RoundTripper) *HTTPProxyHandler {
return &HTTPProxyHandler{
scheme: scheme,
forward: forward,
ReverseProxy: &httputil.ReverseProxy{
Director: director,
Transport: transport,
},
}
}
func director(request *http.Request) {
u, err := url.Parse(request.RequestURI)
if err != nil {
return
}
request.RequestURI = u.RequestURI()
valueConnection := request.Header.Get("Proxy-Connection")
if valueConnection != "" {
request.Header.Del("Connection")
request.Header.Del("Proxy-Connection")
request.Header.Add("Connection", valueConnection)
}
}
// ServeHTTPTunnel serve incoming request with CONNECT method, then route data to proxy server
func (h *HTTPProxyHandler) ServeHTTPTunnel(response http.ResponseWriter, request *http.Request) {
var conn net.Conn
if hj, ok := response.(http.Hijacker); ok {
var err error
if conn, _, err = hj.Hijack(); err != nil {
http.Error(response, err.Error(), http.StatusInternalServerError)
return
}
} else {
http.Error(response, "Hijacker failed", http.StatusInternalServerError)
return
}
defer conn.Close()
dest, err := h.forward.Dial("tcp", request.Host)
if err != nil {
fmt.Fprintf(conn, "HTTP/1.0 500 NewRemoteSocks failed, err:%s\r\n\r\n", err)
return
}
defer dest.Close()
if request.Body != nil {
if _, err = io.Copy(dest, request.Body); err != nil {
fmt.Fprintf(conn, "%d %s", http.StatusBadGateway, err.Error())
return
}
}
fmt.Fprintf(conn, "HTTP/1.0 200 Connection established\r\n\r\n")
go func() {
defer conn.Close()
defer dest.Close()
io.Copy(dest, conn)
}()
io.Copy(conn, dest)
}
// ServeHTTPTunnel serve incoming request with CONNECT method, then route data to proxy server
func (h *HTTPProxyHandler) xInternalConnect(req *http.Request, response http.ResponseWriter) {
var conn net.Conn
if hj, ok := response.(http.Hijacker); ok {
var err error
if conn, _, err = hj.Hijack(); err != nil {
http.Error(response, err.Error(), http.StatusInternalServerError)
return
}
} else {
http.Error(response, "Hijacker failed", http.StatusInternalServerError)
return
}
defer conn.Close()
host := req.Host+":80"
if strings.EqualFold(req.URL.Scheme,"https") {
host = req.Host+":443"
}
dest, err := h.forward.Dial("tcp", host)
if err != nil {
fmt.Fprintf(conn, "HTTP/1.0 500 NewRemoteSocks failed, err:%s\r\n\r\n", err)
return
}
defer dest.Close()
sendRequest, err := httputil.DumpRequestOut(req, true)
if err != nil {
fmt.Fprintf(conn, "HTTP/1.0 500 Dump Request failed, err:%s\r\n\r\n", err)
return
}
println(string(sendRequest))
if _, err = io.Copy(dest, bytes.NewReader(sendRequest)); err != nil {
fmt.Fprintf(conn, "%d %s", http.StatusBadGateway, err.Error())
return
}
go func() {
defer conn.Close()
defer dest.Close()
io.Copy(dest, conn)
}()
io.Copy(conn, dest)
}
// ServeHTTP implements HTTP Handler
func (h *HTTPProxyHandler) ServeHTTP(response http.ResponseWriter, request *http.Request) {
request.URL.Scheme = h.scheme
request.URL.Host = request.Host
if request.Method == "GET" && strings.EqualFold(request.Header.Get("Connection"), "Upgrade") && strings.EqualFold(request.Header.Get("Upgrade"), "websocket") {
h.xInternalConnect(request, response)
}
if request.Method == "CONNECT" {
h.ServeHTTPTunnel(response, request)
} else {
h.ReverseProxy.ServeHTTP(response, request)
}
}