From e6711d1b4877b92933798d64088413b07782baf7 Mon Sep 17 00:00:00 2001 From: yuhan6665 <1588741+yuhan6665@users.noreply.github.com> Date: Tue, 12 Oct 2021 11:58:12 -0400 Subject: [PATCH] Add header and method support to http2 transport (#755) Co-authored-by: Shelikhoo --- infra/conf/transport_internet.go | 28 ++++++++-- transport/internet/http/config.pb.go | 82 +++++++++++++++++++--------- transport/internet/http/config.proto | 4 ++ transport/internet/http/dialer.go | 18 +++++- transport/internet/http/hub.go | 7 +++ 5 files changed, 106 insertions(+), 33 deletions(-) diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index d389f9fe..5983a8d5 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -14,6 +14,7 @@ import ( "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/transport/internet" "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/kcp" "github.com/xtls/xray-core/transport/internet/quic" @@ -180,10 +181,12 @@ func (c *WebSocketConfig) Build() (proto.Message, error) { } type HTTPConfig struct { - Host *StringList `json:"host"` - Path string `json:"path"` - ReadIdleTimeout int32 `json:"read_idle_timeout"` - HealthCheckTimeout int32 `json:"health_check_timeout"` + Host *StringList `json:"host"` + Path string `json:"path"` + ReadIdleTimeout int32 `json:"read_idle_timeout"` + HealthCheckTimeout int32 `json:"health_check_timeout"` + Method string `json:"method"` + Headers map[string]*StringList `json:"headers"` } // Build implements Buildable. @@ -202,6 +205,23 @@ func (c *HTTPConfig) Build() (proto.Message, error) { if c.Host != nil { config.Host = []string(*c.Host) } + if c.Method != "" { + config.Method = c.Method + } + if len(c.Headers) > 0 { + config.Header = make([]*httpheader.Header, 0, len(c.Headers)) + headerNames := sortMapKeys(c.Headers) + for _, key := range headerNames { + value := c.Headers[key] + if value == nil { + return nil, newError("empty HTTP header value: " + key).AtError() + } + config.Header = append(config.Header, &httpheader.Header{ + Name: key, + Value: append([]string(nil), (*value)...), + }) + } + } return config, nil } diff --git a/transport/internet/http/config.pb.go b/transport/internet/http/config.pb.go index a13b278a..1c0cf86f 100644 --- a/transport/internet/http/config.pb.go +++ b/transport/internet/http/config.pb.go @@ -7,6 +7,7 @@ package http import ( + http "github.com/xtls/xray-core/transport/internet/headers/http" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -25,10 +26,12 @@ type Config struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Host []string `protobuf:"bytes,1,rep,name=host,proto3" json:"host,omitempty"` - Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` - IdleTimeout int32 `protobuf:"varint,3,opt,name=idle_timeout,json=idleTimeout,proto3" json:"idle_timeout,omitempty"` - HealthCheckTimeout int32 `protobuf:"varint,4,opt,name=health_check_timeout,json=healthCheckTimeout,proto3" json:"health_check_timeout,omitempty"` + Host []string `protobuf:"bytes,1,rep,name=host,proto3" json:"host,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + IdleTimeout int32 `protobuf:"varint,3,opt,name=idle_timeout,json=idleTimeout,proto3" json:"idle_timeout,omitempty"` + HealthCheckTimeout int32 `protobuf:"varint,4,opt,name=health_check_timeout,json=healthCheckTimeout,proto3" json:"health_check_timeout,omitempty"` + Method string `protobuf:"bytes,5,opt,name=method,proto3" json:"method,omitempty"` + Header []*http.Header `protobuf:"bytes,6,rep,name=header,proto3" json:"header,omitempty"` } func (x *Config) Reset() { @@ -91,6 +94,20 @@ func (x *Config) GetHealthCheckTimeout() int32 { return 0 } +func (x *Config) GetMethod() string { + if x != nil { + return x.Method + } + return "" +} + +func (x *Config) GetHeader() []*http.Header { + if x != nil { + return x.Header + } + return nil +} + var File_transport_internet_http_config_proto protoreflect.FileDescriptor var file_transport_internet_http_config_proto_rawDesc = []byte{ @@ -98,23 +115,32 @@ var file_transport_internet_http_config_proto_rawDesc = []byte{ 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 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, 0x22, 0x85, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x68, - 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x64, 0x6c, 0x65, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x69, - 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x68, 0x65, - 0x61, 0x6c, 0x74, 0x68, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, - 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, - 0x43, 0x68, 0x65, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x42, 0x76, 0x0a, 0x20, - 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, - 0x50, 0x01, 0x5a, 0x31, 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, 0xaa, 0x02, 0x1c, 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, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x68, 0x74, 0x74, 0x70, 0x1a, 0x2c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, + 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0xe3, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, + 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, + 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x69, 0x64, 0x6c, + 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x68, 0x65, 0x61, 0x6c, + 0x74, 0x68, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, + 0x6f, 0x64, 0x12, 0x44, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2c, 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, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x73, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x42, 0x76, 0x0a, 0x20, 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, 0x50, 0x01, 0x5a, 0x31, + 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, 0xaa, 0x02, 0x1c, 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, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -131,14 +157,16 @@ func file_transport_internet_http_config_proto_rawDescGZIP() []byte { var file_transport_internet_http_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_transport_internet_http_config_proto_goTypes = []interface{}{ - (*Config)(nil), // 0: xray.transport.internet.http.Config + (*Config)(nil), // 0: xray.transport.internet.http.Config + (*http.Header)(nil), // 1: xray.transport.internet.headers.http.Header } var file_transport_internet_http_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 + 1, // 0: xray.transport.internet.http.Config.header:type_name -> xray.transport.internet.headers.http.Header + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name } func init() { file_transport_internet_http_config_proto_init() } diff --git a/transport/internet/http/config.proto b/transport/internet/http/config.proto index 641ed5ee..82f5cbeb 100644 --- a/transport/internet/http/config.proto +++ b/transport/internet/http/config.proto @@ -6,9 +6,13 @@ option go_package = "github.com/xtls/xray-core/transport/internet/http"; option java_package = "com.xray.transport.internet.http"; option java_multiple_files = true; +import "transport/internet/headers/http/config.proto"; + message Config { repeated string host = 1; string path = 2; int32 idle_timeout = 3; int32 health_check_timeout = 4; + string method = 5; + repeated xray.transport.internet.headers.http.Header header = 6; } diff --git a/transport/internet/http/dialer.go b/transport/internet/http/dialer.go index 9c8221e2..93a248e3 100644 --- a/transport/internet/http/dialer.go +++ b/transport/internet/http/dialer.go @@ -123,8 +123,22 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me opts := pipe.OptionsFromContext(ctx) preader, pwriter := pipe.New(opts...) breader := &buf.BufferedReader{Reader: preader} + + httpMethod := "PUT" + if httpSettings.Method != "" { + httpMethod = httpSettings.Method + } + + httpHeaders := make(http.Header) + + for _, httpHeader := range httpSettings.Header { + for _, httpHeaderValue := range httpHeader.Value { + httpHeaders.Set(httpHeader.Name, httpHeaderValue) + } + } + request := &http.Request{ - Method: "PUT", + Method: httpMethod, Host: httpSettings.getRandomHost(), Body: breader, URL: &url.URL{ @@ -135,7 +149,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me Proto: "HTTP/2", ProtoMajor: 2, ProtoMinor: 0, - Header: make(http.Header), + Header: httpHeaders, } // Disable any compression method from server. request.Header.Set("Accept-Encoding", "identity") diff --git a/transport/internet/http/hub.go b/transport/internet/http/hub.go index 0a5006e9..4a9b8989 100644 --- a/transport/internet/http/hub.go +++ b/transport/internet/http/hub.go @@ -70,6 +70,13 @@ func (l *Listener) ServeHTTP(writer http.ResponseWriter, request *http.Request) } writer.Header().Set("Cache-Control", "no-store") + + for _, httpHeader := range l.config.Header { + for _, httpHeaderValue := range httpHeader.Value { + writer.Header().Set(httpHeader.Name, httpHeaderValue) + } + } + writer.WriteHeader(200) if f, ok := writer.(http.Flusher); ok { f.Flush()