123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466 |
- package basic
- import (
- "fmt"
- "net/http"
- "net/url"
- "strconv"
- "time"
- "github.com/gorilla/websocket"
- "github.com/onsi/ginkgo/v2"
- "github.com/fatedier/frp/test/e2e/framework"
- "github.com/fatedier/frp/test/e2e/framework/consts"
- "github.com/fatedier/frp/test/e2e/mock/server/httpserver"
- "github.com/fatedier/frp/test/e2e/pkg/request"
- )
- var _ = ginkgo.Describe("[Feature: HTTP]", func() {
- f := framework.NewDefaultFramework()
- getDefaultServerConf := func(vhostHTTPPort int) string {
- conf := consts.DefaultServerConfig + `
- vhostHTTPPort = %d
- `
- return fmt.Sprintf(conf, vhostHTTPPort)
- }
- newHTTPServer := func(port int, respContent string) *httpserver.Server {
- return httpserver.New(
- httpserver.WithBindPort(port),
- httpserver.WithHandler(framework.SpecifiedHTTPBodyHandler([]byte(respContent))),
- )
- }
- ginkgo.It("HTTP route by locations", func() {
- vhostHTTPPort := f.AllocPort()
- serverConf := getDefaultServerConf(vhostHTTPPort)
- fooPort := f.AllocPort()
- f.RunServer("", newHTTPServer(fooPort, "foo"))
- barPort := f.AllocPort()
- f.RunServer("", newHTTPServer(barPort, "bar"))
- clientConf := consts.DefaultClientConfig
- clientConf += fmt.Sprintf(`
- [[proxies]]
- name = "foo"
- type = "http"
- localPort = %d
- customDomains = ["normal.example.com"]
- locations = ["/","/foo"]
- [[proxies]]
- name = "bar"
- type = "http"
- localPort = %d
- customDomains = ["normal.example.com"]
- locations = ["/bar"]
- `, fooPort, barPort)
- f.RunProcesses([]string{serverConf}, []string{clientConf})
- tests := []struct {
- path string
- expectResp string
- desc string
- }{
- {path: "/foo", expectResp: "foo", desc: "foo path"},
- {path: "/bar", expectResp: "bar", desc: "bar path"},
- {path: "/other", expectResp: "foo", desc: "other path"},
- }
- for _, test := range tests {
- framework.NewRequestExpect(f).Explain(test.desc).Port(vhostHTTPPort).
- RequestModify(func(r *request.Request) {
- r.HTTP().HTTPHost("normal.example.com").HTTPPath(test.path)
- }).
- ExpectResp([]byte(test.expectResp)).
- Ensure()
- }
- })
- ginkgo.It("HTTP route by HTTP user", func() {
- vhostHTTPPort := f.AllocPort()
- serverConf := getDefaultServerConf(vhostHTTPPort)
- fooPort := f.AllocPort()
- f.RunServer("", newHTTPServer(fooPort, "foo"))
- barPort := f.AllocPort()
- f.RunServer("", newHTTPServer(barPort, "bar"))
- otherPort := f.AllocPort()
- f.RunServer("", newHTTPServer(otherPort, "other"))
- clientConf := consts.DefaultClientConfig
- clientConf += fmt.Sprintf(`
- [[proxies]]
- name = "foo"
- type = "http"
- localPort = %d
- customDomains = ["normal.example.com"]
- routeByHTTPUser = "user1"
- [[proxies]]
- name = "bar"
- type = "http"
- localPort = %d
- customDomains = ["normal.example.com"]
- routeByHTTPUser = "user2"
- [[proxies]]
- name = "catchAll"
- type = "http"
- localPort = %d
- customDomains = ["normal.example.com"]
- `, fooPort, barPort, otherPort)
- f.RunProcesses([]string{serverConf}, []string{clientConf})
- // user1
- framework.NewRequestExpect(f).Explain("user1").Port(vhostHTTPPort).
- RequestModify(func(r *request.Request) {
- r.HTTP().HTTPHost("normal.example.com").HTTPAuth("user1", "")
- }).
- ExpectResp([]byte("foo")).
- Ensure()
- // user2
- framework.NewRequestExpect(f).Explain("user2").Port(vhostHTTPPort).
- RequestModify(func(r *request.Request) {
- r.HTTP().HTTPHost("normal.example.com").HTTPAuth("user2", "")
- }).
- ExpectResp([]byte("bar")).
- Ensure()
- // other user
- framework.NewRequestExpect(f).Explain("other user").Port(vhostHTTPPort).
- RequestModify(func(r *request.Request) {
- r.HTTP().HTTPHost("normal.example.com").HTTPAuth("user3", "")
- }).
- ExpectResp([]byte("other")).
- Ensure()
- })
- ginkgo.It("HTTP Basic Auth", func() {
- vhostHTTPPort := f.AllocPort()
- serverConf := getDefaultServerConf(vhostHTTPPort)
- clientConf := consts.DefaultClientConfig
- clientConf += fmt.Sprintf(`
- [[proxies]]
- name = "test"
- type = "http"
- localPort = {{ .%s }}
- customDomains = ["normal.example.com"]
- httpUser = "test"
- httpPassword = "test"
- `, framework.HTTPSimpleServerPort)
- f.RunProcesses([]string{serverConf}, []string{clientConf})
- // not set auth header
- framework.NewRequestExpect(f).Port(vhostHTTPPort).
- RequestModify(func(r *request.Request) {
- r.HTTP().HTTPHost("normal.example.com")
- }).
- Ensure(framework.ExpectResponseCode(401))
- // set incorrect auth header
- framework.NewRequestExpect(f).Port(vhostHTTPPort).
- RequestModify(func(r *request.Request) {
- r.HTTP().HTTPHost("normal.example.com").HTTPAuth("test", "invalid")
- }).
- Ensure(framework.ExpectResponseCode(401))
- // set correct auth header
- framework.NewRequestExpect(f).Port(vhostHTTPPort).
- RequestModify(func(r *request.Request) {
- r.HTTP().HTTPHost("normal.example.com").HTTPAuth("test", "test")
- }).
- Ensure()
- })
- ginkgo.It("Wildcard domain", func() {
- vhostHTTPPort := f.AllocPort()
- serverConf := getDefaultServerConf(vhostHTTPPort)
- clientConf := consts.DefaultClientConfig
- clientConf += fmt.Sprintf(`
- [[proxies]]
- name = "test"
- type = "http"
- localPort = {{ .%s }}
- customDomains = ["*.example.com"]
- `, framework.HTTPSimpleServerPort)
- f.RunProcesses([]string{serverConf}, []string{clientConf})
- // not match host
- framework.NewRequestExpect(f).Port(vhostHTTPPort).
- RequestModify(func(r *request.Request) {
- r.HTTP().HTTPHost("not-match.test.com")
- }).
- Ensure(framework.ExpectResponseCode(404))
- // test.example.com match *.example.com
- framework.NewRequestExpect(f).Port(vhostHTTPPort).
- RequestModify(func(r *request.Request) {
- r.HTTP().HTTPHost("test.example.com")
- }).
- Ensure()
- // sub.test.example.com match *.example.com
- framework.NewRequestExpect(f).Port(vhostHTTPPort).
- RequestModify(func(r *request.Request) {
- r.HTTP().HTTPHost("sub.test.example.com")
- }).
- Ensure()
- })
- ginkgo.It("Subdomain", func() {
- vhostHTTPPort := f.AllocPort()
- serverConf := getDefaultServerConf(vhostHTTPPort)
- serverConf += `
- subdomainHost = "example.com"
- `
- fooPort := f.AllocPort()
- f.RunServer("", newHTTPServer(fooPort, "foo"))
- barPort := f.AllocPort()
- f.RunServer("", newHTTPServer(barPort, "bar"))
- clientConf := consts.DefaultClientConfig
- clientConf += fmt.Sprintf(`
- [[proxies]]
- name = "foo"
- type = "http"
- localPort = %d
- subdomain = "foo"
- [[proxies]]
- name = "bar"
- type = "http"
- localPort = %d
- subdomain = "bar"
- `, fooPort, barPort)
- f.RunProcesses([]string{serverConf}, []string{clientConf})
- // foo
- framework.NewRequestExpect(f).Explain("foo subdomain").Port(vhostHTTPPort).
- RequestModify(func(r *request.Request) {
- r.HTTP().HTTPHost("foo.example.com")
- }).
- ExpectResp([]byte("foo")).
- Ensure()
- // bar
- framework.NewRequestExpect(f).Explain("bar subdomain").Port(vhostHTTPPort).
- RequestModify(func(r *request.Request) {
- r.HTTP().HTTPHost("bar.example.com")
- }).
- ExpectResp([]byte("bar")).
- Ensure()
- })
- ginkgo.It("Modify request headers", func() {
- vhostHTTPPort := f.AllocPort()
- serverConf := getDefaultServerConf(vhostHTTPPort)
- localPort := f.AllocPort()
- localServer := httpserver.New(
- httpserver.WithBindPort(localPort),
- httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- _, _ = w.Write([]byte(req.Header.Get("X-From-Where")))
- })),
- )
- f.RunServer("", localServer)
- clientConf := consts.DefaultClientConfig
- clientConf += fmt.Sprintf(`
- [[proxies]]
- name = "test"
- type = "http"
- localPort = %d
- customDomains = ["normal.example.com"]
- requestHeaders.set.x-from-where = "frp"
- `, localPort)
- f.RunProcesses([]string{serverConf}, []string{clientConf})
- framework.NewRequestExpect(f).Port(vhostHTTPPort).
- RequestModify(func(r *request.Request) {
- r.HTTP().HTTPHost("normal.example.com")
- }).
- ExpectResp([]byte("frp")). // local http server will write this X-From-Where header to response body
- Ensure()
- })
- ginkgo.It("Modify response headers", func() {
- vhostHTTPPort := f.AllocPort()
- serverConf := getDefaultServerConf(vhostHTTPPort)
- localPort := f.AllocPort()
- localServer := httpserver.New(
- httpserver.WithBindPort(localPort),
- httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- w.WriteHeader(200)
- })),
- )
- f.RunServer("", localServer)
- clientConf := consts.DefaultClientConfig
- clientConf += fmt.Sprintf(`
- [[proxies]]
- name = "test"
- type = "http"
- localPort = %d
- customDomains = ["normal.example.com"]
- responseHeaders.set.x-from-where = "frp"
- `, localPort)
- f.RunProcesses([]string{serverConf}, []string{clientConf})
- framework.NewRequestExpect(f).Port(vhostHTTPPort).
- RequestModify(func(r *request.Request) {
- r.HTTP().HTTPHost("normal.example.com")
- }).
- Ensure(func(res *request.Response) bool {
- return res.Header.Get("X-From-Where") == "frp"
- })
- })
- ginkgo.It("Host Header Rewrite", func() {
- vhostHTTPPort := f.AllocPort()
- serverConf := getDefaultServerConf(vhostHTTPPort)
- localPort := f.AllocPort()
- localServer := httpserver.New(
- httpserver.WithBindPort(localPort),
- httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- _, _ = w.Write([]byte(req.Host))
- })),
- )
- f.RunServer("", localServer)
- clientConf := consts.DefaultClientConfig
- clientConf += fmt.Sprintf(`
- [[proxies]]
- name = "test"
- type = "http"
- localPort = %d
- customDomains = ["normal.example.com"]
- hostHeaderRewrite = "rewrite.example.com"
- `, localPort)
- f.RunProcesses([]string{serverConf}, []string{clientConf})
- framework.NewRequestExpect(f).Port(vhostHTTPPort).
- RequestModify(func(r *request.Request) {
- r.HTTP().HTTPHost("normal.example.com")
- }).
- ExpectResp([]byte("rewrite.example.com")). // local http server will write host header to response body
- Ensure()
- })
- ginkgo.It("Websocket protocol", func() {
- vhostHTTPPort := f.AllocPort()
- serverConf := getDefaultServerConf(vhostHTTPPort)
- upgrader := websocket.Upgrader{}
- localPort := f.AllocPort()
- localServer := httpserver.New(
- httpserver.WithBindPort(localPort),
- httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- c, err := upgrader.Upgrade(w, req, nil)
- if err != nil {
- return
- }
- defer c.Close()
- for {
- mt, message, err := c.ReadMessage()
- if err != nil {
- break
- }
- err = c.WriteMessage(mt, message)
- if err != nil {
- break
- }
- }
- })),
- )
- f.RunServer("", localServer)
- clientConf := consts.DefaultClientConfig
- clientConf += fmt.Sprintf(`
- [[proxies]]
- name = "test"
- type = "http"
- localPort = %d
- customDomains = ["127.0.0.1"]
- `, localPort)
- f.RunProcesses([]string{serverConf}, []string{clientConf})
- u := url.URL{Scheme: "ws", Host: "127.0.0.1:" + strconv.Itoa(vhostHTTPPort)}
- c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
- framework.ExpectNoError(err)
- err = c.WriteMessage(websocket.TextMessage, []byte(consts.TestString))
- framework.ExpectNoError(err)
- _, msg, err := c.ReadMessage()
- framework.ExpectNoError(err)
- framework.ExpectEqualValues(consts.TestString, string(msg))
- })
- ginkgo.It("vhostHTTPTimeout", func() {
- vhostHTTPPort := f.AllocPort()
- serverConf := getDefaultServerConf(vhostHTTPPort)
- serverConf += `
- vhostHTTPTimeout = 2
- `
- delayDuration := 0 * time.Second
- localPort := f.AllocPort()
- localServer := httpserver.New(
- httpserver.WithBindPort(localPort),
- httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- time.Sleep(delayDuration)
- _, _ = w.Write([]byte(req.Host))
- })),
- )
- f.RunServer("", localServer)
- clientConf := consts.DefaultClientConfig
- clientConf += fmt.Sprintf(`
- [[proxies]]
- name = "test"
- type = "http"
- localPort = %d
- customDomains = ["normal.example.com"]
- `, localPort)
- f.RunProcesses([]string{serverConf}, []string{clientConf})
- framework.NewRequestExpect(f).Port(vhostHTTPPort).
- RequestModify(func(r *request.Request) {
- r.HTTP().HTTPHost("normal.example.com").HTTP().Timeout(time.Second)
- }).
- ExpectResp([]byte("normal.example.com")).
- Ensure()
- delayDuration = 3 * time.Second
- framework.NewRequestExpect(f).Port(vhostHTTPPort).
- RequestModify(func(r *request.Request) {
- r.HTTP().HTTPHost("normal.example.com").HTTP().Timeout(5 * time.Second)
- }).
- Ensure(framework.ExpectResponseCode(504))
- })
- })
|