diff --git a/infra/conf/transport.go b/infra/conf/transport.go index fa8a3886..b7969e47 100644 --- a/infra/conf/transport.go +++ b/infra/conf/transport.go @@ -7,14 +7,15 @@ import ( ) type TransportConfig struct { - TCPConfig *TCPConfig `json:"tcpSettings"` - KCPConfig *KCPConfig `json:"kcpSettings"` - WSConfig *WebSocketConfig `json:"wsSettings"` - HTTPConfig *HTTPConfig `json:"httpSettings"` - DSConfig *DomainSocketConfig `json:"dsSettings"` - QUICConfig *QUICConfig `json:"quicSettings"` - GRPCConfig *GRPCConfig `json:"grpcSettings"` - GUNConfig *GRPCConfig `json:"gunSettings"` + TCPConfig *TCPConfig `json:"tcpSettings"` + KCPConfig *KCPConfig `json:"kcpSettings"` + WSConfig *WebSocketConfig `json:"wsSettings"` + HTTPConfig *HTTPConfig `json:"httpSettings"` + DSConfig *DomainSocketConfig `json:"dsSettings"` + QUICConfig *QUICConfig `json:"quicSettings"` + GRPCConfig *GRPCConfig `json:"grpcSettings"` + GUNConfig *GRPCConfig `json:"gunSettings"` + HTTPUPGRADEConfig *HttpUpgradeConfig `json:"httpupgradeSettings"` } // Build implements Buildable. @@ -101,5 +102,16 @@ func (c *TransportConfig) Build() (*global.Config, error) { }) } + if c.HTTPUPGRADEConfig != nil { + hs, err := c.HTTPUPGRADEConfig.Build() + if err != nil { + return nil, newError("failed to build HttpUpgrade config").Base(err) + } + config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ + ProtocolName: "httpupgrade", + Settings: serial.ToTypedMessage(hs), + }) + } + return config, nil } diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 8c113e92..9ab34cf5 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -19,6 +19,7 @@ import ( "github.com/xtls/xray-core/transport/internet/domainsocket" httpheader "github.com/xtls/xray-core/transport/internet/headers/http" "github.com/xtls/xray-core/transport/internet/http" + "github.com/xtls/xray-core/transport/internet/httpupgrade" "github.com/xtls/xray-core/transport/internet/kcp" "github.com/xtls/xray-core/transport/internet/quic" "github.com/xtls/xray-core/transport/internet/reality" @@ -181,6 +182,24 @@ func (c *WebSocketConfig) Build() (proto.Message, error) { return config, nil } +type HttpUpgradeConfig struct { + Path string `json:"path"` + Host string `json:"host"` + AcceptProxyProtocol bool `json:"acceptProxyProtocol"` +} + +// Build implements Buildable. +func (c *HttpUpgradeConfig) Build() (proto.Message, error) { + config := &httpupgrade.Config{ + Path: c.Path, + Host: c.Host, + } + if c.AcceptProxyProtocol { + config.AcceptProxyProtocol = c.AcceptProxyProtocol + } + return config, nil +} + type HTTPConfig struct { Host *StringList `json:"host"` Path string `json:"path"` @@ -606,6 +625,8 @@ func (p TransportProtocol) Build() (string, error) { return "quic", nil case "grpc", "gun": return "grpc", nil + case "httpupgrade": + return "httpupgrade", nil default: return "", newError("Config: unknown transport protocol: ", p) } @@ -706,19 +727,20 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) { } type StreamConfig struct { - Network *TransportProtocol `json:"network"` - Security string `json:"security"` - TLSSettings *TLSConfig `json:"tlsSettings"` - REALITYSettings *REALITYConfig `json:"realitySettings"` - TCPSettings *TCPConfig `json:"tcpSettings"` - KCPSettings *KCPConfig `json:"kcpSettings"` - WSSettings *WebSocketConfig `json:"wsSettings"` - HTTPSettings *HTTPConfig `json:"httpSettings"` - DSSettings *DomainSocketConfig `json:"dsSettings"` - QUICSettings *QUICConfig `json:"quicSettings"` - SocketSettings *SocketConfig `json:"sockopt"` - GRPCConfig *GRPCConfig `json:"grpcSettings"` - GUNConfig *GRPCConfig `json:"gunSettings"` + Network *TransportProtocol `json:"network"` + Security string `json:"security"` + TLSSettings *TLSConfig `json:"tlsSettings"` + REALITYSettings *REALITYConfig `json:"realitySettings"` + TCPSettings *TCPConfig `json:"tcpSettings"` + KCPSettings *KCPConfig `json:"kcpSettings"` + WSSettings *WebSocketConfig `json:"wsSettings"` + HTTPSettings *HTTPConfig `json:"httpSettings"` + DSSettings *DomainSocketConfig `json:"dsSettings"` + QUICSettings *QUICConfig `json:"quicSettings"` + SocketSettings *SocketConfig `json:"sockopt"` + GRPCConfig *GRPCConfig `json:"grpcSettings"` + GUNConfig *GRPCConfig `json:"gunSettings"` + HTTPUPGRADESettings *HttpUpgradeConfig `json:"httpupgradeSettings"` } // Build implements Buildable. @@ -839,6 +861,16 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) { Settings: serial.ToTypedMessage(gs), }) } + if c.HTTPUPGRADESettings != nil { + hs, err := c.HTTPUPGRADESettings.Build() + if err != nil { + return nil, newError("Failed to build HttpUpgrade config.").Base(err) + } + config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ + ProtocolName: "httpupgrade", + Settings: serial.ToTypedMessage(hs), + }) + } if c.SocketSettings != nil { ss, err := c.SocketSettings.Build() if err != nil { diff --git a/infra/conf/xray.go b/infra/conf/xray.go index 2144815a..c99dca59 100644 --- a/infra/conf/xray.go +++ b/infra/conf/xray.go @@ -544,6 +544,9 @@ func applyTransportConfig(s *StreamConfig, t *TransportConfig) { if s.DSSettings == nil { s.DSSettings = t.DSConfig } + if s.HTTPUPGRADESettings == nil { + s.HTTPUPGRADESettings = t.HTTPUPGRADEConfig + } } // Build implements Buildable. diff --git a/main/distro/all/all.go b/main/distro/all/all.go index d383e043..0f0397f5 100644 --- a/main/distro/all/all.go +++ b/main/distro/all/all.go @@ -53,6 +53,7 @@ import ( _ "github.com/xtls/xray-core/transport/internet/domainsocket" _ "github.com/xtls/xray-core/transport/internet/grpc" _ "github.com/xtls/xray-core/transport/internet/http" + _ "github.com/xtls/xray-core/transport/internet/httpupgrade" _ "github.com/xtls/xray-core/transport/internet/kcp" _ "github.com/xtls/xray-core/transport/internet/quic" _ "github.com/xtls/xray-core/transport/internet/reality" diff --git a/transport/internet/httpupgrade/config.go b/transport/internet/httpupgrade/config.go new file mode 100644 index 00000000..21841bfd --- /dev/null +++ b/transport/internet/httpupgrade/config.go @@ -0,0 +1,12 @@ +package httpupgrade + +func (c *Config) GetNormalizedPath() string { + path := c.Path + if path == "" { + return "/" + } + if path[0] != '/' { + return "/" + path + } + return path +} diff --git a/transport/internet/httpupgrade/config.pb.go b/transport/internet/httpupgrade/config.pb.go new file mode 100644 index 00000000..f097f63f --- /dev/null +++ b/transport/internet/httpupgrade/config.pb.go @@ -0,0 +1,174 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.32.0 +// protoc v4.25.1 +// source: transport/internet/httpupgrade/config.proto + +package httpupgrade + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Config struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + Host string `protobuf:"bytes,3,opt,name=host,proto3" json:"host,omitempty"` + AcceptProxyProtocol bool `protobuf:"varint,4,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"` +} + +func (x *Config) Reset() { + *x = Config{} + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_httpupgrade_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Config) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Config) ProtoMessage() {} + +func (x *Config) ProtoReflect() protoreflect.Message { + mi := &file_transport_internet_httpupgrade_config_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Config.ProtoReflect.Descriptor instead. +func (*Config) Descriptor() ([]byte, []int) { + return file_transport_internet_httpupgrade_config_proto_rawDescGZIP(), []int{0} +} + +func (x *Config) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *Config) GetHost() string { + if x != nil { + return x.Host + } + return "" +} + +func (x *Config) GetAcceptProxyProtocol() bool { + if x != nil { + return x.AcceptProxyProtocol + } + return false +} + +var File_transport_internet_httpupgrade_config_proto protoreflect.FileDescriptor + +var file_transport_internet_httpupgrade_config_proto_rawDesc = []byte{ + 0x0a, 0x2b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, + 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x23, 0x78, + 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x75, 0x70, 0x67, 0x72, 0x61, + 0x64, 0x65, 0x22, 0x6a, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, + 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, + 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x68, 0x6f, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x5f, 0x70, + 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x13, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x42, 0x8b, + 0x01, 0x0a, 0x27, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, + 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, + 0x74, 0x74, 0x70, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x50, 0x01, 0x5a, 0x38, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, + 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, + 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x75, + 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0xaa, 0x02, 0x23, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, + 0x2e, 0x48, 0x74, 0x74, 0x70, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_transport_internet_httpupgrade_config_proto_rawDescOnce sync.Once + file_transport_internet_httpupgrade_config_proto_rawDescData = file_transport_internet_httpupgrade_config_proto_rawDesc +) + +func file_transport_internet_httpupgrade_config_proto_rawDescGZIP() []byte { + file_transport_internet_httpupgrade_config_proto_rawDescOnce.Do(func() { + file_transport_internet_httpupgrade_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_httpupgrade_config_proto_rawDescData) + }) + return file_transport_internet_httpupgrade_config_proto_rawDescData +} + +var file_transport_internet_httpupgrade_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_transport_internet_httpupgrade_config_proto_goTypes = []interface{}{ + (*Config)(nil), // 0: xray.transport.internet.httpupgrade.Config +} +var file_transport_internet_httpupgrade_config_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_transport_internet_httpupgrade_config_proto_init() } +func file_transport_internet_httpupgrade_config_proto_init() { + if File_transport_internet_httpupgrade_config_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_transport_internet_httpupgrade_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_transport_internet_httpupgrade_config_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_transport_internet_httpupgrade_config_proto_goTypes, + DependencyIndexes: file_transport_internet_httpupgrade_config_proto_depIdxs, + MessageInfos: file_transport_internet_httpupgrade_config_proto_msgTypes, + }.Build() + File_transport_internet_httpupgrade_config_proto = out.File + file_transport_internet_httpupgrade_config_proto_rawDesc = nil + file_transport_internet_httpupgrade_config_proto_goTypes = nil + file_transport_internet_httpupgrade_config_proto_depIdxs = nil +} diff --git a/transport/internet/httpupgrade/config.proto b/transport/internet/httpupgrade/config.proto new file mode 100644 index 00000000..2be7cc22 --- /dev/null +++ b/transport/internet/httpupgrade/config.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package xray.transport.internet.httpupgrade; +option csharp_namespace = "Xray.Transport.Internet.HttpUpgrade"; +option go_package = "github.com/xtls/xray-core/transport/internet/httpupgrade"; +option java_package = "com.xray.transport.internet.httpupgrade"; +option java_multiple_files = true; + +message Config { + reserved 1; + + string path = 2; + string host = 3; + + bool accept_proxy_protocol = 4; +} \ No newline at end of file diff --git a/transport/internet/httpupgrade/dialer.go b/transport/internet/httpupgrade/dialer.go new file mode 100644 index 00000000..2ed43ed7 --- /dev/null +++ b/transport/internet/httpupgrade/dialer.go @@ -0,0 +1,87 @@ +package httpupgrade + +import ( + "bufio" + "context" + "net/http" + "net/url" + "strings" + + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/session" + "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/stat" + "github.com/xtls/xray-core/transport/internet/tls" +) + +func dialhttpUpgrade(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (net.Conn, error) { + transportConfiguration := streamSettings.ProtocolSettings.(*Config) + + pconn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings) + if err != nil { + newError("failed to dial to ", dest).Base(err).AtError().WriteToLog() + return nil, err + } + + var conn net.Conn + var requestURL url.URL + if config := tls.ConfigFromStreamSettings(streamSettings); config != nil { + tlsConfig := config.GetTLSConfig(tls.WithDestination(dest), tls.WithNextProto("http/1.1")) + if fingerprint := tls.GetFingerprint(config.Fingerprint); fingerprint != nil { + conn = tls.UClient(pconn, tlsConfig, fingerprint) + if err := conn.(*tls.UConn).WebsocketHandshakeContext(ctx); err != nil { + return nil, err + } + } else { + conn = tls.Client(pconn, tlsConfig) + } + + requestURL.Scheme = "https" + } else { + requestURL.Scheme = "http" + } + + requestURL.Host = dest.NetAddr() + requestURL.Path = transportConfiguration.GetNormalizedPath() + req := &http.Request{ + Method: http.MethodGet, + URL: &requestURL, + Host: transportConfiguration.Host, + Header: make(http.Header), + } + req.Header.Set("Connection", "upgrade") + req.Header.Set("Upgrade", "websocket") + + err = req.Write(conn) + if err != nil { + return nil, err + } + + // TODO The bufio usage here is unreliable + resp, err := http.ReadResponse(bufio.NewReader(conn), req) // nolint:bodyclose + if err != nil { + return nil, err + } + + if resp.Status == "101 Switching Protocols" && + strings.ToLower(resp.Header.Get("Upgrade")) == "websocket" && + strings.ToLower(resp.Header.Get("Connection")) == "upgrade" { + return conn, nil + } + return nil, newError("unrecognized reply") +} + +func dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) { + newError("creating connection to ", dest).WriteToLog(session.ExportIDToError(ctx)) + + conn, err := dialhttpUpgrade(ctx, dest, streamSettings) + if err != nil { + return nil, newError("failed to dial request to ", dest).Base(err) + } + return stat.Connection(conn), nil +} + +func init() { + common.Must(internet.RegisterTransportDialer(protocolName, dial)) +} diff --git a/transport/internet/httpupgrade/errors.generated.go b/transport/internet/httpupgrade/errors.generated.go new file mode 100644 index 00000000..9a3440c0 --- /dev/null +++ b/transport/internet/httpupgrade/errors.generated.go @@ -0,0 +1,9 @@ +package httpupgrade + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/transport/internet/httpupgrade/httpupgrade.go b/transport/internet/httpupgrade/httpupgrade.go new file mode 100644 index 00000000..c1410c61 --- /dev/null +++ b/transport/internet/httpupgrade/httpupgrade.go @@ -0,0 +1,17 @@ +package httpupgrade + +import ( + "context" + + "github.com/xtls/xray-core/common" +) + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + +const protocolName = "httpupgrade" + +func init() { + common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { + return nil, newError("httpupgrade is a transport protocol.") + })) +} diff --git a/transport/internet/httpupgrade/hub.go b/transport/internet/httpupgrade/hub.go new file mode 100644 index 00000000..f87d020f --- /dev/null +++ b/transport/internet/httpupgrade/hub.go @@ -0,0 +1,140 @@ +package httpupgrade + +import ( + "bufio" + "context" + "crypto/tls" + "net/http" + "strings" + + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/session" + "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/stat" + v2tls "github.com/xtls/xray-core/transport/internet/tls" +) + +type server struct { + config *Config + addConn internet.ConnHandler + innnerListener net.Listener +} + +func (s *server) Close() error { + return s.innnerListener.Close() +} + +func (s *server) Addr() net.Addr { + return nil +} + +func (s *server) Handle(conn net.Conn) (stat.Connection, error) { + connReader := bufio.NewReader(conn) + req, err := http.ReadRequest(connReader) + if err != nil { + return nil, err + } + + if s.config != nil { + host := req.Host + if len(s.config.Host) > 0 && host != s.config.Host { + return nil, newError("bad host: ", host) + } + path := s.config.GetNormalizedPath() + if req.URL.Path != path { + return nil, newError("bad path: ", req.URL.Path) + } + } + + connection := strings.ToLower(req.Header.Get("Connection")) + upgrade := strings.ToLower(req.Header.Get("Upgrade")) + if connection != "upgrade" || upgrade != "websocket" { + _ = conn.Close() + return nil, newError("unrecognized request") + } + resp := &http.Response{ + Status: "101 Switching Protocols", + StatusCode: 101, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Header: http.Header{}, + } + resp.Header.Set("Connection", "upgrade") + resp.Header.Set("Upgrade", "websocket") + err = resp.Write(conn) + if err != nil { + _ = conn.Close() + return nil, err + } + return stat.Connection(conn), nil +} + +func (s *server) keepAccepting() { + for { + conn, err := s.innnerListener.Accept() + if err != nil { + return + } + handledConn, err := s.Handle(conn) + if err != nil { + newError("failed to handle request").Base(err).WriteToLog() + continue + } + s.addConn(handledConn) + } +} + +func listenHTTPUpgrade(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) { + transportConfiguration := streamSettings.ProtocolSettings.(*Config) + if transportConfiguration != nil { + if streamSettings.SocketSettings == nil { + streamSettings.SocketSettings = &internet.SocketConfig{} + } + streamSettings.SocketSettings.AcceptProxyProtocol = transportConfiguration.AcceptProxyProtocol || streamSettings.SocketSettings.AcceptProxyProtocol + } + var listener net.Listener + var err error + if port == net.Port(0) { // unix + listener, err = internet.ListenSystem(ctx, &net.UnixAddr{ + Name: address.Domain(), + Net: "unix", + }, streamSettings.SocketSettings) + if err != nil { + return nil, newError("failed to listen unix domain socket(for HttpUpgrade) on ", address).Base(err) + } + newError("listening unix domain socket(for HttpUpgrade) on ", address).WriteToLog(session.ExportIDToError(ctx)) + } else { // tcp + listener, err = internet.ListenSystem(ctx, &net.TCPAddr{ + IP: address.IP(), + Port: int(port), + }, streamSettings.SocketSettings) + if err != nil { + return nil, newError("failed to listen TCP(for HttpUpgrade) on ", address, ":", port).Base(err) + } + newError("listening TCP(for HttpUpgrade) on ", address, ":", port).WriteToLog(session.ExportIDToError(ctx)) + } + + if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol { + newError("accepting PROXY protocol").AtWarning().WriteToLog(session.ExportIDToError(ctx)) + } + + if config := v2tls.ConfigFromStreamSettings(streamSettings); config != nil { + if tlsConfig := config.GetTLSConfig(); tlsConfig != nil { + listener = tls.NewListener(listener, tlsConfig) + } + } + + serverInstance := &server{ + config: transportConfiguration, + addConn: addConn, + innnerListener: listener, + } + go serverInstance.keepAccepting() + return serverInstance, nil +} + +func init() { + common.Must(internet.RegisterTransportListener(protocolName, listenHTTPUpgrade)) +}