github.com/xtls/xray-core@v1.8.12-0.20240518155711-3168d27b0bdb/transport/internet/httpupgrade/hub.go (about) 1 package httpupgrade 2 3 import ( 4 "bufio" 5 "context" 6 "crypto/tls" 7 "net/http" 8 "strings" 9 10 "github.com/xtls/xray-core/common" 11 "github.com/xtls/xray-core/common/net" 12 http_proto "github.com/xtls/xray-core/common/protocol/http" 13 "github.com/xtls/xray-core/common/session" 14 "github.com/xtls/xray-core/transport/internet" 15 "github.com/xtls/xray-core/transport/internet/stat" 16 v2tls "github.com/xtls/xray-core/transport/internet/tls" 17 ) 18 19 type server struct { 20 config *Config 21 addConn internet.ConnHandler 22 innnerListener net.Listener 23 } 24 25 func (s *server) Close() error { 26 return s.innnerListener.Close() 27 } 28 29 func (s *server) Addr() net.Addr { 30 return nil 31 } 32 33 func (s *server) Handle(conn net.Conn) (stat.Connection, error) { 34 connReader := bufio.NewReader(conn) 35 req, err := http.ReadRequest(connReader) 36 if err != nil { 37 return nil, err 38 } 39 40 if s.config != nil { 41 host := req.Host 42 if len(s.config.Host) > 0 && host != s.config.Host { 43 return nil, newError("bad host: ", host) 44 } 45 path := s.config.GetNormalizedPath() 46 if req.URL.Path != path { 47 return nil, newError("bad path: ", req.URL.Path) 48 } 49 } 50 51 connection := strings.ToLower(req.Header.Get("Connection")) 52 upgrade := strings.ToLower(req.Header.Get("Upgrade")) 53 if connection != "upgrade" || upgrade != "websocket" { 54 _ = conn.Close() 55 return nil, newError("unrecognized request") 56 } 57 resp := &http.Response{ 58 Status: "101 Switching Protocols", 59 StatusCode: 101, 60 Proto: "HTTP/1.1", 61 ProtoMajor: 1, 62 ProtoMinor: 1, 63 Header: http.Header{}, 64 } 65 resp.Header.Set("Connection", "upgrade") 66 resp.Header.Set("Upgrade", "websocket") 67 err = resp.Write(conn) 68 if err != nil { 69 _ = conn.Close() 70 return nil, err 71 } 72 73 forwardedAddrs := http_proto.ParseXForwardedFor(req.Header) 74 remoteAddr := conn.RemoteAddr() 75 if len(forwardedAddrs) > 0 && forwardedAddrs[0].Family().IsIP() { 76 remoteAddr = &net.TCPAddr{ 77 IP: forwardedAddrs[0].IP(), 78 Port: int(0), 79 } 80 } 81 if remoteAddr == nil { 82 return nil, newError("remoteAddr is nil") 83 } 84 85 conn = newConnection(conn, remoteAddr) 86 return stat.Connection(conn), nil 87 } 88 89 func (s *server) keepAccepting() { 90 for { 91 conn, err := s.innnerListener.Accept() 92 if err != nil { 93 return 94 } 95 handledConn, err := s.Handle(conn) 96 if err != nil { 97 newError("failed to handle request").Base(err).WriteToLog() 98 continue 99 } 100 s.addConn(handledConn) 101 } 102 } 103 104 func listenHTTPUpgrade(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) { 105 transportConfiguration := streamSettings.ProtocolSettings.(*Config) 106 if transportConfiguration != nil { 107 if streamSettings.SocketSettings == nil { 108 streamSettings.SocketSettings = &internet.SocketConfig{} 109 } 110 streamSettings.SocketSettings.AcceptProxyProtocol = transportConfiguration.AcceptProxyProtocol || streamSettings.SocketSettings.AcceptProxyProtocol 111 } 112 var listener net.Listener 113 var err error 114 if port == net.Port(0) { // unix 115 listener, err = internet.ListenSystem(ctx, &net.UnixAddr{ 116 Name: address.Domain(), 117 Net: "unix", 118 }, streamSettings.SocketSettings) 119 if err != nil { 120 return nil, newError("failed to listen unix domain socket(for HttpUpgrade) on ", address).Base(err) 121 } 122 newError("listening unix domain socket(for HttpUpgrade) on ", address).WriteToLog(session.ExportIDToError(ctx)) 123 } else { // tcp 124 listener, err = internet.ListenSystem(ctx, &net.TCPAddr{ 125 IP: address.IP(), 126 Port: int(port), 127 }, streamSettings.SocketSettings) 128 if err != nil { 129 return nil, newError("failed to listen TCP(for HttpUpgrade) on ", address, ":", port).Base(err) 130 } 131 newError("listening TCP(for HttpUpgrade) on ", address, ":", port).WriteToLog(session.ExportIDToError(ctx)) 132 } 133 134 if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol { 135 newError("accepting PROXY protocol").AtWarning().WriteToLog(session.ExportIDToError(ctx)) 136 } 137 138 if config := v2tls.ConfigFromStreamSettings(streamSettings); config != nil { 139 if tlsConfig := config.GetTLSConfig(); tlsConfig != nil { 140 listener = tls.NewListener(listener, tlsConfig) 141 } 142 } 143 144 serverInstance := &server{ 145 config: transportConfiguration, 146 addConn: addConn, 147 innnerListener: listener, 148 } 149 go serverInstance.keepAccepting() 150 return serverInstance, nil 151 } 152 153 func init() { 154 common.Must(internet.RegisterTransportListener(protocolName, listenHTTPUpgrade)) 155 }