github.com/codingeasygo/util@v0.0.0-20231206062002-1ce2f004b7d9/proxy/http/http.go (about) 1 package http 2 3 import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 "io" 8 "net" 9 "net/http" 10 "strings" 11 "sync" 12 13 "github.com/codingeasygo/util/xio" 14 ) 15 16 //Server is http proxy server 17 type Server struct { 18 BufferSize int 19 listners map[net.Listener]string 20 waiter sync.WaitGroup 21 Dialer xio.PiperDialer 22 Agent string 23 } 24 25 //NewServer will return new server 26 func NewServer() (proxy *Server) { 27 proxy = &Server{ 28 BufferSize: 32 * 1024, 29 listners: map[net.Listener]string{}, 30 waiter: sync.WaitGroup{}, 31 Dialer: xio.PiperDialerF(xio.DialNetPiper), 32 Agent: "EasyGo/v1.0.0", 33 } 34 return 35 } 36 37 //Run will listen tcp on address and accept to ProcConn 38 func (s *Server) loopAccept(l net.Listener) (err error) { 39 var conn net.Conn 40 for { 41 conn, err = l.Accept() 42 if err != nil { 43 break 44 } 45 go s.ProcConn(conn) 46 } 47 s.waiter.Done() 48 return 49 } 50 51 //Run will listen tcp on address and sync accept to ProcConn 52 func (s *Server) Run(addr string) (err error) { 53 listener, err := net.Listen("tcp", addr) 54 if err == nil { 55 s.listners[listener] = addr 56 InfoLog("Server listen http proxy on %v", addr) 57 s.waiter.Add(1) 58 err = s.loopAccept(listener) 59 } 60 return 61 } 62 63 //Start will listen tcp on address and async accept to ProcConn 64 func (s *Server) Start(addr string) (listener net.Listener, err error) { 65 listener, err = net.Listen("tcp", addr) 66 if err == nil { 67 s.listners[listener] = addr 68 InfoLog("Server listen http proxy on %v", addr) 69 s.waiter.Add(1) 70 go s.loopAccept(listener) 71 } 72 return 73 } 74 75 //Stop will stop listener and wait loop stop 76 func (s *Server) Stop() (err error) { 77 for listener, addr := range s.listners { 78 err = listener.Close() 79 delete(s.listners, listener) 80 InfoLog("Server http proxy listener on %v is stopped by %v", addr, err) 81 } 82 s.waiter.Wait() 83 return 84 } 85 86 //ProcConn will processs net connect as http proxy 87 func (s *Server) ProcConn(conn io.ReadWriteCloser) (err error) { 88 // DebugLog("Server proxy http connection on %v from %v", xio.LocalAddr(conn), xio.RemoteAddr(conn)) 89 defer func() { 90 if err != xio.ErrAsyncRunning { 91 DebugLog("Server http proxy connection on %v from %v is done with %v", xio.LocalAddr(conn), xio.RemoteAddr(conn), err) 92 conn.Close() 93 } 94 }() 95 reader := bufio.NewReader(conn) 96 req, err := http.ReadRequest(reader) 97 if err != nil { 98 return 99 } 100 resp := &http.Response{ 101 ProtoMajor: 1, 102 ProtoMinor: 1, 103 Header: http.Header{}, 104 } 105 resp.Header.Add("Proxy-Agent", s.Agent) 106 // if req.Method != http.MethodConnect && len(req.Header.Get("Proxy-Connection")) < 1 { 107 // DebugLog("Server sending proxy server info on %v to %v", xio.LocalAddr(conn), xio.RemoteAddr(conn)) 108 // if req.Method == http.MethodHead { 109 // resp.StatusCode = http.StatusOK 110 // resp.Write(conn) 111 // } else { 112 // resp.StatusCode = http.StatusInternalServerError 113 // resp.Body = xio.NewCombinedReadWriteCloser(bytes.NewBufferString("not supported"), nil, nil) 114 // resp.Write(conn) 115 // WarnLog("Server http proxy received not supported connect by method:%v,url:%v,header:%v", req.Method, req.URL, converter.JSON(req.Header)) 116 // } 117 // return 118 // } 119 req.Header.Del("Proxy-Authorization") 120 req.Header.Del("Proxy-Connection") 121 var raw xio.Piper 122 var uri string 123 if req.Method == "CONNECT" { 124 uri = "tcp://" + req.RequestURI 125 DebugLog("Server http proxy start dial to %v on %v from %v", uri, xio.LocalAddr(conn), xio.RemoteAddr(conn)) 126 raw, err = s.Dialer.DialPiper(uri, s.BufferSize) 127 if err != nil { 128 resp.StatusCode = http.StatusInternalServerError 129 resp.Body = xio.NewCombinedReadWriteCloser(bytes.NewBufferString(err.Error()), nil, nil) 130 resp.Write(conn) 131 InfoLog("Server http proxy dial to %v on %v fail with %v", uri, xio.RemoteAddr(conn), err) 132 return 133 } 134 resp.StatusCode = http.StatusOK 135 resp.Status = "Connection established" 136 resp.Write(conn) 137 } else { 138 host := req.Host 139 if _, port, _ := net.SplitHostPort(host); port == "" { 140 host += ":80" 141 } 142 uri = "tcp://" + host 143 DebugLog("Server http proxy start dial to %v on %v from %v", uri, xio.LocalAddr(conn), xio.RemoteAddr(conn)) 144 raw, err = s.Dialer.DialPiper(uri, s.BufferSize) 145 if err != nil { 146 resp.StatusCode = http.StatusInternalServerError 147 resp.Body = xio.NewCombinedReadWriteCloser(bytes.NewBufferString(err.Error()), nil, nil) 148 resp.Write(conn) 149 InfoLog("Server http proxy dial to %v on %v fail with %v", uri, xio.RemoteAddr(conn), err) 150 return 151 } 152 buffer := bytes.NewBuffer(nil) 153 req.Write(buffer) 154 prefix := xio.NewPrefixReadWriteCloser(conn) 155 prefix.Prefix = buffer.Bytes() 156 conn = prefix 157 } 158 err = raw.PipeConn(conn, uri) 159 if err != xio.ErrAsyncRunning { 160 raw.Close() 161 } 162 return 163 } 164 165 //Dial will dial uri by proxy server 166 func Dial(proxy, uri string) (conn net.Conn, err error) { 167 proxy = strings.TrimPrefix(proxy, "http://") 168 proxy = strings.TrimSuffix(proxy, "/") 169 conn, err = net.Dial("tcp", proxy) 170 if err != nil { 171 return 172 } 173 address := uri 174 if !strings.HasPrefix(address, "http://") { 175 address = "http://" + address 176 } 177 req, err := http.NewRequest("CONNECT", address, nil) 178 if err != nil { 179 return 180 } 181 req.Write(conn) 182 reader := bufio.NewReader(conn) 183 resp, err := http.ReadResponse(reader, req) 184 if resp.StatusCode != http.StatusOK { 185 err = fmt.Errorf("proxy response %v", resp.StatusCode) 186 } 187 return 188 }