예제 #1
0
파일: ssh2.go 프로젝트: johnsonz/goproxy
func NewFilter(config *Config) (filters.Filter, error) {
	ss := &Servers{
		servers:    make([]Server, 0),
		sshClients: lrucache.NewLRUCache(uint(len(config.Servers))),
	}

	for _, s := range config.Servers {
		server := Server{
			Address: s.Addr,
			ClientConfig: &ssh.ClientConfig{
				User: s.Username,
				Auth: []ssh.AuthMethod{
					ssh.Password(s.Password),
				},
			},
		}

		ss.servers = append(ss.servers, server)
	}

	tr := &http.Transport{
		Dial: ss.Dial,
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: false,
			ClientSessionCache: tls.NewLRUClientSessionCache(1000),
		},
		TLSHandshakeTimeout: time.Duration(config.Transport.TLSHandshakeTimeout) * time.Second,
		MaxIdleConnsPerHost: config.Transport.MaxIdleConnsPerHost,
	}

	return &Filter{
		Config:    *config,
		Transport: tr,
	}, nil
}
예제 #2
0
파일: direct.go 프로젝트: taiping-z/goproxy
func NewFilter(config *Config) (filters.Filter, error) {
	d := &dialer.Dialer{
		Dialer: net.Dialer{
			KeepAlive: time.Duration(config.Transport.Dialer.KeepAlive) * time.Second,
			Timeout:   time.Duration(config.Transport.Dialer.Timeout) * time.Second,
			DualStack: config.Transport.Dialer.DualStack,
		},
		RetryTimes:     config.Transport.Dialer.RetryTimes,
		RetryDelay:     time.Duration(config.Transport.Dialer.RetryDelay*1000) * time.Second,
		DNSCache:       lrucache.NewLRUCache(config.Transport.Dialer.DNSCacheSize),
		DNSCacheExpiry: time.Duration(config.Transport.Dialer.DNSCacheExpiry) * time.Second,
		LoopbackAddrs:  make(map[string]struct{}),
	}

	d.LoopbackAddrs = make(map[string]struct{})
	d.LoopbackAddrs["127.0.0.1"] = struct{}{}
	d.LoopbackAddrs["::1"] = struct{}{}
	if addrs, err := net.InterfaceAddrs(); err == nil {
		for _, addr := range addrs {
			switch addr.Network() {
			case "ip":
				d.LoopbackAddrs[addr.String()] = struct{}{}
			}
		}
	}

	tr := &http.Transport{
		Dial: d.Dial,
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: config.Transport.TLSClientConfig.InsecureSkipVerify,
			ClientSessionCache: tls.NewLRUClientSessionCache(config.Transport.TLSClientConfig.ClientSessionCacheSize),
		},
		TLSHandshakeTimeout: time.Duration(config.Transport.TLSHandshakeTimeout) * time.Second,
		MaxIdleConnsPerHost: config.Transport.MaxIdleConnsPerHost,
		DisableCompression:  config.Transport.DisableCompression,
	}

	return &Filter{
		Config:    *config,
		transport: tr,
	}, nil
}
예제 #3
0
파일: php.go 프로젝트: chenzhenjia/goproxy
func NewFilter(config *Config) (filters.Filter, error) {
	servers := make([]Server, 0)
	for _, s := range config.Servers {
		u, err := url.Parse(s.URL)
		if err != nil {
			return nil, err
		}

		server := Server{
			URL:       u,
			Password:  s.Password,
			SSLVerify: s.SSLVerify,
			Host:      s.Host,
		}

		servers = append(servers, server)
	}

	d := &dialer.Dialer{
		Dialer: net.Dialer{
			KeepAlive: time.Duration(config.Transport.Dialer.KeepAlive) * time.Second,
			Timeout:   time.Duration(config.Transport.Dialer.Timeout) * time.Second,
			DualStack: config.Transport.Dialer.DualStack,
		},
		RetryTimes:     config.Transport.Dialer.RetryTimes,
		RetryDelay:     time.Duration(config.Transport.Dialer.RetryDelay*1000) * time.Second,
		DNSCache:       lrucache.NewLRUCache(config.Transport.Dialer.DNSCacheSize),
		DNSCacheExpiry: time.Duration(config.Transport.Dialer.DNSCacheExpiry) * time.Second,
		LoopbackAddrs:  nil,
		Level:          2,
	}

	for _, server := range servers {
		if server.Host != "" {
			host := server.URL.Host
			if _, _, err := net.SplitHostPort(host); err != nil {
				if server.URL.Scheme == "https" {
					host += ":443"
				} else {
					host += ":80"
				}
			}

			host1 := server.Host
			if _, _, err := net.SplitHostPort(host1); err != nil {
				if server.URL.Scheme == "https" {
					host1 += ":443"
				} else {
					host1 += ":80"
				}
			}

			d.DNSCache.Set(host, host1, time.Time{})
		}
	}

	tr := &http.Transport{
		Dial: d.Dial,
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: false,
			ClientSessionCache: tls.NewLRUClientSessionCache(1000),
		},
		TLSHandshakeTimeout: time.Duration(config.Transport.TLSHandshakeTimeout) * time.Second,
		MaxIdleConnsPerHost: config.Transport.MaxIdleConnsPerHost,
	}

	if tr.TLSClientConfig != nil {
		err := http2.ConfigureTransport(tr)
		if err != nil {
			glog.Warningf("PHP: Error enabling Transport HTTP/2 support: %v", err)
		}
	}

	return &Filter{
		Config: *config,
		Transport: &Transport{
			RoundTripper: tr,
			Servers:      servers,
		},
		Sites: helpers.NewHostMatcher(config.Sites),
	}, nil
}
예제 #4
0
파일: gae.go 프로젝트: ChoyesYan/goproxy
func NewFilter(config *Config) (filters.Filter, error) {
	dnsServers := make([]net.IP, 0)
	for _, s := range config.DNSServers {
		if ip := net.ParseIP(s); ip != nil {
			dnsServers = append(dnsServers, ip)
		}
	}

	GoogleG2PKP, err := base64.StdEncoding.DecodeString(config.GoogleG2PKP)
	if err != nil {
		return nil, err
	}

	googleTLSConfig := &tls.Config{
		MinVersion:         tls.VersionTLS12,
		InsecureSkipVerify: true,
		ServerName:         "www.microsoft.com",
		ClientSessionCache: tls.NewLRUClientSessionCache(config.TLSConfig.ClientSessionCacheSize),
		CipherSuites: []uint16{
			tls.TLS_RSA_WITH_AES_128_CBC_SHA,
			tls.TLS_RSA_WITH_AES_256_CBC_SHA,
			tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
			tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
			tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
			tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
		},
	}
	switch config.TLSConfig.Version {
	case "TLSv12", "TLSv1.2":
		googleTLSConfig.MinVersion = tls.VersionTLS12
	case "TLSv11", "TLSv1.1":
		googleTLSConfig.MinVersion = tls.VersionTLS11
	default:
		googleTLSConfig.MinVersion = tls.VersionTLS10
	}
	pickupCiphers := func(names []string) []uint16 {
		ciphers := make([]uint16, 0)
		for _, name := range names {
			cipher := helpers.Cipher(name)
			if cipher == 0 {
				glog.Fatalf("GAE: cipher %#v is not supported.", name)
			}
			ciphers = append(ciphers, cipher)
		}
		helpers.ShuffleUint16s(ciphers)
		ciphers = ciphers[:1+rand.Intn(len(ciphers))]
		ciphers1 := []uint16{}
		for _, name := range []string{
			"TLS_RSA_WITH_AES_256_CBC_SHA256",
			"TLS_RSA_WITH_AES_256_GCM_SHA384",
			"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
			"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
		} {
			if !helpers.ContainsString(names, name) {
				if c := helpers.Cipher(name); c != 0 {
					ciphers1 = append(ciphers1, c)
				}
			}
		}
		helpers.ShuffleUint16s(ciphers1)
		ciphers1 = ciphers1[:rand.Intn(len(ciphers1))]
		ciphers = append(ciphers, ciphers1...)
		helpers.ShuffleUint16s(ciphers)
		return ciphers
	}
	googleTLSConfig.CipherSuites = pickupCiphers(config.TLSConfig.Ciphers)
	if len(config.TLSConfig.ServerName) > 0 {
		googleTLSConfig.ServerName = config.TLSConfig.ServerName[rand.Intn(len(config.TLSConfig.ServerName))]
	}
	if !config.DisableHTTP2 {
		googleTLSConfig.NextProtos = []string{"h2", "h2-14", "http/1.1"}
	}

	if config.Site2Alias == nil {
		config.Site2Alias = make(map[string]string)
	}
	for key, value := range config.SiteToAlias {
		config.Site2Alias[key] = value
	}
	config.SiteToAlias = config.Site2Alias

	hostmap := map[string][]string{}
	for key, value := range config.HostMap {
		hostmap[key] = helpers.UniqueStrings(value)
	}

	d := &net.Dialer{
		KeepAlive: time.Duration(config.Transport.Dialer.KeepAlive) * time.Second,
		Timeout:   time.Duration(config.Transport.Dialer.Timeout) * time.Second,
		DualStack: config.Transport.Dialer.DualStack,
	}

	md := &dialer.MultiDialer{
		Dialer:            d,
		DisableIPv6:       config.DisableIPv6,
		ForceIPv6:         config.ForceIPv6,
		SSLVerify:         config.SSLVerify,
		EnableRemoteDNS:   config.EnableRemoteDNS,
		LogToStderr:       flag.Lookup("logtostderr") != nil,
		TLSConfig:         nil,
		SiteToAlias:       helpers.NewHostMatcherWithString(config.SiteToAlias),
		IPBlackList:       lrucache.NewLRUCache(1024),
		HostMap:           hostmap,
		GoogleTLSConfig:   googleTLSConfig,
		GoogleG2PKP:       GoogleG2PKP,
		DNSServers:        dnsServers,
		DNSCache:          lrucache.NewLRUCache(config.Transport.Dialer.DNSCacheSize),
		DNSCacheExpiry:    time.Duration(config.Transport.Dialer.DNSCacheExpiry) * time.Second,
		TLSConnDuration:   lrucache.NewLRUCache(8192),
		TLSConnError:      lrucache.NewLRUCache(8192),
		TLSConnReadBuffer: config.Transport.Dialer.SocketReadBuffer,
		ConnExpiry:        5 * time.Minute,
		Level:             config.Transport.Dialer.Level,
	}

	for _, ip := range config.IPBlackList {
		md.IPBlackList.Set(ip, struct{}{}, time.Time{})
	}

	var tr http.RoundTripper

	t1 := &http.Transport{
		Dial:                  md.Dial,
		DialTLS:               md.DialTLS,
		DisableKeepAlives:     config.Transport.DisableKeepAlives,
		DisableCompression:    config.Transport.DisableCompression,
		ResponseHeaderTimeout: time.Duration(config.Transport.ResponseHeaderTimeout) * time.Second,
		IdleConnTimeout:       time.Duration(config.Transport.IdleConnTimeout) * time.Second,
		MaxIdleConnsPerHost:   config.Transport.MaxIdleConnsPerHost,
	}

	if config.Transport.Proxy.Enabled {
		fixedURL, err := url.Parse(config.Transport.Proxy.URL)
		if err != nil {
			glog.Fatalf("url.Parse(%#v) error: %s", config.Transport.Proxy.URL, err)
		}

		dialer, err := proxy.FromURL(fixedURL, d, &dialer.MultiResolver{md})
		if err != nil {
			glog.Fatalf("proxy.FromURL(%#v) error: %s", fixedURL.String(), err)
		}

		t1.Dial = dialer.Dial
		t1.DialTLS = nil
		t1.Proxy = nil
		t1.TLSClientConfig = md.GoogleTLSConfig
	}

	switch {
	case config.DisableHTTP2 && config.ForceHTTP2:
		glog.Fatalf("GAE: DisableHTTP2=%v and ForceHTTP2=%v is conflict!", config.DisableHTTP2, config.ForceHTTP2)
	case config.Transport.Proxy.Enabled && config.ForceHTTP2:
		glog.Fatalf("GAE: Proxy.Enabled=%v and ForceHTTP2=%v is conflict!", config.Transport.Proxy.Enabled, config.ForceHTTP2)
	case config.ForceHTTP2:
		tr = &http2.Transport{
			DialTLS:            md.DialTLS2,
			TLSClientConfig:    md.GoogleTLSConfig,
			DisableCompression: config.Transport.DisableCompression,
		}
	case !config.DisableHTTP2:
		err := http2.ConfigureTransport(t1)
		if err != nil {
			glog.Warningf("GAE: Error enabling Transport HTTP/2 support: %v", err)
		}
		tr = t1
	default:
		tr = t1
	}

	forceHTTPSMatcherStrings := make([]string, 0)
	for key, value := range config.SiteToAlias {
		if strings.HasPrefix(value, "google_") {
			forceHTTPSMatcherStrings = append(forceHTTPSMatcherStrings, key)
		}
	}

	forceGAEStrings := make([]string, 0)
	forceGAESuffixs := make([]string, 0)
	forceGAEMatcherStrings := make([]string, 0)
	for _, s := range config.ForceGAE {
		if strings.Contains(s, "/") {
			if strings.HasSuffix(s, "$") {
				forceGAESuffixs = append(forceGAESuffixs, strings.TrimRight(s, "$"))
			} else {
				forceGAEStrings = append(forceGAEStrings, s)
			}
		} else {
			forceGAEMatcherStrings = append(forceGAEMatcherStrings, s)
		}
	}

	if config.EnableDeadProbe && !config.Transport.Proxy.Enabled {
		go func() {
			probe := func() {
				req, _ := http.NewRequest(http.MethodGet, "https://clients3.google.com/generate_204", nil)
				ctx, cancel := context.WithTimeout(req.Context(), 3*time.Second)
				defer cancel()
				req = req.WithContext(ctx)
				resp, err := tr.RoundTrip(req)
				if resp != nil && resp.Body != nil {
					glog.V(3).Infof("GAE EnableDeadProbe \"%s %s\" %d -", req.Method, req.URL.String(), resp.StatusCode)
					resp.Body.Close()
				}
				if err != nil {
					glog.V(2).Infof("GAE EnableDeadProbe \"%s %s\" error: %v", req.Method, req.URL.String(), err)
					s := strings.ToLower(err.Error())
					if strings.HasPrefix(s, "net/http: request canceled") || strings.Contains(s, "timeout") {
						helpers.TryCloseConnections(tr)
					}
				}
			}

			for {
				time.Sleep(time.Duration(5+rand.Intn(6)) * time.Second)
				probe()
			}
		}()
	}

	helpers.ShuffleStrings(config.AppIDs)

	f := &Filter{
		Config: *config,
		GAETransport: &Transport{
			RoundTripper: tr,
			MultiDialer:  md,
			Servers: NewServers(config.AppIDs,
				config.Password,
				config.SSLVerify,
				time.Duration(config.Transport.ResponseHeaderTimeout-4)*time.Second),
			RetryDelay: time.Duration(config.Transport.RetryDelay*1000) * time.Second,
			RetryTimes: config.Transport.RetryTimes,
		},
		DirectTransport:     tr,
		ForceHTTPSMatcher:   helpers.NewHostMatcher(forceHTTPSMatcherStrings),
		ForceGAEMatcher:     helpers.NewHostMatcher(forceGAEMatcherStrings),
		ForceGAEStrings:     forceGAEStrings,
		ForceGAESuffixs:     forceGAESuffixs,
		ForceDeflateMatcher: helpers.NewHostMatcher(config.ForceDeflate),
		FakeOptionsMatcher:  helpers.NewHostMatcherWithStrings(config.FakeOptions),
		SiteMatcher:         helpers.NewHostMatcher(config.Sites),
		DirectSiteMatcher:   helpers.NewHostMatcherWithString(config.Site2Alias),
	}

	if config.Transport.Proxy.Enabled {
		f.GAETransport.MultiDialer = nil
	}

	return f, nil
}
예제 #5
0
파일: php.go 프로젝트: johnsonz/goproxy
func NewFilter(config *Config) (filters.Filter, error) {
	servers := make([]Server, 0)
	for _, s := range config.Servers {
		u, err := url.Parse(s.URL)
		if err != nil {
			return nil, err
		}

		server := Server{
			URL:       u,
			Password:  s.Password,
			SSLVerify: s.SSLVerify,
			Host:      s.Host,
		}

		servers = append(servers, server)
	}

	d0 := &net.Dialer{
		KeepAlive: time.Duration(config.Transport.Dialer.KeepAlive) * time.Second,
		Timeout:   time.Duration(config.Transport.Dialer.Timeout) * time.Second,
		DualStack: config.Transport.Dialer.DualStack,
	}

	d := &helpers.Dialer{
		Dialer: d0,
		Resolver: &helpers.Resolver{
			LRUCache:  lrucache.NewLRUCache(config.Transport.Dialer.DNSCacheSize),
			DNSExpiry: time.Duration(config.Transport.Dialer.DNSCacheExpiry) * time.Second,
		},
		Level: 2,
	}

	for _, server := range servers {
		if server.Host != "" {
			d.Resolver.LRUCache.Set(server.URL.Hostname(), server.Host, time.Time{})
		}
	}

	tr := &http.Transport{
		Dial: d.Dial,
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: false,
			ClientSessionCache: tls.NewLRUClientSessionCache(1000),
		},
		TLSHandshakeTimeout: time.Duration(config.Transport.TLSHandshakeTimeout) * time.Second,
		MaxIdleConnsPerHost: config.Transport.MaxIdleConnsPerHost,
	}

	if config.Transport.Proxy.Enabled {
		fixedURL, err := url.Parse(config.Transport.Proxy.URL)
		if err != nil {
			glog.Fatalf("url.Parse(%#v) error: %s", config.Transport.Proxy.URL, err)
		}

		switch fixedURL.Scheme {
		case "http", "https":
			tr.Proxy = http.ProxyURL(fixedURL)
			tr.Dial = nil
			tr.DialTLS = nil
		default:
			dialer, err := proxy.FromURL(fixedURL, d, nil)
			if err != nil {
				glog.Fatalf("proxy.FromURL(%#v) error: %s", fixedURL.String(), err)
			}

			tr.Dial = dialer.Dial
			tr.DialTLS = nil
			tr.Proxy = nil
		}
	}

	if tr.TLSClientConfig != nil {
		err := http2.ConfigureTransport(tr)
		if err != nil {
			glog.Warningf("PHP: Error enabling Transport HTTP/2 support: %v", err)
		}
	}

	return &Filter{
		Config: *config,
		Transport: &Transport{
			RoundTripper: tr,
			Servers:      servers,
		},
	}, nil
}
예제 #6
0
func NewFilter(config *Config) (_ filters.Filter, err error) {
	var gfwlist GFWList

	gfwlist.Encoding = config.GFWList.Encoding
	gfwlist.Filename = config.GFWList.File
	gfwlist.Expiry = time.Duration(config.GFWList.Expiry) * time.Second
	gfwlist.Duration = time.Duration(config.GFWList.Duration) * time.Second
	gfwlist.URL, err = url.Parse(config.GFWList.URL)
	if err != nil {
		return nil, err
	}

	store := storage.LookupStoreByConfig(filterName)
	if err != nil {
		return nil, err
	}

	if _, err := store.Head(gfwlist.Filename); err != nil {
		return nil, err
	}

	transport := &http.Transport{}

	f := &Filter{
		Config:               *config,
		Store:                store,
		IndexFilesEnabled:    config.IndexFiles.Enabled,
		IndexFiles:           make(map[string]struct{}),
		ProxyPacCache:        lrucache.NewLRUCache(32),
		GFWListEnabled:       config.GFWList.Enabled,
		GFWList:              &gfwlist,
		Transport:            transport,
		SiteFiltersEnabled:   config.SiteFilters.Enabled,
		RegionFiltersEnabled: config.RegionFilters.Enabled,
	}

	for _, name := range config.IndexFiles.Files {
		f.IndexFiles[name] = struct{}{}
	}

	if f.SiteFiltersEnabled {
		fm := make(map[string]interface{})
		for host, name := range config.SiteFilters.Rules {
			f, err := filters.GetFilter(name)
			if err != nil {
				glog.Fatalf("AUTOPROXY: filters.GetFilter(%#v) for %#v error: %v", name, host, err)
			}
			if _, ok := f.(filters.RoundTripFilter); !ok {
				glog.Fatalf("AUTOPROXY: filters.GetFilter(%#v) return %T, not a RoundTripFilter", name, f)
			}
			fm[host] = f
		}
		f.SiteFiltersRules = helpers.NewHostMatcherWithValue(fm)
	}

	if f.RegionFiltersEnabled {
		resp, err := store.Get(f.Config.RegionFilters.DataFile, -1, -1)
		if err != nil {
			glog.Fatalf("AUTOPROXY: store.Get(%#v) error: %v", f.Config.RegionFilters.DataFile, err)
		}
		defer resp.Body.Close()

		data, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			glog.Fatalf("AUTOPROXY: ioutil.ReadAll(%#v) error: %v", resp.Body, err)
		}

		f.RegionLocator = ip17mon.NewLocatorWithData(data)

		fm := make(map[string]filters.RoundTripFilter)
		for region, name := range config.RegionFilters.Rules {
			if name == "" {
				continue
			}
			f, err := filters.GetFilter(name)
			if err != nil {
				glog.Fatalf("AUTOPROXY: filters.GetFilter(%#v) for %#v error: %v", name, region, err)
			}
			f1, ok := f.(filters.RoundTripFilter)
			if !ok {
				glog.Fatalf("AUTOPROXY: filters.GetFilter(%#v) return %T, not a RoundTripFilter", name, f)
			}
			fm[strings.ToLower(region)] = f1
		}
		f.RegionFiltersRules = fm

		f.RegionFilterCache = lrucache.NewLRUCache(uint(f.Config.RegionFilters.DNSCacheSize))
	}

	if f.GFWListEnabled {
		go onceUpdater.Do(f.pacUpdater)
	}

	return f, nil
}
예제 #7
0
func makeLRUCache(capacity uint64) lrucache.Cache {
	return lrucache.NewLRUCache(uint(capacity))
}
예제 #8
0
파일: direct.go 프로젝트: johnsonz/goproxy
func NewFilter(config *Config) (filters.Filter, error) {
	d := &helpers.Dialer{
		Dialer: &net.Dialer{
			KeepAlive: time.Duration(config.Transport.Dialer.KeepAlive) * time.Second,
			Timeout:   time.Duration(config.Transport.Dialer.Timeout) * time.Second,
			DualStack: config.Transport.Dialer.DualStack,
		},
		Resolver: &helpers.Resolver{
			LRUCache:  lrucache.NewLRUCache(config.Transport.Dialer.DNSCacheSize),
			DNSExpiry: time.Duration(config.Transport.Dialer.DNSCacheExpiry) * time.Second,
			BlackList: lrucache.NewLRUCache(1024),
		},
	}

	if ips, err := helpers.LocalIPv4s(); err == nil {
		for _, ip := range ips {
			d.Resolver.BlackList.Set(ip.String(), struct{}{}, time.Time{})
		}
		for _, s := range []string{"127.0.0.1", "::1"} {
			d.Resolver.BlackList.Set(s, struct{}{}, time.Time{})
		}
	}

	tr := &http.Transport{
		Dial: d.Dial,
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: config.Transport.TLSClientConfig.InsecureSkipVerify,
			ClientSessionCache: tls.NewLRUClientSessionCache(config.Transport.TLSClientConfig.ClientSessionCacheSize),
		},
		TLSHandshakeTimeout: time.Duration(config.Transport.TLSHandshakeTimeout) * time.Second,
		MaxIdleConnsPerHost: config.Transport.MaxIdleConnsPerHost,
		DisableCompression:  config.Transport.DisableCompression,
	}

	if config.Transport.Proxy.Enabled {
		fixedURL, err := url.Parse(config.Transport.Proxy.URL)
		if err != nil {
			glog.Fatalf("url.Parse(%#v) error: %s", config.Transport.Proxy.URL, err)
		}

		switch fixedURL.Scheme {
		case "http", "https":
			tr.Proxy = http.ProxyURL(fixedURL)
			tr.Dial = nil
			tr.DialTLS = nil
		default:
			dialer, err := proxy.FromURL(fixedURL, d, nil)
			if err != nil {
				glog.Fatalf("proxy.FromURL(%#v) error: %s", fixedURL.String(), err)
			}

			tr.Dial = dialer.Dial
			tr.DialTLS = nil
			tr.Proxy = nil
		}
	}

	return &Filter{
		Config:    *config,
		transport: tr,
	}, nil
}