Пример #1
0
// initReverseProxy creates a reverse proxy that attempts to exit with any of
// the dialers provided by the balancer.
func (client *Client) initReverseProxy(bal *balancer.Balancer, dumpHeaders bool) {

	transport := &http.Transport{
		// We disable keepalives because some servers pretend to support
		// keep-alives but close their connections immediately, which
		// causes an error inside ReverseProxy.  This is not an issue
		// for HTTPS because  the browser is responsible for handling
		// the problem, which browsers like Chrome and Firefox already
		// know to do.
		//
		// See https://code.google.com/p/go/issues/detail?id=4677
		DisableKeepAlives: true,
	}

	// TODO: would be good to make this sensitive to QOS, which
	// right now is only respected for HTTPS connections. The
	// challenge is that ReverseProxy reuses connections for
	// different requests, so we might have to configure different
	// ReverseProxies for different QOS's or something like that.
	if runtime.GOOS == "android" || client.ProxyAll {
		transport.Dial = bal.Dial
	} else {
		transport.Dial = detour.Dialer(bal.Dial)
	}

	rp := &httputil.ReverseProxy{
		Director: func(req *http.Request) {
			// do nothing
		},
		Transport: withDumpHeaders(
			dumpHeaders,
			transport),
		// Set a FlushInterval to prevent overly aggressive buffering of
		// responses, which helps keep memory usage down
		FlushInterval: 250 * time.Millisecond,
		ErrorLog:      log.AsStdLogger(),
	}

	if client.rpInitialized {
		log.Trace("Draining reverse proxy channel")
		<-client.rpCh
	} else {
		log.Trace("Creating reverse proxy channel")
		client.rpCh = make(chan *httputil.ReverseProxy, 1)
	}

	log.Trace("Publishing reverse proxy")

	client.rpCh <- rp

	// We don't need to protect client.rpInitialized from race conditions because
	// it's only accessed here in initReverseProxy, which always gets called
	// under Configure, which never gets called concurrently with itself.
	client.rpInitialized = true
}
Пример #2
0
func main() {
	go func() {
		log.Println("Starting standard proxy at localhost:8081")
		http.ListenAndServe("localhost:8081", &httputil.ReverseProxy{
			Director: func(req *http.Request) {},
		})
	}()
	log.Println("Starting detour proxy at localhost:8080")
	http.ListenAndServe("localhost:8080", &httputil.ReverseProxy{
		Director: func(req *http.Request) {},
		Transport: &http.Transport{
			// This just detours to net.Dial, meaning that it doesn't accomplish any
			// unblocking, it's just here for performance testing.
			Dial: detour.Dialer(net.Dial),
		},
	})
}
Пример #3
0
func (client *Client) proxiedDialer(orig func(network, addr string) (net.Conn, error)) func(network, addr string) (net.Conn, error) {
	detourDialer := detour.Dialer(orig)

	return func(network, addr string) (net.Conn, error) {
		var proxied func(network, addr string) (net.Conn, error)
		if client.ProxyAll() {
			proxied = orig
		} else {
			proxied = detourDialer
		}

		if isLanternSpecialDomain(addr) {
			rewritten := rewriteLanternSpecialDomain(addr)
			log.Tracef("Rewriting %v to %v", addr, rewritten)
			return net.Dial(network, rewritten)
		}
		return proxied(network, addr)
	}
}
Пример #4
0
// intercept intercepts an HTTP CONNECT request, hijacks the underlying client
// connetion and starts piping the data over a new net.Conn obtained from the
// given dial function.
func (client *Client) intercept(resp http.ResponseWriter, req *http.Request) {

	if req.Method != httpConnectMethod {
		panic("Intercept used for non-CONNECT request!")
	}

	var err error

	// Hijack underlying connection.
	var clientConn net.Conn
	if clientConn, _, err = resp.(http.Hijacker).Hijack(); err != nil {
		respondBadGateway(resp, fmt.Sprintf("Unable to hijack connection: %s", err))
		return
	}
	defer clientConn.Close()

	addr := hostIncludingPort(req, 443)
	// Establish outbound connection.
	d := func(network, addr string) (net.Conn, error) {
		return client.getBalancer().DialQOS("tcp", addr, client.targetQOS(req))
	}

	var connOut net.Conn
	if runtime.GOOS == "android" || client.ProxyAll {
		connOut, err = d("tcp", addr)
	} else {
		connOut, err = detour.Dialer(d)("tcp", addr)
	}

	if err != nil {
		respondBadGateway(clientConn, fmt.Sprintf("Unable to handle CONNECT request: %s", err))
		return
	}

	defer connOut.Close()

	// Pipe data between the client and the proxy.
	pipeData(clientConn, connOut, req)
}
Пример #5
0
// intercept intercepts an HTTP CONNECT request, hijacks the underlying client
// connetion and starts piping the data over a new net.Conn obtained from the
// given dial function.
func (client *Client) intercept(resp http.ResponseWriter, req *http.Request) {

	if req.Method != httpConnectMethod {
		panic("Intercept used for non-CONNECT request!")
	}

	var err error
	var clientConn net.Conn
	var connOut net.Conn

	// Make sure of closing connections only once
	var closeOnce sync.Once

	// Force closing if EOF at the request half or error encountered.
	// A bit arbitrary, but it's rather rare now to use half closing
	// as a way to notify server. Most application closes both connections
	// after completed send / receive so that won't cause problem.
	closeConns := func() {
		if clientConn != nil {
			if err := clientConn.Close(); err != nil {
				log.Debugf("Error closing the out connection: %s", err)
			}
		}
		if connOut != nil {
			if err := connOut.Close(); err != nil {
				log.Debugf("Error closing the client connection: %s", err)
			}
		}
	}

	defer closeOnce.Do(closeConns)

	// Hijack underlying connection.
	if clientConn, _, err = resp.(http.Hijacker).Hijack(); err != nil {
		respondBadGateway(resp, fmt.Sprintf("Unable to hijack connection: %s", err))
		return
	}

	// Respond OK as soon as possible, even if we don't have the outbound connection
	// established yet, to avoid timeouts on the client application
	success := make(chan bool, 1)
	go func() {
		if e := respondOK(clientConn, req); e != nil {
			log.Errorf("Unable to respond OK: %s", e)
			success <- false
			return
		}
		success <- true
	}()

	// Establish outbound connection.
	addr := hostIncludingPort(req, 443)
	d := func(network, addr string) (net.Conn, error) {
		return client.getBalancer().DialQOS("tcp", addr, client.targetQOS(req))
	}

	if runtime.GOOS == "android" || client.ProxyAll {
		connOut, err = d("tcp", addr)
	} else {
		connOut, err = detour.Dialer(d)("tcp", addr)
	}
	if err != nil {
		log.Debugf("Could not dial %v", err)
		return
	}

	if <-success {
		// Pipe data between the client and the proxy.
		pipeData(clientConn, connOut, func() { closeOnce.Do(closeConns) })
	}
}