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