-
Notifications
You must be signed in to change notification settings - Fork 57
/
proxy.go
155 lines (132 loc) · 3.63 KB
/
proxy.go
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
150
151
152
153
154
155
package httpproxy
import (
"crypto/tls"
"net/http"
"sync/atomic"
)
// Proxy defines parameters for running an HTTP Proxy. It implements
// http.Handler interface for ListenAndServe function. If you need, you must
// set Proxy struct before handling requests.
type Proxy struct {
// Session number of last proxy request.
SessionNo int64
// RoundTripper interface to obtain remote response.
// By default, it uses &http.Transport{}.
Rt http.RoundTripper
// Certificate key pair.
Ca tls.Certificate
// User data to use free.
UserData interface{}
// Error callback.
OnError func(ctx *Context, where string, err *Error, opErr error)
// Accept callback. It greets proxy request like ServeHTTP function of
// http.Handler.
// If it returns true, stops processing proxy request.
OnAccept func(ctx *Context, w http.ResponseWriter, r *http.Request) bool
// Auth callback. If you need authentication, set this callback.
// If it returns true, authentication succeeded.
OnAuth func(ctx *Context, authType string, user string, pass string) bool
// Connect callback. It sets connect action and new host.
// If len(newhost) > 0, host changes.
OnConnect func(ctx *Context, host string) (ConnectAction ConnectAction,
newHost string)
// Request callback. It greets remote request.
// If it returns non-nil response, stops processing remote request.
OnRequest func(ctx *Context, req *http.Request) (resp *http.Response)
// Response callback. It greets remote response.
// Remote response sends after this callback.
OnResponse func(ctx *Context, req *http.Request, resp *http.Response)
// If ConnectAction is ConnectMitm, it sets chunked to Transfer-Encoding.
// By default, true.
MitmChunked bool
// HTTP Authentication type. If it's not specified (""), uses "Basic".
// By default, "".
AuthType string
signer *CaSigner
}
// NewProxy returns a new Proxy has default CA certificate and key.
func NewProxy() (*Proxy, error) {
return NewProxyCert(nil, nil)
}
// NewProxyCert returns a new Proxy given CA certificate and key.
func NewProxyCert(caCert, caKey []byte) (*Proxy, error) {
prx := &Proxy{
Rt: &http.Transport{TLSClientConfig: &tls.Config{},
Proxy: http.ProxyFromEnvironment},
MitmChunked: true,
signer: NewCaSignerCache(1024),
}
prx.signer.Ca = &prx.Ca
if caCert == nil {
caCert = DefaultCaCert
}
if caKey == nil {
caKey = DefaultCaKey
}
var err error
prx.Ca, err = tls.X509KeyPair(caCert, caKey)
if err != nil {
return nil, err
}
return prx, nil
}
// ServeHTTP implements http.Handler.
func (prx *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := &Context{Prx: prx, SessionNo: atomic.AddInt64(&prx.SessionNo, 1)}
defer func() {
rec := recover()
if rec != nil {
if err, ok := rec.(error); ok && prx.OnError != nil {
prx.OnError(ctx, "ServeHTTP", ErrPanic, err)
}
panic(rec)
}
}()
if ctx.doAccept(w, r) {
return
}
if ctx.doAuth(w, r) {
return
}
r.Header.Del("Proxy-Connection")
r.Header.Del("Proxy-Authenticate")
r.Header.Del("Proxy-Authorization")
if b := ctx.doConnect(w, r); b {
return
}
for {
var w2 = w
var r2 = r
var cyclic = false
switch ctx.ConnectAction {
case ConnectMitm:
if prx.MitmChunked {
cyclic = true
}
w2, r2 = ctx.doMitm()
}
if w2 == nil || r2 == nil {
break
}
//r.Header.Del("Accept-Encoding")
//r.Header.Del("Connection")
ctx.SubSessionNo++
if b, err := ctx.doRequest(w2, r2); err != nil {
break
} else {
if b {
if !cyclic {
break
} else {
continue
}
}
}
if err := ctx.doResponse(w2, r2); err != nil || !cyclic {
break
}
}
if ctx.hijTLSConn != nil {
ctx.hijTLSConn.Close()
}
}