Skip to content

Commit ce8bc1f

Browse files
Merge pull request #2 from go-httpproxy/develop
v1.1
2 parents 40fe09b + 9455f5e commit ce8bc1f

File tree

8 files changed

+433
-95
lines changed

8 files changed

+433
-95
lines changed

README.md

Lines changed: 186 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,186 @@
1-
# go-httpproxy
1+
# A Go HTTP proxy library which has KISS principle
2+
3+
## Introduction
4+
5+
`github.com/go-httpproxy/httpproxy` repository provides an HTTP proxy library
6+
for Go (golang).
7+
8+
The library is regular HTTP proxy; supports HTTP, HTTPS through CONNECT. And
9+
also provides HTTPS connection using "Man in the Middle" style attack.
10+
11+
It's easy to use. `httpproxy.Proxy` implements `Handler` interface of `net/http`
12+
package to offer `http.ListenAndServe` function.
13+
14+
### Keep it simple, stupid!
15+
16+
> KISS is an acronym for "Keep it simple, stupid" as a design principle. The
17+
KISS principle states that most systems work best if they are kept simple rather
18+
than made complicated; therefore simplicity should be a key goal in design and
19+
unnecessary complexity should be avoided. [Wikipedia]
20+
21+
## Usage
22+
23+
Library has two significant structs: Proxy and Context.
24+
25+
### Proxy struct
26+
27+
```go
28+
// Proxy defines parameters for running an HTTP Proxy. It implements
29+
// http.Handler interface for ListenAndServe function. If you need, you must
30+
// fill Proxy struct before handling requests.
31+
type Proxy struct {
32+
// Session number of last proxy request.
33+
SessionNo int64
34+
35+
// RoundTripper interface to obtain remote response.
36+
// By default, it uses &http.Transport{}.
37+
Rt http.RoundTripper
38+
39+
// Certificate key pair.
40+
Ca tls.Certificate
41+
42+
// User data to use free.
43+
UserData interface{}
44+
45+
// Error handler.
46+
OnError func(ctx *Context, when string, err *Error, opErr error)
47+
48+
// Accept handler. It greets proxy request like ServeHTTP function of
49+
// http.Handler.
50+
// If it returns true, stops processing proxy request.
51+
OnAccept func(ctx *Context, w http.ResponseWriter, r *http.Request) bool
52+
53+
// Auth handler. If you need authentication, set this handler.
54+
// If it returns true, authentication succeeded.
55+
OnAuth func(ctx *Context, user string, pass string) bool
56+
57+
// Connect handler. It sets connect action and new host.
58+
// If len(newhost) > 0, host changes.
59+
OnConnect func(ctx *Context, host string) (ConnectAction ConnectAction,
60+
newHost string)
61+
62+
// Request handler. It greets remote request.
63+
// If it returns non-nil response, stops processing remote request.
64+
OnRequest func(ctx *Context, req *http.Request) (resp *http.Response)
65+
66+
// Response handler. It greets remote response.
67+
// Remote response sends after this handler.
68+
OnResponse func(ctx *Context, req *http.Request, resp *http.Response)
69+
70+
// If ConnectAction is ConnectMitm, it sets chunked to Transfer-Encoding.
71+
// By default, it is true.
72+
MitmChunked bool
73+
}
74+
```
75+
76+
### Context struct
77+
78+
```go
79+
// Context keeps context of each proxy request.
80+
type Context struct {
81+
// Pointer of Proxy struct handled this context.
82+
// It's using internally. Don't change in Context struct!
83+
Prx *Proxy
84+
85+
// Session number of this context obtained from Proxy struct.
86+
SessionNo int64
87+
88+
// Sub session number of processing remote connection.
89+
SubSessionNo int64
90+
91+
// Action of after the CONNECT, if proxy request method is CONNECT.
92+
// It's using internally. Don't change in Context struct!
93+
ConnectAction ConnectAction
94+
95+
// Proxy request, if proxy request method is CONNECT.
96+
// It's using internally. Don't change in Context struct!
97+
ConnectReq *http.Request
98+
99+
// Remote host, if proxy request method is CONNECT.
100+
// It's using internally. Don't change in Context struct!
101+
ConnectHost string
102+
103+
// User data to use free.
104+
UserData interface{}
105+
}
106+
```
107+
108+
### Demo code
109+
110+
```go
111+
package main
112+
113+
import (
114+
"log"
115+
"net/http"
116+
117+
"github.com/go-httpproxy/httpproxy"
118+
)
119+
120+
func OnError(ctx *httpproxy.Context, when string,
121+
err *httpproxy.Error, opErr error) {
122+
// Log errors.
123+
log.Printf("ERR: %s: %s [%s]", when, err, opErr)
124+
}
125+
126+
func OnAccept(ctx *httpproxy.Context, w http.ResponseWriter,
127+
r *http.Request) bool {
128+
// Handle local request has path "/info"
129+
if r.Method == "GET" && !r.URL.IsAbs() && r.URL.Path == "/info" {
130+
w.Write([]byte("This is go-httpproxy."))
131+
return true
132+
}
133+
return false
134+
}
135+
136+
func OnAuth(ctx *httpproxy.Context, user string, pass string) bool {
137+
// Auth test user.
138+
if user == "test" && pass == "1234" {
139+
return true
140+
}
141+
return false
142+
}
143+
144+
func OnConnect(ctx *httpproxy.Context, host string) (
145+
ConnectAction httpproxy.ConnectAction, newHost string) {
146+
// Apply "Man in the Middle" to all ssl connections. Never change host.
147+
return httpproxy.ConnectMitm, host
148+
}
149+
150+
func OnRequest(ctx *httpproxy.Context, req *http.Request) (
151+
resp *http.Response) {
152+
// Log proxying requests.
153+
log.Printf("INFO: Proxy: %s %s", req.Method, req.URL.String())
154+
return
155+
}
156+
157+
func OnResponse(ctx *httpproxy.Context, req *http.Request,
158+
resp *http.Response) {
159+
// Add header "Via: go-httpproxy".
160+
resp.Header.Add("Via", "go-httpproxy")
161+
}
162+
163+
func main() {
164+
// Create a new proxy with default certificate pair.
165+
prx, _ := httpproxy.NewProxy()
166+
167+
// Set handlers.
168+
prx.OnError = OnError
169+
prx.OnAccept = OnAccept
170+
prx.OnAuth = OnAuth
171+
prx.OnConnect = OnConnect
172+
prx.OnRequest = OnRequest
173+
prx.OnResponse = OnResponse
174+
175+
// Listen...
176+
http.ListenAndServe(":8080", prx)
177+
}
178+
```
179+
180+
## GoDoc
181+
182+
[https://godoc.org/github.com/go-httpproxy/httpproxy](https://godoc.org/github.com/go-httpproxy/httpproxy)
183+
184+
## To-Do
185+
186+
* GoDoc

ca.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"time"
1414
)
1515

