github.com/xxf098/lite-proxy@v0.15.1-0.20230422081941-12c69f323218/tunnel/http/server.go (about)

     1  package http
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"net"
     7  
     8  	"bufio"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"net/http"
    13  	"strings"
    14  
    15  	// "github.com/p4gefau1t/trojan-go/common"
    16  	"github.com/xxf098/lite-proxy/common"
    17  	"github.com/xxf098/lite-proxy/log"
    18  	"github.com/xxf098/lite-proxy/tunnel"
    19  )
    20  
    21  type ConnectConn struct {
    22  	net.Conn
    23  	metadata *tunnel.Metadata
    24  }
    25  
    26  func (c *ConnectConn) Metadata() *tunnel.Metadata {
    27  	return c.metadata
    28  }
    29  
    30  type OtherConn struct {
    31  	net.Conn
    32  	metadata   *tunnel.Metadata //fixed
    33  	reqReader  *io.PipeReader
    34  	respWriter *io.PipeWriter
    35  	ctx        context.Context
    36  	cancel     context.CancelFunc
    37  }
    38  
    39  func (c *OtherConn) Metadata() *tunnel.Metadata {
    40  	return c.metadata
    41  }
    42  
    43  type Server struct {
    44  	underlay tunnel.Server
    45  	connChan chan tunnel.Conn
    46  	ctx      context.Context
    47  	cancel   context.CancelFunc
    48  }
    49  
    50  func (s *Server) acceptLoop() {
    51  	for {
    52  		conn, err := s.underlay.AcceptConn(&Tunnel{})
    53  		if err != nil {
    54  			select {
    55  			case <-s.ctx.Done():
    56  				log.Error(common.NewError("http closed"))
    57  				return
    58  			default:
    59  				log.Error(common.NewError("http failed to accept connection").Base(err))
    60  				continue
    61  			}
    62  		}
    63  
    64  		go func(conn net.Conn) {
    65  			reqBufReader := bufio.NewReader(ioutil.NopCloser(conn))
    66  			req, err := http.ReadRequest(reqBufReader)
    67  			if err != nil {
    68  				log.Error(common.NewError("not a valid http request").Base(err))
    69  				return
    70  			}
    71  
    72  			if strings.ToUpper(req.Method) == "CONNECT" { // CONNECT
    73  				addr, err := tunnel.NewAddressFromAddr("tcp", req.Host)
    74  				if err != nil {
    75  					log.Error(common.NewError("invalid http dest address").Base(err))
    76  					conn.Close()
    77  					return
    78  				}
    79  				resp := fmt.Sprintf("HTTP/%d.%d 200 Connection established\r\n\r\n", req.ProtoMajor, req.ProtoMinor)
    80  				_, err = conn.Write([]byte(resp))
    81  				if err != nil {
    82  					log.Error(common.NewError("http failed to respond connect request"))
    83  					conn.Close()
    84  					return
    85  				}
    86  				s.connChan <- &ConnectConn{
    87  					Conn: conn,
    88  					metadata: &tunnel.Metadata{
    89  						Address: addr,
    90  					},
    91  				}
    92  			} else { // GET, POST, PUT...
    93  				defer conn.Close()
    94  				for {
    95  					reqReader, reqWriter := io.Pipe()
    96  					respReader, respWriter := io.Pipe()
    97  					var addr *tunnel.Address
    98  					if addr, err = tunnel.NewAddressFromAddr("tcp", req.Host); err != nil {
    99  						addr = tunnel.NewAddressFromHostPort("tcp", req.Host, 80)
   100  					}
   101  					log.D("http dest", addr)
   102  
   103  					ctx, cancel := context.WithCancel(s.ctx)
   104  					newConn := &OtherConn{
   105  						Conn: conn,
   106  						metadata: &tunnel.Metadata{
   107  							Address: addr,
   108  						},
   109  						ctx:        ctx,
   110  						cancel:     cancel,
   111  						reqReader:  reqReader,
   112  						respWriter: respWriter,
   113  					}
   114  					s.connChan <- newConn // pass this http session connection to proxy.RelayConn
   115  
   116  					err = req.Write(reqWriter) // write request to the remote
   117  					if err != nil {
   118  						log.Error(common.NewError("http failed to write http request").Base(err))
   119  						return
   120  					}
   121  
   122  					respBufReader := bufio.NewReader(ioutil.NopCloser(respReader)) // read response from the remote
   123  					resp, err := http.ReadResponse(respBufReader, req)
   124  					if err != nil {
   125  						log.Error(common.NewError("http failed to read http response").Base(err))
   126  						return
   127  					}
   128  					err = resp.Write(conn) // send the response back to the local
   129  					if err != nil {
   130  						log.Error(common.NewError("http failed to write the response back").Base(err))
   131  						return
   132  					}
   133  					newConn.Close()
   134  					req.Body.Close()
   135  					resp.Body.Close()
   136  
   137  					req, err = http.ReadRequest(reqBufReader) //read the next http request from local
   138  					if err != nil {
   139  						log.Error(common.NewError("http failed to the read request from local").Base(err))
   140  						return
   141  					}
   142  				}
   143  			}
   144  		}(conn)
   145  	}
   146  }
   147  
   148  func (s *Server) AcceptConn(tunnel.Tunnel) (tunnel.Conn, error) {
   149  	select {
   150  	case conn := <-s.connChan:
   151  		return conn, nil
   152  	case <-s.ctx.Done():
   153  		return nil, errors.New("http server closed")
   154  	}
   155  }
   156  
   157  func (s *Server) AcceptPacket(tunnel.Tunnel) (tunnel.PacketConn, error) {
   158  	<-s.ctx.Done()
   159  	return nil, errors.New("http server closed")
   160  }
   161  
   162  func (s *Server) Close() error {
   163  	s.cancel()
   164  	return s.underlay.Close()
   165  }
   166  
   167  func NewServer(ctx context.Context, underlay tunnel.Server) (*Server, error) {
   168  	ctx, cancel := context.WithCancel(ctx)
   169  	server := &Server{
   170  		underlay: underlay,
   171  		connChan: make(chan tunnel.Conn, 32),
   172  		ctx:      ctx,
   173  		cancel:   cancel,
   174  	}
   175  	go server.acceptLoop()
   176  	log.D("http server created")
   177  	return server, nil
   178  }