Files
sing-box/common/httpclient/helpers.go
T

117 lines
3.0 KiB
Go

package httpclient
import (
"context"
stdTLS "crypto/tls"
"io"
"net"
"net/http"
"strings"
"github.com/sagernet/sing-box/common/tls"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"golang.org/x/net/idna"
)
func dialTLS(ctx context.Context, rawDialer N.Dialer, baseTLSConfig tls.Config, destination M.Socksaddr, nextProtos []string, expectProto string) (net.Conn, error) {
if baseTLSConfig == nil {
return nil, E.New("TLS transport unavailable")
}
tlsConfig := baseTLSConfig.Clone()
if tlsConfig.ServerName() == "" && destination.IsValid() {
tlsConfig.SetServerName(destination.AddrString())
}
tlsConfig.SetNextProtos(nextProtos)
conn, err := rawDialer.DialContext(ctx, N.NetworkTCP, destination)
if err != nil {
return nil, err
}
tlsConn, err := tls.ClientHandshake(ctx, conn, tlsConfig)
if err != nil {
conn.Close()
return nil, err
}
if expectProto != "" && tlsConn.ConnectionState().NegotiatedProtocol != expectProto {
tlsConn.Close()
return nil, errHTTP2Fallback
}
return tlsConn, nil
}
func applyHeaders(request *http.Request, headers http.Header, host string) {
for header, values := range headers {
request.Header[header] = append([]string(nil), values...)
}
if host != "" {
request.Host = host
}
}
func requestRequiresHTTP1(request *http.Request) bool {
return strings.Contains(strings.ToLower(request.Header.Get("Connection")), "upgrade") &&
strings.EqualFold(request.Header.Get("Upgrade"), "websocket")
}
func requestReplayable(request *http.Request) bool {
return request.Body == nil || request.Body == http.NoBody || request.GetBody != nil
}
func cloneRequestForRetry(request *http.Request) *http.Request {
cloned := request.Clone(request.Context())
if request.Body != nil && request.Body != http.NoBody && request.GetBody != nil {
cloned.Body = mustGetBody(request)
}
return cloned
}
func mustGetBody(request *http.Request) io.ReadCloser {
body, err := request.GetBody()
if err != nil {
panic(err)
}
return body
}
func requestAuthority(request *http.Request) string {
if request == nil || request.URL == nil || request.URL.Host == "" {
return ""
}
host, port, err := net.SplitHostPort(request.URL.Host)
if err != nil {
host = request.URL.Host
port = ""
}
if port == "" {
if request.URL.Scheme == "http" {
port = "80"
} else {
port = "443"
}
}
if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
return host + ":" + port
}
ascii, idnaErr := idna.Lookup.ToASCII(host)
if idnaErr == nil {
host = ascii
} else {
host = strings.ToLower(host)
}
return net.JoinHostPort(host, port)
}
func buildSTDTLSConfig(baseTLSConfig tls.Config, destination M.Socksaddr, nextProtos []string) (*stdTLS.Config, error) {
if baseTLSConfig == nil {
return nil, nil
}
tlsConfig := baseTLSConfig.Clone()
if tlsConfig.ServerName() == "" && destination.IsValid() {
tlsConfig.SetServerName(destination.AddrString())
}
tlsConfig.SetNextProtos(nextProtos)
return tlsConfig.STDConfig()
}