github.com/database64128/shadowsocks-go@v1.10.2-0.20240315062903-143a773533f1/http/server.go (about) 1 package http 2 3 import ( 4 "bufio" 5 "errors" 6 "fmt" 7 "io" 8 "net/http" 9 "net/url" 10 "strings" 11 "time" 12 _ "unsafe" 13 14 "github.com/database64128/shadowsocks-go/conn" 15 "github.com/database64128/shadowsocks-go/direct" 16 "github.com/database64128/shadowsocks-go/pipe" 17 "github.com/database64128/shadowsocks-go/zerocopy" 18 "go.uber.org/zap" 19 ) 20 21 //go:linkname readRequest net/http.readRequest 22 func readRequest(b *bufio.Reader) (req *http.Request, err error) 23 24 // NewHttpStreamServerReadWriter handles a HTTP request from rw and wraps rw into a ReadWriter ready for use. 25 func NewHttpStreamServerReadWriter(rw zerocopy.DirectReadWriteCloser, logger *zap.Logger) (*direct.DirectStreamReadWriter, conn.Addr, error) { 26 rwbr := bufio.NewReader(rw) 27 req, err := readRequest(rwbr) 28 if err != nil { 29 return nil, conn.Addr{}, err 30 } 31 32 // Host -> targetAddr 33 targetAddr, err := hostHeaderToAddr(req.Host) 34 if err != nil { 35 send418(rw) 36 return nil, conn.Addr{}, err 37 } 38 39 // Fast-track CONNECT. 40 if req.Method == http.MethodConnect { 41 if _, err = fmt.Fprintf(rw, "HTTP/1.1 200 OK\r\nDate: %s\r\n\r\n", time.Now().UTC().Format(http.TimeFormat)); err != nil { 42 return nil, conn.Addr{}, err 43 } 44 return direct.NewDirectStreamReadWriter(rw), targetAddr, nil 45 } 46 47 // Set up pipes. 48 pl, pr := pipe.NewDuplexPipe() 49 50 // Spin up a goroutine to write processed requests to pl 51 // and read responses from pl. 52 go func() { 53 var rerr, werr error 54 55 plbr := bufio.NewReader(pl) 56 plbw := bufio.NewWriter(pl) 57 rwbw := bufio.NewWriter(rw) 58 59 for { 60 // Delete hop-by-hop headers specified in Connection. 61 connectionHeader := req.Header["Connection"] 62 for i := range connectionHeader { 63 req.Header.Del(connectionHeader[i]) 64 } 65 delete(req.Header, "Connection") 66 67 delete(req.Header, "Proxy-Connection") 68 69 if ce := logger.Check(zap.DebugLevel, "Writing HTTP request"); ce != nil { 70 ce.Write( 71 zap.String("proto", req.Proto), 72 zap.String("method", req.Method), 73 zap.String("url", req.RequestURI), 74 ) 75 } 76 77 // Write request. 78 if werr = req.Write(plbw); werr != nil { 79 werr = fmt.Errorf("failed to write HTTP request: %w", werr) 80 break 81 } 82 83 // Flush request. 84 if werr = plbw.Flush(); werr != nil { 85 werr = fmt.Errorf("failed to flush HTTP request: %w", werr) 86 break 87 } 88 89 var resp *http.Response 90 91 // Read response. 92 resp, rerr = http.ReadResponse(plbr, req) 93 if rerr != nil { 94 rerr = fmt.Errorf("failed to read HTTP response: %w", rerr) 95 break 96 } 97 98 // Add Connection: close if response is 301, 302, or 307, 99 // and Location points to a different host. 100 switch resp.StatusCode { 101 case http.StatusMovedPermanently, http.StatusFound, http.StatusTemporaryRedirect: 102 location := resp.Header["Location"] 103 104 if ce := logger.Check(zap.DebugLevel, "Checking HTTP 3xx response Location header"); ce != nil { 105 ce.Write( 106 zap.String("proto", resp.Proto), 107 zap.String("status", resp.Status), 108 zap.Strings("location", location), 109 ) 110 } 111 112 if len(location) != 1 { 113 break 114 } 115 116 url, err := url.Parse(location[0]) 117 if err != nil { 118 break 119 } 120 121 switch url.Host { 122 case req.Host, "": 123 default: 124 resp.Close = true 125 } 126 } 127 128 if ce := logger.Check(zap.DebugLevel, "Writing HTTP response"); ce != nil { 129 ce.Write( 130 zap.String("proto", resp.Proto), 131 zap.String("status", resp.Status), 132 ) 133 } 134 135 // Write response. 136 if rerr = resp.Write(rwbw); rerr != nil { 137 rerr = fmt.Errorf("failed to write HTTP response: %w", rerr) 138 break 139 } 140 141 // Flush response. 142 if rerr = rwbw.Flush(); rerr != nil { 143 rerr = fmt.Errorf("failed to flush HTTP response: %w", rerr) 144 break 145 } 146 147 // Stop relaying if either client or server indicates that the connection should be closed. 148 // 149 // RFC 7230 section 6.6 says: 150 // The server SHOULD send a "close" connection option in its final response on that connection. 151 // 152 // It's not a "MUST", so we check both. 153 if req.Close || resp.Close { 154 break 155 } 156 157 // Read request. 158 req, werr = readRequest(rwbr) 159 if werr != nil { 160 if werr != io.EOF { 161 werr = fmt.Errorf("failed to read HTTP request: %w", werr) 162 } 163 break 164 } 165 } 166 167 pl.CloseReadWithError(rerr) 168 pl.CloseWriteWithError(werr) 169 rw.Close() 170 }() 171 172 // Wrap pr into a direct stream ReadWriter. 173 return direct.NewDirectStreamReadWriter(pr), targetAddr, nil 174 } 175 176 var errEmptyHostHeader = errors.New("empty host header") 177 178 // hostHeaderToAddr parses the Host header into an address. 179 // 180 // Host may be in any of the following forms: 181 // - example.com 182 // - example.com:443 183 // - 1.1.1.1 184 // - 1.1.1.1:443 185 // - [2606:4700:4700::1111] 186 // - [2606:4700:4700::1111]:443 187 func hostHeaderToAddr(host string) (conn.Addr, error) { 188 switch { 189 case len(host) == 0: 190 return conn.Addr{}, errEmptyHostHeader 191 case strings.IndexByte(host, ':') == -1: 192 return conn.AddrFromHostPort(host, 80) 193 case host[0] == '[' && host[len(host)-1] == ']': 194 return conn.AddrFromHostPort(host[1:len(host)-1], 80) 195 default: 196 return conn.ParseAddr(host) 197 } 198 } 199 200 func send418(w io.Writer) error { 201 _, err := fmt.Fprint(w, "HTTP/1.1 418 I'm a teapot\r\n\r\n") 202 return err 203 }