func TestContentType(t *testing.T) { client, proxy, l := oneShotProxy(t) defer l.Close() proxy.OnResponse(goproxy.ContentTypeIs("image/png")).DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response { resp.Header.Set("X-Shmoopi", "1") return resp }) for _, file := range []string{"test_data/panda.png", "test_data/football.png"} { if resp, err := client.Get(localFile(file)); err != nil || resp.Header.Get("X-Shmoopi") != "1" { if err == nil { t.Error("pngs should have X-Shmoopi header = 1, actually", resp.Header.Get("X-Shmoopi")) } else { t.Error("error reading png", err) } } } file := "baby.jpg" if resp, err := client.Get(localFile(file)); err != nil || resp.Header.Get("X-Shmoopi") != "" { if err == nil { t.Error("Non png images should NOT have X-Shmoopi header at all", resp.Header.Get("X-Shmoopi")) } else { t.Error("error reading png", err) } } }
package goproxy_html import ( "bytes" "errors" "io" "io/ioutil" "net/http" "strings" "code.google.com/p/go-charset/charset" _ "code.google.com/p/go-charset/data" "github.com/elazarl/goproxy" ) var IsHtml goproxy.RespCondition = goproxy.ContentTypeIs("text/html") var IsCss goproxy.RespCondition = goproxy.ContentTypeIs("text/css") var IsJavaScript goproxy.RespCondition = goproxy.ContentTypeIs("text/javascript", "application/javascript") var IsJson goproxy.RespCondition = goproxy.ContentTypeIs("text/json") var IsXml goproxy.RespCondition = goproxy.ContentTypeIs("text/xml") var IsWebRelatedText goproxy.RespCondition = goproxy.ContentTypeIs("text/html", "text/css", "text/javascript", "application/javascript", "text/xml", "text/json")
func main() { proxy := goproxy.NewProxyHttpServer() proxy.OnRequest().DoFunc(func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { r.Header.Add("Accept-Encoding", "sdch") if len(dictName) > 0 { rawId, err := hex.DecodeString(dictName) if err != nil { log.Println(err) return r, nil } uaId := base64.URLEncoding.EncodeToString(rawId[:6]) r.Header.Set("Avail-Dictionary", uaId) } return r, nil }) proxy.OnResponse().DoFunc(func(r *http.Response, ctx *goproxy.ProxyCtx) *http.Response { dictUrl := r.Header.Get("Get-Dictionary") if dictUrl == "" { return r } dictName := path.Base(dictUrl) _, err := os.Stat(path.Join("dicts", dictName)) if err != nil { if os.IsNotExist(err) { u, err := url.Parse(dictUrl) if err != nil { log.Println(err) return r } if u.Scheme == "" { dictUrl = "http://" + r.Request.Host + dictUrl } downloadDict(dictUrl) } return r } return r }) proxy.OnResponse(goproxy.ContentTypeIs("sdch")).DoFunc(func(r *http.Response, ctx *goproxy.ProxyCtx) *http.Response { var origBody bytes.Buffer tr := bufio.NewReader(io.TeeReader(r.Body, &origBody)) serverId, err := tr.ReadString(byte(0)) if err != nil { log.Println(err) r.Body = ioutil.NopCloser(io.MultiReader(&origBody, r.Body)) return r } // Chop off the last 0x00 serverId = serverId[:len(serverId)-1] rawServerId, err := base64.URLEncoding.DecodeString(serverId) if err != nil { log.Println(err) r.Body = ioutil.NopCloser(io.MultiReader(&origBody, r.Body)) return r } ourDict, err := hex.DecodeString(dictName) if err != nil { log.Println(err) r.Body = ioutil.NopCloser(io.MultiReader(&origBody, r.Body)) return r } if bytes.Compare(rawServerId, ourDict[6:12]) != 0 { r.Body = ioutil.NopCloser(io.MultiReader(&origBody, r.Body)) return r } cmd := exec.Command("vcdiff", "patch", "-dictionary", path.Join("dicts/", dictName), "-stats") var out bytes.Buffer cmd.Stdout = &out cmd.Stdin = tr var stderr bytes.Buffer cmd.Stderr = &stderr if err := cmd.Run(); err != nil { log.Println(err) } log.Println("[VCDIFF]", stderr.String()) // TODO: send original content type in headers r.Header.Set("Content-Type", "text/html") r.Body = ioutil.NopCloser(&out) return r }) proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm) os.Mkdir("dicts", 0755) dir, err := os.Open("dicts") if err != nil { log.Fatal(err) } fis, err := dir.Readdir(-1) if err != nil { log.Fatal(err) } if len(fis) > 0 { last := fis[0] for _, fi := range fis[1:] { if fi.ModTime().After(last.ModTime()) { os.Remove(path.Join("dicts", last.Name())) last = fi } } dictName = last.Name() } log.Println("Let's go!") log.Fatal(http.ListenAndServe(":8081", proxy)) }
func main() { proxy := goproxy.NewProxyHttpServer() db, err := sql.Open("sqlite3", "memory") if err != nil { log.Fatal(err) } if err = db.Ping(); err != nil { log.Fatal(err) } _, err = db.Exec(`CREATE TABLE IF NOT EXISTS chunks ( content BLOB, hash BLOB UNIQUE ON CONFLICT REPLACE, count INTEGER );`) if err != nil { log.Fatal(err) } bh := &bodyHandler{ db: db, } matchPath := regexp.MustCompile("reddit.com") proxy.OnResponse( goproxy.ContentTypeIs("text/html"), goproxy.ReqHostMatches(matchPath), ).DoFunc(bh.handle) proxy.OnResponse( goproxy.ReqHostMatches(matchPath), ).DoFunc(func(r *http.Response, ctx *goproxy.ProxyCtx) *http.Response { r.Header.Set("X-Sdch-Encoding", "0") return r }) proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm) err = os.Mkdir(DICT_PATH, 0755) if err != nil && !os.IsExist(err) { log.Fatal(err) } err = os.Mkdir(DICT_HDR_PATH, 0755) if err != nil && !os.IsExist(err) { log.Fatal(err) } dir, err := os.Open(DICT_PATH) if err != nil { log.Fatal(err) } fis, err := dir.Readdir(-1) if err != nil { log.Fatal(err) } if len(fis) > 0 { current := fis[0] for _, fi := range fis[1:] { if fi.ModTime().After(current.ModTime()) { err := os.Remove(path.Join(DICT_PATH, fi.Name())) if err != nil { log.Fatal(err) } err = os.Remove(path.Join(DICT_HDR_PATH, fi.Name())) if err != nil { log.Fatal(err) } current = fi } } bh.SetDictName(path.Join(DICT_PATH, current.Name())) bh.SetDictHdrName(path.Join(DICT_HDR_PATH, current.Name())) } proxy.OnRequest().DoFunc(func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { if !strings.HasPrefix(r.URL.Path, "/_dictionary") { return r, nil } dictQuery := strings.Replace(r.URL.Path, "/_dictionary/", "", 1) parts := strings.Split(dictQuery, "/") if len(parts) != 2 { log.Println("Wrong query:", dictQuery) resp := goproxy.NewResponse(r, "text/plain", http.StatusNotFound, http.StatusText(http.StatusNotFound)) return nil, resp } dictName := parts[1] if dictName != path.Base(bh.DictName()) { log.Println(err) resp := goproxy.NewResponse(r, "text/plain", http.StatusNotFound, http.StatusText(http.StatusNotFound)) return nil, resp } dict, modTime, err := bh.makeSdchDict() if err != nil { log.Println(err) resp := goproxy.NewResponse(r, "text/plain", http.StatusNotFound, http.StatusText(http.StatusNotFound)) return nil, resp } size, err1 := dict.Seek(0, os.SEEK_END) _, err2 := dict.Seek(0, os.SEEK_SET) if err1 != nil || err2 != nil { log.Println(err, err2) resp := goproxy.NewResponse(r, "text/plain", http.StatusNotFound, http.StatusText(http.StatusNotFound)) return nil, resp } resp := &http.Response{} resp.Request = r resp.TransferEncoding = r.TransferEncoding resp.Header = make(http.Header) resp.Header.Add("Content-Type", "application/x-sdch-dictionary") resp.Header.Add("Content-Length", strconv.Itoa(int(size))) resp.Header.Add("X-Sdch-Encode", "0") resp.Header.Add("Date", time.Now().Format(time.RFC1123)) resp.Header.Add("Expires", time.Now().Add(1*time.Hour).Format(time.RFC1123)) resp.Header.Add("Last-Modified", modTime.Format(time.RFC1123)) resp.StatusCode = 200 resp.ContentLength = size resp.Body = ioutil.NopCloser(dict) log.Println("Sending back dict") return nil, resp }) log.Println("Let's go !") log.Fatal(http.ListenAndServe(":8080", proxy)) }