http.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. package basic
  2. import (
  3. "fmt"
  4. "net/http"
  5. "net/url"
  6. "strconv"
  7. "time"
  8. "github.com/gorilla/websocket"
  9. "github.com/onsi/ginkgo/v2"
  10. "github.com/fatedier/frp/test/e2e/framework"
  11. "github.com/fatedier/frp/test/e2e/framework/consts"
  12. "github.com/fatedier/frp/test/e2e/mock/server/httpserver"
  13. "github.com/fatedier/frp/test/e2e/pkg/request"
  14. )
  15. var _ = ginkgo.Describe("[Feature: HTTP]", func() {
  16. f := framework.NewDefaultFramework()
  17. getDefaultServerConf := func(vhostHTTPPort int) string {
  18. conf := consts.DefaultServerConfig + `
  19. vhostHTTPPort = %d
  20. `
  21. return fmt.Sprintf(conf, vhostHTTPPort)
  22. }
  23. newHTTPServer := func(port int, respContent string) *httpserver.Server {
  24. return httpserver.New(
  25. httpserver.WithBindPort(port),
  26. httpserver.WithHandler(framework.SpecifiedHTTPBodyHandler([]byte(respContent))),
  27. )
  28. }
  29. ginkgo.It("HTTP route by locations", func() {
  30. vhostHTTPPort := f.AllocPort()
  31. serverConf := getDefaultServerConf(vhostHTTPPort)
  32. fooPort := f.AllocPort()
  33. f.RunServer("", newHTTPServer(fooPort, "foo"))
  34. barPort := f.AllocPort()
  35. f.RunServer("", newHTTPServer(barPort, "bar"))
  36. clientConf := consts.DefaultClientConfig
  37. clientConf += fmt.Sprintf(`
  38. [[proxies]]
  39. name = "foo"
  40. type = "http"
  41. localPort = %d
  42. customDomains = ["normal.example.com"]
  43. locations = ["/","/foo"]
  44. [[proxies]]
  45. name = "bar"
  46. type = "http"
  47. localPort = %d
  48. customDomains = ["normal.example.com"]
  49. locations = ["/bar"]
  50. `, fooPort, barPort)
  51. f.RunProcesses([]string{serverConf}, []string{clientConf})
  52. tests := []struct {
  53. path string
  54. expectResp string
  55. desc string
  56. }{
  57. {path: "/foo", expectResp: "foo", desc: "foo path"},
  58. {path: "/bar", expectResp: "bar", desc: "bar path"},
  59. {path: "/other", expectResp: "foo", desc: "other path"},
  60. }
  61. for _, test := range tests {
  62. framework.NewRequestExpect(f).Explain(test.desc).Port(vhostHTTPPort).
  63. RequestModify(func(r *request.Request) {
  64. r.HTTP().HTTPHost("normal.example.com").HTTPPath(test.path)
  65. }).
  66. ExpectResp([]byte(test.expectResp)).
  67. Ensure()
  68. }
  69. })
  70. ginkgo.It("HTTP route by HTTP user", func() {
  71. vhostHTTPPort := f.AllocPort()
  72. serverConf := getDefaultServerConf(vhostHTTPPort)
  73. fooPort := f.AllocPort()
  74. f.RunServer("", newHTTPServer(fooPort, "foo"))
  75. barPort := f.AllocPort()
  76. f.RunServer("", newHTTPServer(barPort, "bar"))
  77. otherPort := f.AllocPort()
  78. f.RunServer("", newHTTPServer(otherPort, "other"))
  79. clientConf := consts.DefaultClientConfig
  80. clientConf += fmt.Sprintf(`
  81. [[proxies]]
  82. name = "foo"
  83. type = "http"
  84. localPort = %d
  85. customDomains = ["normal.example.com"]
  86. routeByHTTPUser = "user1"
  87. [[proxies]]
  88. name = "bar"
  89. type = "http"
  90. localPort = %d
  91. customDomains = ["normal.example.com"]
  92. routeByHTTPUser = "user2"
  93. [[proxies]]
  94. name = "catchAll"
  95. type = "http"
  96. localPort = %d
  97. customDomains = ["normal.example.com"]
  98. `, fooPort, barPort, otherPort)
  99. f.RunProcesses([]string{serverConf}, []string{clientConf})
  100. // user1
  101. framework.NewRequestExpect(f).Explain("user1").Port(vhostHTTPPort).
  102. RequestModify(func(r *request.Request) {
  103. r.HTTP().HTTPHost("normal.example.com").HTTPAuth("user1", "")
  104. }).
  105. ExpectResp([]byte("foo")).
  106. Ensure()
  107. // user2
  108. framework.NewRequestExpect(f).Explain("user2").Port(vhostHTTPPort).
  109. RequestModify(func(r *request.Request) {
  110. r.HTTP().HTTPHost("normal.example.com").HTTPAuth("user2", "")
  111. }).
  112. ExpectResp([]byte("bar")).
  113. Ensure()
  114. // other user
  115. framework.NewRequestExpect(f).Explain("other user").Port(vhostHTTPPort).
  116. RequestModify(func(r *request.Request) {
  117. r.HTTP().HTTPHost("normal.example.com").HTTPAuth("user3", "")
  118. }).
  119. ExpectResp([]byte("other")).
  120. Ensure()
  121. })
  122. ginkgo.It("HTTP Basic Auth", func() {
  123. vhostHTTPPort := f.AllocPort()
  124. serverConf := getDefaultServerConf(vhostHTTPPort)
  125. clientConf := consts.DefaultClientConfig
  126. clientConf += fmt.Sprintf(`
  127. [[proxies]]
  128. name = "test"
  129. type = "http"
  130. localPort = {{ .%s }}
  131. customDomains = ["normal.example.com"]
  132. httpUser = "test"
  133. httpPassword = "test"
  134. `, framework.HTTPSimpleServerPort)
  135. f.RunProcesses([]string{serverConf}, []string{clientConf})
  136. // not set auth header
  137. framework.NewRequestExpect(f).Port(vhostHTTPPort).
  138. RequestModify(func(r *request.Request) {
  139. r.HTTP().HTTPHost("normal.example.com")
  140. }).
  141. Ensure(framework.ExpectResponseCode(401))
  142. // set incorrect auth header
  143. framework.NewRequestExpect(f).Port(vhostHTTPPort).
  144. RequestModify(func(r *request.Request) {
  145. r.HTTP().HTTPHost("normal.example.com").HTTPAuth("test", "invalid")
  146. }).
  147. Ensure(framework.ExpectResponseCode(401))
  148. // set correct auth header
  149. framework.NewRequestExpect(f).Port(vhostHTTPPort).
  150. RequestModify(func(r *request.Request) {
  151. r.HTTP().HTTPHost("normal.example.com").HTTPAuth("test", "test")
  152. }).
  153. Ensure()
  154. })
  155. ginkgo.It("Wildcard domain", func() {
  156. vhostHTTPPort := f.AllocPort()
  157. serverConf := getDefaultServerConf(vhostHTTPPort)
  158. clientConf := consts.DefaultClientConfig
  159. clientConf += fmt.Sprintf(`
  160. [[proxies]]
  161. name = "test"
  162. type = "http"
  163. localPort = {{ .%s }}
  164. customDomains = ["*.example.com"]
  165. `, framework.HTTPSimpleServerPort)
  166. f.RunProcesses([]string{serverConf}, []string{clientConf})
  167. // not match host
  168. framework.NewRequestExpect(f).Port(vhostHTTPPort).
  169. RequestModify(func(r *request.Request) {
  170. r.HTTP().HTTPHost("not-match.test.com")
  171. }).
  172. Ensure(framework.ExpectResponseCode(404))
  173. // test.example.com match *.example.com
  174. framework.NewRequestExpect(f).Port(vhostHTTPPort).
  175. RequestModify(func(r *request.Request) {
  176. r.HTTP().HTTPHost("test.example.com")
  177. }).
  178. Ensure()
  179. // sub.test.example.com match *.example.com
  180. framework.NewRequestExpect(f).Port(vhostHTTPPort).
  181. RequestModify(func(r *request.Request) {
  182. r.HTTP().HTTPHost("sub.test.example.com")
  183. }).
  184. Ensure()
  185. })
  186. ginkgo.It("Subdomain", func() {
  187. vhostHTTPPort := f.AllocPort()
  188. serverConf := getDefaultServerConf(vhostHTTPPort)
  189. serverConf += `
  190. subdomainHost = "example.com"
  191. `
  192. fooPort := f.AllocPort()
  193. f.RunServer("", newHTTPServer(fooPort, "foo"))
  194. barPort := f.AllocPort()
  195. f.RunServer("", newHTTPServer(barPort, "bar"))
  196. clientConf := consts.DefaultClientConfig
  197. clientConf += fmt.Sprintf(`
  198. [[proxies]]
  199. name = "foo"
  200. type = "http"
  201. localPort = %d
  202. subdomain = "foo"
  203. [[proxies]]
  204. name = "bar"
  205. type = "http"
  206. localPort = %d
  207. subdomain = "bar"
  208. `, fooPort, barPort)
  209. f.RunProcesses([]string{serverConf}, []string{clientConf})
  210. // foo
  211. framework.NewRequestExpect(f).Explain("foo subdomain").Port(vhostHTTPPort).
  212. RequestModify(func(r *request.Request) {
  213. r.HTTP().HTTPHost("foo.example.com")
  214. }).
  215. ExpectResp([]byte("foo")).
  216. Ensure()
  217. // bar
  218. framework.NewRequestExpect(f).Explain("bar subdomain").Port(vhostHTTPPort).
  219. RequestModify(func(r *request.Request) {
  220. r.HTTP().HTTPHost("bar.example.com")
  221. }).
  222. ExpectResp([]byte("bar")).
  223. Ensure()
  224. })
  225. ginkgo.It("Modify headers", func() {
  226. vhostHTTPPort := f.AllocPort()
  227. serverConf := getDefaultServerConf(vhostHTTPPort)
  228. localPort := f.AllocPort()
  229. localServer := httpserver.New(
  230. httpserver.WithBindPort(localPort),
  231. httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  232. _, _ = w.Write([]byte(req.Header.Get("X-From-Where")))
  233. })),
  234. )
  235. f.RunServer("", localServer)
  236. clientConf := consts.DefaultClientConfig
  237. clientConf += fmt.Sprintf(`
  238. [[proxies]]
  239. name = "test"
  240. type = "http"
  241. localPort = %d
  242. customDomains = ["normal.example.com"]
  243. requestHeaders.set.x-from-where = "frp"
  244. `, localPort)
  245. f.RunProcesses([]string{serverConf}, []string{clientConf})
  246. // not set auth header
  247. framework.NewRequestExpect(f).Port(vhostHTTPPort).
  248. RequestModify(func(r *request.Request) {
  249. r.HTTP().HTTPHost("normal.example.com")
  250. }).
  251. ExpectResp([]byte("frp")). // local http server will write this X-From-Where header to response body
  252. Ensure()
  253. })
  254. ginkgo.It("Host Header Rewrite", func() {
  255. vhostHTTPPort := f.AllocPort()
  256. serverConf := getDefaultServerConf(vhostHTTPPort)
  257. localPort := f.AllocPort()
  258. localServer := httpserver.New(
  259. httpserver.WithBindPort(localPort),
  260. httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  261. _, _ = w.Write([]byte(req.Host))
  262. })),
  263. )
  264. f.RunServer("", localServer)
  265. clientConf := consts.DefaultClientConfig
  266. clientConf += fmt.Sprintf(`
  267. [[proxies]]
  268. name = "test"
  269. type = "http"
  270. localPort = %d
  271. customDomains = ["normal.example.com"]
  272. hostHeaderRewrite = "rewrite.example.com"
  273. `, localPort)
  274. f.RunProcesses([]string{serverConf}, []string{clientConf})
  275. framework.NewRequestExpect(f).Port(vhostHTTPPort).
  276. RequestModify(func(r *request.Request) {
  277. r.HTTP().HTTPHost("normal.example.com")
  278. }).
  279. ExpectResp([]byte("rewrite.example.com")). // local http server will write host header to response body
  280. Ensure()
  281. })
  282. ginkgo.It("Websocket protocol", func() {
  283. vhostHTTPPort := f.AllocPort()
  284. serverConf := getDefaultServerConf(vhostHTTPPort)
  285. upgrader := websocket.Upgrader{}
  286. localPort := f.AllocPort()
  287. localServer := httpserver.New(
  288. httpserver.WithBindPort(localPort),
  289. httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  290. c, err := upgrader.Upgrade(w, req, nil)
  291. if err != nil {
  292. return
  293. }
  294. defer c.Close()
  295. for {
  296. mt, message, err := c.ReadMessage()
  297. if err != nil {
  298. break
  299. }
  300. err = c.WriteMessage(mt, message)
  301. if err != nil {
  302. break
  303. }
  304. }
  305. })),
  306. )
  307. f.RunServer("", localServer)
  308. clientConf := consts.DefaultClientConfig
  309. clientConf += fmt.Sprintf(`
  310. [[proxies]]
  311. name = "test"
  312. type = "http"
  313. localPort = %d
  314. customDomains = ["127.0.0.1"]
  315. `, localPort)
  316. f.RunProcesses([]string{serverConf}, []string{clientConf})
  317. u := url.URL{Scheme: "ws", Host: "127.0.0.1:" + strconv.Itoa(vhostHTTPPort)}
  318. c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
  319. framework.ExpectNoError(err)
  320. err = c.WriteMessage(websocket.TextMessage, []byte(consts.TestString))
  321. framework.ExpectNoError(err)
  322. _, msg, err := c.ReadMessage()
  323. framework.ExpectNoError(err)
  324. framework.ExpectEqualValues(consts.TestString, string(msg))
  325. })
  326. ginkgo.It("vhostHTTPTimeout", func() {
  327. vhostHTTPPort := f.AllocPort()
  328. serverConf := getDefaultServerConf(vhostHTTPPort)
  329. serverConf += `
  330. vhostHTTPTimeout = 2
  331. `
  332. delayDuration := 0 * time.Second
  333. localPort := f.AllocPort()
  334. localServer := httpserver.New(
  335. httpserver.WithBindPort(localPort),
  336. httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  337. time.Sleep(delayDuration)
  338. _, _ = w.Write([]byte(req.Host))
  339. })),
  340. )
  341. f.RunServer("", localServer)
  342. clientConf := consts.DefaultClientConfig
  343. clientConf += fmt.Sprintf(`
  344. [[proxies]]
  345. name = "test"
  346. type = "http"
  347. localPort = %d
  348. customDomains = ["normal.example.com"]
  349. `, localPort)
  350. f.RunProcesses([]string{serverConf}, []string{clientConf})
  351. framework.NewRequestExpect(f).Port(vhostHTTPPort).
  352. RequestModify(func(r *request.Request) {
  353. r.HTTP().HTTPHost("normal.example.com").HTTP().Timeout(time.Second)
  354. }).
  355. ExpectResp([]byte("normal.example.com")).
  356. Ensure()
  357. delayDuration = 3 * time.Second
  358. framework.NewRequestExpect(f).Port(vhostHTTPPort).
  359. RequestModify(func(r *request.Request) {
  360. r.HTTP().HTTPHost("normal.example.com").HTTP().Timeout(5 * time.Second)
  361. }).
  362. Ensure(framework.ExpectResponseCode(504))
  363. })
  364. })