mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-04-29 16:58:34 +00:00
v1.0.0
This commit is contained in:
parent
47d23e9972
commit
c7f7c08ead
711 changed files with 82154 additions and 2 deletions
100
transport/internet/headers/http/config.go
Normal file
100
transport/internet/headers/http/config.go
Normal file
|
@ -0,0 +1,100 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/xtls/xray-core/v1/common/dice"
|
||||
)
|
||||
|
||||
func pickString(arr []string) string {
|
||||
n := len(arr)
|
||||
switch n {
|
||||
case 0:
|
||||
return ""
|
||||
case 1:
|
||||
return arr[0]
|
||||
default:
|
||||
return arr[dice.Roll(n)]
|
||||
}
|
||||
}
|
||||
|
||||
func (v *RequestConfig) PickURI() string {
|
||||
return pickString(v.Uri)
|
||||
}
|
||||
|
||||
func (v *RequestConfig) PickHeaders() []string {
|
||||
n := len(v.Header)
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
headers := make([]string, n)
|
||||
for idx, headerConfig := range v.Header {
|
||||
headerName := headerConfig.Name
|
||||
headerValue := pickString(headerConfig.Value)
|
||||
headers[idx] = headerName + ": " + headerValue
|
||||
}
|
||||
return headers
|
||||
}
|
||||
|
||||
func (v *RequestConfig) GetVersionValue() string {
|
||||
if v == nil || v.Version == nil {
|
||||
return "1.1"
|
||||
}
|
||||
return v.Version.Value
|
||||
}
|
||||
|
||||
func (v *RequestConfig) GetMethodValue() string {
|
||||
if v == nil || v.Method == nil {
|
||||
return "GET"
|
||||
}
|
||||
return v.Method.Value
|
||||
}
|
||||
|
||||
func (v *RequestConfig) GetFullVersion() string {
|
||||
return "HTTP/" + v.GetVersionValue()
|
||||
}
|
||||
|
||||
func (v *ResponseConfig) HasHeader(header string) bool {
|
||||
cHeader := strings.ToLower(header)
|
||||
for _, tHeader := range v.Header {
|
||||
if strings.EqualFold(tHeader.Name, cHeader) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (v *ResponseConfig) PickHeaders() []string {
|
||||
n := len(v.Header)
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
headers := make([]string, n)
|
||||
for idx, headerConfig := range v.Header {
|
||||
headerName := headerConfig.Name
|
||||
headerValue := pickString(headerConfig.Value)
|
||||
headers[idx] = headerName + ": " + headerValue
|
||||
}
|
||||
return headers
|
||||
}
|
||||
|
||||
func (v *ResponseConfig) GetVersionValue() string {
|
||||
if v == nil || v.Version == nil {
|
||||
return "1.1"
|
||||
}
|
||||
return v.Version.Value
|
||||
}
|
||||
|
||||
func (v *ResponseConfig) GetFullVersion() string {
|
||||
return "HTTP/" + v.GetVersionValue()
|
||||
}
|
||||
|
||||
func (v *ResponseConfig) GetStatusValue() *Status {
|
||||
if v == nil || v.Status == nil {
|
||||
return &Status{
|
||||
Code: "200",
|
||||
Reason: "OK",
|
||||
}
|
||||
}
|
||||
return v.Status
|
||||
}
|
654
transport/internet/headers/http/config.pb.go
Normal file
654
transport/internet/headers/http/config.pb.go
Normal file
|
@ -0,0 +1,654 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.25.0
|
||||
// protoc v3.14.0
|
||||
// source: transport/internet/headers/http/config.proto
|
||||
|
||||
package http
|
||||
|
||||
import (
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
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)
|
||||
)
|
||||
|
||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||
// of the legacy proto package is being used.
|
||||
const _ = proto.ProtoPackageIsVersion4
|
||||
|
||||
type Header struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// "Accept", "Cookie", etc
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
// Each entry must be valid in one piece. Random entry will be chosen if
|
||||
// multiple entries present.
|
||||
Value []string `protobuf:"bytes,2,rep,name=value,proto3" json:"value,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Header) Reset() {
|
||||
*x = Header{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_transport_internet_headers_http_config_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Header) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Header) ProtoMessage() {}
|
||||
|
||||
func (x *Header) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_headers_http_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 Header.ProtoReflect.Descriptor instead.
|
||||
func (*Header) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_headers_http_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Header) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Header) GetValue() []string {
|
||||
if x != nil {
|
||||
return x.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HTTP version. Default value "1.1".
|
||||
type Version struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Version) Reset() {
|
||||
*x = Version{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_transport_internet_headers_http_config_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Version) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Version) ProtoMessage() {}
|
||||
|
||||
func (x *Version) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_headers_http_config_proto_msgTypes[1]
|
||||
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 Version.ProtoReflect.Descriptor instead.
|
||||
func (*Version) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_headers_http_config_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *Version) GetValue() string {
|
||||
if x != nil {
|
||||
return x.Value
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// HTTP method. Default value "GET".
|
||||
type Method struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Method) Reset() {
|
||||
*x = Method{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_transport_internet_headers_http_config_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Method) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Method) ProtoMessage() {}
|
||||
|
||||
func (x *Method) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_headers_http_config_proto_msgTypes[2]
|
||||
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 Method.ProtoReflect.Descriptor instead.
|
||||
func (*Method) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_headers_http_config_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *Method) GetValue() string {
|
||||
if x != nil {
|
||||
return x.Value
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type RequestConfig struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Full HTTP version like "1.1".
|
||||
Version *Version `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"`
|
||||
// GET, POST, CONNECT etc
|
||||
Method *Method `protobuf:"bytes,2,opt,name=method,proto3" json:"method,omitempty"`
|
||||
// URI like "/login.php"
|
||||
Uri []string `protobuf:"bytes,3,rep,name=uri,proto3" json:"uri,omitempty"`
|
||||
Header []*Header `protobuf:"bytes,4,rep,name=header,proto3" json:"header,omitempty"`
|
||||
}
|
||||
|
||||
func (x *RequestConfig) Reset() {
|
||||
*x = RequestConfig{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_transport_internet_headers_http_config_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *RequestConfig) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RequestConfig) ProtoMessage() {}
|
||||
|
||||
func (x *RequestConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_headers_http_config_proto_msgTypes[3]
|
||||
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 RequestConfig.ProtoReflect.Descriptor instead.
|
||||
func (*RequestConfig) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_headers_http_config_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *RequestConfig) GetVersion() *Version {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *RequestConfig) GetMethod() *Method {
|
||||
if x != nil {
|
||||
return x.Method
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *RequestConfig) GetUri() []string {
|
||||
if x != nil {
|
||||
return x.Uri
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *RequestConfig) GetHeader() []*Header {
|
||||
if x != nil {
|
||||
return x.Header
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Status struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Status code. Default "200".
|
||||
Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"`
|
||||
// Statue reason. Default "OK".
|
||||
Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Status) Reset() {
|
||||
*x = Status{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_transport_internet_headers_http_config_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Status) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Status) ProtoMessage() {}
|
||||
|
||||
func (x *Status) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_headers_http_config_proto_msgTypes[4]
|
||||
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 Status.ProtoReflect.Descriptor instead.
|
||||
func (*Status) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_headers_http_config_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *Status) GetCode() string {
|
||||
if x != nil {
|
||||
return x.Code
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Status) GetReason() string {
|
||||
if x != nil {
|
||||
return x.Reason
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ResponseConfig struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Version *Version `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"`
|
||||
Status *Status `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"`
|
||||
Header []*Header `protobuf:"bytes,3,rep,name=header,proto3" json:"header,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ResponseConfig) Reset() {
|
||||
*x = ResponseConfig{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_transport_internet_headers_http_config_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ResponseConfig) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ResponseConfig) ProtoMessage() {}
|
||||
|
||||
func (x *ResponseConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_headers_http_config_proto_msgTypes[5]
|
||||
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 ResponseConfig.ProtoReflect.Descriptor instead.
|
||||
func (*ResponseConfig) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_headers_http_config_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *ResponseConfig) GetVersion() *Version {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ResponseConfig) GetStatus() *Status {
|
||||
if x != nil {
|
||||
return x.Status
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ResponseConfig) GetHeader() []*Header {
|
||||
if x != nil {
|
||||
return x.Header
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Settings for authenticating requests. If not set, client side will not send
|
||||
// authenication header, and server side will bypass authentication.
|
||||
Request *RequestConfig `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"`
|
||||
// Settings for authenticating responses. If not set, client side will bypass
|
||||
// authentication, and server side will not send authentication header.
|
||||
Response *ResponseConfig `protobuf:"bytes,2,opt,name=response,proto3" json:"response,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_transport_internet_headers_http_config_proto_msgTypes[6]
|
||||
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_headers_http_config_proto_msgTypes[6]
|
||||
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_headers_http_config_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *Config) GetRequest() *RequestConfig {
|
||||
if x != nil {
|
||||
return x.Request
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Config) GetResponse() *ResponseConfig {
|
||||
if x != nil {
|
||||
return x.Response
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_transport_internet_headers_http_config_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_transport_internet_headers_http_config_proto_rawDesc = []byte{
|
||||
0x0a, 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, 0x12, 0x24,
|
||||
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, 0x22, 0x32, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x12,
|
||||
0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28,
|
||||
0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x1f, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73,
|
||||
0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x1e, 0x0a, 0x06, 0x4d, 0x65, 0x74,
|
||||
0x68, 0x6f, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xf6, 0x01, 0x0a, 0x0d, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x47, 0x0a, 0x07, 0x76,
|
||||
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 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, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x76, 0x65, 0x72,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02,
|
||||
0x20, 0x01, 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, 0x4d, 0x65, 0x74, 0x68,
|
||||
0x6f, 0x64, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72,
|
||||
0x69, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x44, 0x0a, 0x06,
|
||||
0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x04, 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, 0x22, 0x34, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04,
|
||||
0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65,
|
||||
0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0xe5, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x47, 0x0a, 0x07, 0x76,
|
||||
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 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, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x76, 0x65, 0x72,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02,
|
||||
0x20, 0x01, 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, 0x53, 0x74, 0x61, 0x74,
|
||||
0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x44, 0x0a, 0x06, 0x68, 0x65,
|
||||
0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 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,
|
||||
0x22, 0xa9, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x4d, 0x0a, 0x07, 0x72,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 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, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||
0x67, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x50, 0x0a, 0x08, 0x72, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 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, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66,
|
||||
0x69, 0x67, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x91, 0x01, 0x0a,
|
||||
0x28, 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, 0x65, 0x61,
|
||||
0x64, 0x65, 0x72, 0x73, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x50, 0x01, 0x5a, 0x3c, 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, 0x76, 0x31, 0x2f, 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, 0xaa, 0x02, 0x24, 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, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x48, 0x74, 0x74, 0x70,
|
||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_transport_internet_headers_http_config_proto_rawDescOnce sync.Once
|
||||
file_transport_internet_headers_http_config_proto_rawDescData = file_transport_internet_headers_http_config_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_transport_internet_headers_http_config_proto_rawDescGZIP() []byte {
|
||||
file_transport_internet_headers_http_config_proto_rawDescOnce.Do(func() {
|
||||
file_transport_internet_headers_http_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_headers_http_config_proto_rawDescData)
|
||||
})
|
||||
return file_transport_internet_headers_http_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_transport_internet_headers_http_config_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||
var file_transport_internet_headers_http_config_proto_goTypes = []interface{}{
|
||||
(*Header)(nil), // 0: xray.transport.internet.headers.http.Header
|
||||
(*Version)(nil), // 1: xray.transport.internet.headers.http.Version
|
||||
(*Method)(nil), // 2: xray.transport.internet.headers.http.Method
|
||||
(*RequestConfig)(nil), // 3: xray.transport.internet.headers.http.RequestConfig
|
||||
(*Status)(nil), // 4: xray.transport.internet.headers.http.Status
|
||||
(*ResponseConfig)(nil), // 5: xray.transport.internet.headers.http.ResponseConfig
|
||||
(*Config)(nil), // 6: xray.transport.internet.headers.http.Config
|
||||
}
|
||||
var file_transport_internet_headers_http_config_proto_depIdxs = []int32{
|
||||
1, // 0: xray.transport.internet.headers.http.RequestConfig.version:type_name -> xray.transport.internet.headers.http.Version
|
||||
2, // 1: xray.transport.internet.headers.http.RequestConfig.method:type_name -> xray.transport.internet.headers.http.Method
|
||||
0, // 2: xray.transport.internet.headers.http.RequestConfig.header:type_name -> xray.transport.internet.headers.http.Header
|
||||
1, // 3: xray.transport.internet.headers.http.ResponseConfig.version:type_name -> xray.transport.internet.headers.http.Version
|
||||
4, // 4: xray.transport.internet.headers.http.ResponseConfig.status:type_name -> xray.transport.internet.headers.http.Status
|
||||
0, // 5: xray.transport.internet.headers.http.ResponseConfig.header:type_name -> xray.transport.internet.headers.http.Header
|
||||
3, // 6: xray.transport.internet.headers.http.Config.request:type_name -> xray.transport.internet.headers.http.RequestConfig
|
||||
5, // 7: xray.transport.internet.headers.http.Config.response:type_name -> xray.transport.internet.headers.http.ResponseConfig
|
||||
8, // [8:8] is the sub-list for method output_type
|
||||
8, // [8:8] is the sub-list for method input_type
|
||||
8, // [8:8] is the sub-list for extension type_name
|
||||
8, // [8:8] is the sub-list for extension extendee
|
||||
0, // [0:8] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_transport_internet_headers_http_config_proto_init() }
|
||||
func file_transport_internet_headers_http_config_proto_init() {
|
||||
if File_transport_internet_headers_http_config_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_transport_internet_headers_http_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Header); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_transport_internet_headers_http_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Version); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_transport_internet_headers_http_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Method); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_transport_internet_headers_http_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*RequestConfig); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_transport_internet_headers_http_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Status); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_transport_internet_headers_http_config_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ResponseConfig); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_transport_internet_headers_http_config_proto_msgTypes[6].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_headers_http_config_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 7,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_transport_internet_headers_http_config_proto_goTypes,
|
||||
DependencyIndexes: file_transport_internet_headers_http_config_proto_depIdxs,
|
||||
MessageInfos: file_transport_internet_headers_http_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_transport_internet_headers_http_config_proto = out.File
|
||||
file_transport_internet_headers_http_config_proto_rawDesc = nil
|
||||
file_transport_internet_headers_http_config_proto_goTypes = nil
|
||||
file_transport_internet_headers_http_config_proto_depIdxs = nil
|
||||
}
|
65
transport/internet/headers/http/config.proto
Normal file
65
transport/internet/headers/http/config.proto
Normal file
|
@ -0,0 +1,65 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package xray.transport.internet.headers.http;
|
||||
option csharp_namespace = "Xray.Transport.Internet.Headers.Http";
|
||||
option go_package = "github.com/xtls/xray-core/v1/transport/internet/headers/http";
|
||||
option java_package = "com.xray.transport.internet.headers.http";
|
||||
option java_multiple_files = true;
|
||||
|
||||
message Header {
|
||||
// "Accept", "Cookie", etc
|
||||
string name = 1;
|
||||
|
||||
// Each entry must be valid in one piece. Random entry will be chosen if
|
||||
// multiple entries present.
|
||||
repeated string value = 2;
|
||||
}
|
||||
|
||||
// HTTP version. Default value "1.1".
|
||||
message Version {
|
||||
string value = 1;
|
||||
}
|
||||
|
||||
// HTTP method. Default value "GET".
|
||||
message Method {
|
||||
string value = 1;
|
||||
}
|
||||
|
||||
message RequestConfig {
|
||||
// Full HTTP version like "1.1".
|
||||
Version version = 1;
|
||||
|
||||
// GET, POST, CONNECT etc
|
||||
Method method = 2;
|
||||
|
||||
// URI like "/login.php"
|
||||
repeated string uri = 3;
|
||||
|
||||
repeated Header header = 4;
|
||||
}
|
||||
|
||||
message Status {
|
||||
// Status code. Default "200".
|
||||
string code = 1;
|
||||
|
||||
// Statue reason. Default "OK".
|
||||
string reason = 2;
|
||||
}
|
||||
|
||||
message ResponseConfig {
|
||||
Version version = 1;
|
||||
|
||||
Status status = 2;
|
||||
|
||||
repeated Header header = 3;
|
||||
}
|
||||
|
||||
message Config {
|
||||
// Settings for authenticating requests. If not set, client side will not send
|
||||
// authenication header, and server side will bypass authentication.
|
||||
RequestConfig request = 1;
|
||||
|
||||
// Settings for authenticating responses. If not set, client side will bypass
|
||||
// authentication, and server side will not send authentication header.
|
||||
ResponseConfig response = 2;
|
||||
}
|
9
transport/internet/headers/http/errors.generated.go
Normal file
9
transport/internet/headers/http/errors.generated.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package http
|
||||
|
||||
import "github.com/xtls/xray-core/v1/common/errors"
|
||||
|
||||
type errPathObjHolder struct{}
|
||||
|
||||
func newError(values ...interface{}) *errors.Error {
|
||||
return errors.New(values...).WithPathObj(errPathObjHolder{})
|
||||
}
|
321
transport/internet/headers/http/http.go
Normal file
321
transport/internet/headers/http/http.go
Normal file
|
@ -0,0 +1,321 @@
|
|||
package http
|
||||
|
||||
//go:generate go run github.com/xtls/xray-core/v1/common/errors/errorgen
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/v1/common"
|
||||
"github.com/xtls/xray-core/v1/common/buf"
|
||||
)
|
||||
|
||||
const (
|
||||
// CRLF is the line ending in HTTP header
|
||||
CRLF = "\r\n"
|
||||
|
||||
// ENDING is the double line ending between HTTP header and body.
|
||||
ENDING = CRLF + CRLF
|
||||
|
||||
// max length of HTTP header. Safety precaution for DDoS attack.
|
||||
maxHeaderLength = 8192
|
||||
)
|
||||
|
||||
var (
|
||||
ErrHeaderToLong = newError("Header too long.")
|
||||
|
||||
ErrHeaderMisMatch = newError("Header Mismatch.")
|
||||
)
|
||||
|
||||
type Reader interface {
|
||||
Read(io.Reader) (*buf.Buffer, error)
|
||||
}
|
||||
|
||||
type Writer interface {
|
||||
Write(io.Writer) error
|
||||
}
|
||||
|
||||
type NoOpReader struct{}
|
||||
|
||||
func (NoOpReader) Read(io.Reader) (*buf.Buffer, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type NoOpWriter struct{}
|
||||
|
||||
func (NoOpWriter) Write(io.Writer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type HeaderReader struct {
|
||||
req *http.Request
|
||||
expectedHeader *RequestConfig
|
||||
}
|
||||
|
||||
func (h *HeaderReader) ExpectThisRequest(expectedHeader *RequestConfig) *HeaderReader {
|
||||
h.expectedHeader = expectedHeader
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *HeaderReader) Read(reader io.Reader) (*buf.Buffer, error) {
|
||||
buffer := buf.New()
|
||||
totalBytes := int32(0)
|
||||
endingDetected := false
|
||||
|
||||
var headerBuf bytes.Buffer
|
||||
|
||||
for totalBytes < maxHeaderLength {
|
||||
_, err := buffer.ReadFrom(reader)
|
||||
if err != nil {
|
||||
buffer.Release()
|
||||
return nil, err
|
||||
}
|
||||
if n := bytes.Index(buffer.Bytes(), []byte(ENDING)); n != -1 {
|
||||
headerBuf.Write(buffer.BytesRange(0, int32(n+len(ENDING))))
|
||||
buffer.Advance(int32(n + len(ENDING)))
|
||||
endingDetected = true
|
||||
break
|
||||
}
|
||||
lenEnding := int32(len(ENDING))
|
||||
if buffer.Len() >= lenEnding {
|
||||
totalBytes += buffer.Len() - lenEnding
|
||||
headerBuf.Write(buffer.BytesRange(0, buffer.Len()-lenEnding))
|
||||
leftover := buffer.BytesFrom(-lenEnding)
|
||||
buffer.Clear()
|
||||
copy(buffer.Extend(lenEnding), leftover)
|
||||
|
||||
if _, err := readRequest(bufio.NewReader(bytes.NewReader(headerBuf.Bytes())), false); err != io.ErrUnexpectedEOF {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !endingDetected {
|
||||
buffer.Release()
|
||||
return nil, ErrHeaderToLong
|
||||
}
|
||||
|
||||
if h.expectedHeader == nil {
|
||||
if buffer.IsEmpty() {
|
||||
buffer.Release()
|
||||
return nil, nil
|
||||
}
|
||||
return buffer, nil
|
||||
}
|
||||
|
||||
// Parse the request
|
||||
if req, err := readRequest(bufio.NewReader(bytes.NewReader(headerBuf.Bytes())), false); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
h.req = req
|
||||
}
|
||||
|
||||
// Check req
|
||||
path := h.req.URL.Path
|
||||
hasThisURI := false
|
||||
for _, u := range h.expectedHeader.Uri {
|
||||
if u == path {
|
||||
hasThisURI = true
|
||||
}
|
||||
}
|
||||
|
||||
if !hasThisURI {
|
||||
return nil, ErrHeaderMisMatch
|
||||
}
|
||||
|
||||
if buffer.IsEmpty() {
|
||||
buffer.Release()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return buffer, nil
|
||||
}
|
||||
|
||||
type HeaderWriter struct {
|
||||
header *buf.Buffer
|
||||
}
|
||||
|
||||
func NewHeaderWriter(header *buf.Buffer) *HeaderWriter {
|
||||
return &HeaderWriter{
|
||||
header: header,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *HeaderWriter) Write(writer io.Writer) error {
|
||||
if w.header == nil {
|
||||
return nil
|
||||
}
|
||||
err := buf.WriteAllBytes(writer, w.header.Bytes())
|
||||
w.header.Release()
|
||||
w.header = nil
|
||||
return err
|
||||
}
|
||||
|
||||
type Conn struct {
|
||||
net.Conn
|
||||
|
||||
readBuffer *buf.Buffer
|
||||
oneTimeReader Reader
|
||||
oneTimeWriter Writer
|
||||
errorWriter Writer
|
||||
errorMismatchWriter Writer
|
||||
errorTooLongWriter Writer
|
||||
errReason error
|
||||
}
|
||||
|
||||
func NewConn(conn net.Conn, reader Reader, writer Writer, errorWriter Writer, errorMismatchWriter Writer, errorTooLongWriter Writer) *Conn {
|
||||
return &Conn{
|
||||
Conn: conn,
|
||||
oneTimeReader: reader,
|
||||
oneTimeWriter: writer,
|
||||
errorWriter: errorWriter,
|
||||
errorMismatchWriter: errorMismatchWriter,
|
||||
errorTooLongWriter: errorTooLongWriter,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) Read(b []byte) (int, error) {
|
||||
if c.oneTimeReader != nil {
|
||||
buffer, err := c.oneTimeReader.Read(c.Conn)
|
||||
if err != nil {
|
||||
c.errReason = err
|
||||
return 0, err
|
||||
}
|
||||
c.readBuffer = buffer
|
||||
c.oneTimeReader = nil
|
||||
}
|
||||
|
||||
if !c.readBuffer.IsEmpty() {
|
||||
nBytes, _ := c.readBuffer.Read(b)
|
||||
if c.readBuffer.IsEmpty() {
|
||||
c.readBuffer.Release()
|
||||
c.readBuffer = nil
|
||||
}
|
||||
return nBytes, nil
|
||||
}
|
||||
|
||||
return c.Conn.Read(b)
|
||||
}
|
||||
|
||||
// Write implements io.Writer.
|
||||
func (c *Conn) Write(b []byte) (int, error) {
|
||||
if c.oneTimeWriter != nil {
|
||||
err := c.oneTimeWriter.Write(c.Conn)
|
||||
c.oneTimeWriter = nil
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return c.Conn.Write(b)
|
||||
}
|
||||
|
||||
// Close implements net.Conn.Close().
|
||||
func (c *Conn) Close() error {
|
||||
if c.oneTimeWriter != nil && c.errorWriter != nil {
|
||||
// Connection is being closed but header wasn't sent. This means the client request
|
||||
// is probably not valid. Sending back a server error header in this case.
|
||||
|
||||
// Write response based on error reason
|
||||
switch c.errReason {
|
||||
case ErrHeaderMisMatch:
|
||||
c.errorMismatchWriter.Write(c.Conn)
|
||||
case ErrHeaderToLong:
|
||||
c.errorTooLongWriter.Write(c.Conn)
|
||||
default:
|
||||
c.errorWriter.Write(c.Conn)
|
||||
}
|
||||
}
|
||||
|
||||
return c.Conn.Close()
|
||||
}
|
||||
|
||||
func formResponseHeader(config *ResponseConfig) *HeaderWriter {
|
||||
header := buf.New()
|
||||
common.Must2(header.WriteString(strings.Join([]string{config.GetFullVersion(), config.GetStatusValue().Code, config.GetStatusValue().Reason}, " ")))
|
||||
common.Must2(header.WriteString(CRLF))
|
||||
|
||||
headers := config.PickHeaders()
|
||||
for _, h := range headers {
|
||||
common.Must2(header.WriteString(h))
|
||||
common.Must2(header.WriteString(CRLF))
|
||||
}
|
||||
if !config.HasHeader("Date") {
|
||||
common.Must2(header.WriteString("Date: "))
|
||||
common.Must2(header.WriteString(time.Now().Format(http.TimeFormat)))
|
||||
common.Must2(header.WriteString(CRLF))
|
||||
}
|
||||
common.Must2(header.WriteString(CRLF))
|
||||
return &HeaderWriter{
|
||||
header: header,
|
||||
}
|
||||
}
|
||||
|
||||
type Authenticator struct {
|
||||
config *Config
|
||||
}
|
||||
|
||||
func (a Authenticator) GetClientWriter() *HeaderWriter {
|
||||
header := buf.New()
|
||||
config := a.config.Request
|
||||
common.Must2(header.WriteString(strings.Join([]string{config.GetMethodValue(), config.PickURI(), config.GetFullVersion()}, " ")))
|
||||
common.Must2(header.WriteString(CRLF))
|
||||
|
||||
headers := config.PickHeaders()
|
||||
for _, h := range headers {
|
||||
common.Must2(header.WriteString(h))
|
||||
common.Must2(header.WriteString(CRLF))
|
||||
}
|
||||
common.Must2(header.WriteString(CRLF))
|
||||
return &HeaderWriter{
|
||||
header: header,
|
||||
}
|
||||
}
|
||||
|
||||
func (a Authenticator) GetServerWriter() *HeaderWriter {
|
||||
return formResponseHeader(a.config.Response)
|
||||
}
|
||||
|
||||
func (a Authenticator) Client(conn net.Conn) net.Conn {
|
||||
if a.config.Request == nil && a.config.Response == nil {
|
||||
return conn
|
||||
}
|
||||
var reader Reader = NoOpReader{}
|
||||
if a.config.Request != nil {
|
||||
reader = new(HeaderReader)
|
||||
}
|
||||
|
||||
var writer Writer = NoOpWriter{}
|
||||
if a.config.Response != nil {
|
||||
writer = a.GetClientWriter()
|
||||
}
|
||||
return NewConn(conn, reader, writer, NoOpWriter{}, NoOpWriter{}, NoOpWriter{})
|
||||
}
|
||||
|
||||
func (a Authenticator) Server(conn net.Conn) net.Conn {
|
||||
if a.config.Request == nil && a.config.Response == nil {
|
||||
return conn
|
||||
}
|
||||
return NewConn(conn, new(HeaderReader).ExpectThisRequest(a.config.Request), a.GetServerWriter(),
|
||||
formResponseHeader(resp400),
|
||||
formResponseHeader(resp404),
|
||||
formResponseHeader(resp400))
|
||||
}
|
||||
|
||||
func NewAuthenticator(ctx context.Context, config *Config) (Authenticator, error) {
|
||||
return Authenticator{
|
||||
config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||
return NewAuthenticator(ctx, config.(*Config))
|
||||
}))
|
||||
}
|
305
transport/internet/headers/http/http_test.go
Normal file
305
transport/internet/headers/http/http_test.go
Normal file
|
@ -0,0 +1,305 @@
|
|||
package http_test
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/v1/common"
|
||||
"github.com/xtls/xray-core/v1/common/buf"
|
||||
"github.com/xtls/xray-core/v1/common/net"
|
||||
. "github.com/xtls/xray-core/v1/transport/internet/headers/http"
|
||||
)
|
||||
|
||||
func TestReaderWriter(t *testing.T) {
|
||||
cache := buf.New()
|
||||
b := buf.New()
|
||||
common.Must2(b.WriteString("abcd" + ENDING))
|
||||
writer := NewHeaderWriter(b)
|
||||
err := writer.Write(cache)
|
||||
common.Must(err)
|
||||
if v := cache.Len(); v != 8 {
|
||||
t.Error("cache len: ", v)
|
||||
}
|
||||
_, err = cache.Write([]byte{'e', 'f', 'g'})
|
||||
common.Must(err)
|
||||
|
||||
reader := &HeaderReader{}
|
||||
_, err = reader.Read(cache)
|
||||
if err != nil && !strings.HasPrefix(err.Error(), "malformed HTTP request") {
|
||||
t.Error("unknown error ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestHeader(t *testing.T) {
|
||||
auth, err := NewAuthenticator(context.Background(), &Config{
|
||||
Request: &RequestConfig{
|
||||
Uri: []string{"/"},
|
||||
Header: []*Header{
|
||||
{
|
||||
Name: "Test",
|
||||
Value: []string{"Value"},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
cache := buf.New()
|
||||
err = auth.GetClientWriter().Write(cache)
|
||||
common.Must(err)
|
||||
|
||||
if cache.String() != "GET / HTTP/1.1\r\nTest: Value\r\n\r\n" {
|
||||
t.Error("cache: ", cache.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestLongRequestHeader(t *testing.T) {
|
||||
payload := make([]byte, buf.Size+2)
|
||||
common.Must2(rand.Read(payload[:buf.Size-2]))
|
||||
copy(payload[buf.Size-2:], ENDING)
|
||||
payload = append(payload, []byte("abcd")...)
|
||||
|
||||
reader := HeaderReader{}
|
||||
_, err := reader.Read(bytes.NewReader(payload))
|
||||
|
||||
if err != nil && !(strings.HasPrefix(err.Error(), "invalid") || strings.HasPrefix(err.Error(), "malformed")) {
|
||||
t.Error("unknown error ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnection(t *testing.T) {
|
||||
auth, err := NewAuthenticator(context.Background(), &Config{
|
||||
Request: &RequestConfig{
|
||||
Method: &Method{Value: "Post"},
|
||||
Uri: []string{"/testpath"},
|
||||
Header: []*Header{
|
||||
{
|
||||
Name: "Host",
|
||||
Value: []string{"www.example.com", "www.google.com"},
|
||||
},
|
||||
{
|
||||
Name: "User-Agent",
|
||||
Value: []string{"Test-Agent"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Response: &ResponseConfig{
|
||||
Version: &Version{
|
||||
Value: "1.1",
|
||||
},
|
||||
Status: &Status{
|
||||
Code: "404",
|
||||
Reason: "Not Found",
|
||||
},
|
||||
},
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
common.Must(err)
|
||||
|
||||
go func() {
|
||||
conn, err := listener.Accept()
|
||||
common.Must(err)
|
||||
authConn := auth.Server(conn)
|
||||
b := make([]byte, 256)
|
||||
for {
|
||||
n, err := authConn.Read(b)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
_, err = authConn.Write(b[:n])
|
||||
common.Must(err)
|
||||
}
|
||||
}()
|
||||
|
||||
conn, err := net.DialTCP("tcp", nil, listener.Addr().(*net.TCPAddr))
|
||||
common.Must(err)
|
||||
|
||||
authConn := auth.Client(conn)
|
||||
defer authConn.Close()
|
||||
|
||||
authConn.Write([]byte("Test payload"))
|
||||
authConn.Write([]byte("Test payload 2"))
|
||||
|
||||
expectedResponse := "Test payloadTest payload 2"
|
||||
actualResponse := make([]byte, 256)
|
||||
deadline := time.Now().Add(time.Second * 5)
|
||||
totalBytes := 0
|
||||
for {
|
||||
n, err := authConn.Read(actualResponse[totalBytes:])
|
||||
common.Must(err)
|
||||
totalBytes += n
|
||||
if totalBytes >= len(expectedResponse) || time.Now().After(deadline) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if string(actualResponse[:totalBytes]) != expectedResponse {
|
||||
t.Error("response: ", string(actualResponse[:totalBytes]))
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnectionInvPath(t *testing.T) {
|
||||
auth, err := NewAuthenticator(context.Background(), &Config{
|
||||
Request: &RequestConfig{
|
||||
Method: &Method{Value: "Post"},
|
||||
Uri: []string{"/testpath"},
|
||||
Header: []*Header{
|
||||
{
|
||||
Name: "Host",
|
||||
Value: []string{"www.example.com", "www.google.com"},
|
||||
},
|
||||
{
|
||||
Name: "User-Agent",
|
||||
Value: []string{"Test-Agent"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Response: &ResponseConfig{
|
||||
Version: &Version{
|
||||
Value: "1.1",
|
||||
},
|
||||
Status: &Status{
|
||||
Code: "404",
|
||||
Reason: "Not Found",
|
||||
},
|
||||
},
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
authR, err := NewAuthenticator(context.Background(), &Config{
|
||||
Request: &RequestConfig{
|
||||
Method: &Method{Value: "Post"},
|
||||
Uri: []string{"/testpathErr"},
|
||||
Header: []*Header{
|
||||
{
|
||||
Name: "Host",
|
||||
Value: []string{"www.example.com", "www.google.com"},
|
||||
},
|
||||
{
|
||||
Name: "User-Agent",
|
||||
Value: []string{"Test-Agent"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Response: &ResponseConfig{
|
||||
Version: &Version{
|
||||
Value: "1.1",
|
||||
},
|
||||
Status: &Status{
|
||||
Code: "404",
|
||||
Reason: "Not Found",
|
||||
},
|
||||
},
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
common.Must(err)
|
||||
|
||||
go func() {
|
||||
conn, err := listener.Accept()
|
||||
common.Must(err)
|
||||
authConn := auth.Server(conn)
|
||||
b := make([]byte, 256)
|
||||
for {
|
||||
n, err := authConn.Read(b)
|
||||
if err != nil {
|
||||
authConn.Close()
|
||||
break
|
||||
}
|
||||
_, err = authConn.Write(b[:n])
|
||||
common.Must(err)
|
||||
}
|
||||
}()
|
||||
|
||||
conn, err := net.DialTCP("tcp", nil, listener.Addr().(*net.TCPAddr))
|
||||
common.Must(err)
|
||||
|
||||
authConn := authR.Client(conn)
|
||||
defer authConn.Close()
|
||||
|
||||
authConn.Write([]byte("Test payload"))
|
||||
authConn.Write([]byte("Test payload 2"))
|
||||
|
||||
expectedResponse := "Test payloadTest payload 2"
|
||||
actualResponse := make([]byte, 256)
|
||||
deadline := time.Now().Add(time.Second * 5)
|
||||
totalBytes := 0
|
||||
for {
|
||||
n, err := authConn.Read(actualResponse[totalBytes:])
|
||||
if err == nil {
|
||||
t.Error("Error Expected", err)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
totalBytes += n
|
||||
if totalBytes >= len(expectedResponse) || time.Now().After(deadline) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnectionInvReq(t *testing.T) {
|
||||
auth, err := NewAuthenticator(context.Background(), &Config{
|
||||
Request: &RequestConfig{
|
||||
Method: &Method{Value: "Post"},
|
||||
Uri: []string{"/testpath"},
|
||||
Header: []*Header{
|
||||
{
|
||||
Name: "Host",
|
||||
Value: []string{"www.example.com", "www.google.com"},
|
||||
},
|
||||
{
|
||||
Name: "User-Agent",
|
||||
Value: []string{"Test-Agent"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Response: &ResponseConfig{
|
||||
Version: &Version{
|
||||
Value: "1.1",
|
||||
},
|
||||
Status: &Status{
|
||||
Code: "404",
|
||||
Reason: "Not Found",
|
||||
},
|
||||
},
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
common.Must(err)
|
||||
|
||||
go func() {
|
||||
conn, err := listener.Accept()
|
||||
common.Must(err)
|
||||
authConn := auth.Server(conn)
|
||||
b := make([]byte, 256)
|
||||
for {
|
||||
n, err := authConn.Read(b)
|
||||
if err != nil {
|
||||
authConn.Close()
|
||||
break
|
||||
}
|
||||
_, err = authConn.Write(b[:n])
|
||||
common.Must(err)
|
||||
}
|
||||
}()
|
||||
|
||||
conn, err := net.DialTCP("tcp", nil, listener.Addr().(*net.TCPAddr))
|
||||
common.Must(err)
|
||||
|
||||
conn.Write([]byte("ABCDEFGHIJKMLN\r\n\r\n"))
|
||||
l, _, err := bufio.NewReader(conn).ReadLine()
|
||||
common.Must(err)
|
||||
if !strings.HasPrefix(string(l), "HTTP/1.1 400 Bad Request") {
|
||||
t.Error("Resp to non http conn", string(l))
|
||||
}
|
||||
}
|
11
transport/internet/headers/http/linkedreadRequest.go
Normal file
11
transport/internet/headers/http/linkedreadRequest.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"net/http"
|
||||
|
||||
_ "unsafe" // required to use //go:linkname
|
||||
)
|
||||
|
||||
//go:linkname readRequest net/http.readRequest
|
||||
func readRequest(b *bufio.Reader, deleteHostHeader bool) (req *http.Request, err error)
|
49
transport/internet/headers/http/resp.go
Normal file
49
transport/internet/headers/http/resp.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
package http
|
||||
|
||||
var resp400 = &ResponseConfig{
|
||||
Version: &Version{
|
||||
Value: "1.1",
|
||||
},
|
||||
Status: &Status{
|
||||
Code: "400",
|
||||
Reason: "Bad Request",
|
||||
},
|
||||
Header: []*Header{
|
||||
{
|
||||
Name: "Connection",
|
||||
Value: []string{"close"},
|
||||
},
|
||||
{
|
||||
Name: "Cache-Control",
|
||||
Value: []string{"private"},
|
||||
},
|
||||
{
|
||||
Name: "Content-Length",
|
||||
Value: []string{"0"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var resp404 = &ResponseConfig{
|
||||
Version: &Version{
|
||||
Value: "1.1",
|
||||
},
|
||||
Status: &Status{
|
||||
Code: "404",
|
||||
Reason: "Not Found",
|
||||
},
|
||||
Header: []*Header{
|
||||
{
|
||||
Name: "Connection",
|
||||
Value: []string{"close"},
|
||||
},
|
||||
{
|
||||
Name: "Cache-Control",
|
||||
Value: []string{"private"},
|
||||
},
|
||||
{
|
||||
Name: "Content-Length",
|
||||
Value: []string{"0"},
|
||||
},
|
||||
},
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue