diff --git a/docs/configuration/outbound/http.md b/docs/configuration/outbound/http.md index 40217122..c6942223 100644 --- a/docs/configuration/outbound/http.md +++ b/docs/configuration/outbound/http.md @@ -11,6 +11,8 @@ "server_port": 1080, "username": "sekai", "password": "admin", + "path": "", + "headers": {}, "tls": {}, ... // Dial Fields @@ -39,6 +41,14 @@ Basic authorization username. Basic authorization password. +#### path + +Path of HTTP request. + +#### headers + +Extra headers of HTTP request. + #### tls TLS configuration, see [TLS](/configuration/shared/tls/#outbound). diff --git a/docs/configuration/outbound/http.zh.md b/docs/configuration/outbound/http.zh.md index c633a203..53dd1b6a 100644 --- a/docs/configuration/outbound/http.zh.md +++ b/docs/configuration/outbound/http.zh.md @@ -11,6 +11,8 @@ "server_port": 1080, "username": "sekai", "password": "admin", + "path": "", + "headers": {}, "tls": {}, ... // 拨号字段 @@ -39,6 +41,14 @@ Basic 认证用户名。 Basic 认证密码。 +#### path + +HTTP 请求路径。 + +#### headers + +HTTP 请求的额外标头。 + #### tls TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。 diff --git a/go.mod b/go.mod index 6ab410c7..db37c4ae 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/sagernet/gomobile v0.0.0-20221130124640-349ebaa752ca github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 - github.com/sagernet/sing v0.2.3-0.20230411023500-28b06822071a + github.com/sagernet/sing v0.2.3-0.20230412070623-d88db59703cd github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440 github.com/sagernet/sing-shadowsocks v0.2.1-0.20230409094647-5c830455eb9b github.com/sagernet/sing-shadowtls v0.1.1-0.20230409094821-9abef019436f diff --git a/go.sum b/go.sum index ab22dc8c..effc09d4 100644 --- a/go.sum +++ b/go.sum @@ -111,8 +111,8 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= -github.com/sagernet/sing v0.2.3-0.20230411023500-28b06822071a h1:Wt9rCd4Efev8SesIyG3FiUeN75oONlezWVSKRRkXR2E= -github.com/sagernet/sing v0.2.3-0.20230411023500-28b06822071a/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= +github.com/sagernet/sing v0.2.3-0.20230412070623-d88db59703cd h1:SVO7Lr04d8P6EMrBDgv2fkpxPBeJuz/OZukk7+ymTqw= +github.com/sagernet/sing v0.2.3-0.20230412070623-d88db59703cd/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440 h1:VH8/BcOVuApHtS+vKP+khxlGRcXH7KKhgkTDtNynqSQ= github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440/go.mod h1:69PNSHyEmXdjf6C+bXBOdr2GQnPeEyWjIzo/MV8gmz8= github.com/sagernet/sing-shadowsocks v0.2.1-0.20230409094647-5c830455eb9b h1:nmP+V4nlc8lqEMpwjjbny8ISkrFIjvKWIETsjs7nSic= diff --git a/option/simple.go b/option/simple.go index 60cf074b..dec1e0e0 100644 --- a/option/simple.go +++ b/option/simple.go @@ -30,5 +30,6 @@ type HTTPOutboundOptions struct { Username string `json:"username,omitempty"` Password string `json:"password,omitempty"` TLS *OutboundTLSOptions `json:"tls,omitempty"` + Path string `json:"path,omitempty"` Headers map[string]Listable[string] `json:"headers,omitempty"` } diff --git a/outbound/http.go b/outbound/http.go index 075c1e53..019cb37e 100644 --- a/outbound/http.go +++ b/outbound/http.go @@ -45,7 +45,14 @@ func NewHTTP(router adapter.Router, logger log.ContextLogger, tag string, option logger: logger, tag: tag, }, - sHTTP.NewClient(detour, options.ServerOptions.Build(), options.Username, options.Password, headers), + sHTTP.NewClient(sHTTP.Options{ + Dialer: detour, + Server: options.ServerOptions.Build(), + Username: options.Username, + Password: options.Password, + Path: options.Path, + Headers: headers, + }), }, nil } diff --git a/transport/v2ray/transport.go b/transport/v2ray/transport.go index a70e4c02..3481c852 100644 --- a/transport/v2ray/transport.go +++ b/transport/v2ray/transport.go @@ -46,7 +46,7 @@ func NewClientTransport(ctx context.Context, dialer N.Dialer, serverAddr M.Socks } switch options.Type { case C.V2RayTransportTypeHTTP: - return v2rayhttp.NewClient(ctx, dialer, serverAddr, options.HTTPOptions, tlsConfig), nil + return v2rayhttp.NewClient(ctx, dialer, serverAddr, options.HTTPOptions, tlsConfig) case C.V2RayTransportTypeGRPC: return NewGRPCClient(ctx, dialer, serverAddr, options.GRPCOptions, tlsConfig) case C.V2RayTransportTypeWebsocket: diff --git a/transport/v2rayhttp/client.go b/transport/v2rayhttp/client.go index b2095bee..80ff13db 100644 --- a/transport/v2rayhttp/client.go +++ b/transport/v2rayhttp/client.go @@ -7,7 +7,6 @@ import ( "net" "net/http" "net/url" - "strings" "time" "github.com/sagernet/sing-box/adapter" @@ -16,6 +15,7 @@ import ( E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" + sHTTP "github.com/sagernet/sing/protocol/http" "golang.org/x/net/http2" ) @@ -34,7 +34,7 @@ type Client struct { headers http.Header } -func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayHTTPOptions, tlsConfig tls.Config) adapter.V2RayClientTransport { +func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayHTTPOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) { var transport http.RoundTripper if tlsConfig == nil { transport = &http.Transport{ @@ -79,14 +79,15 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt } uri.Host = serverAddr.String() uri.Path = options.Path - if !strings.HasPrefix(uri.Path, "/") { - uri.Path = "/" + uri.Path + err := sHTTP.URLSetPath(&uri, options.Path) + if err != nil { + return nil, E.New("failed to set path: " + err.Error()) } for key, valueList := range options.Headers { client.headers[key] = valueList } client.url = &uri - return client + return client, nil } func (c *Client) DialContext(ctx context.Context) (net.Conn, error) { diff --git a/transport/v2raywebsocket/client.go b/transport/v2raywebsocket/client.go index ea17455f..f61d0c90 100644 --- a/transport/v2raywebsocket/client.go +++ b/transport/v2raywebsocket/client.go @@ -5,7 +5,6 @@ import ( "net" "net/http" "net/url" - "strings" "time" "github.com/sagernet/sing-box/adapter" @@ -15,6 +14,7 @@ import ( E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" + sHTTP "github.com/sagernet/sing/protocol/http" "github.com/sagernet/websocket" ) @@ -58,12 +58,9 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt } uri.Host = serverAddr.String() uri.Path = options.Path - if !strings.HasPrefix(uri.Path, "/") { - uri.Path = "/" + uri.Path - } - if strings.HasSuffix(uri.Path, "?") { - uri.ForceQuery = true - uri.Path = strings.TrimSuffix(uri.Path, "?") + err := sHTTP.URLSetPath(&uri, options.Path) + if err != nil { + return nil } headers := make(http.Header) for key, value := range options.Headers {