github.com/database64128/shadowsocks-go@v1.10.2-0.20240315062903-143a773533f1/http/client.go (about)

     1  package http
     2  
     3  import (
     4  	"bufio"
     5  	"errors"
     6  	"fmt"
     7  	"net/http"
     8  
     9  	"github.com/database64128/shadowsocks-go/conn"
    10  	"github.com/database64128/shadowsocks-go/direct"
    11  	"github.com/database64128/shadowsocks-go/zerocopy"
    12  )
    13  
    14  var ErrServerSpokeFirst = errors.New("server-speaks-first protocols are not supported by this HTTP proxy client implementation")
    15  
    16  // NewHttpStreamClientReadWriter writes a HTTP/1.1 CONNECT request to rw and wraps rw into a ReadWriter ready for use.
    17  func NewHttpStreamClientReadWriter(rw zerocopy.DirectReadWriteCloser, targetAddr conn.Addr) (*direct.DirectStreamReadWriter, error) {
    18  	targetAddress := targetAddr.String()
    19  
    20  	// Write CONNECT.
    21  	_, err := fmt.Fprintf(rw, "CONNECT %s HTTP/1.1\r\nHost: %s\r\nUser-Agent: shadowsocks-go/0.0.0\r\nProxy-Connection: Keep-Alive\r\n\r\n", targetAddress, targetAddress)
    22  	if err != nil {
    23  		return nil, err
    24  	}
    25  
    26  	// Read response.
    27  	br := bufio.NewReader(rw)
    28  	resp, err := http.ReadResponse(br, nil)
    29  	if err != nil {
    30  		return nil, err
    31  	}
    32  	if resp.StatusCode != 200 {
    33  		return nil, fmt.Errorf("HTTP %s", resp.Status)
    34  	}
    35  
    36  	// Check if server spoke first.
    37  	if br.Buffered() > 0 {
    38  		return nil, ErrServerSpokeFirst
    39  	}
    40  
    41  	return direct.NewDirectStreamReadWriter(rw), nil
    42  }