dialer_test.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. package proxyutil
  2. import (
  3. "net/http"
  4. "net/url"
  5. "testing"
  6. "github.com/stretchr/testify/assert"
  7. "github.com/stretchr/testify/require"
  8. )
  9. func TestConfigureTransportProxy_Nil(t *testing.T) {
  10. transport := &http.Transport{}
  11. err := ConfigureTransportProxy(transport, nil)
  12. require.NoError(t, err)
  13. assert.Nil(t, transport.Proxy, "nil proxy should not set Proxy")
  14. assert.Nil(t, transport.DialContext, "nil proxy should not set DialContext")
  15. }
  16. func TestConfigureTransportProxy_HTTP(t *testing.T) {
  17. transport := &http.Transport{}
  18. proxyURL, _ := url.Parse("http://proxy.example.com:8080")
  19. err := ConfigureTransportProxy(transport, proxyURL)
  20. require.NoError(t, err)
  21. assert.NotNil(t, transport.Proxy, "HTTP proxy should set Proxy")
  22. assert.Nil(t, transport.DialContext, "HTTP proxy should not set DialContext")
  23. }
  24. func TestConfigureTransportProxy_HTTPS(t *testing.T) {
  25. transport := &http.Transport{}
  26. proxyURL, _ := url.Parse("https://secure-proxy.example.com:8443")
  27. err := ConfigureTransportProxy(transport, proxyURL)
  28. require.NoError(t, err)
  29. assert.NotNil(t, transport.Proxy, "HTTPS proxy should set Proxy")
  30. assert.Nil(t, transport.DialContext, "HTTPS proxy should not set DialContext")
  31. }
  32. func TestConfigureTransportProxy_SOCKS5(t *testing.T) {
  33. transport := &http.Transport{}
  34. proxyURL, _ := url.Parse("socks5://socks.example.com:1080")
  35. err := ConfigureTransportProxy(transport, proxyURL)
  36. require.NoError(t, err)
  37. assert.Nil(t, transport.Proxy, "SOCKS5 proxy should not set Proxy")
  38. assert.NotNil(t, transport.DialContext, "SOCKS5 proxy should set DialContext")
  39. }
  40. func TestConfigureTransportProxy_SOCKS5H(t *testing.T) {
  41. transport := &http.Transport{}
  42. proxyURL, _ := url.Parse("socks5h://socks.example.com:1080")
  43. err := ConfigureTransportProxy(transport, proxyURL)
  44. require.NoError(t, err)
  45. assert.Nil(t, transport.Proxy, "SOCKS5H proxy should not set Proxy")
  46. assert.NotNil(t, transport.DialContext, "SOCKS5H proxy should set DialContext")
  47. }
  48. func TestConfigureTransportProxy_CaseInsensitive(t *testing.T) {
  49. testCases := []struct {
  50. scheme string
  51. useProxy bool // true = uses Transport.Proxy, false = uses DialContext
  52. }{
  53. {"HTTP://proxy.example.com:8080", true},
  54. {"Http://proxy.example.com:8080", true},
  55. {"HTTPS://proxy.example.com:8443", true},
  56. {"Https://proxy.example.com:8443", true},
  57. {"SOCKS5://socks.example.com:1080", false},
  58. {"Socks5://socks.example.com:1080", false},
  59. {"SOCKS5H://socks.example.com:1080", false},
  60. {"Socks5h://socks.example.com:1080", false},
  61. }
  62. for _, tc := range testCases {
  63. t.Run(tc.scheme, func(t *testing.T) {
  64. transport := &http.Transport{}
  65. proxyURL, _ := url.Parse(tc.scheme)
  66. err := ConfigureTransportProxy(transport, proxyURL)
  67. require.NoError(t, err)
  68. if tc.useProxy {
  69. assert.NotNil(t, transport.Proxy)
  70. assert.Nil(t, transport.DialContext)
  71. } else {
  72. assert.Nil(t, transport.Proxy)
  73. assert.NotNil(t, transport.DialContext)
  74. }
  75. })
  76. }
  77. }
  78. func TestConfigureTransportProxy_Unsupported(t *testing.T) {
  79. testCases := []string{
  80. "ftp://ftp.example.com",
  81. "file:///path/to/file",
  82. "unknown://example.com",
  83. }
  84. for _, tc := range testCases {
  85. t.Run(tc, func(t *testing.T) {
  86. transport := &http.Transport{}
  87. proxyURL, _ := url.Parse(tc)
  88. err := ConfigureTransportProxy(transport, proxyURL)
  89. require.Error(t, err)
  90. assert.Contains(t, err.Error(), "unsupported proxy scheme")
  91. })
  92. }
  93. }
  94. func TestConfigureTransportProxy_WithAuth(t *testing.T) {
  95. transport := &http.Transport{}
  96. proxyURL, _ := url.Parse("socks5://user:password@socks.example.com:1080")
  97. err := ConfigureTransportProxy(transport, proxyURL)
  98. require.NoError(t, err)
  99. assert.NotNil(t, transport.DialContext, "SOCKS5 with auth should set DialContext")
  100. }
  101. func TestConfigureTransportProxy_EmptyScheme(t *testing.T) {
  102. transport := &http.Transport{}
  103. // 空 scheme 的 URL
  104. proxyURL := &url.URL{Host: "proxy.example.com:8080"}
  105. err := ConfigureTransportProxy(transport, proxyURL)
  106. require.Error(t, err)
  107. assert.Contains(t, err.Error(), "unsupported proxy scheme")
  108. }
  109. func TestConfigureTransportProxy_PreservesExistingConfig(t *testing.T) {
  110. // 验证代理配置不会覆盖 Transport 的其他配置
  111. transport := &http.Transport{
  112. MaxIdleConns: 100,
  113. MaxIdleConnsPerHost: 10,
  114. }
  115. proxyURL, _ := url.Parse("socks5://socks.example.com:1080")
  116. err := ConfigureTransportProxy(transport, proxyURL)
  117. require.NoError(t, err)
  118. assert.Equal(t, 100, transport.MaxIdleConns, "MaxIdleConns should be preserved")
  119. assert.Equal(t, 10, transport.MaxIdleConnsPerHost, "MaxIdleConnsPerHost should be preserved")
  120. assert.NotNil(t, transport.DialContext, "DialContext should be set")
  121. }
  122. func TestConfigureTransportProxy_IPv6(t *testing.T) {
  123. testCases := []struct {
  124. name string
  125. proxyURL string
  126. }{
  127. {"SOCKS5H with IPv6 loopback", "socks5h://[::1]:1080"},
  128. {"SOCKS5 with full IPv6", "socks5://[2001:db8::1]:1080"},
  129. {"HTTP with IPv6", "http://[::1]:8080"},
  130. }
  131. for _, tc := range testCases {
  132. t.Run(tc.name, func(t *testing.T) {
  133. transport := &http.Transport{}
  134. proxyURL, err := url.Parse(tc.proxyURL)
  135. require.NoError(t, err, "URL should be parseable")
  136. err = ConfigureTransportProxy(transport, proxyURL)
  137. require.NoError(t, err)
  138. })
  139. }
  140. }
  141. func TestConfigureTransportProxy_SpecialCharsInPassword(t *testing.T) {
  142. testCases := []struct {
  143. name string
  144. proxyURL string
  145. }{
  146. // 密码包含 @ 符号(URL 编码为 %40)
  147. {"password with @", "socks5://user:p%40ssword@proxy.example.com:1080"},
  148. // 密码包含 : 符号(URL 编码为 %3A)
  149. {"password with :", "socks5://user:pass%3Aword@proxy.example.com:1080"},
  150. // 密码包含 / 符号(URL 编码为 %2F)
  151. {"password with /", "socks5://user:pass%2Fword@proxy.example.com:1080"},
  152. // 复杂密码
  153. {"complex password", "socks5h://admin:P%40ss%3Aw0rd%2F123@proxy.example.com:1080"},
  154. }
  155. for _, tc := range testCases {
  156. t.Run(tc.name, func(t *testing.T) {
  157. transport := &http.Transport{}
  158. proxyURL, err := url.Parse(tc.proxyURL)
  159. require.NoError(t, err, "URL should be parseable")
  160. err = ConfigureTransportProxy(transport, proxyURL)
  161. require.NoError(t, err)
  162. assert.NotNil(t, transport.DialContext, "SOCKS5 should set DialContext")
  163. })
  164. }
  165. }