/caddytest/integration/reverseproxy_test.go

https://gitlab.com/caddy/caddy · Go · 491 lines · 460 code · 28 blank · 3 comment · 29 complexity · c4c0f35af82ad2f0e6f3ded61a1bcb7f MD5 · raw file

  1. package integration
  2. import (
  3. "fmt"
  4. "net"
  5. "net/http"
  6. "os"
  7. "runtime"
  8. "strings"
  9. "testing"
  10. "github.com/caddyserver/caddy/v2/caddytest"
  11. )
  12. func TestSRVReverseProxy(t *testing.T) {
  13. tester := caddytest.NewTester(t)
  14. tester.InitServer(`
  15. {
  16. "apps": {
  17. "http": {
  18. "servers": {
  19. "srv0": {
  20. "listen": [
  21. ":8080"
  22. ],
  23. "routes": [
  24. {
  25. "handle": [
  26. {
  27. "handler": "reverse_proxy",
  28. "upstreams": [
  29. {
  30. "lookup_srv": "srv.host.service.consul"
  31. }
  32. ]
  33. }
  34. ]
  35. }
  36. ]
  37. }
  38. }
  39. }
  40. }
  41. }
  42. `, "json")
  43. }
  44. func TestSRVWithDial(t *testing.T) {
  45. caddytest.AssertLoadError(t, `
  46. {
  47. "apps": {
  48. "http": {
  49. "servers": {
  50. "srv0": {
  51. "listen": [
  52. ":8080"
  53. ],
  54. "routes": [
  55. {
  56. "handle": [
  57. {
  58. "handler": "reverse_proxy",
  59. "upstreams": [
  60. {
  61. "dial": "tcp/address.to.upstream:80",
  62. "lookup_srv": "srv.host.service.consul"
  63. }
  64. ]
  65. }
  66. ]
  67. }
  68. ]
  69. }
  70. }
  71. }
  72. }
  73. }
  74. `, "json", `upstream: specifying dial address is incompatible with lookup_srv: 0: {\"dial\": \"tcp/address.to.upstream:80\", \"lookup_srv\": \"srv.host.service.consul\"}`)
  75. }
  76. func TestDialWithPlaceholderUnix(t *testing.T) {
  77. if runtime.GOOS == "windows" {
  78. t.SkipNow()
  79. }
  80. f, err := os.CreateTemp("", "*.sock")
  81. if err != nil {
  82. t.Errorf("failed to create TempFile: %s", err)
  83. return
  84. }
  85. // a hack to get a file name within a valid path to use as socket
  86. socketName := f.Name()
  87. os.Remove(f.Name())
  88. server := http.Server{
  89. Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  90. w.Write([]byte("Hello, World!"))
  91. }),
  92. }
  93. unixListener, err := net.Listen("unix", socketName)
  94. if err != nil {
  95. t.Errorf("failed to listen on the socket: %s", err)
  96. return
  97. }
  98. go server.Serve(unixListener)
  99. t.Cleanup(func() {
  100. server.Close()
  101. })
  102. runtime.Gosched() // Allow other goroutines to run
  103. tester := caddytest.NewTester(t)
  104. tester.InitServer(`
  105. {
  106. "apps": {
  107. "http": {
  108. "servers": {
  109. "srv0": {
  110. "listen": [
  111. ":8080"
  112. ],
  113. "routes": [
  114. {
  115. "handle": [
  116. {
  117. "handler": "reverse_proxy",
  118. "upstreams": [
  119. {
  120. "dial": "unix/{http.request.header.X-Caddy-Upstream-Dial}"
  121. }
  122. ]
  123. }
  124. ]
  125. }
  126. ]
  127. }
  128. }
  129. }
  130. }
  131. }
  132. `, "json")
  133. req, err := http.NewRequest(http.MethodGet, "http://localhost:8080", nil)
  134. if err != nil {
  135. t.Fail()
  136. return
  137. }
  138. req.Header.Set("X-Caddy-Upstream-Dial", socketName)
  139. tester.AssertResponse(req, 200, "Hello, World!")
  140. }
  141. func TestReverseProxyWithPlaceholderDialAddress(t *testing.T) {
  142. tester := caddytest.NewTester(t)
  143. tester.InitServer(`
  144. {
  145. "apps": {
  146. "http": {
  147. "servers": {
  148. "srv0": {
  149. "listen": [
  150. ":8080"
  151. ],
  152. "routes": [
  153. {
  154. "match": [
  155. {
  156. "host": [
  157. "localhost"
  158. ]
  159. }
  160. ],
  161. "handle": [
  162. {
  163. "handler": "static_response",
  164. "body": "Hello, World!"
  165. }
  166. ],
  167. "terminal": true
  168. }
  169. ],
  170. "automatic_https": {
  171. "skip": [
  172. "localhost"
  173. ]
  174. }
  175. },
  176. "srv1": {
  177. "listen": [
  178. ":9080"
  179. ],
  180. "routes": [
  181. {
  182. "match": [
  183. {
  184. "host": [
  185. "localhost"
  186. ]
  187. }
  188. ],
  189. "handle": [
  190. {
  191. "handler": "reverse_proxy",
  192. "upstreams": [
  193. {
  194. "dial": "{http.request.header.X-Caddy-Upstream-Dial}"
  195. }
  196. ]
  197. }
  198. ],
  199. "terminal": true
  200. }
  201. ],
  202. "automatic_https": {
  203. "skip": [
  204. "localhost"
  205. ]
  206. }
  207. }
  208. }
  209. }
  210. }
  211. }
  212. `, "json")
  213. req, err := http.NewRequest(http.MethodGet, "http://localhost:9080", nil)
  214. if err != nil {
  215. t.Fail()
  216. return
  217. }
  218. req.Header.Set("X-Caddy-Upstream-Dial", "localhost:8080")
  219. tester.AssertResponse(req, 200, "Hello, World!")
  220. }
  221. func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
  222. tester := caddytest.NewTester(t)
  223. tester.InitServer(`
  224. {
  225. "apps": {
  226. "http": {
  227. "servers": {
  228. "srv0": {
  229. "listen": [
  230. ":8080"
  231. ],
  232. "routes": [
  233. {
  234. "match": [
  235. {
  236. "host": [
  237. "localhost"
  238. ]
  239. }
  240. ],
  241. "handle": [
  242. {
  243. "handler": "static_response",
  244. "body": "Hello, World!"
  245. }
  246. ],
  247. "terminal": true
  248. }
  249. ],
  250. "automatic_https": {
  251. "skip": [
  252. "localhost"
  253. ]
  254. }
  255. },
  256. "srv1": {
  257. "listen": [
  258. ":9080"
  259. ],
  260. "routes": [
  261. {
  262. "match": [
  263. {
  264. "host": [
  265. "localhost"
  266. ]
  267. }
  268. ],
  269. "handle": [
  270. {
  271. "handler": "reverse_proxy",
  272. "upstreams": [
  273. {
  274. "dial": "tcp/{http.request.header.X-Caddy-Upstream-Dial}:8080"
  275. }
  276. ]
  277. }
  278. ],
  279. "terminal": true
  280. }
  281. ],
  282. "automatic_https": {
  283. "skip": [
  284. "localhost"
  285. ]
  286. }
  287. }
  288. }
  289. }
  290. }
  291. }
  292. `, "json")
  293. req, err := http.NewRequest(http.MethodGet, "http://localhost:9080", nil)
  294. if err != nil {
  295. t.Fail()
  296. return
  297. }
  298. req.Header.Set("X-Caddy-Upstream-Dial", "localhost")
  299. tester.AssertResponse(req, 200, "Hello, World!")
  300. }
  301. func TestSRVWithActiveHealthcheck(t *testing.T) {
  302. caddytest.AssertLoadError(t, `
  303. {
  304. "apps": {
  305. "http": {
  306. "servers": {
  307. "srv0": {
  308. "listen": [
  309. ":8080"
  310. ],
  311. "routes": [
  312. {
  313. "handle": [
  314. {
  315. "handler": "reverse_proxy",
  316. "health_checks": {
  317. "active": {
  318. "path": "/ok"
  319. }
  320. },
  321. "upstreams": [
  322. {
  323. "lookup_srv": "srv.host.service.consul"
  324. }
  325. ]
  326. }
  327. ]
  328. }
  329. ]
  330. }
  331. }
  332. }
  333. }
  334. }
  335. `, "json", `upstream: lookup_srv is incompatible with active health checks: 0: {\"dial\": \"\", \"lookup_srv\": \"srv.host.service.consul\"}`)
  336. }
  337. func TestReverseProxyHealthCheck(t *testing.T) {
  338. tester := caddytest.NewTester(t)
  339. tester.InitServer(`
  340. {
  341. http_port 9080
  342. https_port 9443
  343. }
  344. http://localhost:2020 {
  345. respond "Hello, World!"
  346. }
  347. http://localhost:2021 {
  348. respond "ok"
  349. }
  350. http://localhost:9080 {
  351. reverse_proxy {
  352. to localhost:2020
  353. health_uri /health
  354. health_port 2021
  355. health_interval 2s
  356. health_timeout 5s
  357. }
  358. }
  359. `, "caddyfile")
  360. tester.AssertGetResponse("http://localhost:9080/", 200, "Hello, World!")
  361. }
  362. func TestReverseProxyHealthCheckUnixSocket(t *testing.T) {
  363. if runtime.GOOS == "windows" {
  364. t.SkipNow()
  365. }
  366. tester := caddytest.NewTester(t)
  367. f, err := os.CreateTemp("", "*.sock")
  368. if err != nil {
  369. t.Errorf("failed to create TempFile: %s", err)
  370. return
  371. }
  372. // a hack to get a file name within a valid path to use as socket
  373. socketName := f.Name()
  374. os.Remove(f.Name())
  375. server := http.Server{
  376. Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  377. if strings.HasPrefix(req.URL.Path, "/health") {
  378. w.Write([]byte("ok"))
  379. return
  380. }
  381. w.Write([]byte("Hello, World!"))
  382. }),
  383. }
  384. unixListener, err := net.Listen("unix", socketName)
  385. if err != nil {
  386. t.Errorf("failed to listen on the socket: %s", err)
  387. return
  388. }
  389. go server.Serve(unixListener)
  390. t.Cleanup(func() {
  391. server.Close()
  392. })
  393. runtime.Gosched() // Allow other goroutines to run
  394. tester.InitServer(fmt.Sprintf(`
  395. {
  396. http_port 9080
  397. https_port 9443
  398. }
  399. http://localhost:9080 {
  400. reverse_proxy {
  401. to unix/%s
  402. health_uri /health
  403. health_port 2021
  404. health_interval 2s
  405. health_timeout 5s
  406. }
  407. }
  408. `, socketName), "caddyfile")
  409. tester.AssertGetResponse("http://localhost:9080/", 200, "Hello, World!")
  410. }
  411. func TestReverseProxyHealthCheckUnixSocketWithoutPort(t *testing.T) {
  412. if runtime.GOOS == "windows" {
  413. t.SkipNow()
  414. }
  415. tester := caddytest.NewTester(t)
  416. f, err := os.CreateTemp("", "*.sock")
  417. if err != nil {
  418. t.Errorf("failed to create TempFile: %s", err)
  419. return
  420. }
  421. // a hack to get a file name within a valid path to use as socket
  422. socketName := f.Name()
  423. os.Remove(f.Name())
  424. server := http.Server{
  425. Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  426. if strings.HasPrefix(req.URL.Path, "/health") {
  427. w.Write([]byte("ok"))
  428. return
  429. }
  430. w.Write([]byte("Hello, World!"))
  431. }),
  432. }
  433. unixListener, err := net.Listen("unix", socketName)
  434. if err != nil {
  435. t.Errorf("failed to listen on the socket: %s", err)
  436. return
  437. }
  438. go server.Serve(unixListener)
  439. t.Cleanup(func() {
  440. server.Close()
  441. })
  442. runtime.Gosched() // Allow other goroutines to run
  443. tester.InitServer(fmt.Sprintf(`
  444. {
  445. http_port 9080
  446. https_port 9443
  447. }
  448. http://localhost:9080 {
  449. reverse_proxy {
  450. to unix/%s
  451. health_uri /health
  452. health_interval 2s
  453. health_timeout 5s
  454. }
  455. }
  456. `, socketName), "caddyfile")
  457. tester.AssertGetResponse("http://localhost:9080/", 200, "Hello, World!")
  458. }