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") }
func main() { cwd, _ := os.Getwd() 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") cacheDir := flag.String("p", cwd, "cache directory, by default the working directory") flag.Parse() proxy := goproxy.NewProxyHttpServer() proxy.Verbose = *verbose store, err := NewPkgStore(*cacheDir) if err != nil { log.Fatal(fmt.Printf("Could not create package cache directory: %s", *cacheDir)) } else { log.Printf("Using cache directory: %s", *cacheDir) } tr := transport.Transport{Proxy: transport.ProxyFromEnvironment} r := regexp.MustCompile(`/([^/]+)/os/(i686|x86_64)/(.+\.pkg\.tar.+)`) proxy.OnRequest().DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { uri := req.URL.RequestURI() if r.MatchString(uri) == true { pkgInfo := r.FindStringSubmatch(uri) pkgFile := store.NewPkg(pkgInfo[1], pkgInfo[2], pkgInfo[3]) log.Printf("Request: %s/%s/%s", pkgFile.repo, pkgFile.arch, pkgFile.fname) if store.HasPkg(pkgFile) { log.Printf("Serving cached package: %s/%s/%s", pkgFile.repo, pkgFile.arch, pkgFile.fname) return req, NewCachedResponse(req, store, pkgFile) } else { ctx.RoundTripper = goproxy.RoundTripperFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (resp *http.Response, err error) { resp, err = tr.RoundTrip(req) ctx.UserData = pkgFile return }) } } return req, nil }) proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response { if ctx.UserData != nil { store.PutPkg(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() sl.Done() }() log.Println("Starting Proxy") http.Serve(sl, proxy) sl.Wait() log.Println("All connections closed - exit") }
func NewProxy(cfg Config) (*goproxy.ProxyHttpServer, error) { proxy := goproxy.NewProxyHttpServer() // Use two transports, one secure (certificate checks are // done) and one insecure. secureTransport := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: false}} insecureTransport := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} log.Info("start serving requests on %s", cfg.Proxy.Listen) // Register configuration inside context proxy.OnRequest(). DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { url := fmt.Sprintf("%s://%s%s", req.URL.Scheme, req.URL.Host, req.URL.Path) ctx.UserData = cfg.UrlConfiguration(url) return req, nil }) // Remove X-Frame-Options header proxy.OnResponse(). DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response { if resp != nil { allowed := ctx.UserData.(*UrlConfig).Allow_Framing if allowed != nil && *allowed { resp.Header.Del("X-Frame-Options") } } return resp }) // Add X-Forwarded-For header proxy.OnRequest(). DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { x := ctx.UserData.(*UrlConfig).Append_XForwardedFor if x != nil && *x { src := strings.Split(ctx.Req.RemoteAddr, ":")[0] req.Header.Set("X-Forwarded-For", src) } return req, nil }) // Make the request over HTTPS proxy.OnRequest(). DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { ssl := ctx.UserData.(*UrlConfig).Https if ssl != nil && *ssl { // For tests, we also need to get the // appropriate port for SSL. We expect to // found it in X-Test-HTTPS header. host := req.Header.Get("X-Test-HTTPS") if host != "" { req.URL.Host = host[8:] } req.URL.Scheme = "https" } return req, nil }) // Convert back location to non-HTTP proxy.OnResponse(). DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response { if resp == nil { return resp } location := resp.Header.Get("Location") if location == "" { return resp } // If the requested location would have been // redirected to HTTPS, give it back as an HTTP // location. url, err := url.Parse(location) if err != nil || url.Scheme != "https" { return resp } // For testing purposes, the address of the right server is in X-Test-HTTP header host := resp.Header.Get("X-Test-HTTP") if host != "" { host = host[7:] } ncfg := cfg.UrlConfiguration( fmt.Sprintf("http://%s%s", host, url.Path)) if ncfg.Https != nil && *ncfg.Https { url.Scheme = "http" url.Host = host resp.Header.Set("Location", url.String()) } return resp }) // For HTTPS, do certificate checks proxy.OnRequest(). DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { if req.URL.Scheme != "https" { return req, nil } verify := ctx.UserData.(*UrlConfig).Https_Verify_Cert ctx.RoundTripper = goproxy.RoundTripperFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Response, error) { if verify != nil && *verify { log.Debug("proxy: selecting secure transport for %v", req.URL) return secureTransport.RoundTrip(req) } else { log.Debug("proxy: selecting insecure transport for %v", req.URL) return insecureTransport.RoundTrip(req) } }) return req, nil }) // Log requests if cfg.Proxy.Debug { proxy.OnResponse(). DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response { var status int if resp != nil { status = resp.StatusCode } else { status = 0 } log.Debug("proxy: from %s: %d: %s %s %s", ctx.Req.RemoteAddr, status, ctx.Req.Method, ctx.Req.URL.String(), ctx.Req.Proto) return resp }) } return proxy, nil }