| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- 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()
- }
|