func (f *Filter) Request(ctx *filters.Context, req *http.Request) (*filters.Context, *http.Request, error) { if req.Method != "CONNECT" || !f.Match(req.Host) { return ctx, req, nil } hijacker, ok := ctx.GetResponseWriter().(http.Hijacker) if !ok { return ctx, nil, fmt.Errorf("%#v does not implments Hijacker", ctx.GetResponseWriter()) } conn, _, err := hijacker.Hijack() if err != nil { return ctx, nil, fmt.Errorf("http.ResponseWriter Hijack failed: %s", err) } _, err = io.WriteString(conn, "HTTP/1.1 200 OK\r\n\r\n") if err != nil { conn.Close() return ctx, nil, err } glog.Infof("%s \"STRIP %s %s %s\" - -", req.RemoteAddr, req.Method, req.Host, req.Proto) cert, err := f.issue(req.Host) if err != nil { conn.Close() return ctx, nil, err } tlsConfig := &tls.Config{ Certificates: []tls.Certificate{*cert}, ClientAuth: tls.VerifyClientCertIfGiven, } tlsConn := tls.Server(conn, tlsConfig) if err := tlsConn.Handshake(); err != nil { conn.Close() return ctx, nil, err } if ln1, ok := ctx.GetListener().(httpproxy.Listener); ok { ln1.Add(tlsConn) ctx.SetHijacked(true) return ctx, nil, nil } loConn, err := net.Dial("tcp", ctx.GetListener().Addr().String()) if err != nil { return ctx, nil, err } go httpproxy.IoCopy(loConn, tlsConn) go httpproxy.IoCopy(tlsConn, loConn) ctx.SetHijacked(true) return ctx, nil, nil }
func (f *Filter) RoundTrip(ctx *filters.Context, req *http.Request) (*filters.Context, *http.Response, error) { if _, ok := f.dialer.hosts.Lookup(req.Host); !ok { return ctx, nil, nil } switch req.Method { case "CONNECT": glog.Infof("%s \"IPLIST %s %s %s\" - -", req.RemoteAddr, req.Method, req.Host, req.Proto) remote, err := f.transport.Dial("tcp", req.Host) if err != nil { return ctx, nil, err } switch req.Proto { case "HTTP/2.0": rw := ctx.GetResponseWriter() io.WriteString(rw, "HTTP/1.1 200 OK\r\n\r\n") go httpproxy.IoCopy(remote, req.Body) httpproxy.IoCopy(rw, remote) case "HTTP/1.1", "HTTP/1.0": rw := ctx.GetResponseWriter() hijacker, ok := rw.(http.Hijacker) if !ok { return ctx, nil, fmt.Errorf("http.ResponseWriter(%#v) does not implments Hijacker", rw) } local, _, err := hijacker.Hijack() if err != nil { return ctx, nil, err } local.Write([]byte("HTTP/1.1 200 OK\r\n\r\n")) go httpproxy.IoCopy(remote, local) httpproxy.IoCopy(local, remote) default: glog.Warningf("Unkown req=%#v", req) } ctx.SetHijacked(true) return ctx, nil, nil case "PRI": //TODO: fix for http2 return ctx, nil, nil default: resp, err := f.transport.RoundTrip(req) if err != nil { glog.Errorf("%s \"IPLIST %s %s %s\" error: %s", req.RemoteAddr, req.Method, req.URL.String(), req.Proto, err) data := err.Error() resp = &http.Response{ Status: "502 Bad Gateway", StatusCode: 502, Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, Header: http.Header{}, Request: req, Close: true, ContentLength: int64(len(data)), Body: ioutil.NopCloser(bytes.NewReader([]byte(data))), } err = nil } else { if req.RemoteAddr != "" { glog.Infof("%s \"IPLIST %s %s %s\" %d %s", req.RemoteAddr, req.Method, req.URL.String(), req.Proto, resp.StatusCode, resp.Header.Get("Content-Length")) } } return ctx, resp, err } }
func (f *Filter) RoundTrip(ctx *filters.Context, req *http.Request) (*filters.Context, *http.Response, error) { if !f.Sites.Match(req.Host) { return ctx, nil, nil } i := 0 switch path.Ext(req.URL.Path) { case ".jpg", ".png", ".webp", ".bmp", ".gif", ".flv", ".mp4": i = rand.Intn(len(f.FetchServers)) case "": name := path.Base(req.URL.Path) if strings.Contains(name, "play") || strings.Contains(name, "video") { i = rand.Intn(len(f.FetchServers)) } default: if strings.Contains(req.URL.Host, "img.") || strings.Contains(req.URL.Host, "cache.") || strings.Contains(req.URL.Host, "video.") || strings.Contains(req.URL.Host, "static.") || strings.HasPrefix(req.URL.Host, "img") || strings.HasPrefix(req.URL.Path, "/static") || strings.HasPrefix(req.URL.Path, "/asset") || strings.Contains(req.URL.Path, "min.js") || strings.Contains(req.URL.Path, "static") || strings.Contains(req.URL.Path, "asset") || strings.Contains(req.URL.Path, "/cache/") { i = rand.Intn(len(f.FetchServers)) } } fetchServer := f.FetchServers[i] if req.Method == "CONNECT" { rconn, err := fetchServer.Transport.Connect(req) if err != nil { return ctx, nil, err } defer rconn.Close() rw := ctx.GetResponseWriter() hijacker, ok := rw.(http.Hijacker) if !ok { return ctx, nil, fmt.Errorf("http.ResponseWriter(%#v) does not implments http.Hijacker", rw) } flusher, ok := rw.(http.Flusher) if !ok { return ctx, nil, fmt.Errorf("http.ResponseWriter(%#v) does not implments http.Flusher", rw) } rw.WriteHeader(http.StatusOK) flusher.Flush() lconn, _, err := hijacker.Hijack() if err != nil { return ctx, nil, fmt.Errorf("%#v.Hijack() error: %v", hijacker, err) } defer lconn.Close() go httpproxy.IoCopy(rconn, lconn) httpproxy.IoCopy(lconn, rconn) ctx.SetHijacked(true) return ctx, nil, nil } resp, err := fetchServer.RoundTrip(req) if err != nil { return ctx, nil, err } else { glog.Infof("%s \"VPS %s %s %s\" %d %s", req.RemoteAddr, req.Method, req.URL.String(), req.Proto, resp.StatusCode, resp.Header.Get("Content-Length")) } return ctx, resp, err }
func (f *Filter) RoundTrip(ctx *filters.Context, req *http.Request) (*filters.Context, *http.Response, error) { switch req.Method { case "CONNECT": glog.Infof("%s \"DIRECT %s %s %s\" - -", req.RemoteAddr, req.Method, req.Host, req.Proto) rconn, err := f.transport.Dial("tcp", req.Host) if err != nil { return ctx, nil, err } rw := ctx.GetResponseWriter() hijacker, ok := rw.(http.Hijacker) if !ok { return ctx, nil, fmt.Errorf("http.ResponseWriter(%#v) does not implments http.Hijacker", rw) } flusher, ok := rw.(http.Flusher) if !ok { return ctx, nil, fmt.Errorf("http.ResponseWriter(%#v) does not implments http.Flusher", rw) } rw.WriteHeader(http.StatusOK) flusher.Flush() lconn, _, err := hijacker.Hijack() if err != nil { return ctx, nil, fmt.Errorf("%#v.Hijack() error: %v", hijacker, err) } defer lconn.Close() go httpproxy.IoCopy(rconn, lconn) httpproxy.IoCopy(lconn, rconn) ctx.SetHijacked(true) return ctx, nil, nil case "PRI": //TODO: fix for http2 return ctx, nil, nil default: resp, err := f.transport.RoundTrip(req) if err == ErrLoopbackAddr { http.FileServer(http.Dir(".")).ServeHTTP(ctx.GetResponseWriter(), req) ctx.SetHijacked(true) return ctx, nil, nil } if err != nil { glog.Errorf("%s \"DIRECT %s %s %s\" error: %s", req.RemoteAddr, req.Method, req.URL.String(), req.Proto, err) data := err.Error() resp = &http.Response{ Status: "502 Bad Gateway", StatusCode: 502, Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, Header: http.Header{}, Request: req, Close: true, ContentLength: int64(len(data)), Body: ioutil.NopCloser(bytes.NewReader([]byte(data))), } err = nil } else { if req.RemoteAddr != "" { glog.Infof("%s \"DIRECT %s %s %s\" %d %s", req.RemoteAddr, req.Method, req.URL.String(), req.Proto, resp.StatusCode, resp.Header.Get("Content-Length")) } } if f.ratelimt.Rate > 0 && resp.ContentLength > f.ratelimt.Threshold { glog.V(2).Infof("RateLimit %#v rate to %#v", req.URL.String(), f.ratelimt.Rate) resp.Body = httpproxy.NewRateLimitReader(resp.Body, f.ratelimt.Rate, f.ratelimt.Capacity) } return ctx, resp, err } }