github.com/v2fly/v2ray-core/v4@v4.45.2/transport/internet/http/hub.go (about)

     1  //go:build !confonly
     2  // +build !confonly
     3  
     4  package http
     5  
     6  import (
     7  	"context"
     8  	"io"
     9  	"net/http"
    10  	"strings"
    11  	"time"
    12  
    13  	"golang.org/x/net/http2"
    14  	"golang.org/x/net/http2/h2c"
    15  
    16  	"github.com/v2fly/v2ray-core/v4/common"
    17  	"github.com/v2fly/v2ray-core/v4/common/net"
    18  	http_proto "github.com/v2fly/v2ray-core/v4/common/protocol/http"
    19  	"github.com/v2fly/v2ray-core/v4/common/serial"
    20  	"github.com/v2fly/v2ray-core/v4/common/session"
    21  	"github.com/v2fly/v2ray-core/v4/common/signal/done"
    22  	"github.com/v2fly/v2ray-core/v4/transport/internet"
    23  	"github.com/v2fly/v2ray-core/v4/transport/internet/tls"
    24  )
    25  
    26  type Listener struct {
    27  	server  *http.Server
    28  	handler internet.ConnHandler
    29  	local   net.Addr
    30  	config  *Config
    31  	locker  *internet.FileLocker // for unix domain socket
    32  }
    33  
    34  func (l *Listener) Addr() net.Addr {
    35  	return l.local
    36  }
    37  
    38  func (l *Listener) Close() error {
    39  	if l.locker != nil {
    40  		l.locker.Release()
    41  	}
    42  	return l.server.Close()
    43  }
    44  
    45  type flushWriter struct {
    46  	w io.Writer
    47  	d *done.Instance
    48  }
    49  
    50  func (fw flushWriter) Write(p []byte) (n int, err error) {
    51  	if fw.d.Done() {
    52  		return 0, io.ErrClosedPipe
    53  	}
    54  
    55  	n, err = fw.w.Write(p)
    56  	if f, ok := fw.w.(http.Flusher); ok {
    57  		f.Flush()
    58  	}
    59  	return
    60  }
    61  
    62  func (l *Listener) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
    63  	host := request.Host
    64  	if !l.config.isValidHost(host) {
    65  		writer.WriteHeader(404)
    66  		return
    67  	}
    68  	path := l.config.getNormalizedPath()
    69  	if !strings.HasPrefix(request.URL.Path, path) {
    70  		writer.WriteHeader(404)
    71  		return
    72  	}
    73  
    74  	writer.Header().Set("Cache-Control", "no-store")
    75  
    76  	for _, httpHeader := range l.config.Header {
    77  		for _, httpHeaderValue := range httpHeader.Value {
    78  			writer.Header().Set(httpHeader.Name, httpHeaderValue)
    79  		}
    80  	}
    81  
    82  	writer.WriteHeader(200)
    83  	if f, ok := writer.(http.Flusher); ok {
    84  		f.Flush()
    85  	}
    86  
    87  	remoteAddr := l.Addr()
    88  	dest, err := net.ParseDestination(request.RemoteAddr)
    89  	if err != nil {
    90  		newError("failed to parse request remote addr: ", request.RemoteAddr).Base(err).WriteToLog()
    91  	} else {
    92  		remoteAddr = &net.TCPAddr{
    93  			IP:   dest.Address.IP(),
    94  			Port: int(dest.Port),
    95  		}
    96  	}
    97  
    98  	forwardedAddress := http_proto.ParseXForwardedFor(request.Header)
    99  	if len(forwardedAddress) > 0 && forwardedAddress[0].Family().IsIP() {
   100  		remoteAddr = &net.TCPAddr{
   101  			IP:   forwardedAddress[0].IP(),
   102  			Port: 0,
   103  		}
   104  	}
   105  
   106  	done := done.New()
   107  	conn := net.NewConnection(
   108  		net.ConnectionOutput(request.Body),
   109  		net.ConnectionInput(flushWriter{w: writer, d: done}),
   110  		net.ConnectionOnClose(common.ChainedClosable{done, request.Body}),
   111  		net.ConnectionLocalAddr(l.Addr()),
   112  		net.ConnectionRemoteAddr(remoteAddr),
   113  	)
   114  	l.handler(conn)
   115  	<-done.Wait()
   116  }
   117  
   118  func Listen(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) {
   119  	httpSettings := streamSettings.ProtocolSettings.(*Config)
   120  	var listener *Listener
   121  	if port == net.Port(0) { // unix
   122  		listener = &Listener{
   123  			handler: handler,
   124  			local: &net.UnixAddr{
   125  				Name: address.Domain(),
   126  				Net:  "unix",
   127  			},
   128  			config: httpSettings,
   129  		}
   130  	} else { // tcp
   131  		listener = &Listener{
   132  			handler: handler,
   133  			local: &net.TCPAddr{
   134  				IP:   address.IP(),
   135  				Port: int(port),
   136  			},
   137  			config: httpSettings,
   138  		}
   139  	}
   140  
   141  	var server *http.Server
   142  	config := tls.ConfigFromStreamSettings(streamSettings)
   143  	if config == nil {
   144  		h2s := &http2.Server{}
   145  
   146  		server = &http.Server{
   147  			Addr:              serial.Concat(address, ":", port),
   148  			Handler:           h2c.NewHandler(listener, h2s),
   149  			ReadHeaderTimeout: time.Second * 4,
   150  		}
   151  	} else {
   152  		server = &http.Server{
   153  			Addr:              serial.Concat(address, ":", port),
   154  			TLSConfig:         config.GetTLSConfig(tls.WithNextProto("h2")),
   155  			Handler:           listener,
   156  			ReadHeaderTimeout: time.Second * 4,
   157  		}
   158  	}
   159  
   160  	if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol {
   161  		newError("accepting PROXY protocol").AtWarning().WriteToLog(session.ExportIDToError(ctx))
   162  	}
   163  
   164  	listener.server = server
   165  	go func() {
   166  		var streamListener net.Listener
   167  		var err error
   168  		if port == net.Port(0) { // unix
   169  			streamListener, err = internet.ListenSystem(ctx, &net.UnixAddr{
   170  				Name: address.Domain(),
   171  				Net:  "unix",
   172  			}, streamSettings.SocketSettings)
   173  			if err != nil {
   174  				newError("failed to listen on ", address).Base(err).AtError().WriteToLog(session.ExportIDToError(ctx))
   175  				return
   176  			}
   177  			locker := ctx.Value(address.Domain())
   178  			if locker != nil {
   179  				listener.locker = locker.(*internet.FileLocker)
   180  			}
   181  		} else { // tcp
   182  			streamListener, err = internet.ListenSystem(ctx, &net.TCPAddr{
   183  				IP:   address.IP(),
   184  				Port: int(port),
   185  			}, streamSettings.SocketSettings)
   186  			if err != nil {
   187  				newError("failed to listen on ", address, ":", port).Base(err).AtError().WriteToLog(session.ExportIDToError(ctx))
   188  				return
   189  			}
   190  		}
   191  
   192  		if config == nil {
   193  			err = server.Serve(streamListener)
   194  			if err != nil {
   195  				newError("stopping serving H2C").Base(err).WriteToLog(session.ExportIDToError(ctx))
   196  			}
   197  		} else {
   198  			err = server.ServeTLS(streamListener, "", "")
   199  			if err != nil {
   200  				newError("stopping serving TLS").Base(err).WriteToLog(session.ExportIDToError(ctx))
   201  			}
   202  		}
   203  	}()
   204  
   205  	return listener, nil
   206  }
   207  
   208  func init() {
   209  	common.Must(internet.RegisterTransportListener(protocolName, Listen))
   210  }