package main import ( "bufio" "fmt" "net" "net/http" "net/url" "crypto/tls" "golang.org/x/net/proxy" ) type direct struct{} // Direct is a direct proxy: one that makes network connections directly. var Direct = direct{} func (direct) Dial(network, addr string) (net.Conn, error) { return net.Dial(network, addr) } // httpsDialer type httpsDialer struct {} // HTTPSDialer is a https proxy: one that makes network connections on tls. var HttpsDialer = httpsDialer{} var TlsConfig = &tls.Config{} func (d httpsDialer) Dial(network, addr string) (c net.Conn, err error) { c, err = tls.Dial("tcp", addr, TlsConfig) if err != nil { fmt.Println(err) } return } // httpProxy is a HTTP/HTTPS connect proxy. type httpProxy struct { host string haveAuth bool username string password string forward proxy.Dialer } func newHTTPProxy(uri *url.URL, forward proxy.Dialer) (proxy.Dialer, error) { s := new(httpProxy) s.host = uri.Host s.forward = forward if uri.User != nil { s.haveAuth = true s.username = uri.User.Username() s.password, _ = uri.User.Password() } return s, nil } func (s *httpProxy) Dial(network, addr string) (net.Conn, error) { // Dial and create the https client connection. c, err := s.forward.Dial("tcp", s.host) if err != nil { return nil, err } // HACK. http.ReadRequest also does this. reqURL, err := url.Parse("http://" + addr) if err != nil { c.Close() return nil, err } reqURL.Scheme = "" req, err := http.NewRequest("CONNECT", reqURL.String(), nil) if err != nil { c.Close() return nil, err } req.Close = false if s.haveAuth { req.SetBasicAuth(s.username, s.password) } req.Header.Set("User-Agent", "Powerby Gota") err = req.Write(c) if err != nil { c.Close() return nil, err } resp, err := http.ReadResponse(bufio.NewReader(c), req) if err != nil { // TODO close resp body ? resp.Body.Close() c.Close() return nil, err } resp.Body.Close() if resp.StatusCode != 200 { c.Close() err = fmt.Errorf("Connect server using proxy error, StatusCode [%d]", resp.StatusCode) return nil, err } return c, nil } func FromURL(u *url.URL, forward proxy.Dialer) (proxy.Dialer, error){ return proxy.FromURL(u, forward) } func FromEnvironment() proxy.Dialer { return proxy.FromEnvironment() } func init() { proxy.RegisterDialerType("http", newHTTPProxy) proxy.RegisterDialerType("https", newHTTPProxy) } func main() { // http proxy httpProxyURI, _ := url.Parse("http://your http proxy:3128") httpDialer, err := proxy.FromURL(httpProxyURI, Direct) conn, err := httpDialer.Dial("tcp", "google.com:80") if err != nil { panic(err) } fmt.Printf("Create http tunnel OK: %+v\n", conn) httpsProxyURI, _ := url.Parse("https://your https proxy:443") httpsDialer, err := proxy.FromURL(httpsProxyURI, HttpsDialer) conn, err = httpsDialer.Dial("tcp", "google.com:443") if err != nil { panic(err) } fmt.Printf("Create https tunnel OK: %+v\n", conn) // TODO use conn }