http_proxy.go 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. package process
  2. import (
  3. "bufio"
  4. "ehang.io/nps/lib/enet"
  5. "ehang.io/nps/lib/logger"
  6. "encoding/base64"
  7. "github.com/pkg/errors"
  8. "go.uber.org/zap"
  9. "net/http"
  10. "strconv"
  11. "strings"
  12. )
  13. type HttpProxyProcess struct {
  14. DefaultProcess
  15. BasicAuth map[string]string `json:"basic_auth" placeholder:"username1 password1\nusername2 password2" zh_name:"basic认证"`
  16. }
  17. func (hpp *HttpProxyProcess) GetName() string {
  18. return "http_proxy"
  19. }
  20. func (hpp *HttpProxyProcess) GetZhName() string {
  21. return "http代理"
  22. }
  23. func (hpp *HttpProxyProcess) ProcessConn(c enet.Conn) (bool, error) {
  24. r, err := http.ReadRequest(bufio.NewReader(c))
  25. if err != nil {
  26. return false, errors.Wrap(err, "read proxy request")
  27. }
  28. if len(hpp.BasicAuth) != 0 && !hpp.checkAuth(r) {
  29. return true, hpp.response(http.StatusProxyAuthRequired, map[string]string{"Proxy-Authenticate": "Basic realm=" + strconv.Quote("nps")}, c)
  30. }
  31. if r.Method == "CONNECT" {
  32. err = hpp.response(200, map[string]string{}, c)
  33. if err != nil {
  34. return true, errors.Wrap(err, "http proxy response")
  35. }
  36. } else if err = c.Reset(0); err != nil {
  37. logger.Warn("reset enet.Conn error", zap.Error(err))
  38. return true, err
  39. }
  40. address := r.Host
  41. if !strings.Contains(r.Host, ":") {
  42. if r.URL.Scheme == "https" {
  43. address = r.Host + ":443"
  44. } else {
  45. address = r.Host + ":80"
  46. }
  47. }
  48. return true, hpp.ac.RunConnWithAddr(c, address)
  49. }
  50. func (hpp *HttpProxyProcess) response(statusCode int, headers map[string]string, c enet.Conn) error {
  51. resp := &http.Response{
  52. Status: http.StatusText(statusCode),
  53. StatusCode: statusCode,
  54. Proto: "HTTP/1.1",
  55. ProtoMajor: 1,
  56. ProtoMinor: 1,
  57. Header: http.Header{},
  58. }
  59. for k, v := range headers {
  60. resp.Header.Set(k, v)
  61. }
  62. return resp.Write(c)
  63. }
  64. func (hpp *HttpProxyProcess) checkAuth(r *http.Request) bool {
  65. s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
  66. if len(s) != 2 {
  67. s = strings.SplitN(r.Header.Get("Proxy-Authorization"), " ", 2)
  68. if len(s) != 2 {
  69. return false
  70. }
  71. }
  72. b, err := base64.StdEncoding.DecodeString(s[1])
  73. if err != nil {
  74. return false
  75. }
  76. pair := strings.SplitN(string(b), ":", 2)
  77. if len(pair) != 2 {
  78. return false
  79. }
  80. for u, p := range hpp.BasicAuth {
  81. if pair[0] == u && pair[1] == p {
  82. return true
  83. }
  84. }
  85. return false
  86. }