http_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. package storage
  2. import (
  3. "crypto/tls"
  4. "net/http"
  5. "os"
  6. "path"
  7. "testing"
  8. "time"
  9. assert2 "github.com/stretchr/testify/assert"
  10. "github.com/stretchr/testify/require"
  11. )
  12. const (
  13. caFileName = "testCA.pem"
  14. keyFileName = "testkey.pem"
  15. invalidFileName = "invalid.pem"
  16. nonExistentFileName = "no.exist"
  17. // valid CA File for test purposes only
  18. caContent = `-----BEGIN CERTIFICATE-----
  19. MIIF2TCCA8GgAwIBAgIUIY1Kop8xSQEwlz4EeykhGEviRLIwDQYJKoZIhvcNAQEL
  20. BQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExDTALBgNVBAcM
  21. BHRlc3QxDTALBgNVBAoMBHRlc3QxDTALBgNVBAsMBHRlc3QxDTALBgNVBAMMBHRl
  22. c3QxHDAaBgkqhkiG9w0BCQEWDXRlc3RAdGVzdC5jb20wHhcNMjUwNjI0MjEwMTM0
  23. WhcNMjUwNzI0MjEwMTM0WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZv
  24. cm5pYTENMAsGA1UEBwwEdGVzdDENMAsGA1UECgwEdGVzdDENMAsGA1UECwwEdGVz
  25. dDENMAsGA1UEAwwEdGVzdDEcMBoGCSqGSIb3DQEJARYNdGVzdEB0ZXN0LmNvbTCC
  26. AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALYBm4UDowPTNvBxanFKdJ5g
  27. +ZIKkzvIqlAVxKWPWopdlQinoRl6jyofDJ1yhuLiqz4CxDczTv+A1TjxH0RaSdj2
  28. qebOqqhHl+EahV/stc16vOz4mywkrV+C5i5Vk2y1SXqxyzZQtthhvPquHD2C/Z8M
  29. PVVzyN4+gGog0srdXffPhEI774uenkkcBZNh1ycvJJPv6nQ3Ib0Gjk/J7nnV5AvI
  30. glfloy2sENZagtx2EuPxuQzeuJoR62hrBLLG/gR50Mqr1RRxn3BV61Z2q+8vmhK0
  31. qyRF6RiFqrtJy67NGyhlBmlCttI5rZX8lBADCpaLRWDRlZlGtA08Mh9FbiawHRwd
  32. pcN4AVpMfsqRxZJ18LLzZe9XzWrVpaWoF5JFNB8rcF5+eH7ry671AkK8nV0BrbdR
  33. H+zJnbqi1ewQBpL49dYsUheqqZw9w/bq7fgvefxEL2urAfbEwnGfyygReYjiNw7D
  34. z9uMudBoYNQyCTe/lYH0q6xP1Ycso0WfjKL50mcvMOQNaux0Nd/oH1B0WFe4OnFE
  35. QqAvs6g1hxd4W8Q3mjbiTNVmpFU9O5W3yCvNgJt/5UKi/zfXClTsMicAPznvQTlB
  36. +O7GxL4B9mgSNL+8qLwx8NmfJlskk4HrJZZkdk1hTIr9Lj/uu/ooR1tbRDkbeLuY
  37. N6wqN7+jcBWoDZcHh+gDAgMBAAGjUzBRMB0GA1UdDgQWBBQsrXi8RmTF705Ct3RR
  38. Fng02lj7+TAfBgNVHSMEGDAWgBQsrXi8RmTF705Ct3RRFng02lj7+TAPBgNVHRMB
  39. Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQA7MLXyWIRghrsJeOtxfi2xmr2F
  40. di417pi5cGrq98liRlISF/bqXLqLWcTSDpCLdHzLBbUA5CVGU//1g88HuHUC1mbp
  41. ENxdKqRzlK5wkxDifGiLFqX87SOb+dSqkhn85fs6kBkHRBixZwoVcs/WQWQNSbPw
  42. lFx6sVa5oKOPkJbre7AJf9BTWtCUftps0rwQfrlZw9FYmBSGQ1PkhJOdOkbWKhJ6
  43. F55lWdfpcxZRPuemQFWzErQRLMXwzOm9MdmrISkQbRttTjZCvoo0njphckPjdrZJ
  44. kqN2OSFUGmoia/0LfIOaC6LdWri2tEemS69JDJxIXxpxv5GE1PAu6zdQBmV5/rBn
  45. e2EwLSLUxleNDGp4YhyXOXSS3Tm2zCxVmLUKLAqq6IqvIO155J0uTBFKpRGs+3DL
  46. P+c0XFHgp3+tVWnkYGszh6hrKMgYWCneEDAdCXrv2GQjY7ObbRo3f4dR0Iswz8pR
  47. KdrUSftz1CNVmQpjq06nUa9pdZpqwAxuyvKKzBrefprHqUS2WBsiGdmhjpNIagGl
  48. jF3fZ24qJxDOv6vAvF+9jHxfTq+WUrR+tS6BpgvrvVVJkaWsj6s8wlRFny8zrWln
  49. gy8s1O9SI6+368+p37nPPgIfBVjOJpGVLrG3QL/e1kg91mUdhXkfan81NVXdQif5
  50. JXNAW1pC0i+sxoHYfg==
  51. -----END CERTIFICATE-----
  52. `
  53. // valid key file, only used for testing
  54. keyContent = `-----BEGIN PRIVATE KEY-----
  55. MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQC2AZuFA6MD0zbw
  56. cWpxSnSeYPmSCpM7yKpQFcSlj1qKXZUIp6EZeo8qHwydcobi4qs+AsQ3M07/gNU4
  57. 8R9EWknY9qnmzqqoR5fhGoVf7LXNerzs+JssJK1fguYuVZNstUl6scs2ULbYYbz6
  58. rhw9gv2fDD1Vc8jePoBqINLK3V33z4RCO++Lnp5JHAWTYdcnLyST7+p0NyG9Bo5P
  59. ye551eQLyIJX5aMtrBDWWoLcdhLj8bkM3riaEetoawSyxv4EedDKq9UUcZ9wVetW
  60. dqvvL5oStKskRekYhaq7ScuuzRsoZQZpQrbSOa2V/JQQAwqWi0Vg0ZWZRrQNPDIf
  61. RW4msB0cHaXDeAFaTH7KkcWSdfCy82XvV81q1aWlqBeSRTQfK3Befnh+68uu9QJC
  62. vJ1dAa23UR/syZ26otXsEAaS+PXWLFIXqqmcPcP26u34L3n8RC9rqwH2xMJxn8so
  63. EXmI4jcOw8/bjLnQaGDUMgk3v5WB9KusT9WHLKNFn4yi+dJnLzDkDWrsdDXf6B9Q
  64. dFhXuDpxREKgL7OoNYcXeFvEN5o24kzVZqRVPTuVt8grzYCbf+VCov831wpU7DIn
  65. AD8570E5QfjuxsS+AfZoEjS/vKi8MfDZnyZbJJOB6yWWZHZNYUyK/S4/7rv6KEdb
  66. W0Q5G3i7mDesKje/o3AVqA2XB4foAwIDAQABAoICAANcxbTyPadTke5T5LsNVjaa
  67. S13DqKZnoG1Sdm3DgiZ/ChgBTh5nP26BT6xq1Dq0rOiJGb+Nt9cH4Ju+GBf7G9Ty
  68. nV0OShefPaPLcBnholr8+lvdlCpuaBkD8xACEnM6izjFYuRKdIIIDdBxW+68DJOR
  69. S5XYWuIaVm1jw9ihDekG9WhZcRy8vecxEBGS5eOE9pKhFdXQdqkqlrS7DJ+J2wrf
  70. FeN/R9Ko6diVhgPogbaRRAsMn+TWGi35uX5LwRcRz7cNvK55rDqU2rNs0n2HUnew
  71. RDyaLk0YaGfr5MGcBRdrwIDE61m4XIfd9BV/YFpdok5rH7qXpXXlIlRyw9m4UkN0
  72. CzuBMQbUj5eLXurKXZm/+g9v1lTx3tpgyoQXrNljyhQvfNi8DdeijlQj7zOeTXMW
  73. fe+1XZ/R+KahBtgytq9/caKdEbo/CokY5DZFz+RGUiV+jjHGBAj5l4K8gQdLVm3L
  74. v9m69fMvBFXX3WGz1jPfVsvNGOPPothd9fDTQAdjSBWYP8WTBMn62ivojKYIXX6O
  75. ggEQmQ/9HENS0chKz0mYrFPTJ2bauPRzo3ikVXTG7YwyvmAa+CpxaTNBi0z3XGTs
  76. B7lkT0/vYZls2ZApMQc53DJnsUD4hn2zr2SPZry3jotiR2Ww/e/t/nosMxTe4FUW
  77. p/HSOzAHJGY/Lom+5swJAoIBAQD6i/fo1fT5b2pyh/SW4UIgx2GEQK7f3mh4VnnR
  78. Qgtz1PjMCOULIlOyy88ZZxOA+vIwL85lwdFuTaG27C6hfceWAOYtcbWjqen+QXZ8
  79. b5uyyNmFov3+sdazfl7O0Q1VE0jbAxNioSa//8YsqOxVR4PVxJ7cPMqAU701ykHK
  80. IeE5lzm0U74ibvm7sdr6oYSNIfZ+z4dWBE6OFFNPyGhMv9nJA8W6U+nT3DVCqryx
  81. q58mIcN77tKkyFaSCfP8VWcHJdavZBOe8hlv9j+eUakWqTC9RMr47rDpvdUPT5gm
  82. tW+D1304+6HwSu7iysm3zPaDqSTGulTtCIimKDFdTEThwZZ1AoIBAQC597xUkoEj
  83. he4QXs6j/Ds/gocqnTdmRBQH1AXnfWrvnE4CK8ej+4hA/ng70KZcNAFm65DJb5lk
  84. z4vdVmh3cxQgto+EOPY2h81NjDEXscr2hZt7+RX28s7RwL36+Pxs521ut7D5UiFe
  85. gRi/hkbJr2NUSMP6EZMMegvQ65XsAKbdVvJEOKPluUS4w9H2fPplHd3heyM9HtNI
  86. BUY7/eb6PHUZRfexRTYds9ioGlgTFfIMuuZJ8YdK3mM6pJybbYdoqHSuU7aRoneD
  87. bMus82GfDRZh+cw5NrFsWv2ieQiaZpy8MQKY6Zdilq7mXUhSh0llcdt0crXF2Dqn
  88. tiPXZUKejGWXAoIBAC3lF9uB3ecXPrOOLgK5bqicfUOBqcb+cbqhdJ0dcQWd3Jlb
  89. g8FfX1+gL+aiWBNHZLfo+fDv6RJAjD/60avpY3cZ4RAwBSrexCs8CJ1QwH+mhRoS
  90. ul4+a2rj2jAeYUfVSYI89P8bMAL5sm6Z3vjcKc0twD/trtaFAGLrEtQZEq2/AuYC
  91. dRDPrVVxhgBlN+e2cfXWxB7AmTczh/NUba6pchZ9Z2nzVyDk9Kiqp/gPzQ5qHuoD
  92. 3Hgs7pa/1f7CEiZgCwyD04hJJtm4jPzOTqAFDBWPlXK2HpgimvW8Cc4FbFEFVz3p
  93. 8kcXIt1OclcF555EjKUOmuH0rzton2pMv01vbcUCggEAfGuSFic6vVCS2WME79QG
  94. s9QZqNoswYAUwrQJCzru+8bgrjUqSb01CP735FURqKims3wxj4PZ5gex9PElzZ0x
  95. vz1FQdp2aD9tjU+ZXNf4Cf2T7FrXZjRHSTCiKrLA9//SSHwfrH9VkgvfSeyFmdR9
  96. KVvRupJdhsB0/V9RG+fHvFi6mAgpJ75PiyqAZGBziolz9LLU/cSM6SeWOPcDvTIL
  97. yk/0iybaMP8tmjKd8I8DNZ8qChjNQrsNOqP9n0Olj9D819FsWX2QZl642kqvaqFv
  98. 8zcUesbr56ns/fHqXpr+jC5iJXpLbYuREtEgXQ7kfTmy8PL6SJcFj0WeLzMxYjBe
  99. mwKCAQEA+kaxUOF2aj3ML4K0c4DfQCSCYEv29qsd0u9gR4QLoaSSnetWpULBdAJ7
  100. lsFFhuCHLjrkkazXUv/l4a/u8BmSF37wIzb/AWPE5CluC0D7tHENaNj/XTjvvTxV
  101. nVE2fojWIrxRk3qxfy30AVrULCKp8RHn3FilpUUjJGNUBeFV8xsVS3YF75IV7gQB
  102. op0zquisxioZ+5rCR+vn2BErzv/ILJpW2zk9FEJxJ72rcCucpfiTeaBADv7twFxT
  103. rc129p/U6/PKzEE/2voTY1GRb+8sroL1LIEA+K7fvd87C6HEsabGG7fss1Fthi2W
  104. Hef5W/FKNtov4fx8QuMhwA4lWIuliw==
  105. -----END PRIVATE KEY-----`
  106. invalidContent = "invalid"
  107. )
  108. func createKeyFiles(t *testing.T, tmpDir string) func() {
  109. return createTempFiles(
  110. t,
  111. tmpDir,
  112. map[string]string{
  113. caFileName: caContent,
  114. keyFileName: keyContent,
  115. },
  116. )
  117. }
  118. // createTempFiles takes a map of file name and content pairs, creates the files in a tmp dir and returns a cleanup
  119. // function
  120. func createTempFiles(t *testing.T, tmpDir string, files map[string]string) func() {
  121. var filesToRemove []string
  122. for name, content := range files {
  123. tmpFile, err := os.Create(path.Join(tmpDir, name))
  124. require.NoError(t, err)
  125. _, err = tmpFile.WriteString(content)
  126. require.NoError(t, err)
  127. filesToRemove = append(filesToRemove, tmpFile.Name())
  128. tmpFile.Close()
  129. }
  130. return func() {
  131. for _, name := range filesToRemove {
  132. os.Remove(name)
  133. }
  134. }
  135. }
  136. func TestHTTPConfig_GetHTTPTransport(t *testing.T) {
  137. testCases := map[string]struct {
  138. config HTTPConfig
  139. wantError bool
  140. validateFunc func(t *testing.T, transport http.RoundTripper)
  141. errorContains string
  142. }{
  143. "default configuration": {
  144. config: HTTPConfig{
  145. IdleConnTimeout: 90 * time.Second,
  146. ResponseHeaderTimeout: 2 * time.Minute,
  147. TLSHandshakeTimeout: 10 * time.Second,
  148. ExpectContinueTimeout: 1 * time.Second,
  149. MaxIdleConns: 100,
  150. MaxIdleConnsPerHost: 100,
  151. MaxConnsPerHost: 0,
  152. DisableCompression: false,
  153. InsecureSkipVerify: false,
  154. },
  155. wantError: false,
  156. validateFunc: func(t *testing.T, transport http.RoundTripper) {
  157. httpTransport, ok := transport.(*http.Transport)
  158. require.True(t, ok, "Expected *http.Transport")
  159. assert2.Equal(t, 90*time.Second, httpTransport.IdleConnTimeout)
  160. assert2.Equal(t, 2*time.Minute, httpTransport.ResponseHeaderTimeout)
  161. assert2.Equal(t, 10*time.Second, httpTransport.TLSHandshakeTimeout)
  162. assert2.Equal(t, 1*time.Second, httpTransport.ExpectContinueTimeout)
  163. assert2.Equal(t, 100, httpTransport.MaxIdleConns)
  164. assert2.Equal(t, 100, httpTransport.MaxIdleConnsPerHost)
  165. assert2.Equal(t, 0, httpTransport.MaxConnsPerHost)
  166. assert2.False(t, httpTransport.DisableCompression)
  167. assert2.False(t, httpTransport.TLSClientConfig.InsecureSkipVerify)
  168. },
  169. },
  170. "with insecure skip verify": {
  171. config: HTTPConfig{
  172. InsecureSkipVerify: true,
  173. },
  174. wantError: false,
  175. validateFunc: func(t *testing.T, transport http.RoundTripper) {
  176. httpTransport, ok := transport.(*http.Transport)
  177. require.True(t, ok)
  178. assert2.True(t, httpTransport.TLSClientConfig.InsecureSkipVerify)
  179. },
  180. },
  181. "with injected transport": {
  182. config: HTTPConfig{
  183. Transport: &http.Transport{},
  184. },
  185. wantError: false,
  186. validateFunc: func(t *testing.T, transport http.RoundTripper) {
  187. _, ok := transport.(*http.Transport)
  188. require.True(t, ok)
  189. },
  190. },
  191. "with server name": {
  192. config: HTTPConfig{
  193. TLSConfig: TLSConfig{
  194. ServerName: "example.com",
  195. },
  196. },
  197. wantError: false,
  198. validateFunc: func(t *testing.T, transport http.RoundTripper) {
  199. httpTransport, ok := transport.(*http.Transport)
  200. require.True(t, ok)
  201. assert2.Equal(t, "example.com", httpTransport.TLSClientConfig.ServerName)
  202. },
  203. },
  204. "with disable compression": {
  205. config: HTTPConfig{
  206. DisableCompression: true,
  207. },
  208. wantError: false,
  209. validateFunc: func(t *testing.T, transport http.RoundTripper) {
  210. httpTransport, ok := transport.(*http.Transport)
  211. require.True(t, ok)
  212. assert2.True(t, httpTransport.DisableCompression)
  213. },
  214. },
  215. "with invalid TLS config": {
  216. config: HTTPConfig{
  217. TLSConfig: TLSConfig{
  218. CertFile: "cert.pem", // cert without key should cause error
  219. },
  220. },
  221. wantError: true,
  222. errorContains: "client cert file",
  223. },
  224. }
  225. for name, tc := range testCases {
  226. t.Run(name, func(t *testing.T) {
  227. transport, err := tc.config.GetHTTPTransport()
  228. if tc.wantError {
  229. assert2.Error(t, err)
  230. if tc.errorContains != "" {
  231. assert2.Contains(t, err.Error(), tc.errorContains)
  232. }
  233. return
  234. }
  235. require.NoError(t, err)
  236. require.NotNil(t, transport)
  237. if tc.validateFunc != nil {
  238. tc.validateFunc(t, transport)
  239. }
  240. })
  241. }
  242. }
  243. func TestTLSConfig_ToConfig(t *testing.T) {
  244. tmpDir := os.TempDir()
  245. cleanupFn := createKeyFiles(t, tmpDir)
  246. t.Cleanup(cleanupFn)
  247. testCases := map[string]struct {
  248. config *TLSConfig
  249. want *tls.Config
  250. wantError bool
  251. validateFunc func(t *testing.T, tlsConfig *tls.Config)
  252. }{
  253. "default configuration": {
  254. config: &TLSConfig{},
  255. want: &tls.Config{},
  256. wantError: false,
  257. },
  258. "with insecure skip verify": {
  259. config: &TLSConfig{
  260. InsecureSkipVerify: true,
  261. },
  262. want: &tls.Config{
  263. InsecureSkipVerify: true,
  264. },
  265. wantError: false,
  266. },
  267. "missing CA file": {
  268. config: &TLSConfig{
  269. CAFile: path.Join(tmpDir, nonExistentFileName),
  270. },
  271. wantError: true,
  272. },
  273. "invalid CA file": {
  274. config: &TLSConfig{
  275. CAFile: path.Join(tmpDir, invalidFileName),
  276. },
  277. wantError: true,
  278. },
  279. "with server name": {
  280. config: &TLSConfig{
  281. ServerName: "example.com",
  282. },
  283. want: &tls.Config{
  284. ServerName: "example.com",
  285. },
  286. wantError: false,
  287. },
  288. "cert file without key file": {
  289. config: &TLSConfig{
  290. CertFile: path.Join(tmpDir, caFileName),
  291. },
  292. wantError: true,
  293. },
  294. "key file without cert file": {
  295. config: &TLSConfig{
  296. KeyFile: path.Join(tmpDir, keyFileName),
  297. },
  298. wantError: true,
  299. },
  300. "invalid cert file": {
  301. config: &TLSConfig{
  302. CertFile: path.Join(tmpDir, invalidFileName),
  303. KeyFile: path.Join(tmpDir, keyFileName),
  304. },
  305. wantError: true,
  306. },
  307. "invalid key file": {
  308. config: &TLSConfig{
  309. CertFile: path.Join(tmpDir, caFileName),
  310. KeyFile: path.Join(tmpDir, invalidFileName),
  311. },
  312. wantError: true,
  313. },
  314. "valid Cert and Key file": {
  315. config: &TLSConfig{
  316. CertFile: path.Join(tmpDir, caFileName),
  317. KeyFile: path.Join(tmpDir, keyFileName),
  318. },
  319. want: &tls.Config{
  320. GetClientCertificate: TLSConfig{
  321. CertFile: path.Join(tmpDir, caFileName),
  322. KeyFile: path.Join(tmpDir, keyFileName),
  323. }.getClientCertificate,
  324. },
  325. wantError: false,
  326. },
  327. }
  328. for name, tc := range testCases {
  329. t.Run(name, func(t *testing.T) {
  330. tlsConfig, err := tc.config.ToConfig()
  331. if tc.wantError {
  332. assert2.Error(t, err)
  333. return
  334. }
  335. require.NoError(t, err)
  336. tlsConfigEqual(t, tlsConfig, tc.want)
  337. })
  338. }
  339. }
  340. func tlsConfigEqual(t *testing.T, got, want *tls.Config) {
  341. if want == nil {
  342. assert2.Nil(t, got)
  343. return
  344. } else {
  345. assert2.NotNil(t, got)
  346. }
  347. assert2.Equal(t, got.InsecureSkipVerify, want.InsecureSkipVerify)
  348. assert2.Equal(t, got.ServerName, want.ServerName)
  349. assert2.Equal(t, got.GetClientCertificate == nil, want.GetClientCertificate == nil)
  350. if want.GetClientCertificate != nil {
  351. gotCert, gotError := got.GetClientCertificate(nil)
  352. assert2.NoError(t, gotError)
  353. wantCert, wantError := want.GetClientCertificate(nil)
  354. assert2.NoError(t, wantError)
  355. assert2.Equal(t, gotCert, wantCert)
  356. }
  357. }
  358. func TestReadCAFile(t *testing.T) {
  359. tmpDir := os.TempDir()
  360. cleanupFn := createKeyFiles(t, tmpDir)
  361. t.Cleanup(cleanupFn)
  362. testCases := map[string]struct {
  363. fileName string
  364. content string
  365. wantError bool
  366. }{
  367. "nonexistent file": {
  368. fileName: nonExistentFileName,
  369. content: "",
  370. wantError: true,
  371. },
  372. "invalid file": {
  373. fileName: invalidFileName,
  374. content: invalidContent,
  375. wantError: true,
  376. },
  377. "valid file": {
  378. fileName: caFileName,
  379. content: caContent,
  380. wantError: false,
  381. },
  382. }
  383. for name, tc := range testCases {
  384. t.Run(name, func(t *testing.T) {
  385. data, err := readCAFile(path.Join(tmpDir, tc.fileName))
  386. if tc.wantError {
  387. assert2.Error(t, err)
  388. return
  389. }
  390. require.NoError(t, err)
  391. assert2.Equal(t, tc.content, string(data))
  392. })
  393. }
  394. }
  395. func TestUpdateRootCA(t *testing.T) {
  396. testCases := map[string]struct {
  397. input []byte
  398. expectedOk bool
  399. }{
  400. "valid PEM certificate": {
  401. input: []byte(caContent),
  402. expectedOk: true, // This is a valid certificate
  403. },
  404. "invalid PEM data": {
  405. input: []byte(invalidContent),
  406. expectedOk: false,
  407. },
  408. "empty data": {
  409. input: []byte(""),
  410. expectedOk: false,
  411. },
  412. }
  413. for name, tc := range testCases {
  414. t.Run(name, func(t *testing.T) {
  415. tlsConfig := &tls.Config{}
  416. result := updateRootCA(tlsConfig, tc.input)
  417. assert2.Equal(t, tc.expectedOk, result)
  418. if result {
  419. assert2.NotNil(t, tlsConfig.RootCAs)
  420. } else {
  421. assert2.Nil(t, tlsConfig.RootCAs)
  422. }
  423. })
  424. }
  425. }
  426. func TestTLSConfig_getClientCertificate(t *testing.T) {
  427. tmpDir := os.TempDir()
  428. cleanupFn := createKeyFiles(t, tmpDir)
  429. t.Cleanup(cleanupFn)
  430. testCases := map[string]struct {
  431. config *TLSConfig
  432. wantError bool
  433. }{
  434. "empty config": {
  435. config: &TLSConfig{},
  436. wantError: true,
  437. },
  438. "nonexistent cert files": {
  439. config: &TLSConfig{
  440. CertFile: path.Join(tmpDir, nonExistentFileName),
  441. KeyFile: path.Join(tmpDir, nonExistentFileName),
  442. },
  443. wantError: true,
  444. },
  445. "missing cert file": {
  446. config: &TLSConfig{
  447. CertFile: path.Join(tmpDir, nonExistentFileName),
  448. KeyFile: path.Join(tmpDir, keyFileName),
  449. },
  450. wantError: true,
  451. },
  452. "missing key file": {
  453. config: &TLSConfig{
  454. CertFile: path.Join(tmpDir, caFileName),
  455. KeyFile: path.Join(tmpDir, nonExistentFileName),
  456. },
  457. wantError: true,
  458. },
  459. "invalid cert file": {
  460. config: &TLSConfig{
  461. CertFile: path.Join(tmpDir, invalidFileName),
  462. KeyFile: path.Join(tmpDir, keyFileName),
  463. },
  464. wantError: true,
  465. },
  466. "invalid key file": {
  467. config: &TLSConfig{
  468. CertFile: path.Join(tmpDir, caFileName),
  469. KeyFile: path.Join(tmpDir, invalidFileName),
  470. },
  471. wantError: true,
  472. },
  473. "valid": {
  474. config: &TLSConfig{
  475. CertFile: path.Join(tmpDir, caFileName),
  476. KeyFile: path.Join(tmpDir, keyFileName),
  477. },
  478. wantError: false,
  479. },
  480. }
  481. for name, tc := range testCases {
  482. t.Run(name, func(t *testing.T) {
  483. _, err := tc.config.getClientCertificate(nil)
  484. if tc.wantError {
  485. assert2.Error(t, err)
  486. return
  487. }
  488. require.NoError(t, err)
  489. })
  490. }
  491. }
  492. func TestHTTPConfigIntegration(t *testing.T) {
  493. config := HTTPConfig{
  494. IdleConnTimeout: 30 * time.Second,
  495. ResponseHeaderTimeout: 1 * time.Minute,
  496. TLSHandshakeTimeout: 5 * time.Second,
  497. ExpectContinueTimeout: 500 * time.Millisecond,
  498. MaxIdleConns: 50,
  499. MaxIdleConnsPerHost: 25,
  500. MaxConnsPerHost: 10,
  501. DisableCompression: true,
  502. InsecureSkipVerify: true,
  503. TLSConfig: TLSConfig{
  504. ServerName: "test-server.com",
  505. InsecureSkipVerify: false, // This should be overridden by HTTPConfig.InsecureSkipVerify
  506. },
  507. }
  508. transport, err := config.GetHTTPTransport()
  509. require.NoError(t, err)
  510. httpTransport, ok := transport.(*http.Transport)
  511. require.True(t, ok)
  512. // Verify all settings are applied correctly
  513. assert2.Equal(t, 30*time.Second, httpTransport.IdleConnTimeout)
  514. assert2.Equal(t, 1*time.Minute, httpTransport.ResponseHeaderTimeout)
  515. assert2.Equal(t, 5*time.Second, httpTransport.TLSHandshakeTimeout)
  516. assert2.Equal(t, 500*time.Millisecond, httpTransport.ExpectContinueTimeout)
  517. assert2.Equal(t, 50, httpTransport.MaxIdleConns)
  518. assert2.Equal(t, 25, httpTransport.MaxIdleConnsPerHost)
  519. assert2.Equal(t, 10, httpTransport.MaxConnsPerHost)
  520. assert2.True(t, httpTransport.DisableCompression)
  521. assert2.True(t, httpTransport.TLSClientConfig.InsecureSkipVerify) // Should be overridden
  522. assert2.Equal(t, "test-server.com", httpTransport.TLSClientConfig.ServerName)
  523. assert2.NotNil(t, httpTransport.Proxy)
  524. assert2.NotNil(t, httpTransport.DialContext)
  525. }