https_transport.go 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. package transport
  2. import (
  3. "context"
  4. "errors"
  5. "net"
  6. "net/http"
  7. "sync/atomic"
  8. "github.com/sagernet/sing-box/common/tls"
  9. E "github.com/sagernet/sing/common/exceptions"
  10. M "github.com/sagernet/sing/common/metadata"
  11. "golang.org/x/net/http2"
  12. )
  13. var errFallback = E.New("fallback to HTTP/1.1")
  14. type HTTPSTransportWrapper struct {
  15. http2Transport *http2.Transport
  16. httpTransport *http.Transport
  17. fallback *atomic.Bool
  18. }
  19. func NewHTTPSTransportWrapper(dialer tls.Dialer, serverAddr M.Socksaddr) *HTTPSTransportWrapper {
  20. var fallback atomic.Bool
  21. return &HTTPSTransportWrapper{
  22. http2Transport: &http2.Transport{
  23. DialTLSContext: func(ctx context.Context, _, _ string, _ *tls.STDConfig) (net.Conn, error) {
  24. tlsConn, err := dialer.DialTLSContext(ctx, serverAddr)
  25. if err != nil {
  26. return nil, err
  27. }
  28. state := tlsConn.ConnectionState()
  29. if state.NegotiatedProtocol == http2.NextProtoTLS {
  30. return tlsConn, nil
  31. }
  32. tlsConn.Close()
  33. fallback.Store(true)
  34. return nil, errFallback
  35. },
  36. },
  37. httpTransport: &http.Transport{
  38. DialTLSContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
  39. return dialer.DialTLSContext(ctx, serverAddr)
  40. },
  41. },
  42. fallback: &fallback,
  43. }
  44. }
  45. func (h *HTTPSTransportWrapper) RoundTrip(request *http.Request) (*http.Response, error) {
  46. if h.fallback.Load() {
  47. return h.httpTransport.RoundTrip(request)
  48. } else {
  49. response, err := h.http2Transport.RoundTrip(request)
  50. if err != nil {
  51. if errors.Is(err, errFallback) {
  52. return h.httpTransport.RoundTrip(request)
  53. }
  54. return nil, err
  55. }
  56. return response, nil
  57. }
  58. }
  59. func (h *HTTPSTransportWrapper) CloseIdleConnections() {
  60. h.http2Transport.CloseIdleConnections()
  61. h.httpTransport.CloseIdleConnections()
  62. }
  63. func (h *HTTPSTransportWrapper) Clone() *HTTPSTransportWrapper {
  64. return &HTTPSTransportWrapper{
  65. httpTransport: h.httpTransport,
  66. http2Transport: &http2.Transport{
  67. DialTLSContext: h.http2Transport.DialTLSContext,
  68. },
  69. fallback: h.fallback,
  70. }
  71. }