github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/transport/internet/request/roundtripper/httprt/httprt.go (about)

     1  package httprt
     2  
     3  //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen
     4  
     5  import (
     6  	"bytes"
     7  	"context"
     8  	"encoding/base64"
     9  	"io"
    10  	gonet "net"
    11  	"net/http"
    12  	"time"
    13  
    14  	"github.com/v2fly/v2ray-core/v5/transport/internet/transportcommon"
    15  
    16  	"github.com/v2fly/v2ray-core/v5/common"
    17  	"github.com/v2fly/v2ray-core/v5/common/net"
    18  	"github.com/v2fly/v2ray-core/v5/transport/internet/request"
    19  )
    20  
    21  func newHTTPRoundTripperClient(ctx context.Context, config *ClientConfig) request.RoundTripperClient {
    22  	_ = ctx
    23  	return &httpTripperClient{config: config}
    24  }
    25  
    26  type httpTripperClient struct {
    27  	httpRTT  http.RoundTripper
    28  	config   *ClientConfig
    29  	assembly request.TransportClientAssembly
    30  }
    31  
    32  type unimplementedBackDrop struct{}
    33  
    34  func (u unimplementedBackDrop) RoundTrip(r *http.Request) (*http.Response, error) {
    35  	return nil, newError("unimplemented")
    36  }
    37  
    38  func (h *httpTripperClient) OnTransportClientAssemblyReady(assembly request.TransportClientAssembly) {
    39  	h.assembly = assembly
    40  }
    41  
    42  func (h *httpTripperClient) RoundTrip(ctx context.Context, req request.Request, opts ...request.RoundTripperOption) (resp request.Response, err error) {
    43  	if h.httpRTT == nil {
    44  		h.httpRTT = transportcommon.NewALPNAwareHTTPRoundTripper(ctx, func(ctx context.Context, addr string) (gonet.Conn, error) {
    45  			return h.assembly.AutoImplDialer().Dial(ctx)
    46  		}, unimplementedBackDrop{})
    47  	}
    48  
    49  	connectionTagStr := base64.RawURLEncoding.EncodeToString(req.ConnectionTag)
    50  
    51  	httpRequest, err := http.NewRequest("POST", h.config.Http.UrlPrefix+h.config.Http.Path, bytes.NewReader(req.Data))
    52  	if err != nil {
    53  		return
    54  	}
    55  
    56  	httpRequest.Header.Set("X-Session-ID", connectionTagStr)
    57  
    58  	httpResp, err := h.httpRTT.RoundTrip(httpRequest)
    59  	if err != nil {
    60  		return
    61  	}
    62  	defer httpResp.Body.Close()
    63  	result, err := io.ReadAll(httpResp.Body)
    64  	if err != nil {
    65  		return
    66  	}
    67  	return request.Response{Data: result}, err
    68  }
    69  
    70  func newHTTPRoundTripperServer(ctx context.Context, config *ServerConfig) request.RoundTripperServer {
    71  	return &httpTripperServer{ctx: ctx, config: config}
    72  }
    73  
    74  type httpTripperServer struct {
    75  	ctx      context.Context
    76  	listener net.Listener
    77  	assembly request.TransportServerAssembly
    78  
    79  	config *ServerConfig
    80  }
    81  
    82  func (h *httpTripperServer) OnTransportServerAssemblyReady(assembly request.TransportServerAssembly) {
    83  	h.assembly = assembly
    84  }
    85  
    86  func (h *httpTripperServer) ServeHTTP(writer http.ResponseWriter, r *http.Request) {
    87  	h.onRequest(writer, r)
    88  }
    89  
    90  func (h *httpTripperServer) onRequest(resp http.ResponseWriter, req *http.Request) {
    91  	tail := req.Header.Get("X-Session-ID")
    92  	data := []byte(tail)
    93  	if !h.config.NoDecodingSessionTag {
    94  		decodedData, err := base64.RawURLEncoding.DecodeString(tail)
    95  		if err != nil {
    96  			newError("unable to decode tag").Base(err).AtInfo().WriteToLog()
    97  			return
    98  		}
    99  		data = decodedData
   100  	}
   101  	body, err := io.ReadAll(req.Body)
   102  	req.Body.Close()
   103  	if err != nil {
   104  		newError("unable to read body").Base(err).AtInfo().WriteToLog()
   105  	}
   106  	recvResp, err := h.assembly.TripperReceiver().OnRoundTrip(h.ctx, request.Request{Data: body, ConnectionTag: data})
   107  	if err != nil {
   108  		newError("unable to process roundtrip").Base(err).AtInfo().WriteToLog()
   109  	}
   110  	_, err = io.Copy(resp, bytes.NewReader(recvResp.Data))
   111  	if err != nil {
   112  		newError("unable to send response").Base(err).AtInfo().WriteToLog()
   113  	}
   114  }
   115  
   116  func (h *httpTripperServer) Start() error {
   117  	listener, err := h.assembly.AutoImplListener().Listen(h.ctx)
   118  	if err != nil {
   119  		return newError("unable to create a listener for http tripper server").Base(err)
   120  	}
   121  	h.listener = listener
   122  	go func() {
   123  		httpServer := http.Server{
   124  			ReadHeaderTimeout: 15 * time.Second,
   125  			ReadTimeout:       15 * time.Second,
   126  			WriteTimeout:      10 * time.Second,
   127  			IdleTimeout:       30 * time.Second,
   128  		}
   129  		httpServer.Handler = h
   130  		err := httpServer.Serve(h.listener)
   131  		if err != nil {
   132  			newError("unable to serve listener for http tripper server").Base(err).WriteToLog()
   133  		}
   134  	}()
   135  	return nil
   136  }
   137  
   138  func (h *httpTripperServer) Close() error {
   139  	return h.listener.Close()
   140  }
   141  
   142  func init() {
   143  	common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
   144  		clientConfig, ok := config.(*ClientConfig)
   145  		if !ok {
   146  			return nil, newError("not a ClientConfig")
   147  		}
   148  		return newHTTPRoundTripperClient(ctx, clientConfig), nil
   149  	}))
   150  	common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
   151  		serverConfig, ok := config.(*ServerConfig)
   152  		if !ok {
   153  			return nil, newError("not a ServerConfig")
   154  		}
   155  		return newHTTPRoundTripperServer(ctx, serverConfig), nil
   156  	}))
   157  }