├── .gitignore
├── ext
├── html
│ ├── cp1255.txt
│ ├── cp1255.html
│ ├── html_test.go
│ └── html.go
├── image
│ └── image.go
└── auth
│ ├── basic.go
│ └── basic_test.go
├── test_data
├── baby.jpg
├── panda.png
└── football.png
├── examples
├── goproxy-jquery-version
│ ├── jquery1.html
│ ├── jquery2.html
│ ├── README.md
│ ├── main.go
│ ├── jquery_test.go
│ ├── jquery_homepage.html
│ └── php_man.html
├── goproxy-basic
│ ├── main.go
│ └── README.md
├── goproxy-customca
│ ├── main.go
│ └── cert.go
├── goproxy-no-reddit-at-worktime
│ ├── README.md
│ └── noreddit.go
├── goproxy-sokeepalive
│ └── sokeepalive.go
├── goproxy-upside-down-ternet
│ └── main.go
├── goproxy-sslstrip
│ └── sslstrip.go
├── goproxy-httpdump
│ ├── README.md
│ └── httpdump.go
├── goproxy-transparent
│ ├── proxy.sh
│ ├── README.md
│ └── transparent.go
├── goproxy-stats
│ ├── README.md
│ └── main.go
├── goproxy-eavesdropper
│ └── main.go
└── goproxy-yui-minify
│ └── yui.go
├── certs
├── openssl-gen.sh
└── openssl.cnf
├── transport
├── util.go
├── roundtripper.go
└── transport.go
├── all.bash
├── responses.go
├── LICENSE
├── counterecryptor.go
├── chunked.go
├── ca.pem
├── signer.go
├── signer_test.go
├── regretable
├── regretreader.go
└── regretreader_test.go
├── actions.go
├── key.pem
├── counterecryptor_test.go
├── doc.go
├── ctx.go
├── README.md
├── certs.go
├── proxy.go
├── https.go
└── dispatcher.go
/.gitignore:
--------------------------------------------------------------------------------
1 | bin
2 | *.swp
3 |
--------------------------------------------------------------------------------
/ext/html/cp1255.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heixiaoma/goproxy/master/ext/html/cp1255.txt
--------------------------------------------------------------------------------
/test_data/baby.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heixiaoma/goproxy/master/test_data/baby.jpg
--------------------------------------------------------------------------------
/test_data/panda.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heixiaoma/goproxy/master/test_data/panda.png
--------------------------------------------------------------------------------
/ext/html/cp1255.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heixiaoma/goproxy/master/ext/html/cp1255.html
--------------------------------------------------------------------------------
/test_data/football.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heixiaoma/goproxy/master/test_data/football.png
--------------------------------------------------------------------------------
/examples/goproxy-jquery-version/jquery1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/examples/goproxy-jquery-version/jquery2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/certs/openssl-gen.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -ex
3 | # generate CA's key
4 | openssl genrsa -aes256 -passout pass:1 -out ca.key.pem 4096
5 | openssl rsa -passin pass:1 -in ca.key.pem -out ca.key.pem.tmp
6 | mv ca.key.pem.tmp ca.key.pem
7 |
8 | openssl req -config openssl.cnf -key ca.key.pem -new -x509 -days 7300 -sha256 -extensions v3_ca -out ca.pem
9 |
--------------------------------------------------------------------------------
/transport/util.go:
--------------------------------------------------------------------------------
1 | package transport
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | )
7 |
8 | type badStringError struct {
9 | what string
10 | str string
11 | }
12 |
13 | func (e *badStringError) Error() string { return fmt.Sprintf("%s %q", e.what, e.str) }
14 |
15 | func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }
16 |
--------------------------------------------------------------------------------
/all.bash:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | go test || exit
4 | for action in $@; do go $action; done
5 |
6 | mkdir -p bin
7 | find regretable examples/* ext/* -maxdepth 0 -type d | while read d; do
8 | (cd $d
9 | go build -o ../../bin/$(basename $d)
10 | find *_test.go -maxdepth 0 2>/dev/null|while read f;do
11 | for action in $@; do go $action; done
12 | go test
13 | break
14 | done)
15 | done
16 |
--------------------------------------------------------------------------------
/examples/goproxy-basic/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "github.com/heixiaoma/goproxy"
6 | "log"
7 | "net/http"
8 | )
9 |
10 | func main() {
11 | verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
12 | addr := flag.String("addr", ":8080", "proxy listen address")
13 | flag.Parse()
14 | proxy := goproxy.NewProxyHttpServer()
15 | proxy.Verbose = *verbose
16 | log.Fatal(http.ListenAndServe(*addr, proxy))
17 | }
18 |
--------------------------------------------------------------------------------
/examples/goproxy-customca/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "log"
6 | "net/http"
7 |
8 | "github.com/heixiaoma/goproxy"
9 | )
10 |
11 | func main() {
12 | verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
13 | addr := flag.String("addr", ":8080", "proxy listen address")
14 | flag.Parse()
15 | setCA(caCert, caKey)
16 | proxy := goproxy.NewProxyHttpServer()
17 | proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm)
18 | proxy.Verbose = *verbose
19 | log.Fatal(http.ListenAndServe(*addr, proxy))
20 | }
21 |
--------------------------------------------------------------------------------
/examples/goproxy-no-reddit-at-worktime/README.md:
--------------------------------------------------------------------------------
1 | # Request Filtering
2 |
3 | `goproxy-no-reddit-at-work` starts an HTTP proxy on :8080. It denies requests
4 | to "www.reddit.com" made between 8am to 5pm inclusive, local time.
5 |
6 | Start it in one shell:
7 |
8 | ```sh
9 | $ goproxy-no-reddit-at-work
10 | ```
11 |
12 | Fetch reddit in another:
13 |
14 | ```sh
15 | $ http_proxy=http://127.0.0.1:8080 wget -O - http://www.reddit.com
16 | --2015-04-11 16:59:01-- http://www.reddit.com/
17 | Connecting to 127.0.0.1:8080... connected.
18 | Proxy request sent, awaiting response... 403 Forbidden
19 | 2015-04-11 16:59:01 ERROR 403: Forbidden.
20 | ```
21 |
22 |
--------------------------------------------------------------------------------
/examples/goproxy-sokeepalive/sokeepalive.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "github.com/heixiaoma/goproxy"
6 | "log"
7 | "net"
8 | "net/http"
9 | )
10 |
11 | func main() {
12 | verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
13 | addr := flag.String("addr", ":8080", "proxy listen address")
14 | flag.Parse()
15 | proxy := goproxy.NewProxyHttpServer()
16 | proxy.Tr.Dial = func(network, addr string) (c net.Conn, err error) {
17 | c, err = net.Dial(network, addr)
18 | if c, ok := c.(*net.TCPConn); err == nil && ok {
19 | c.SetKeepAlive(true)
20 | }
21 | return
22 | }
23 | proxy.Verbose = *verbose
24 | log.Fatal(http.ListenAndServe(*addr, proxy))
25 | }
26 |
--------------------------------------------------------------------------------
/examples/goproxy-upside-down-ternet/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/heixiaoma/goproxy"
5 | "github.com/heixiaoma/goproxy/ext/image"
6 | "image"
7 | "log"
8 | "net/http"
9 | )
10 |
11 | func main() {
12 | proxy := goproxy.NewProxyHttpServer()
13 | proxy.OnResponse().Do(goproxy_image.HandleImage(func(img image.Image, ctx *goproxy.ProxyCtx) image.Image {
14 | dx, dy := img.Bounds().Dx(), img.Bounds().Dy()
15 |
16 | nimg := image.NewRGBA(img.Bounds())
17 | for i := 0; i < dx; i++ {
18 | for j := 0; j <= dy; j++ {
19 | nimg.Set(i, j, img.At(i, dy-j-1))
20 | }
21 | }
22 | return nimg
23 | }))
24 | proxy.Verbose = true
25 | log.Fatal(http.ListenAndServe(":8080", proxy))
26 | }
27 |
--------------------------------------------------------------------------------
/examples/goproxy-sslstrip/sslstrip.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "github.com/heixiaoma/goproxy"
6 | "log"
7 | "net/http"
8 | )
9 |
10 | func main() {
11 | verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
12 | addr := flag.String("addr", ":8080", "proxy listen address")
13 | flag.Parse()
14 | proxy := goproxy.NewProxyHttpServer()
15 | proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm)
16 | proxy.OnRequest().DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
17 | if req.URL.Scheme == "https" {
18 | req.URL.Scheme = "http"
19 | }
20 | return req, nil
21 | })
22 | proxy.Verbose = *verbose
23 | log.Fatal(http.ListenAndServe(*addr, proxy))
24 | }
25 |
--------------------------------------------------------------------------------
/examples/goproxy-no-reddit-at-worktime/noreddit.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/heixiaoma/goproxy"
5 | "log"
6 | "net/http"
7 | "time"
8 | )
9 |
10 | func main() {
11 | proxy := goproxy.NewProxyHttpServer()
12 | proxy.OnRequest(goproxy.DstHostIs("www.reddit.com")).DoFunc(
13 | func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
14 | h, _, _ := time.Now().Clock()
15 | if h >= 8 && h <= 17 {
16 | return r, goproxy.NewResponse(r,
17 | goproxy.ContentTypeText, http.StatusForbidden,
18 | "Don't waste your time!")
19 | } else {
20 | ctx.Warnf("clock: %d, you can waste your time...", h)
21 | }
22 | return r, nil
23 | })
24 | log.Fatalln(http.ListenAndServe(":8080", proxy))
25 | }
26 |
--------------------------------------------------------------------------------
/transport/roundtripper.go:
--------------------------------------------------------------------------------
1 | package transport
2 | import "net/http"
3 | type RoundTripper interface {
4 | // RoundTrip executes a single HTTP transaction, returning
5 | // the Response for the request req. RoundTrip should not
6 | // attempt to interpret the response. In particular,
7 | // RoundTrip must return err == nil if it obtained a response,
8 | // regardless of the response's HTTP status code. A non-nil
9 | // err should be reserved for failure to obtain a response.
10 | // Similarly, RoundTrip should not attempt to handle
11 | // higher-level protocol details such as redirects,
12 | // authentication, or cookies.
13 | //
14 | // RoundTrip should not modify the request, except for
15 | // consuming the Body. The request's URL and Header fields
16 | // are guaranteed to be initialized.
17 | RoundTrip(*http.Request) (*http.Response, error)
18 | DetailedRoundTrip(*http.Request) (*RoundTripDetails, *http.Response, error)
19 | }
20 |
--------------------------------------------------------------------------------
/examples/goproxy-basic/README.md:
--------------------------------------------------------------------------------
1 | # Simple HTTP Proxy
2 |
3 | `goproxy-basic` starts an HTTP proxy on :8080. It only handles explicit CONNECT
4 | requests.
5 |
6 | Start it in one shell:
7 |
8 | ```sh
9 | goproxy-basic -v
10 | ```
11 |
12 | Fetch goproxy homepage in another:
13 |
14 | ```sh
15 | http_proxy=http://127.0.0.1:8080 wget -O - \
16 | http://ripper234.com/p/introducing-goproxy-light-http-proxy/
17 | ```
18 |
19 | The homepage HTML content should be displayed in the console. The proxy should
20 | have logged the request being processed:
21 |
22 | ```sh
23 | 2015/04/09 18:19:17 [001] INFO: Got request /p/introducing-goproxy-light-http-proxy/ ripper234.com GET http://ripper234.com/p/introducing-goproxy-light-http-proxy/
24 | 2015/04/09 18:19:17 [001] INFO: Sending request GET http://ripper234.com/p/introducing-goproxy-light-http-proxy/
25 | 2015/04/09 18:19:18 [001] INFO: Received response 200 OK
26 | 2015/04/09 18:19:18 [001] INFO: Copying response to client 200 OK [200]
27 | 2015/04/09 18:19:18 [001] INFO: Copied 44333 bytes to client error=
28 | ```
29 |
30 |
--------------------------------------------------------------------------------
/examples/goproxy-httpdump/README.md:
--------------------------------------------------------------------------------
1 | # Trace HTTP Requests and Responses
2 |
3 | `goproxy-httpdump` starts an HTTP proxy on :8080. It handles explicit CONNECT
4 | requests and traces them in a "db" directory created in the proxy working
5 | directory. Each request type and headers are logged in a "log" file, while
6 | their bodies are dumped in files prefixed with the request session identifier.
7 |
8 | Additionally, the example demonstrates how to:
9 | - Log information asynchronously (see HttpLogger)
10 | - Allow the proxy to be stopped manually while ensuring all pending requests
11 | have been processed (in this case, logged).
12 |
13 | Start it in one shell:
14 |
15 | ```sh
16 | goproxy-httpdump
17 | ```
18 |
19 | Fetch goproxy homepage in another:
20 |
21 | ```sh
22 | http_proxy=http://127.0.0.1:8080 wget -O - \
23 | http://ripper234.com/p/introducing-goproxy-light-http-proxy/
24 | ```
25 |
26 | A "db" directory should have appeared where you started the proxy, containing
27 | two files:
28 | - log: the request/response traces
29 | - 1\_resp: the first response body
30 |
31 |
--------------------------------------------------------------------------------
/examples/goproxy-jquery-version/README.md:
--------------------------------------------------------------------------------
1 | # Content Analysis
2 |
3 | `goproxy-jquery-version` starts an HTTP proxy on :8080. It checks HTML
4 | responses, looks for scripts referencing jQuery library and emits warnings if
5 | different versions of the library are being used for a given host.
6 |
7 | Start it in one shell:
8 |
9 | ```sh
10 | goproxy-jquery-version
11 | ```
12 |
13 | Fetch goproxy homepage in another:
14 |
15 | ```sh
16 | http_proxy=http://127.0.0.1:8080 wget -O - \
17 | http://ripper234.com/p/introducing-goproxy-light-http-proxy/
18 | ```
19 |
20 | Goproxy homepage uses jQuery and a mix of plugins. First the proxy reports the
21 | first use of jQuery it detects for the domain. Then, because the regular
22 | expression matching the jQuery sources is imprecise, it reports a mismatch with
23 | a plugin reference:
24 |
25 | ```sh
26 | 2015/04/11 11:23:02 [001] WARN: ripper234.com uses //ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js
27 | 2015/04/11 11:23:02 [001] WARN: In http://ripper234.com/p/introducing-goproxy-light-http-proxy/, \
28 | Contradicting jqueries //ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js \
29 | http://ripper234.wpengine.netdna-cdn.com/wp-content/plugins/wp-ajax-edit-comments/js/jquery.colorbox.min.js?ver=5.0.36
30 | ```
31 |
32 |
--------------------------------------------------------------------------------
/certs/openssl.cnf:
--------------------------------------------------------------------------------
1 | [ ca ]
2 | default_ca = CA_default
3 | [ CA_default ]
4 | default_md = sha256
5 | [ v3_ca ]
6 | subjectKeyIdentifier=hash
7 | authorityKeyIdentifier=keyid:always,issuer
8 | basicConstraints = critical,CA:true
9 | [ req ]
10 | distinguished_name = req_distinguished_name
11 | [ req_distinguished_name ]
12 | countryName = Country Name (2 letter code)
13 | countryName_default = IL
14 | countryName_min = 2
15 | countryName_max = 2
16 |
17 | stateOrProvinceName = State or Province Name (full name)
18 | stateOrProvinceName_default = Center
19 |
20 | localityName = Locality Name (eg, city)
21 | localityName_default = Lod
22 |
23 | 0.organizationName = Organization Name (eg, company)
24 | 0.organizationName_default = GoProxy
25 |
26 | # we can do this but it is not needed normally :-)
27 | #1.organizationName = Second Organization Name (eg, company)
28 | #1.organizationName_default = World Wide Web Pty Ltd
29 |
30 | organizationalUnitName = Organizational Unit Name (eg, section)
31 | organizationalUnitName_default = GoProxy
32 |
33 | commonName = Common Name (e.g. server FQDN or YOUR name)
34 | commonName_default = goproxy.github.io
35 | commonName_max = 64
36 |
37 | emailAddress = Email Address
38 | emailAddress_default = elazarl@gmail.com
39 | emailAddress_max = 64
40 |
--------------------------------------------------------------------------------
/examples/goproxy-transparent/proxy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # goproxy IP
3 | GOPROXY_SERVER="10.10.10.1"
4 | # goproxy port
5 | GOPROXY_PORT="3129"
6 | GOPROXY_PORT_TLS="3128"
7 | # DO NOT MODIFY BELOW
8 | # Load IPTABLES modules for NAT and IP conntrack support
9 | modprobe ip_conntrack
10 | modprobe ip_conntrack_ftp
11 | echo 1 > /proc/sys/net/ipv4/ip_forward
12 | echo 2 > /proc/sys/net/ipv4/conf/all/rp_filter
13 |
14 | # Clean old firewall
15 | iptables -t nat -F
16 | iptables -t nat -X
17 | iptables -t mangle -F
18 | iptables -t mangle -X
19 |
20 | # Write new rules
21 | iptables -t nat -A PREROUTING -s $GOPROXY_SERVER -p tcp --dport $GOPROXY_PORT -j ACCEPT
22 | iptables -t nat -A PREROUTING -s $GOPROXY_SERVER -p tcp --dport $GOPROXY_PORT_TLS -j ACCEPT
23 | iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination $GOPROXY_SERVER:$GOPROXY_PORT
24 | iptables -t nat -A PREROUTING -p tcp --dport 443 -j DNAT --to-destination $GOPROXY_SERVER:$GOPROXY_PORT_TLS
25 | # The following line supports using goproxy as an explicit proxy in addition
26 | iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination $GOPROXY_SERVER:$GOPROXY_PORT
27 | iptables -t nat -A POSTROUTING -j MASQUERADE
28 | iptables -t mangle -A PREROUTING -p tcp --dport $GOPROXY_PORT -j DROP
29 | iptables -t mangle -A PREROUTING -p tcp --dport $GOPROXY_PORT_TLS -j DROP
30 |
--------------------------------------------------------------------------------
/responses.go:
--------------------------------------------------------------------------------
1 | package goproxy
2 |
3 | import (
4 | "bytes"
5 | "io/ioutil"
6 | "net/http"
7 | )
8 |
9 | // Will generate a valid http response to the given request the response will have
10 | // the given contentType, and http status.
11 | // Typical usage, refuse to process requests to local addresses:
12 | //
13 | // proxy.OnRequest(IsLocalHost()).DoFunc(func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request,*http.Response) {
14 | // return nil,NewResponse(r,goproxy.ContentTypeHtml,http.StatusUnauthorized,
15 | // `Can't use proxy for local addresses `)
16 | // })
17 | func NewResponse(r *http.Request, contentType string, status int, body string) *http.Response {
18 | resp := &http.Response{}
19 | resp.Request = r
20 | resp.TransferEncoding = r.TransferEncoding
21 | resp.Header = make(http.Header)
22 | resp.Header.Add("Content-Type", contentType)
23 | resp.StatusCode = status
24 | buf := bytes.NewBufferString(body)
25 | resp.ContentLength = int64(buf.Len())
26 | resp.Body = ioutil.NopCloser(buf)
27 | return resp
28 | }
29 |
30 | const (
31 | ContentTypeText = "text/plain"
32 | ContentTypeHtml = "text/html"
33 | )
34 |
35 | // Alias for NewResponse(r,ContentTypeText,http.StatusAccepted,text)
36 | func TextResponse(r *http.Request, text string) *http.Response {
37 | return NewResponse(r, ContentTypeText, http.StatusAccepted, text)
38 | }
39 |
--------------------------------------------------------------------------------
/examples/goproxy-stats/README.md:
--------------------------------------------------------------------------------
1 | # Gather Browsing Statistics
2 |
3 | `goproxy-stats` starts an HTTP proxy on :8080, counts the bytes received for
4 | web resources and prints the cumulative sum per URL every 20 seconds.
5 |
6 | Start it in one shell:
7 |
8 | ```sh
9 | goproxy-stats
10 | ```
11 |
12 | Fetch goproxy homepage in another:
13 |
14 | ```sh
15 | mkdir tmp
16 | cd tmp
17 | http_proxy=http://127.0.0.1:8080 wget -r -l 1 -H \
18 | http://ripper234.com/p/introducing-goproxy-light-http-proxy/
19 | ```
20 |
21 | Stop it after a moment. `goproxy-stats` should eventually print:
22 | ```sh
23 | listening on :8080
24 | statistics
25 | http://www.telerik.com/fiddler -> 84335
26 | http://msmvps.com/robots.txt -> 157
27 | http://eli.thegreenplace.net/robots.txt -> 294
28 | http://www.phdcomics.com/robots.txt -> 211
29 | http://resharper.blogspot.com/robots.txt -> 221
30 | http://idanz.blogli.co.il/robots.txt -> 271
31 | http://ripper234.com/p/introducing-goproxy-light-http-proxy/ -> 44407
32 | http://live.gnome.org/robots.txt -> 298
33 | http://ponetium.wordpress.com/robots.txt -> 178
34 | http://pilaheleg.blogli.co.il/robots.txt -> 321
35 | http://pilaheleg.wordpress.com/robots.txt -> 178
36 | http://blogli.co.il/ -> 9165
37 | http://nimrod-code.org/robots.txt -> 289
38 | http://www.joelonsoftware.com/robots.txt -> 1245
39 | http://top-performance.blogspot.com/robots.txt -> 227
40 | http://ooc-lang.org/robots.txt -> 345
41 | http://blogs.jetbrains.com/robots.txt -> 293
42 | ```
43 |
44 |
--------------------------------------------------------------------------------
/examples/goproxy-stats/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/heixiaoma/goproxy"
6 | "github.com/heixiaoma/goproxy/ext/html"
7 | "io"
8 | "log"
9 | . "net/http"
10 | "time"
11 | )
12 |
13 | type Count struct {
14 | Id string
15 | Count int64
16 | }
17 | type CountReadCloser struct {
18 | Id string
19 | R io.ReadCloser
20 | ch chan<- Count
21 | nr int64
22 | }
23 |
24 | func (c *CountReadCloser) Read(b []byte) (n int, err error) {
25 | n, err = c.R.Read(b)
26 | c.nr += int64(n)
27 | return
28 | }
29 | func (c CountReadCloser) Close() error {
30 | c.ch <- Count{c.Id, c.nr}
31 | return c.R.Close()
32 | }
33 |
34 | func main() {
35 | proxy := goproxy.NewProxyHttpServer()
36 | timer := make(chan bool)
37 | ch := make(chan Count, 10)
38 | go func() {
39 | for {
40 | time.Sleep(20 * time.Second)
41 | timer <- true
42 | }
43 | }()
44 | go func() {
45 | m := make(map[string]int64)
46 | for {
47 | select {
48 | case c := <-ch:
49 | m[c.Id] = m[c.Id] + c.Count
50 | case <-timer:
51 | fmt.Printf("statistics\n")
52 | for k, v := range m {
53 | fmt.Printf("%s -> %d\n", k, v)
54 | }
55 | }
56 | }
57 | }()
58 |
59 | // IsWebRelatedText filters on html/javascript/css resources
60 | proxy.OnResponse(goproxy_html.IsWebRelatedText).DoFunc(func(resp *Response, ctx *goproxy.ProxyCtx) *Response {
61 | resp.Body = &CountReadCloser{ctx.Req.URL.String(), resp.Body, ch, 0}
62 | return resp
63 | })
64 | fmt.Printf("listening on :8080\n")
65 | log.Fatal(ListenAndServe(":8080", proxy))
66 | }
67 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012 Elazar Leibovich. All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without
4 | modification, are permitted provided that the following conditions are
5 | met:
6 |
7 | * Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 | * Neither the name of Elazar Leibovich. nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/counterecryptor.go:
--------------------------------------------------------------------------------
1 | package goproxy
2 |
3 | import (
4 | "crypto/aes"
5 | "crypto/cipher"
6 | "crypto/rsa"
7 | "crypto/sha256"
8 | "crypto/x509"
9 | "errors"
10 | )
11 |
12 | type CounterEncryptorRand struct {
13 | cipher cipher.Block
14 | counter []byte
15 | rand []byte
16 | ix int
17 | }
18 |
19 | func NewCounterEncryptorRandFromKey(key interface{}, seed []byte) (r CounterEncryptorRand, err error) {
20 | var keyBytes []byte
21 | switch key := key.(type) {
22 | case *rsa.PrivateKey:
23 | keyBytes = x509.MarshalPKCS1PrivateKey(key)
24 | default:
25 | err = errors.New("only RSA keys supported")
26 | return
27 | }
28 | h := sha256.New()
29 | if r.cipher, err = aes.NewCipher(h.Sum(keyBytes)[:aes.BlockSize]); err != nil {
30 | return
31 | }
32 | r.counter = make([]byte, r.cipher.BlockSize())
33 | if seed != nil {
34 | copy(r.counter, h.Sum(seed)[:r.cipher.BlockSize()])
35 | }
36 | r.rand = make([]byte, r.cipher.BlockSize())
37 | r.ix = len(r.rand)
38 | return
39 | }
40 |
41 | func (c *CounterEncryptorRand) Seed(b []byte) {
42 | if len(b) != len(c.counter) {
43 | panic("SetCounter: wrong counter size")
44 | }
45 | copy(c.counter, b)
46 | }
47 |
48 | func (c *CounterEncryptorRand) refill() {
49 | c.cipher.Encrypt(c.rand, c.counter)
50 | for i := 0; i < len(c.counter); i++ {
51 | if c.counter[i]++; c.counter[i] != 0 {
52 | break
53 | }
54 | }
55 | c.ix = 0
56 | }
57 |
58 | func (c *CounterEncryptorRand) Read(b []byte) (n int, err error) {
59 | if c.ix == len(c.rand) {
60 | c.refill()
61 | }
62 | if n = len(c.rand) - c.ix; n > len(b) {
63 | n = len(b)
64 | }
65 | copy(b, c.rand[c.ix:c.ix+n])
66 | c.ix += n
67 | return
68 | }
69 |
--------------------------------------------------------------------------------
/ext/html/html_test.go:
--------------------------------------------------------------------------------
1 | package goproxy_html_test
2 |
3 | import (
4 | "github.com/heixiaoma/goproxy"
5 | "github.com/heixiaoma/goproxy/ext/html"
6 | "io/ioutil"
7 | "net/http"
8 | "net/http/httptest"
9 | "net/url"
10 | "testing"
11 | )
12 |
13 | type ConstantServer int
14 |
15 | func (s ConstantServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
16 | w.Header().Set("Content-Type", "text/plain; charset=iso-8859-8")
17 | //w.Header().Set("Content-Type","text/plain; charset=cp-1255")
18 | w.Write([]byte{0xe3, 0xf3})
19 | }
20 |
21 | func TestCharset(t *testing.T) {
22 | s := httptest.NewServer(ConstantServer(1))
23 | defer s.Close()
24 |
25 | ch := make(chan string, 2)
26 | proxy := goproxy.NewProxyHttpServer()
27 | proxy.OnResponse().Do(goproxy_html.HandleString(
28 | func(s string, ctx *goproxy.ProxyCtx) string {
29 | ch <- s
30 | return s
31 | }))
32 | proxyServer := httptest.NewServer(proxy)
33 | defer proxyServer.Close()
34 |
35 | proxyUrl, _ := url.Parse(proxyServer.URL)
36 | client := &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyUrl)}}
37 |
38 | resp, err := client.Get(s.URL + "/cp1255.txt")
39 | if err != nil {
40 | t.Fatal("GET:", err)
41 | }
42 | b, err := ioutil.ReadAll(resp.Body)
43 | if err != nil {
44 | t.Fatal("readAll:", err)
45 | }
46 | resp.Body.Close()
47 |
48 | inHandleString := ""
49 | select {
50 | case inHandleString = <-ch:
51 | default:
52 | }
53 |
54 | if len(b) != 2 || b[0] != 0xe3 || b[1] != 0xf3 {
55 | t.Error("Did not translate back to 0xe3,0xf3, instead", b)
56 | }
57 | if inHandleString != "דף" {
58 | t.Error("HandleString did not convert DALET & PEH SOFIT (דף) from ISO-8859-8 to utf-8, got", []byte(inHandleString))
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/examples/goproxy-transparent/README.md:
--------------------------------------------------------------------------------
1 | # Transparent Proxy
2 |
3 | This transparent example in goproxy is meant to show how to transparenty proxy and hijack all http and https connections while doing a man-in-the-middle to the TLS session. It requires that goproxy sees all the packets traversing out to the internet. Linux iptables rules deal with changing the source/destination IPs to act transparently, but you do need to setup your network configuration so that goproxy is a mandatory stop on the outgoing route. Primarily you can do this by placing the proxy inline. goproxy does not have any WCCP support itself; patches welcome.
4 |
5 | ## Why not explicit?
6 |
7 | Transparent proxies are more difficult to maintain and setup from a server side, but they require no configuration on the client(s) which could be in unmanaged systems or systems that don't support a proxy configuration. See the [eavesdropper example](https://github.com/elazarl/goproxy/blob/master/examples/goproxy-eavesdropper/main.go) if you want to see an explicit proxy example.
8 |
9 | ## Potential Issues
10 |
11 | Support for very old clients using HTTPS will fail. Clients need to send the SNI value in the TLS ClientHello which most modern clients do these days, but old clients will break.
12 |
13 | If you're routing table allows for it, an explicit http request to goproxy will cause it to fail in an endless loop since it will try to request resources from itself repeatedly. This could be solved in the goproxy code by looking up the hostnames, but it adds a delay that is much easier/faster to handle on the routing side.
14 |
15 | ## Routing Rules
16 |
17 | Example routing rules are included in [proxy.sh](https://github.com/elazarl/goproxy/blob/master/examples/goproxy-transparent/proxy.sh) but are best when setup using your distribution's configuration.
18 |
--------------------------------------------------------------------------------
/examples/goproxy-eavesdropper/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bufio"
5 | "flag"
6 | "log"
7 | "net"
8 | "net/http"
9 | "regexp"
10 |
11 | "github.com/heixiaoma/goproxy"
12 | )
13 |
14 | func orPanic(err error) {
15 | if err != nil {
16 | panic(err)
17 | }
18 | }
19 |
20 | func main() {
21 | proxy := goproxy.NewProxyHttpServer()
22 | proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*baidu.com$"))).
23 | HandleConnect(goproxy.AlwaysReject)
24 | proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*$"))).
25 | HandleConnect(goproxy.AlwaysMitm)
26 | // enable curl -p for all hosts on port 80
27 | proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*:80$"))).
28 | HijackConnect(func(req *http.Request, client net.Conn, ctx *goproxy.ProxyCtx) {
29 | defer func() {
30 | if e := recover(); e != nil {
31 | ctx.Logf("error connecting to remote: %v", e)
32 | client.Write([]byte("HTTP/1.1 500 Cannot reach destination\r\n\r\n"))
33 | }
34 | client.Close()
35 | }()
36 | clientBuf := bufio.NewReadWriter(bufio.NewReader(client), bufio.NewWriter(client))
37 | remote, err := net.Dial("tcp", req.URL.Host)
38 | orPanic(err)
39 | remoteBuf := bufio.NewReadWriter(bufio.NewReader(remote), bufio.NewWriter(remote))
40 | for {
41 | req, err := http.ReadRequest(clientBuf.Reader)
42 | orPanic(err)
43 | orPanic(req.Write(remoteBuf))
44 | orPanic(remoteBuf.Flush())
45 | resp, err := http.ReadResponse(remoteBuf.Reader, req)
46 | orPanic(err)
47 | orPanic(resp.Write(clientBuf.Writer))
48 | orPanic(clientBuf.Flush())
49 | }
50 | })
51 | verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
52 | addr := flag.String("addr", ":8080", "proxy listen address")
53 | flag.Parse()
54 | proxy.Verbose = *verbose
55 | log.Fatal(http.ListenAndServe(*addr, proxy))
56 | }
57 |
--------------------------------------------------------------------------------
/chunked.go:
--------------------------------------------------------------------------------
1 | // Taken from $GOROOT/src/pkg/net/http/chunked
2 | // needed to write https responses to client.
3 | package goproxy
4 |
5 | import (
6 | "io"
7 | "strconv"
8 | )
9 |
10 | // newChunkedWriter returns a new chunkedWriter that translates writes into HTTP
11 | // "chunked" format before writing them to w. Closing the returned chunkedWriter
12 | // sends the final 0-length chunk that marks the end of the stream.
13 | //
14 | // newChunkedWriter is not needed by normal applications. The http
15 | // package adds chunking automatically if handlers don't set a
16 | // Content-Length header. Using newChunkedWriter inside a handler
17 | // would result in double chunking or chunking with a Content-Length
18 | // length, both of which are wrong.
19 | func newChunkedWriter(w io.Writer) io.WriteCloser {
20 | return &chunkedWriter{w}
21 | }
22 |
23 | // Writing to chunkedWriter translates to writing in HTTP chunked Transfer
24 | // Encoding wire format to the underlying Wire chunkedWriter.
25 | type chunkedWriter struct {
26 | Wire io.Writer
27 | }
28 |
29 | // Write the contents of data as one chunk to Wire.
30 | // NOTE: Note that the corresponding chunk-writing procedure in Conn.Write has
31 | // a bug since it does not check for success of io.WriteString
32 | func (cw *chunkedWriter) Write(data []byte) (n int, err error) {
33 |
34 | // Don't send 0-length data. It looks like EOF for chunked encoding.
35 | if len(data) == 0 {
36 | return 0, nil
37 | }
38 |
39 | head := strconv.FormatInt(int64(len(data)), 16) + "\r\n"
40 |
41 | if _, err = io.WriteString(cw.Wire, head); err != nil {
42 | return 0, err
43 | }
44 | if n, err = cw.Wire.Write(data); err != nil {
45 | return
46 | }
47 | if n != len(data) {
48 | err = io.ErrShortWrite
49 | return
50 | }
51 | _, err = io.WriteString(cw.Wire, "\r\n")
52 |
53 | return
54 | }
55 |
56 | func (cw *chunkedWriter) Close() error {
57 | _, err := io.WriteString(cw.Wire, "0\r\n")
58 | return err
59 | }
60 |
--------------------------------------------------------------------------------
/examples/goproxy-jquery-version/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/heixiaoma/goproxy"
5 | "github.com/heixiaoma/goproxy/ext/html"
6 | "log"
7 | "net/http"
8 | "regexp"
9 | )
10 |
11 | var (
12 | // who said we can't parse HTML with regexp?
13 | scriptMatcher = regexp.MustCompile(`(?i:
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
47 |
48 |
49 |
50 |
51 |
jQuery is a new kind of JavaScript Library.
52 |
jQuery is a fast and concise JavaScript Library that simplifies HTML document traversing, event handling, animating, and Ajax interactions for rapid web development. jQuery is designed to change the way that you write JavaScript.
53 |
54 | Lightweight Footprint
55 |
56 |
57 |
About 31KB in size (Minified and Gzipped)
58 |
59 |
60 | CSS3 Compliant
61 |
62 |
Supports CSS 1-3 selectors and more!
63 |
64 |
65 |
66 | Cross-browser
67 |
68 |
IE 6.0+, FF 3.6+, Safari 5.0+, Opera, Chrome
69 |
70 |
71 |
72 |
73 |
74 |
75 |
Grab the latest version!
76 |
91 |
97 |
98 |
99 |
100 |
Who's using jQuery?
101 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
Learn jQuery Now!
122 |
What does jQuery code look like? Here's the quick and dirty:
123 |
124 |
$("p.neat").addClass("ohmy").show("slow");
125 |
Run Code
126 |
127 |
Congratulations! You just ran a snippet of jQuery code. Wasn't that easy? There's lots of example code throughout the documentation on this site. Be sure to give all the code a test run to see what happens.
128 |
129 |
130 |
131 |
132 |
133 |
134 |
jQuery Resources
135 |
136 |
137 |
Getting Started With jQuery
138 |
145 |
146 |
147 |
Developer Resources
148 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
Books About jQuery
162 |
163 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
224 |
225 |
226 |
232 |
233 |