proxy.go 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. // Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package websocket
  5. import (
  6. "bufio"
  7. "encoding/base64"
  8. "errors"
  9. "log"
  10. "net"
  11. "net/http"
  12. "net/url"
  13. "strings"
  14. "golang.org/x/net/proxy"
  15. )
  16. type netDialerFunc func(network, addr string) (net.Conn, error)
  17. func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) {
  18. return fn(network, addr)
  19. }
  20. func init() {
  21. proxy.RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy.Dialer) (proxy.Dialer, error) {
  22. return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial}, nil
  23. })
  24. }
  25. type httpProxyDialer struct {
  26. proxyURL *url.URL
  27. forwardDial func(network, addr string) (net.Conn, error)
  28. }
  29. func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error) {
  30. hostPort, _ := hostPortNoPort(hpd.proxyURL)
  31. conn, err := hpd.forwardDial(network, hostPort)
  32. if err != nil {
  33. return nil, err
  34. }
  35. connectHeader := make(http.Header)
  36. if user := hpd.proxyURL.User; user != nil {
  37. proxyUser := user.Username()
  38. if proxyPassword, passwordSet := user.Password(); passwordSet {
  39. credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword))
  40. connectHeader.Set("Proxy-Authorization", "Basic "+credential)
  41. }
  42. }
  43. connectReq := &http.Request{
  44. Method: http.MethodConnect,
  45. URL: &url.URL{Opaque: addr},
  46. Host: addr,
  47. Header: connectHeader,
  48. }
  49. if err := connectReq.Write(conn); err != nil {
  50. if err := conn.Close(); err != nil {
  51. log.Printf("httpProxyDialer: failed to close connection: %v", err)
  52. }
  53. return nil, err
  54. }
  55. // Read response. It's OK to use and discard buffered reader here becaue
  56. // the remote server does not speak until spoken to.
  57. br := bufio.NewReader(conn)
  58. resp, err := http.ReadResponse(br, connectReq)
  59. if err != nil {
  60. if err := conn.Close(); err != nil {
  61. log.Printf("httpProxyDialer: failed to close connection: %v", err)
  62. }
  63. return nil, err
  64. }
  65. if resp.StatusCode != 200 {
  66. if err := conn.Close(); err != nil {
  67. log.Printf("httpProxyDialer: failed to close connection: %v", err)
  68. }
  69. f := strings.SplitN(resp.Status, " ", 2)
  70. return nil, errors.New(f[1])
  71. }
  72. return conn, nil
  73. }