http_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  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. MinVersion: tls.VersionTLS12,
  257. },
  258. wantError: false,
  259. },
  260. "with insecure skip verify": {
  261. config: &TLSConfig{
  262. InsecureSkipVerify: true,
  263. },
  264. want: &tls.Config{
  265. InsecureSkipVerify: true,
  266. MinVersion: tls.VersionTLS12,
  267. },
  268. wantError: false,
  269. },
  270. "missing CA file": {
  271. config: &TLSConfig{
  272. CAFile: path.Join(tmpDir, nonExistentFileName),
  273. },
  274. wantError: true,
  275. },
  276. "invalid CA file": {
  277. config: &TLSConfig{
  278. CAFile: path.Join(tmpDir, invalidFileName),
  279. },
  280. wantError: true,
  281. },
  282. "with server name": {
  283. config: &TLSConfig{
  284. ServerName: "example.com",
  285. },
  286. want: &tls.Config{
  287. ServerName: "example.com",
  288. MinVersion: tls.VersionTLS12,
  289. },
  290. wantError: false,
  291. },
  292. "cert file without key file": {
  293. config: &TLSConfig{
  294. CertFile: path.Join(tmpDir, caFileName),
  295. },
  296. wantError: true,
  297. },
  298. "key file without cert file": {
  299. config: &TLSConfig{
  300. KeyFile: path.Join(tmpDir, keyFileName),
  301. },
  302. wantError: true,
  303. },
  304. "invalid cert file": {
  305. config: &TLSConfig{
  306. CertFile: path.Join(tmpDir, invalidFileName),
  307. KeyFile: path.Join(tmpDir, keyFileName),
  308. },
  309. wantError: true,
  310. },
  311. "invalid key file": {
  312. config: &TLSConfig{
  313. CertFile: path.Join(tmpDir, caFileName),
  314. KeyFile: path.Join(tmpDir, invalidFileName),
  315. },
  316. wantError: true,
  317. },
  318. "valid Cert and Key file": {
  319. config: &TLSConfig{
  320. CertFile: path.Join(tmpDir, caFileName),
  321. KeyFile: path.Join(tmpDir, keyFileName),
  322. },
  323. want: &tls.Config{
  324. GetClientCertificate: TLSConfig{
  325. CertFile: path.Join(tmpDir, caFileName),
  326. KeyFile: path.Join(tmpDir, keyFileName),
  327. }.getClientCertificate,
  328. MinVersion: tls.VersionTLS12,
  329. },
  330. wantError: false,
  331. },
  332. }
  333. for name, tc := range testCases {
  334. t.Run(name, func(t *testing.T) {
  335. tlsConfig, err := tc.config.ToConfig()
  336. if tc.wantError {
  337. assert2.Error(t, err)
  338. return
  339. }
  340. require.NoError(t, err)
  341. tlsConfigEqual(t, tlsConfig, tc.want)
  342. })
  343. }
  344. }
  345. func tlsConfigEqual(t *testing.T, got, want *tls.Config) {
  346. if want == nil {
  347. assert2.Nil(t, got)
  348. return
  349. } else {
  350. assert2.NotNil(t, got)
  351. }
  352. assert2.Equal(t, got.InsecureSkipVerify, want.InsecureSkipVerify)
  353. assert2.Equal(t, got.ServerName, want.ServerName)
  354. assert2.Equal(t, got.GetClientCertificate == nil, want.GetClientCertificate == nil)
  355. if want.GetClientCertificate != nil {
  356. gotCert, gotError := got.GetClientCertificate(nil)
  357. assert2.NoError(t, gotError)
  358. wantCert, wantError := want.GetClientCertificate(nil)
  359. assert2.NoError(t, wantError)
  360. assert2.Equal(t, gotCert, wantCert)
  361. }
  362. }
  363. func TestReadCAFile(t *testing.T) {
  364. tmpDir := os.TempDir()
  365. cleanupFn := createKeyFiles(t, tmpDir)
  366. t.Cleanup(cleanupFn)
  367. testCases := map[string]struct {
  368. fileName string
  369. content string
  370. wantError bool
  371. }{
  372. "nonexistent file": {
  373. fileName: nonExistentFileName,
  374. content: "",
  375. wantError: true,
  376. },
  377. "invalid file": {
  378. fileName: invalidFileName,
  379. content: invalidContent,
  380. wantError: true,
  381. },
  382. "valid file": {
  383. fileName: caFileName,
  384. content: caContent,
  385. wantError: false,
  386. },
  387. }
  388. for name, tc := range testCases {
  389. t.Run(name, func(t *testing.T) {
  390. data, err := readCAFile(path.Join(tmpDir, tc.fileName))
  391. if tc.wantError {
  392. assert2.Error(t, err)
  393. return
  394. }
  395. require.NoError(t, err)
  396. assert2.Equal(t, tc.content, string(data))
  397. })
  398. }
  399. }
  400. func TestUpdateRootCA(t *testing.T) {
  401. testCases := map[string]struct {
  402. input []byte
  403. expectedOk bool
  404. }{
  405. "valid PEM certificate": {
  406. input: []byte(caContent),
  407. expectedOk: true, // This is a valid certificate
  408. },
  409. "invalid PEM data": {
  410. input: []byte(invalidContent),
  411. expectedOk: false,
  412. },
  413. "empty data": {
  414. input: []byte(""),
  415. expectedOk: false,
  416. },
  417. }
  418. for name, tc := range testCases {
  419. t.Run(name, func(t *testing.T) {
  420. tlsConfig := &tls.Config{}
  421. result := updateRootCA(tlsConfig, tc.input)
  422. assert2.Equal(t, tc.expectedOk, result)
  423. if result {
  424. assert2.NotNil(t, tlsConfig.RootCAs)
  425. } else {
  426. assert2.Nil(t, tlsConfig.RootCAs)
  427. }
  428. })
  429. }
  430. }
  431. func TestTLSConfig_getClientCertificate(t *testing.T) {
  432. tmpDir := os.TempDir()
  433. cleanupFn := createKeyFiles(t, tmpDir)
  434. t.Cleanup(cleanupFn)
  435. testCases := map[string]struct {
  436. config *TLSConfig
  437. wantError bool
  438. }{
  439. "empty config": {
  440. config: &TLSConfig{},
  441. wantError: true,
  442. },
  443. "nonexistent cert files": {
  444. config: &TLSConfig{
  445. CertFile: path.Join(tmpDir, nonExistentFileName),
  446. KeyFile: path.Join(tmpDir, nonExistentFileName),
  447. },
  448. wantError: true,
  449. },
  450. "missing cert file": {
  451. config: &TLSConfig{
  452. CertFile: path.Join(tmpDir, nonExistentFileName),
  453. KeyFile: path.Join(tmpDir, keyFileName),
  454. },
  455. wantError: true,
  456. },
  457. "missing key file": {
  458. config: &TLSConfig{
  459. CertFile: path.Join(tmpDir, caFileName),
  460. KeyFile: path.Join(tmpDir, nonExistentFileName),
  461. },
  462. wantError: true,
  463. },
  464. "invalid cert file": {
  465. config: &TLSConfig{
  466. CertFile: path.Join(tmpDir, invalidFileName),
  467. KeyFile: path.Join(tmpDir, keyFileName),
  468. },
  469. wantError: true,
  470. },
  471. "invalid key file": {
  472. config: &TLSConfig{
  473. CertFile: path.Join(tmpDir, caFileName),
  474. KeyFile: path.Join(tmpDir, invalidFileName),
  475. },
  476. wantError: true,
  477. },
  478. "valid": {
  479. config: &TLSConfig{
  480. CertFile: path.Join(tmpDir, caFileName),
  481. KeyFile: path.Join(tmpDir, keyFileName),
  482. },
  483. wantError: false,
  484. },
  485. }
  486. for name, tc := range testCases {
  487. t.Run(name, func(t *testing.T) {
  488. _, err := tc.config.getClientCertificate(nil)
  489. if tc.wantError {
  490. assert2.Error(t, err)
  491. return
  492. }
  493. require.NoError(t, err)
  494. })
  495. }
  496. }
  497. func TestHTTPConfigIntegration(t *testing.T) {
  498. config := HTTPConfig{
  499. IdleConnTimeout: 30 * time.Second,
  500. ResponseHeaderTimeout: 1 * time.Minute,
  501. TLSHandshakeTimeout: 5 * time.Second,
  502. ExpectContinueTimeout: 500 * time.Millisecond,
  503. MaxIdleConns: 50,
  504. MaxIdleConnsPerHost: 25,
  505. MaxConnsPerHost: 10,
  506. DisableCompression: true,
  507. InsecureSkipVerify: true,
  508. TLSConfig: TLSConfig{
  509. ServerName: "test-server.com",
  510. InsecureSkipVerify: false, // This should be overridden by HTTPConfig.InsecureSkipVerify
  511. },
  512. }
  513. transport, err := config.GetHTTPTransport()
  514. require.NoError(t, err)
  515. httpTransport, ok := transport.(*http.Transport)
  516. require.True(t, ok)
  517. // Verify all settings are applied correctly
  518. assert2.Equal(t, 30*time.Second, httpTransport.IdleConnTimeout)
  519. assert2.Equal(t, 1*time.Minute, httpTransport.ResponseHeaderTimeout)
  520. assert2.Equal(t, 5*time.Second, httpTransport.TLSHandshakeTimeout)
  521. assert2.Equal(t, 500*time.Millisecond, httpTransport.ExpectContinueTimeout)
  522. assert2.Equal(t, 50, httpTransport.MaxIdleConns)
  523. assert2.Equal(t, 25, httpTransport.MaxIdleConnsPerHost)
  524. assert2.Equal(t, 10, httpTransport.MaxConnsPerHost)
  525. assert2.True(t, httpTransport.DisableCompression)
  526. assert2.True(t, httpTransport.TLSClientConfig.InsecureSkipVerify) // Should be overridden
  527. assert2.Equal(t, "test-server.com", httpTransport.TLSClientConfig.ServerName)
  528. assert2.NotNil(t, httpTransport.Proxy)
  529. assert2.NotNil(t, httpTransport.DialContext)
  530. }