16+
// Default certificate.
1617
var DefaultCaCert = []byte(`-----BEGIN CERTIFICATE-----
1718
MIIFkzCCA3ugAwIBAgIJAKEbW2ujNjX9MA0GCSqGSIb3DQEBCwUAMGAxCzAJBgNV
1819
BAYTAlRSMREwDwYDVQQIDAhJc3RhbmJ1bDEVMBMGA1UECgwMZ28taHR0cHByb3h5
@@ -46,6 +47,7 @@ Ii9Vb07WDMQXou0ZZs7rnjAKo+sfFElTFewtS1wif4ZYBUJN1ln9G8qKaxbAiElm
4647
MgzNfZ7WlnaJf2rfHJbvK9VqJ9z6dLRYPjCHhakJBtzsMdxysEGJ
4748
-----END CERTIFICATE-----`)
4849

50+
// Default key.
4951
var DefaultCaKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
5052
MIIJKQIBAAKCAgEA18cwaaZzhdDEpUXpR9pkYRqsSdT30WhynFhFtcaBOf4eYdpt
5153
AJWL2ipo3Ac6bh+YgWfywG4prrSfWOJl+dQ59w439vLek/waBcEeFx+wJ6PFu0ur

context.go

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,38 @@
11
package httpproxy
22

3-
import "net/http"
3+
import (
4+
"bufio"
5+
"crypto/tls"
6+
"net/http"
7+
)
48

