diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json
index a8e5f859ef..f3accec366 100644
--- a/Godeps/Godeps.json
+++ b/Godeps/Godeps.json
@@ -213,6 +213,11 @@
"ImportPath": "github.com/elazarl/go-bindata-assetfs",
"Rev": "3dcc96556217539f50599357fb481ac0dc7439b9"
},
+ {
+ "ImportPath": "github.com/elazarl/goproxy",
+ "Comment": "v1.0-66-g07b16b6",
+ "Rev": "07b16b6e30fcac0ad8c0435548e743bcf2ca7e92"
+ },
{
"ImportPath": "github.com/emicklei/go-restful",
"Comment": "v1.1.3-98-g1f9a0ee",
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/.gitignore b/Godeps/_workspace/src/github.com/elazarl/goproxy/.gitignore
new file mode 100644
index 0000000000..1005f6f1ec
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/.gitignore
@@ -0,0 +1,2 @@
+bin
+*.swp
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/LICENSE b/Godeps/_workspace/src/github.com/elazarl/goproxy/LICENSE
new file mode 100644
index 0000000000..2067e567c9
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2012 Elazar Leibovich. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Elazar Leibovich. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/README.md b/Godeps/_workspace/src/github.com/elazarl/goproxy/README.md
new file mode 100644
index 0000000000..e94fef9ac4
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/README.md
@@ -0,0 +1,115 @@
+# Introduction
+
+[![Join the chat at https://gitter.im/elazarl/goproxy](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/elazarl/goproxy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+Package goproxy provides a customizable HTTP proxy library for Go (golang),
+
+It supports regular HTTP proxy, HTTPS through CONNECT, and "hijacking" HTTPS
+connection using "Man in the Middle" style attack.
+
+The intent of the proxy, is to be usable with reasonable amount of traffic
+yet, customizable and programable.
+
+The proxy itself is simply a `net/http` handler.
+
+In order to use goproxy, one should set their browser to use goproxy as an HTTP
+proxy. Here is how you do that [in Chrome](https://support.google.com/chrome/answer/96815?hl=en)
+and [in Firefox](http://www.wikihow.com/Enter-Proxy-Settings-in-Firefox).
+
+For example, the URL you should use as proxy when running `./bin/basic` is
+`localhost:8080`, as this is the default binding for the basic proxy.
+
+## Mailing List
+
+New features would be discussed on the [mailing list](https://groups.google.com/forum/#!forum/goproxy-dev)
+before their development.
+
+## Latest Stable Release
+
+Get the latest goproxy from `gopkg.in/elazarl/goproxy.v1`.
+
+# Why not Fiddler2?
+
+Fiddler is an excellent software with similar intent. However, Fiddler is not
+as customable as goproxy intend to be. The main difference is, Fiddler is not
+intended to be used as a real proxy.
+
+A possible use case that suits goproxy but
+not Fiddler, is, gathering statisitics on page load times for a certain website over a week.
+With goproxy you could ask all your users to set their proxy to a dedicated machine running a
+goproxy server. Fiddler is a GUI app not designed to be ran like a server for multiple users.
+
+# A taste of goproxy
+
+To get a taste of `goproxy`, a basic HTTP/HTTPS transparent proxy
+
+
+ import (
+ "github.com/elazarl/goproxy"
+ "log"
+ "net/http"
+ )
+
+ func main() {
+ proxy := goproxy.NewProxyHttpServer()
+ proxy.Verbose = true
+ log.Fatal(http.ListenAndServe(":8080", proxy))
+ }
+
+
+This line will add `X-GoProxy: yxorPoG-X` header to all requests sent through the proxy
+
+ proxy.OnRequest().DoFunc(
+ func(r *http.Request,ctx *goproxy.ProxyCtx)(*http.Request,*http.Response) {
+ r.Header.Set("X-GoProxy","yxorPoG-X")
+ return r,nil
+ })
+
+`DoFunc` will process all incoming requests to the proxy. It will add a header to the request
+and return it. The proxy will send the modified request.
+
+Note that we returned nil value as the response. Have we returned a response, goproxy would
+have discarded the request and sent the new response to the client.
+
+In order to refuse connections to reddit at work time
+
+ proxy.OnRequest(goproxy.DstHostIs("www.reddit.com")).DoFunc(
+ func(r *http.Request,ctx *goproxy.ProxyCtx)(*http.Request,*http.Response) {
+ if h,_,_ := time.Now().Clock(); h >= 8 && h <= 17 {
+ return r,goproxy.NewResponse(r,
+ goproxy.ContentTypeText,http.StatusForbidden,
+ "Don't waste your time!")
+ }
+ return r,nil
+ })
+
+`DstHostIs` returns a `ReqCondition`, that is a function receiving a `Request` and returning a boolean
+we will only process requests that matches the condition. `DstHostIs("www.reddit.com")` will return
+a `ReqCondition` accepting only requests directed to "www.reddit.com".
+
+`DoFunc` will recieve a function that will preprocess the request. We can change the request, or
+return a response. If the time is between 8:00am and 17:00pm, we will neglect the request, and
+return a precanned text response saying "do not waste your time".
+
+See additional examples in the examples directory.
+
+# What's New
+
+ 1. Ability to `Hijack` CONNECT requests. See
+[the eavesdropper example](https://github.com/elazarl/goproxy/blob/master/examples/goproxy-eavesdropper/main.go#L27)
+2. Transparent proxy support for http/https including MITM certificate generation for TLS. See the [transparent example.](https://github.com/elazarl/goproxy/tree/master/examples/goproxy-transparent)
+
+# License
+
+I put the software temporarily under the Go-compatible BSD license,
+if this prevents someone from using the software, do let mee know and I'll consider changing it.
+
+At any rate, user feedback is very important for me, so I'll be delighted to know if you're using this package.
+
+# Beta Software
+
+I've received a positive feedback from a few people who use goproxy in production settings.
+I believe it is good enough for usage.
+
+I'll try to keep reasonable backwards compatability. In case of a major API change,
+I'll change the import path.
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/actions.go b/Godeps/_workspace/src/github.com/elazarl/goproxy/actions.go
new file mode 100644
index 0000000000..e1a3e7ff17
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/actions.go
@@ -0,0 +1,57 @@
+package goproxy
+
+import "net/http"
+
+// ReqHandler will "tamper" with the request coming to the proxy server
+// If Handle returns req,nil the proxy will send the returned request
+// to the destination server. If it returns nil,resp the proxy will
+// skip sending any requests, and will simply return the response `resp`
+// to the client.
+type ReqHandler interface {
+ Handle(req *http.Request, ctx *ProxyCtx) (*http.Request, *http.Response)
+}
+
+// A wrapper that would convert a function to a ReqHandler interface type
+type FuncReqHandler func(req *http.Request, ctx *ProxyCtx) (*http.Request, *http.Response)
+
+// FuncReqHandler.Handle(req,ctx) <=> FuncReqHandler(req,ctx)
+func (f FuncReqHandler) Handle(req *http.Request, ctx *ProxyCtx) (*http.Request, *http.Response) {
+ return f(req, ctx)
+}
+
+// after the proxy have sent the request to the destination server, it will
+// "filter" the response through the RespHandlers it has.
+// The proxy server will send to the client the response returned by the RespHandler.
+// In case of error, resp will be nil, and ctx.RoundTrip.Error will contain the error
+type RespHandler interface {
+ Handle(resp *http.Response, ctx *ProxyCtx) *http.Response
+}
+
+// A wrapper that would convert a function to a RespHandler interface type
+type FuncRespHandler func(resp *http.Response, ctx *ProxyCtx) *http.Response
+
+// FuncRespHandler.Handle(req,ctx) <=> FuncRespHandler(req,ctx)
+func (f FuncRespHandler) Handle(resp *http.Response, ctx *ProxyCtx) *http.Response {
+ return f(resp, ctx)
+}
+
+// When a client send a CONNECT request to a host, the request is filtered through
+// all the HttpsHandlers the proxy has, and if one returns true, the connection is
+// sniffed using Man in the Middle attack.
+// That is, the proxy will create a TLS connection with the client, another TLS
+// connection with the destination the client wished to connect to, and would
+// send back and forth all messages from the server to the client and vice versa.
+// The request and responses sent in this Man In the Middle channel are filtered
+// through the usual flow (request and response filtered through the ReqHandlers
+// and RespHandlers)
+type HttpsHandler interface {
+ HandleConnect(req string, ctx *ProxyCtx) (*ConnectAction, string)
+}
+
+// A wrapper that would convert a function to a HttpsHandler interface type
+type FuncHttpsHandler func(host string, ctx *ProxyCtx) (*ConnectAction, string)
+
+// FuncHttpsHandler should implement the RespHandler interface
+func (f FuncHttpsHandler) HandleConnect(host string, ctx *ProxyCtx) (*ConnectAction, string) {
+ return f(host, ctx)
+}
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/all.bash b/Godeps/_workspace/src/github.com/elazarl/goproxy/all.bash
new file mode 100644
index 0000000000..6503e73dc9
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/all.bash
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+go test || exit
+for action in $@; do go $action; done
+
+mkdir -p bin
+find regretable examples/* ext/* -maxdepth 0 -type d | while read d; do
+ (cd $d
+ go build -o ../../bin/$(basename $d)
+ find *_test.go -maxdepth 0 2>/dev/null|while read f;do
+ for action in $@; do go $action; done
+ go test
+ break
+ done)
+done
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/ca.pem b/Godeps/_workspace/src/github.com/elazarl/goproxy/ca.pem
new file mode 100644
index 0000000000..f138424932
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/ca.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICSjCCAbWgAwIBAgIBADALBgkqhkiG9w0BAQUwSjEjMCEGA1UEChMaZ2l0aHVi
+LmNvbS9lbGF6YXJsL2dvcHJveHkxIzAhBgNVBAMTGmdpdGh1Yi5jb20vZWxhemFy
+bC9nb3Byb3h5MB4XDTAwMDEwMTAwMDAwMFoXDTQ5MTIzMTIzNTk1OVowSjEjMCEG
+A1UEChMaZ2l0aHViLmNvbS9lbGF6YXJsL2dvcHJveHkxIzAhBgNVBAMTGmdpdGh1
+Yi5jb20vZWxhemFybC9nb3Byb3h5MIGdMAsGCSqGSIb3DQEBAQOBjQAwgYkCgYEA
+vz9BbCaJjxs73Tvcq3leP32hAGerQ1RgvlZ68Z4nZmoVHfl+2Nr/m0dmW+GdOfpT
+cs/KzfJjYGr/84x524fiuR8GdZ0HOtXJzyF5seoWnbBIuyr1PbEpgRhGQMqqOUuj
+YExeLbfNHPIoJ8XZ1Vzyv3YxjbmjWA+S/uOe9HWtDbMCAwEAAaNGMEQwDgYDVR0P
+AQH/BAQDAgCkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8w
+DAYDVR0RBAUwA4IBKjALBgkqhkiG9w0BAQUDgYEAIcL8huSmGMompNujsvePTUnM
+oEUKtX4Eh/+s+DSfV/TyI0I+3GiPpLplEgFWuoBIJGios0r1dKh5N0TGjxX/RmGm
+qo7E4jjJuo8Gs5U8/fgThZmshax2lwLtbRNwhvUVr65GdahLsZz8I+hySLuatVvR
+qHHq/FQORIiNyNpq/Hg=
+-----END CERTIFICATE-----
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/certs.go b/Godeps/_workspace/src/github.com/elazarl/goproxy/certs.go
new file mode 100644
index 0000000000..8da2e6240a
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/certs.go
@@ -0,0 +1,56 @@
+package goproxy
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+)
+
+func init() {
+ if goproxyCaErr != nil {
+ panic("Error parsing builtin CA " + goproxyCaErr.Error())
+ }
+ var err error
+ if GoproxyCa.Leaf, err = x509.ParseCertificate(GoproxyCa.Certificate[0]); err != nil {
+ panic("Error parsing builtin CA " + err.Error())
+ }
+}
+
+var tlsClientSkipVerify = &tls.Config{InsecureSkipVerify: true}
+
+var defaultTLSConfig = &tls.Config{
+ InsecureSkipVerify: true,
+}
+
+var CA_CERT = []byte(`-----BEGIN CERTIFICATE-----
+MIICSjCCAbWgAwIBAgIBADALBgkqhkiG9w0BAQUwSjEjMCEGA1UEChMaZ2l0aHVi
+LmNvbS9lbGF6YXJsL2dvcHJveHkxIzAhBgNVBAMTGmdpdGh1Yi5jb20vZWxhemFy
+bC9nb3Byb3h5MB4XDTAwMDEwMTAwMDAwMFoXDTQ5MTIzMTIzNTk1OVowSjEjMCEG
+A1UEChMaZ2l0aHViLmNvbS9lbGF6YXJsL2dvcHJveHkxIzAhBgNVBAMTGmdpdGh1
+Yi5jb20vZWxhemFybC9nb3Byb3h5MIGdMAsGCSqGSIb3DQEBAQOBjQAwgYkCgYEA
+vz9BbCaJjxs73Tvcq3leP32hAGerQ1RgvlZ68Z4nZmoVHfl+2Nr/m0dmW+GdOfpT
+cs/KzfJjYGr/84x524fiuR8GdZ0HOtXJzyF5seoWnbBIuyr1PbEpgRhGQMqqOUuj
+YExeLbfNHPIoJ8XZ1Vzyv3YxjbmjWA+S/uOe9HWtDbMCAwEAAaNGMEQwDgYDVR0P
+AQH/BAQDAgCkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8w
+DAYDVR0RBAUwA4IBKjALBgkqhkiG9w0BAQUDgYEAIcL8huSmGMompNujsvePTUnM
+oEUKtX4Eh/+s+DSfV/TyI0I+3GiPpLplEgFWuoBIJGios0r1dKh5N0TGjxX/RmGm
+qo7E4jjJuo8Gs5U8/fgThZmshax2lwLtbRNwhvUVr65GdahLsZz8I+hySLuatVvR
+qHHq/FQORIiNyNpq/Hg=
+-----END CERTIFICATE-----`)
+
+var CA_KEY = []byte(`-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQC/P0FsJomPGzvdO9yreV4/faEAZ6tDVGC+VnrxnidmahUd+X7Y
+2v+bR2Zb4Z05+lNyz8rN8mNgav/zjHnbh+K5HwZ1nQc61cnPIXmx6hadsEi7KvU9
+sSmBGEZAyqo5S6NgTF4tt80c8ignxdnVXPK/djGNuaNYD5L+4570da0NswIDAQAB
+AoGBALzIv1b4D7ARTR3NOr6V9wArjiOtMjUrdLhO+9vIp9IEA8ZsA9gjDlCEwbkP
+VDnoLjnWfraff5Os6+3JjHy1fYpUiCdnk2XA6iJSL1XWKQZPt3wOunxP4lalDgED
+QTRReFbA/y/Z4kSfTXpVj68ytcvSRW/N7q5/qRtbN9804jpBAkEA0s6lvH2btSLA
+mcEdwhs7zAslLbdld7rvfUeP82gPPk0S6yUqTNyikqshM9AwAktHY7WvYdKl+ghZ
+HTxKVC4DoQJBAOg/IAW5RbXknP+Lf7AVtBgw3E+Yfa3mcdLySe8hjxxyZq825Zmu
+Rt5Qj4Lw6ifSFNy4kiiSpE/ZCukYvUXGENMCQFkPxSWlS6tzSzuqQxBGwTSrYMG3
+wb6b06JyIXcMd6Qym9OMmBpw/J5KfnSNeDr/4uFVWQtTG5xO+pdHaX+3EQECQQDl
+qcbY4iX1gWVfr2tNjajSYz751yoxVbkpiT9joiQLVXYFvpu+JYEfRzsjmWl0h2Lq
+AftG8/xYmaEYcMZ6wSrRAkBUwiom98/8wZVlB6qbwhU1EKDFANvICGSWMIhPx3v7
+MJqTIj4uJhte2/uyVvZ6DC6noWYgy+kLgqG0S97tUEG8
+-----END RSA PRIVATE KEY-----`)
+
+var GoproxyCa, goproxyCaErr = tls.X509KeyPair(CA_CERT, CA_KEY)
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/chunked.go b/Godeps/_workspace/src/github.com/elazarl/goproxy/chunked.go
new file mode 100644
index 0000000000..83654f6586
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/chunked.go
@@ -0,0 +1,59 @@
+// Taken from $GOROOT/src/pkg/net/http/chunked
+// needed to write https responses to client.
+package goproxy
+
+import (
+ "io"
+ "strconv"
+)
+
+// newChunkedWriter returns a new chunkedWriter that translates writes into HTTP
+// "chunked" format before writing them to w. Closing the returned chunkedWriter
+// sends the final 0-length chunk that marks the end of the stream.
+//
+// newChunkedWriter is not needed by normal applications. The http
+// package adds chunking automatically if handlers don't set a
+// Content-Length header. Using newChunkedWriter inside a handler
+// would result in double chunking or chunking with a Content-Length
+// length, both of which are wrong.
+func newChunkedWriter(w io.Writer) io.WriteCloser {
+ return &chunkedWriter{w}
+}
+
+// Writing to chunkedWriter translates to writing in HTTP chunked Transfer
+// Encoding wire format to the underlying Wire chunkedWriter.
+type chunkedWriter struct {
+ Wire io.Writer
+}
+
+// Write the contents of data as one chunk to Wire.
+// NOTE: Note that the corresponding chunk-writing procedure in Conn.Write has
+// a bug since it does not check for success of io.WriteString
+func (cw *chunkedWriter) Write(data []byte) (n int, err error) {
+
+ // Don't send 0-length data. It looks like EOF for chunked encoding.
+ if len(data) == 0 {
+ return 0, nil
+ }
+
+ head := strconv.FormatInt(int64(len(data)), 16) + "\r\n"
+
+ if _, err = io.WriteString(cw.Wire, head); err != nil {
+ return 0, err
+ }
+ if n, err = cw.Wire.Write(data); err != nil {
+ return
+ }
+ if n != len(data) {
+ err = io.ErrShortWrite
+ return
+ }
+ _, err = io.WriteString(cw.Wire, "\r\n")
+
+ return
+}
+
+func (cw *chunkedWriter) Close() error {
+ _, err := io.WriteString(cw.Wire, "0\r\n")
+ return err
+}
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/counterecryptor.go b/Godeps/_workspace/src/github.com/elazarl/goproxy/counterecryptor.go
new file mode 100644
index 0000000000..494e7a4fed
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/counterecryptor.go
@@ -0,0 +1,68 @@
+package goproxy
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/rsa"
+ "crypto/sha256"
+ "crypto/x509"
+ "errors"
+)
+
+type CounterEncryptorRand struct {
+ cipher cipher.Block
+ counter []byte
+ rand []byte
+ ix int
+}
+
+func NewCounterEncryptorRandFromKey(key interface{}, seed []byte) (r CounterEncryptorRand, err error) {
+ var keyBytes []byte
+ switch key := key.(type) {
+ case *rsa.PrivateKey:
+ keyBytes = x509.MarshalPKCS1PrivateKey(key)
+ default:
+ err = errors.New("only RSA keys supported")
+ return
+ }
+ h := sha256.New()
+ if r.cipher, err = aes.NewCipher(h.Sum(keyBytes)[:aes.BlockSize]); err != nil {
+ return
+ }
+ r.counter = make([]byte, r.cipher.BlockSize())
+ if seed != nil {
+ copy(r.counter, h.Sum(seed)[:r.cipher.BlockSize()])
+ }
+ r.rand = make([]byte, r.cipher.BlockSize())
+ r.ix = len(r.rand)
+ return
+}
+
+func (c *CounterEncryptorRand) Seed(b []byte) {
+ if len(b) != len(c.counter) {
+ panic("SetCounter: wrong counter size")
+ }
+ copy(c.counter, b)
+}
+
+func (c *CounterEncryptorRand) refill() {
+ c.cipher.Encrypt(c.rand, c.counter)
+ for i := 0; i < len(c.counter); i++ {
+ if c.counter[i]++; c.counter[i] != 0 {
+ break
+ }
+ }
+ c.ix = 0
+}
+
+func (c *CounterEncryptorRand) Read(b []byte) (n int, err error) {
+ if c.ix == len(c.rand) {
+ c.refill()
+ }
+ if n = len(c.rand) - c.ix; n > len(b) {
+ n = len(b)
+ }
+ copy(b, c.rand[c.ix:c.ix+n])
+ c.ix += n
+ return
+}
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/counterecryptor_test.go b/Godeps/_workspace/src/github.com/elazarl/goproxy/counterecryptor_test.go
new file mode 100644
index 0000000000..12b31e16f4
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/counterecryptor_test.go
@@ -0,0 +1,99 @@
+package goproxy_test
+
+import (
+ "bytes"
+ "crypto/rsa"
+ "encoding/binary"
+ "github.com/elazarl/goproxy"
+ "io"
+ "math"
+ "math/rand"
+ "testing"
+)
+
+type RandSeedReader struct {
+ r rand.Rand
+}
+
+func (r *RandSeedReader) Read(b []byte) (n int, err error) {
+ for i := range b {
+ b[i] = byte(r.r.Int() & 0xFF)
+ }
+ return len(b), nil
+}
+
+func TestCounterEncDifferentConsecutive(t *testing.T) {
+ k, err := rsa.GenerateKey(&RandSeedReader{*rand.New(rand.NewSource(0xFF43109))}, 128)
+ fatalOnErr(err, "rsa.GenerateKey", t)
+ c, err := goproxy.NewCounterEncryptorRandFromKey(k, []byte("the quick brown fox run over the lazy dog"))
+ fatalOnErr(err, "NewCounterEncryptorRandFromKey", t)
+ for i := 0; i < 100*1000; i++ {
+ var a, b int64
+ binary.Read(&c, binary.BigEndian, &a)
+ binary.Read(&c, binary.BigEndian, &b)
+ if a == b {
+ t.Fatal("two consecutive equal int64", a, b)
+ }
+ }
+}
+
+func TestCounterEncIdenticalStreams(t *testing.T) {
+ k, err := rsa.GenerateKey(&RandSeedReader{*rand.New(rand.NewSource(0xFF43109))}, 128)
+ fatalOnErr(err, "rsa.GenerateKey", t)
+ c1, err := goproxy.NewCounterEncryptorRandFromKey(k, []byte("the quick brown fox run over the lazy dog"))
+ fatalOnErr(err, "NewCounterEncryptorRandFromKey", t)
+ c2, err := goproxy.NewCounterEncryptorRandFromKey(k, []byte("the quick brown fox run over the lazy dog"))
+ fatalOnErr(err, "NewCounterEncryptorRandFromKey", t)
+ nout := 1000
+ out1, out2 := make([]byte, nout), make([]byte, nout)
+ io.ReadFull(&c1, out1)
+ tmp := out2[:]
+ rand.Seed(0xFF43109)
+ for len(tmp) > 0 {
+ n := 1 + rand.Intn(256)
+ if n > len(tmp) {
+ n = len(tmp)
+ }
+ n, err := c2.Read(tmp[:n])
+ fatalOnErr(err, "CounterEncryptorRand.Read", t)
+ tmp = tmp[n:]
+ }
+ if !bytes.Equal(out1, out2) {
+ t.Error("identical CSPRNG does not produce the same output")
+ }
+}
+
+func stddev(data []int) float64 {
+ var sum, sum_sqr float64 = 0, 0
+ for _, h := range data {
+ sum += float64(h)
+ sum_sqr += float64(h) * float64(h)
+ }
+ n := float64(len(data))
+ variance := (sum_sqr - ((sum * sum) / n)) / (n - 1)
+ return math.Sqrt(variance)
+}
+
+func TestCounterEncStreamHistogram(t *testing.T) {
+ k, err := rsa.GenerateKey(&RandSeedReader{*rand.New(rand.NewSource(0xFF43109))}, 128)
+ fatalOnErr(err, "rsa.GenerateKey", t)
+ c, err := goproxy.NewCounterEncryptorRandFromKey(k, []byte("the quick brown fox run over the lazy dog"))
+ fatalOnErr(err, "NewCounterEncryptorRandFromKey", t)
+ nout := 100 * 1000
+ out := make([]byte, nout)
+ io.ReadFull(&c, out)
+ refhist := make([]int, 256)
+ for i := 0; i < nout; i++ {
+ refhist[rand.Intn(256)]++
+ }
+ hist := make([]int, 256)
+ for _, b := range out {
+ hist[int(b)]++
+ }
+ refstddev, stddev := stddev(refhist), stddev(hist)
+ // due to lack of time, I guestimate
+ t.Logf("ref:%v - act:%v = %v", refstddev, stddev, math.Abs(refstddev-stddev))
+ if math.Abs(refstddev-stddev) >= 1 {
+ t.Errorf("stddev of ref histogram different than regular PRNG: %v %v", refstddev, stddev)
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/ctx.go b/Godeps/_workspace/src/github.com/elazarl/goproxy/ctx.go
new file mode 100644
index 0000000000..95bfd80043
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/ctx.go
@@ -0,0 +1,87 @@
+package goproxy
+
+import (
+ "net/http"
+ "regexp"
+)
+
+// ProxyCtx is the Proxy context, contains useful information about every request. It is passed to
+// every user function. Also used as a logger.
+type ProxyCtx struct {
+ // Will contain the client request from the proxy
+ Req *http.Request
+ // Will contain the remote server's response (if available. nil if the request wasn't send yet)
+ Resp *http.Response
+ RoundTripper RoundTripper
+ // will contain the recent error that occured while trying to send receive or parse traffic
+ Error error
+ // A handle for the user to keep data in the context, from the call of ReqHandler to the
+ // call of RespHandler
+ UserData interface{}
+ // Will connect a request to a response
+ Session int64
+ proxy *ProxyHttpServer
+}
+
+type RoundTripper interface {
+ RoundTrip(req *http.Request, ctx *ProxyCtx) (*http.Response, error)
+}
+
+type RoundTripperFunc func(req *http.Request, ctx *ProxyCtx) (*http.Response, error)
+
+func (f RoundTripperFunc) RoundTrip(req *http.Request, ctx *ProxyCtx) (*http.Response, error) {
+ return f(req, ctx)
+}
+
+func (ctx *ProxyCtx) RoundTrip(req *http.Request) (*http.Response, error) {
+ if ctx.RoundTripper != nil {
+ return ctx.RoundTripper.RoundTrip(req, ctx)
+ }
+ return ctx.proxy.Tr.RoundTrip(req)
+}
+
+func (ctx *ProxyCtx) printf(msg string, argv ...interface{}) {
+ ctx.proxy.Logger.Printf("[%03d] "+msg+"\n", append([]interface{}{ctx.Session & 0xFF}, argv...)...)
+}
+
+// Logf prints a message to the proxy's log. Should be used in a ProxyHttpServer's filter
+// This message will be printed only if the Verbose field of the ProxyHttpServer is set to true
+//
+// proxy.OnRequest().DoFunc(func(r *http.Request,ctx *goproxy.ProxyCtx) (*http.Request, *http.Response){
+// nr := atomic.AddInt32(&counter,1)
+// ctx.Printf("So far %d requests",nr)
+// return r, nil
+// })
+func (ctx *ProxyCtx) Logf(msg string, argv ...interface{}) {
+ if ctx.proxy.Verbose {
+ ctx.printf("INFO: "+msg, argv...)
+ }
+}
+
+// Warnf prints a message to the proxy's log. Should be used in a ProxyHttpServer's filter
+// This message will always be printed.
+//
+// proxy.OnRequest().DoFunc(func(r *http.Request,ctx *goproxy.ProxyCtx) (*http.Request, *http.Response){
+// f,err := os.OpenFile(cachedContent)
+// if err != nil {
+// ctx.Warnf("error open file %v: %v",cachedContent,err)
+// return r, nil
+// }
+// return r, nil
+// })
+func (ctx *ProxyCtx) Warnf(msg string, argv ...interface{}) {
+ ctx.printf("WARN: "+msg, argv...)
+}
+
+var charsetFinder = regexp.MustCompile("charset=([^ ;]*)")
+
+// Will try to infer the character set of the request from the headers.
+// Returns the empty string if we don't know which character set it used.
+// Currently it will look for charset= in the Content-Type header of the request.
+func (ctx *ProxyCtx) Charset() string {
+ charsets := charsetFinder.FindStringSubmatch(ctx.Resp.Header.Get("Content-Type"))
+ if charsets == nil {
+ return ""
+ }
+ return charsets[1]
+}
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/dispatcher.go b/Godeps/_workspace/src/github.com/elazarl/goproxy/dispatcher.go
new file mode 100644
index 0000000000..69219b365e
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/dispatcher.go
@@ -0,0 +1,320 @@
+package goproxy
+
+import (
+ "bytes"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "regexp"
+ "strings"
+)
+
+// ReqCondition.HandleReq will decide whether or not to use the ReqHandler on an HTTP request
+// before sending it to the remote server
+type ReqCondition interface {
+ RespCondition
+ HandleReq(req *http.Request, ctx *ProxyCtx) bool
+}
+
+// RespCondition.HandleReq will decide whether or not to use the RespHandler on an HTTP response
+// before sending it to the proxy client. Note that resp might be nil, in case there was an
+// error sending the request.
+type RespCondition interface {
+ HandleResp(resp *http.Response, ctx *ProxyCtx) bool
+}
+
+// ReqConditionFunc.HandleReq(req,ctx) <=> ReqConditionFunc(req,ctx)
+type ReqConditionFunc func(req *http.Request, ctx *ProxyCtx) bool
+
+// RespConditionFunc.HandleResp(resp,ctx) <=> RespConditionFunc(resp,ctx)
+type RespConditionFunc func(resp *http.Response, ctx *ProxyCtx) bool
+
+func (c ReqConditionFunc) HandleReq(req *http.Request, ctx *ProxyCtx) bool {
+ return c(req, ctx)
+}
+
+// ReqConditionFunc cannot test responses. It only satisfies RespCondition interface so that
+// to be usable as RespCondition.
+func (c ReqConditionFunc) HandleResp(resp *http.Response, ctx *ProxyCtx) bool {
+ return c(ctx.Req, ctx)
+}
+
+func (c RespConditionFunc) HandleResp(resp *http.Response, ctx *ProxyCtx) bool {
+ return c(resp, ctx)
+}
+
+// UrlHasPrefix returns a ReqCondition checking wether the destination URL the proxy client has requested
+// has the given prefix, with or without the host.
+// For example UrlHasPrefix("host/x") will match requests of the form 'GET host/x', and will match
+// requests to url 'http://host/x'
+func UrlHasPrefix(prefix string) ReqConditionFunc {
+ return func(req *http.Request, ctx *ProxyCtx) bool {
+ return strings.HasPrefix(req.URL.Path, prefix) ||
+ strings.HasPrefix(req.URL.Host+req.URL.Path, prefix) ||
+ strings.HasPrefix(req.URL.Scheme+req.URL.Host+req.URL.Path, prefix)
+ }
+}
+
+// UrlIs returns a ReqCondition, testing whether or not the request URL is one of the given strings
+// with or without the host prefix.
+// UrlIs("google.com/","foo") will match requests 'GET /' to 'google.com', requests `'GET google.com/' to
+// any host, and requests of the form 'GET foo'.
+func UrlIs(urls ...string) ReqConditionFunc {
+ urlSet := make(map[string]bool)
+ for _, u := range urls {
+ urlSet[u] = true
+ }
+ return func(req *http.Request, ctx *ProxyCtx) bool {
+ _, pathOk := urlSet[req.URL.Path]
+ _, hostAndOk := urlSet[req.URL.Host+req.URL.Path]
+ return pathOk || hostAndOk
+ }
+}
+
+// ReqHostMatches returns a ReqCondition, testing whether the host to which the request was directed to matches
+// any of the given regular expressions.
+func ReqHostMatches(regexps ...*regexp.Regexp) ReqConditionFunc {
+ return func(req *http.Request, ctx *ProxyCtx) bool {
+ for _, re := range regexps {
+ if re.MatchString(req.Host) {
+ return true
+ }
+ }
+ return false
+ }
+}
+
+// ReqHostIs returns a ReqCondition, testing whether the host to which the request is directed to equal
+// to one of the given strings
+func ReqHostIs(hosts ...string) ReqConditionFunc {
+ hostSet := make(map[string]bool)
+ for _, h := range hosts {
+ hostSet[h] = true
+ }
+ return func(req *http.Request, ctx *ProxyCtx) bool {
+ _, ok := hostSet[req.URL.Host]
+ return ok
+ }
+}
+
+var localHostIpv4 = regexp.MustCompile(`127\.0\.0\.\d+`)
+
+// IsLocalHost checks whether the destination host is explicitly local host
+// (buggy, there can be IPv6 addresses it doesn't catch)
+var IsLocalHost ReqConditionFunc = func(req *http.Request, ctx *ProxyCtx) bool {
+ return req.URL.Host == "::1" ||
+ req.URL.Host == "0:0:0:0:0:0:0:1" ||
+ localHostIpv4.MatchString(req.URL.Host) ||
+ req.URL.Host == "localhost"
+}
+
+// UrlMatches returns a ReqCondition testing whether the destination URL
+// of the request matches the given regexp, with or without prefix
+func UrlMatches(re *regexp.Regexp) ReqConditionFunc {
+ return func(req *http.Request, ctx *ProxyCtx) bool {
+ return re.MatchString(req.URL.Path) ||
+ re.MatchString(req.URL.Host+req.URL.Path)
+ }
+}
+
+// DstHostIs returns a ReqCondition testing wether the host in the request url is the given string
+func DstHostIs(host string) ReqConditionFunc {
+ return func(req *http.Request, ctx *ProxyCtx) bool {
+ return req.URL.Host == host
+ }
+}
+
+// SrcIpIs returns a ReqCondition testing wether the source IP of the request is the given string
+func SrcIpIs(ip string) ReqCondition {
+ return ReqConditionFunc(func(req *http.Request, ctx *ProxyCtx) bool {
+ return strings.HasPrefix(req.RemoteAddr, ip+":")
+ })
+}
+
+// Not returns a ReqCondition negating the given ReqCondition
+func Not(r ReqCondition) ReqConditionFunc {
+ return func(req *http.Request, ctx *ProxyCtx) bool {
+ return !r.HandleReq(req, ctx)
+ }
+}
+
+// ContentTypeIs returns a RespCondition testing whether the HTTP response has Content-Type header equal
+// to one of the given strings.
+func ContentTypeIs(typ string, types ...string) RespCondition {
+ types = append(types, typ)
+ return RespConditionFunc(func(resp *http.Response, ctx *ProxyCtx) bool {
+ if resp == nil {
+ return false
+ }
+ contentType := resp.Header.Get("Content-Type")
+ for _, typ := range types {
+ if contentType == typ || strings.HasPrefix(contentType, typ+";") {
+ return true
+ }
+ }
+ return false
+ })
+}
+
+// ProxyHttpServer.OnRequest Will return a temporary ReqProxyConds struct, aggregating the given condtions.
+// You will use the ReqProxyConds struct to register a ReqHandler, that would filter
+// the request, only if all the given ReqCondition matched.
+// Typical usage:
+// proxy.OnRequest(UrlIs("example.com/foo"),UrlMatches(regexp.MustParse(`.*\.exampl.\com\./.*`)).Do(...)
+func (proxy *ProxyHttpServer) OnRequest(conds ...ReqCondition) *ReqProxyConds {
+ return &ReqProxyConds{proxy, conds}
+}
+
+// ReqProxyConds aggregate ReqConditions for a ProxyHttpServer. Upon calling Do, it will register a ReqHandler that would
+// handle the request if all conditions on the HTTP request are met.
+type ReqProxyConds struct {
+ proxy *ProxyHttpServer
+ reqConds []ReqCondition
+}
+
+// DoFunc is equivalent to proxy.OnRequest().Do(FuncReqHandler(f))
+func (pcond *ReqProxyConds) DoFunc(f func(req *http.Request, ctx *ProxyCtx) (*http.Request, *http.Response)) {
+ pcond.Do(FuncReqHandler(f))
+}
+
+// ReqProxyConds.Do will register the ReqHandler on the proxy,
+// the ReqHandler will handle the HTTP request if all the conditions
+// aggregated in the ReqProxyConds are met. Typical usage:
+// proxy.OnRequest().Do(handler) // will call handler.Handle(req,ctx) on every request to the proxy
+// proxy.OnRequest(cond1,cond2).Do(handler)
+// // given request to the proxy, will test if cond1.HandleReq(req,ctx) && cond2.HandleReq(req,ctx) are true
+// // if they are, will call handler.Handle(req,ctx)
+func (pcond *ReqProxyConds) Do(h ReqHandler) {
+ pcond.proxy.reqHandlers = append(pcond.proxy.reqHandlers,
+ FuncReqHandler(func(r *http.Request, ctx *ProxyCtx) (*http.Request, *http.Response) {
+ for _, cond := range pcond.reqConds {
+ if !cond.HandleReq(r, ctx) {
+ return r, nil
+ }
+ }
+ return h.Handle(r, ctx)
+ }))
+}
+
+// HandleConnect is used when proxy receives an HTTP CONNECT request,
+// it'll then use the HttpsHandler to determine what should it
+// do with this request. The handler returns a ConnectAction struct, the Action field in the ConnectAction
+// struct returned will determine what to do with this request. ConnectAccept will simply accept the request
+// forwarding all bytes from the client to the remote host, ConnectReject will close the connection with the
+// client, and ConnectMitm, will assume the underlying connection is an HTTPS connection, and will use Man
+// in the Middle attack to eavesdrop the connection. All regular handler will be active on this eavesdropped
+// connection.
+// The ConnectAction struct contains possible tlsConfig that will be used for eavesdropping. If nil, the proxy
+// will use the default tls configuration.
+// proxy.OnRequest().HandleConnect(goproxy.AlwaysReject) // rejects all CONNECT requests
+func (pcond *ReqProxyConds) HandleConnect(h HttpsHandler) {
+ pcond.proxy.httpsHandlers = append(pcond.proxy.httpsHandlers,
+ FuncHttpsHandler(func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
+ for _, cond := range pcond.reqConds {
+ if !cond.HandleReq(ctx.Req, ctx) {
+ return nil, ""
+ }
+ }
+ return h.HandleConnect(host, ctx)
+ }))
+}
+
+// HandleConnectFunc is equivalent to HandleConnect,
+// for example, accepting CONNECT request if they contain a password in header
+// io.WriteString(h,password)
+// passHash := h.Sum(nil)
+// proxy.OnRequest().HandleConnectFunc(func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
+// c := sha1.New()
+// io.WriteString(c,ctx.Req.Header.Get("X-GoProxy-Auth"))
+// if c.Sum(nil) == passHash {
+// return OkConnect, host
+// }
+// return RejectConnect, host
+// })
+func (pcond *ReqProxyConds) HandleConnectFunc(f func(host string, ctx *ProxyCtx) (*ConnectAction, string)) {
+ pcond.HandleConnect(FuncHttpsHandler(f))
+}
+
+func (pcond *ReqProxyConds) HijackConnect(f func(req *http.Request, client net.Conn, ctx *ProxyCtx)) {
+ pcond.proxy.httpsHandlers = append(pcond.proxy.httpsHandlers,
+ FuncHttpsHandler(func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
+ for _, cond := range pcond.reqConds {
+ if !cond.HandleReq(ctx.Req, ctx) {
+ return nil, ""
+ }
+ }
+ return &ConnectAction{Action: ConnectHijack, Hijack: f}, host
+ }))
+}
+
+// ProxyConds is used to aggregate RespConditions for a ProxyHttpServer.
+// Upon calling ProxyConds.Do, it will register a RespHandler that would
+// handle the HTTP response from remote server if all conditions on the HTTP response are met.
+type ProxyConds struct {
+ proxy *ProxyHttpServer
+ reqConds []ReqCondition
+ respCond []RespCondition
+}
+
+// ProxyConds.DoFunc is equivalent to proxy.OnResponse().Do(FuncRespHandler(f))
+func (pcond *ProxyConds) DoFunc(f func(resp *http.Response, ctx *ProxyCtx) *http.Response) {
+ pcond.Do(FuncRespHandler(f))
+}
+
+// ProxyConds.Do will register the RespHandler on the proxy, h.Handle(resp,ctx) will be called on every
+// request that matches the conditions aggregated in pcond.
+func (pcond *ProxyConds) Do(h RespHandler) {
+ pcond.proxy.respHandlers = append(pcond.proxy.respHandlers,
+ FuncRespHandler(func(resp *http.Response, ctx *ProxyCtx) *http.Response {
+ for _, cond := range pcond.reqConds {
+ if !cond.HandleReq(ctx.Req, ctx) {
+ return resp
+ }
+ }
+ for _, cond := range pcond.respCond {
+ if !cond.HandleResp(resp, ctx) {
+ return resp
+ }
+ }
+ return h.Handle(resp, ctx)
+ }))
+}
+
+// OnResponse is used when adding a response-filter to the HTTP proxy, usual pattern is
+// proxy.OnResponse(cond1,cond2).Do(handler) // handler.Handle(resp,ctx) will be used
+// // if cond1.HandleResp(resp) && cond2.HandleResp(resp)
+func (proxy *ProxyHttpServer) OnResponse(conds ...RespCondition) *ProxyConds {
+ return &ProxyConds{proxy, make([]ReqCondition, 0), conds}
+}
+
+// AlwaysMitm is a HttpsHandler that always eavesdrop https connections, for example to
+// eavesdrop all https connections to www.google.com, we can use
+// proxy.OnRequest(goproxy.ReqHostIs("www.google.com")).HandleConnect(goproxy.AlwaysMitm)
+var AlwaysMitm FuncHttpsHandler = func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
+ return MitmConnect, host
+}
+
+// AlwaysReject is a HttpsHandler that drops any CONNECT request, for example, this code will disallow
+// connections to hosts on any other port than 443
+// proxy.OnRequest(goproxy.Not(goproxy.ReqHostMatches(regexp.MustCompile(":443$"))).
+// HandleConnect(goproxy.AlwaysReject)
+var AlwaysReject FuncHttpsHandler = func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
+ return RejectConnect, host
+}
+
+// HandleBytes will return a RespHandler that read the entire body of the request
+// to a byte array in memory, would run the user supplied f function on the byte arra,
+// and will replace the body of the original response with the resulting byte array.
+func HandleBytes(f func(b []byte, ctx *ProxyCtx) []byte) RespHandler {
+ return FuncRespHandler(func(resp *http.Response, ctx *ProxyCtx) *http.Response {
+ b, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ ctx.Warnf("Cannot read response %s", err)
+ return resp
+ }
+ resp.Body.Close()
+
+ resp.Body = ioutil.NopCloser(bytes.NewBuffer(f(b, ctx)))
+ return resp
+ })
+}
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/doc.go b/Godeps/_workspace/src/github.com/elazarl/goproxy/doc.go
new file mode 100644
index 0000000000..50aaa71f80
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/doc.go
@@ -0,0 +1,100 @@
+/*
+Package goproxy provides a customizable HTTP proxy,
+supporting hijacking HTTPS connection.
+
+The intent of the proxy, is to be usable with reasonable amount of traffic
+yet, customizable and programable.
+
+The proxy itself is simply an `net/http` handler.
+
+Typical usage is
+
+ proxy := goproxy.NewProxyHttpServer()
+ proxy.OnRequest(..conditions..).Do(..requesthandler..)
+ proxy.OnRequest(..conditions..).DoFunc(..requesthandlerFunction..)
+ proxy.OnResponse(..conditions..).Do(..responesHandler..)
+ proxy.OnResponse(..conditions..).DoFunc(..responesHandlerFunction..)
+ http.ListenAndServe(":8080", proxy)
+
+Adding a header to each request
+
+ proxy.OnRequest().DoFunc(func(r *http.Request,ctx *goproxy.ProxyCtx) (*http.Request, *http.Response){
+ r.Header.Set("X-GoProxy","1")
+ return r, nil
+ })
+
+Note that the function is called before the proxy sends the request to the server
+
+For printing the content type of all incoming responses
+
+ proxy.OnResponse().DoFunc(func(r *http.Response, ctx *goproxy.ProxyCtx)*http.Response{
+ println(ctx.Req.Host,"->",r.Header.Get("Content-Type"))
+ return r
+ })
+
+note that we used the ProxyCtx context variable here. It contains the request
+and the response (Req and Resp, Resp is nil if unavailable) of this specific client
+interaction with the proxy.
+
+To print the content type of all responses from a certain url, we'll add a
+ReqCondition to the OnResponse function:
+
+ proxy.OnResponse(goproxy.UrlIs("golang.org/pkg")).DoFunc(func(r *http.Response, ctx *goproxy.ProxyCtx)*http.Response{
+ println(ctx.Req.Host,"->",r.Header.Get("Content-Type"))
+ return r
+ })
+
+We can write the condition ourselves, conditions can be set on request and on response
+
+ var random = ReqConditionFunc(func(r *http.Request) bool {
+ return rand.Intn(1) == 0
+ })
+ var hasGoProxyHeader = RespConditionFunc(func(resp *http.Response,req *http.Request)bool {
+ return resp.Header.Get("X-GoProxy") != ""
+ })
+
+Caution! If you give a RespCondition to the OnRequest function, you'll get a run time panic! It doesn't
+make sense to read the response, if you still haven't got it!
+
+Finally, we have convenience function to throw a quick response
+
+ proxy.OnResponse(hasGoProxyHeader).DoFunc(func(r*http.Response,ctx *goproxy.ProxyCtx)*http.Response {
+ r.Body.Close()
+ return goproxy.ForbiddenTextResponse(ctx.Req,"Can't see response with X-GoProxy header!")
+ })
+
+we close the body of the original repsonse, and return a new 403 response with a short message.
+
+Example use cases:
+
+1. https://github.com/elazarl/goproxy/tree/master/examples/goproxy-avgsize
+
+To measure the average size of an Html served in your site. One can ask
+all the QA team to access the website by a proxy, and the proxy will
+measure the average size of all text/html responses from your host.
+
+2. [not yet implemented]
+
+All requests to your web servers should be directed through the proxy,
+when the proxy will detect html pieces sent as a response to AJAX
+request, it'll send a warning email.
+
+3. https://github.com/elazarl/goproxy/blob/master/examples/goproxy-httpdump/
+
+Generate a real traffic to your website by real users using through
+proxy. Record the traffic, and try it again for more real load testing.
+
+4. https://github.com/elazarl/goproxy/tree/master/examples/goproxy-no-reddit-at-worktime
+
+Will allow browsing to reddit.com between 8:00am and 17:00pm
+
+5. https://github.com/elazarl/goproxy/tree/master/examples/goproxy-jquery-version
+
+Will warn if multiple versions of jquery are used in the same domain.
+
+6. https://github.com/elazarl/goproxy/blob/master/examples/goproxy-upside-down-ternet/
+
+Modifies image files in an HTTP response via goproxy's image extension found in ext/.
+
+*/
+package goproxy
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-basic/README.md b/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-basic/README.md
new file mode 100644
index 0000000000..8778f2a75b
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-basic/README.md
@@ -0,0 +1,29 @@
+# Simple HTTP Proxy
+
+`goproxy-basic` starts an HTTP proxy on :8080. It only handles explicit CONNECT
+requests.
+
+Start it in one shell:
+
+```sh
+goproxy-basic -v
+```
+
+Fetch goproxy homepage in another:
+
+```sh
+http_proxy=http://127.0.0.1:8080 wget -O - \
+ http://ripper234.com/p/introducing-goproxy-light-http-proxy/
+```
+
+The homepage HTML content should be displayed in the console. The proxy should
+have logged the request being processed:
+
+```sh
+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/
+2015/04/09 18:19:17 [001] INFO: Sending request GET http://ripper234.com/p/introducing-goproxy-light-http-proxy/
+2015/04/09 18:19:18 [001] INFO: Received response 200 OK
+2015/04/09 18:19:18 [001] INFO: Copying response to client 200 OK [200]
+2015/04/09 18:19:18 [001] INFO: Copied 44333 bytes to client error=
+```
+
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-basic/main.go b/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-basic/main.go
new file mode 100644
index 0000000000..22dc4a9073
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-basic/main.go
@@ -0,0 +1,17 @@
+package main
+
+import (
+ "github.com/elazarl/goproxy"
+ "log"
+ "flag"
+ "net/http"
+)
+
+func main() {
+ verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
+ addr := flag.String("addr", ":8080", "proxy listen address")
+ flag.Parse()
+ proxy := goproxy.NewProxyHttpServer()
+ proxy.Verbose = *verbose
+ log.Fatal(http.ListenAndServe(*addr, proxy))
+}
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-eavesdropper/main.go b/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-eavesdropper/main.go
new file mode 100644
index 0000000000..9d80653be5
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-eavesdropper/main.go
@@ -0,0 +1,56 @@
+package main
+
+import (
+ "bufio"
+ "flag"
+ "log"
+ "net"
+ "net/http"
+ "regexp"
+
+ "github.com/elazarl/goproxy"
+)
+
+func orPanic(err error) {
+ if err != nil {
+ panic(err)
+ }
+}
+
+func main() {
+ proxy := goproxy.NewProxyHttpServer()
+ proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*baidu.com$"))).
+ HandleConnect(goproxy.AlwaysReject)
+ proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*$"))).
+ HandleConnect(goproxy.AlwaysMitm)
+ // enable curl -p for all hosts on port 80
+ proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*:80$"))).
+ HijackConnect(func(req *http.Request, client net.Conn, ctx *goproxy.ProxyCtx) {
+ defer func() {
+ if e := recover(); e != nil {
+ ctx.Logf("error connecting to remote: %v", e)
+ client.Write([]byte("HTTP/1.1 500 Cannot reach destination\r\n\r\n"))
+ }
+ client.Close()
+ }()
+ clientBuf := bufio.NewReadWriter(bufio.NewReader(client), bufio.NewWriter(client))
+ remote, err := net.Dial("tcp", req.URL.Host)
+ orPanic(err)
+ remoteBuf := bufio.NewReadWriter(bufio.NewReader(remote), bufio.NewWriter(remote))
+ for {
+ req, err := http.ReadRequest(clientBuf.Reader)
+ orPanic(err)
+ orPanic(req.Write(remoteBuf))
+ orPanic(remoteBuf.Flush())
+ resp, err := http.ReadResponse(remoteBuf.Reader, req)
+ orPanic(err)
+ orPanic(resp.Write(clientBuf.Writer))
+ orPanic(clientBuf.Flush())
+ }
+ })
+ verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
+ addr := flag.String("addr", ":8080", "proxy listen address")
+ flag.Parse()
+ proxy.Verbose = *verbose
+ log.Fatal(http.ListenAndServe(*addr, proxy))
+}
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-httpdump/README.md b/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-httpdump/README.md
new file mode 100644
index 0000000000..7240d8eacf
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-httpdump/README.md
@@ -0,0 +1,30 @@
+# Trace HTTP Requests and Responses
+
+`goproxy-httpdump` starts an HTTP proxy on :8080. It handles explicit CONNECT
+requests and traces them in a "db" directory created in the proxy working
+directory. Each request type and headers are logged in a "log" file, while
+their bodies are dumped in files prefixed with the request session identifier.
+
+Additionally, the example demonstrates how to:
+- Log information asynchronously (see HttpLogger)
+- Allow the proxy to be stopped manually while ensuring all pending requests
+ have been processed (in this case, logged).
+
+Start it in one shell:
+
+```sh
+goproxy-httpdump
+```
+
+Fetch goproxy homepage in another:
+
+```sh
+http_proxy=http://127.0.0.1:8080 wget -O - \
+ http://ripper234.com/p/introducing-goproxy-light-http-proxy/
+```
+
+A "db" directory should have appeared where you started the proxy, containing
+two files:
+- log: the request/response traces
+- 1\_resp: the first response body
+
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-httpdump/httpdump.go b/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-httpdump/httpdump.go
new file mode 100644
index 0000000000..62a9b88237
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-httpdump/httpdump.go
@@ -0,0 +1,285 @@
+package main
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "io"
+ "log"
+ "net"
+ "net/http"
+ "net/http/httputil"
+ "os"
+ "os/signal"
+ "path"
+ "sync"
+ "time"
+
+ "github.com/elazarl/goproxy"
+ "github.com/elazarl/goproxy/transport"
+)
+
+type FileStream struct {
+ path string
+ f *os.File
+}
+
+func NewFileStream(path string) *FileStream {
+ return &FileStream{path, nil}
+}
+
+func (fs *FileStream) Write(b []byte) (nr int, err error) {
+ if fs.f == nil {
+ fs.f, err = os.Create(fs.path)
+ if err != nil {
+ return 0, err
+ }
+ }
+ return fs.f.Write(b)
+}
+
+func (fs *FileStream) Close() error {
+ fmt.Println("Close", fs.path)
+ if fs.f == nil {
+ return errors.New("FileStream was never written into")
+ }
+ return fs.f.Close()
+}
+
+type Meta struct {
+ req *http.Request
+ resp *http.Response
+ err error
+ t time.Time
+ sess int64
+ bodyPath string
+ from string
+}
+
+func fprintf(nr *int64, err *error, w io.Writer, pat string, a ...interface{}) {
+ if *err != nil {
+ return
+ }
+ var n int
+ n, *err = fmt.Fprintf(w, pat, a...)
+ *nr += int64(n)
+}
+
+func write(nr *int64, err *error, w io.Writer, b []byte) {
+ if *err != nil {
+ return
+ }
+ var n int
+ n, *err = w.Write(b)
+ *nr += int64(n)
+}
+
+func (m *Meta) WriteTo(w io.Writer) (nr int64, err error) {
+ if m.req != nil {
+ fprintf(&nr, &err, w, "Type: request\r\n")
+ } else if m.resp != nil {
+ fprintf(&nr, &err, w, "Type: response\r\n")
+ }
+ fprintf(&nr, &err, w, "ReceivedAt: %v\r\n", m.t)
+ fprintf(&nr, &err, w, "Session: %d\r\n", m.sess)
+ fprintf(&nr, &err, w, "From: %v\r\n", m.from)
+ if m.err != nil {
+ // note the empty response
+ fprintf(&nr, &err, w, "Error: %v\r\n\r\n\r\n\r\n", m.err)
+ } else if m.req != nil {
+ fprintf(&nr, &err, w, "\r\n")
+ buf, err2 := httputil.DumpRequest(m.req, false)
+ if err2 != nil {
+ return nr, err2
+ }
+ write(&nr, &err, w, buf)
+ } else if m.resp != nil {
+ fprintf(&nr, &err, w, "\r\n")
+ buf, err2 := httputil.DumpResponse(m.resp, false)
+ if err2 != nil {
+ return nr, err2
+ }
+ write(&nr, &err, w, buf)
+ }
+ return
+}
+
+// HttpLogger is an asynchronous HTTP request/response logger. It traces
+// requests and responses headers in a "log" file in logger directory and dumps
+// their bodies in files prefixed with the session identifiers.
+// Close it to ensure pending items are correctly logged.
+type HttpLogger struct {
+ path string
+ c chan *Meta
+ errch chan error
+}
+
+func NewLogger(basepath string) (*HttpLogger, error) {
+ f, err := os.Create(path.Join(basepath, "log"))
+ if err != nil {
+ return nil, err
+ }
+ logger := &HttpLogger{basepath, make(chan *Meta), make(chan error)}
+ go func() {
+ for m := range logger.c {
+ if _, err := m.WriteTo(f); err != nil {
+ log.Println("Can't write meta", err)
+ }
+ }
+ logger.errch <- f.Close()
+ }()
+ return logger, nil
+}
+
+func (logger *HttpLogger) LogResp(resp *http.Response, ctx *goproxy.ProxyCtx) {
+ body := path.Join(logger.path, fmt.Sprintf("%d_resp", ctx.Session))
+ from := ""
+ if ctx.UserData != nil {
+ from = ctx.UserData.(*transport.RoundTripDetails).TCPAddr.String()
+ }
+ if resp == nil {
+ resp = emptyResp
+ } else {
+ resp.Body = NewTeeReadCloser(resp.Body, NewFileStream(body))
+ }
+ logger.LogMeta(&Meta{
+ resp: resp,
+ err: ctx.Error,
+ t: time.Now(),
+ sess: ctx.Session,
+ from: from})
+}
+
+var emptyResp = &http.Response{}
+var emptyReq = &http.Request{}
+
+func (logger *HttpLogger) LogReq(req *http.Request, ctx *goproxy.ProxyCtx) {
+ body := path.Join(logger.path, fmt.Sprintf("%d_req", ctx.Session))
+ if req == nil {
+ req = emptyReq
+ } else {
+ req.Body = NewTeeReadCloser(req.Body, NewFileStream(body))
+ }
+ logger.LogMeta(&Meta{
+ req: req,
+ err: ctx.Error,
+ t: time.Now(),
+ sess: ctx.Session,
+ from: req.RemoteAddr})
+}
+
+func (logger *HttpLogger) LogMeta(m *Meta) {
+ logger.c <- m
+}
+
+func (logger *HttpLogger) Close() error {
+ close(logger.c)
+ return <-logger.errch
+}
+
+// TeeReadCloser extends io.TeeReader by allowing reader and writer to be
+// closed.
+type TeeReadCloser struct {
+ r io.Reader
+ w io.WriteCloser
+ c io.Closer
+}
+
+func NewTeeReadCloser(r io.ReadCloser, w io.WriteCloser) io.ReadCloser {
+ return &TeeReadCloser{io.TeeReader(r, w), w, r}
+}
+
+func (t *TeeReadCloser) Read(b []byte) (int, error) {
+ return t.r.Read(b)
+}
+
+// Close attempts to close the reader and write. It returns an error if both
+// failed to Close.
+func (t *TeeReadCloser) Close() error {
+ err1 := t.c.Close()
+ err2 := t.w.Close()
+ if err1 != nil {
+ return err1
+ }
+ return err2
+}
+
+// stoppableListener serves stoppableConn and tracks their lifetime to notify
+// when it is safe to terminate the application.
+type stoppableListener struct {
+ net.Listener
+ sync.WaitGroup
+}
+
+type stoppableConn struct {
+ net.Conn
+ wg *sync.WaitGroup
+}
+
+func newStoppableListener(l net.Listener) *stoppableListener {
+ return &stoppableListener{l, sync.WaitGroup{}}
+}
+
+func (sl *stoppableListener) Accept() (net.Conn, error) {
+ c, err := sl.Listener.Accept()
+ if err != nil {
+ return c, err
+ }
+ sl.Add(1)
+ return &stoppableConn{c, &sl.WaitGroup}, nil
+}
+
+func (sc *stoppableConn) Close() error {
+ sc.wg.Done()
+ return sc.Conn.Close()
+}
+
+func main() {
+ verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
+ addr := flag.String("l", ":8080", "on which address should the proxy listen")
+ flag.Parse()
+ proxy := goproxy.NewProxyHttpServer()
+ proxy.Verbose = *verbose
+ if err := os.MkdirAll("db", 0755); err != nil {
+ log.Fatal("Can't create dir", err)
+ }
+ logger, err := NewLogger("db")
+ if err != nil {
+ log.Fatal("can't open log file", err)
+ }
+ tr := transport.Transport{Proxy: transport.ProxyFromEnvironment}
+ // For every incoming request, override the RoundTripper to extract
+ // connection information. Store it is session context log it after
+ // handling the response.
+ proxy.OnRequest().DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
+ ctx.RoundTripper = goproxy.RoundTripperFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (resp *http.Response, err error) {
+ ctx.UserData, resp, err = tr.DetailedRoundTrip(req)
+ return
+ })
+ logger.LogReq(req, ctx)
+ return req, nil
+ })
+ proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
+ logger.LogResp(resp, ctx)
+ return resp
+ })
+ l, err := net.Listen("tcp", *addr)
+ if err != nil {
+ log.Fatal("listen:", err)
+ }
+ sl := newStoppableListener(l)
+ ch := make(chan os.Signal)
+ signal.Notify(ch, os.Interrupt)
+ go func() {
+ <-ch
+ log.Println("Got SIGINT exiting")
+ sl.Add(1)
+ sl.Close()
+ logger.Close()
+ sl.Done()
+ }()
+ log.Println("Starting Proxy")
+ http.Serve(sl, proxy)
+ sl.Wait()
+ log.Println("All connections closed - exit")
+}
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-jquery-version/README.md b/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-jquery-version/README.md
new file mode 100644
index 0000000000..6efba22adc
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-jquery-version/README.md
@@ -0,0 +1,31 @@
+# Content Analysis
+
+`goproxy-jquery-version` starts an HTTP proxy on :8080. It checks HTML
+responses, looks for scripts referencing jQuery library and emits warnings if
+different versions of the library are being used for a given host.
+
+Start it in one shell:
+
+```sh
+goproxy-jquery-version
+```
+
+Fetch goproxy homepage in another:
+
+```sh
+http_proxy=http://127.0.0.1:8080 wget -O - \
+ http://ripper234.com/p/introducing-goproxy-light-http-proxy/
+```
+
+Goproxy homepage uses jQuery and a mix of plugins. First the proxy reports the
+first use of jQuery it detects for the domain. Then, because the regular
+expression matching the jQuery sources is imprecise, it reports a mismatch with
+a plugin reference:
+
+```sh
+2015/04/11 11:23:02 [001] WARN: ripper234.com uses //ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js
+2015/04/11 11:23:02 [001] WARN: In http://ripper234.com/p/introducing-goproxy-light-http-proxy/, \
+ Contradicting jqueries //ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js \
+ http://ripper234.wpengine.netdna-cdn.com/wp-content/plugins/wp-ajax-edit-comments/js/jquery.colorbox.min.js?ver=5.0.36
+```
+
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-jquery-version/jquery1.html b/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-jquery-version/jquery1.html
new file mode 100644
index 0000000000..26771ce34d
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-jquery-version/jquery1.html
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-jquery-version/jquery2.html b/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-jquery-version/jquery2.html
new file mode 100644
index 0000000000..7dce036146
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-jquery-version/jquery2.html
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-jquery-version/jquery_homepage.html b/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-jquery-version/jquery_homepage.html
new file mode 100644
index 0000000000..27dd0b38a7
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/elazarl/goproxy/examples/goproxy-jquery-version/jquery_homepage.html
@@ -0,0 +1,233 @@
+
+
+
+
+ jQuery: The Write Less, Do More, JavaScript Library
+
+
+
+
+
+
+
+
+
+
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.
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.