Example #1
0
// initBalancer takes hosts from cfg.ChainedServers and it uses them to create a
// balancer.
func (client *Client) initBalancer(cfg *ClientConfig) (*balancer.Balancer, error) {
	if len(cfg.ChainedServers) == 0 {
		return nil, fmt.Errorf("No chained servers configured, not initializing balancer")
	}
	// The dialers slice must be large enough to handle all chained
	// servers.
	dialers := make([]*balancer.Dialer, 0, len(cfg.ChainedServers))

	// Add chained (CONNECT proxy) servers.
	log.Debugf("Adding %d chained servers", len(cfg.ChainedServers))
	for _, s := range cfg.ChainedServers {
		dialer, err := s.Dialer(cfg.DeviceID)
		if err == nil {
			dialers = append(dialers, dialer)
		} else {
			log.Errorf("Unable to configure chained server. Received error: %v", err)
		}
	}

	bal := balancer.New(dialers...)
	var oldBal *balancer.Balancer
	var ok bool
	ob, ok := client.bal.Get(0 * time.Millisecond)
	if ok {
		oldBal = ob.(*balancer.Balancer)
	}

	log.Trace("Publishing balancer")
	client.bal.Set(bal)

	if oldBal != nil {
		// Close old balancer on a goroutine to avoid blocking here
		go func() {
			oldBal.Close()
			log.Debug("Closed old balancer")
		}()
	}

	return bal, nil
}
Example #2
0
// newReverseProxy creates a reverse proxy that uses the client's balancer to
// dial out.
func (client *Client) newReverseProxy(bal *balancer.Balancer) *httputil.ReverseProxy {
	transport := &http.Transport{
		TLSHandshakeTimeout: 40 * time.Second,
	}

	// 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.
	transport.Dial = client.proxiedDialer(bal.Dial)

	allAuthTokens := bal.AllAuthTokens()
	return &httputil.ReverseProxy{
		// We need to set the authentication tokens for all servers that we might
		// connect to because we don't know which one the dialer will actually
		// pick. We also need to strip out X-Forwarded-For that reverseproxy adds
		// because it confuses the upstream servers with the additional 127.0.0.1
		// field when upstream servers are trying to determine the client IP.
		// We need to add also the X-Lantern-Device-Id field.
		Director: func(req *http.Request) {
			// Add back the Host header which was stripped by the ReverseProxy. This
			// is needed for sites that do virtual hosting.
			req.Header.Set("Host", req.Host)
			req.Header.Set("X-LANTERN-DEVICE-ID", client.cfg().DeviceID)
			for _, authToken := range allAuthTokens {
				req.Header.Add("X-LANTERN-AUTH-TOKEN", authToken)
			}
		},
		Transport: &errorRewritingRoundTripper{
			&noForwardedForRoundTripper{withDumpHeaders(false, transport)},
		},
		// Set a FlushInterval to prevent overly aggressive buffering of
		// responses, which helps keep memory usage down
		FlushInterval: 250 * time.Millisecond,
		ErrorLog:      log.AsStdLogger(),
	}
}