5-
// Context defines context of each proxy connection.
9+
// Context keeps context of each proxy request.
610
type Context struct {
7-
Prx *Proxy
8-
SessionNo int64
11+
// Pointer of Proxy struct handled this context.
12+
// It's using internally. Don't change in Context struct!
13+
Prx *Proxy
14+
15+
// Session number of this context obtained from Proxy struct.
16+
SessionNo int64
17+
18+
// Sub session number of processing remote connection.
19+
SubSessionNo int64
20+
21+
// Action of after the CONNECT, if proxy request method is CONNECT.
22+
// It's using internally. Don't change in Context struct!
923
ConnectAction ConnectAction
10-
ConnectReq *http.Request
11-
UserData interface{}
24+
25+
// Proxy request, if proxy request method is CONNECT.
26+
// It's using internally. Don't change in Context struct!
27+
ConnectReq *http.Request
28+
29+
// Remote host, if proxy request method is CONNECT.
30+
// It's using internally. Don't change in Context struct!
31+
ConnectHost string
32+
33+
// User data to use free.
34+
UserData interface{}
35+
36+
hijTLSConn *tls.Conn
37+
hijTLSReader *bufio.Reader
1238
}

demo/main.go

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,41 +7,61 @@ import (
77
"github.com/go-httpproxy/httpproxy"
88
)
99

10-
func OnError(ctx *httpproxy.Context, when string, err *httpproxy.Error, opErr error) {
11-
log.Printf("%s %s %s", when, err, opErr)
10+
func OnError(ctx *httpproxy.Context, when string,
11+
err *httpproxy.Error, opErr error) {
12+
// Log errors.
13+
log.Printf("ERR: %s: %s [%s]", when, err, opErr)
1214
}
1315

14-
func OnAccept(ctx *httpproxy.Context, req *http.Request) *http.Response {
15-
return nil
16+
func OnAccept(ctx *httpproxy.Context, w http.ResponseWriter,
17+
r *http.Request) bool {
18+
// Handle local request has path "/info"
19+
if r.Method == "GET" && !r.URL.IsAbs() && r.URL.Path == "/info" {
20+
w.Write([]byte("This is go-httpproxy."))
21+
return true
22+
}
23+
return false
1624
}
1725

1826
func OnAuth(ctx *httpproxy.Context, user string, pass string) bool {
27+
// Auth test user.
1928
if user == "test" && pass == "1234" {
2029
return true
2130
}
2231
return false
2332
}
2433

25-
func OnConnect(ctx *httpproxy.Context, host string) (httpproxy.ConnectAction, string) {
34+
func OnConnect(ctx *httpproxy.Context, host string) (
35+
ConnectAction httpproxy.ConnectAction, newHost string) {
36+
// Apply "Man in the Middle" to all ssl connections. Never change host.
2637
return httpproxy.ConnectMitm, host
2738
}
2839

29-
func OnRequest(ctx *httpproxy.Context, req *http.Request) *http.Response {
30-
return nil
40+
func OnRequest(ctx *httpproxy.Context, req *http.Request) (
41+
resp *http.Response) {
42+
// Log proxying requests.
43+
log.Printf("INFO: Proxy: %s %s", req.Method, req.URL.String())
44+
return
3145
}
3246

33-
func OnResponse(ctx *httpproxy.Context, req *http.Request, resp *http.Response) {
34-
resp.Header.Add("Via", "test")
47+
func OnResponse(ctx *httpproxy.Context, req *http.Request,
48+
resp *http.Response) {
49+
// Add header "Via: go-httpproxy".
50+
resp.Header.Add("Via", "go-httpproxy")
3551
}
3652

3753
func main() {
54+
// Create a new proxy with default certificate pair.
3855
prx, _ := httpproxy.NewProxy()
56+
57+
// Set handlers.
3958
prx.OnError = OnError
4059
prx.OnAccept = OnAccept
4160
prx.OnAuth = OnAuth
4261
prx.OnConnect = OnConnect
4362
prx.OnRequest = OnRequest
4463
prx.OnResponse = OnResponse
4564

65+
// Listen...
4666
http.ListenAndServe(":8080", prx)
4767
}

0 commit comments

Comments
 (0)