// 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 }
// 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(), } }