diff --git a/api/internal/handler/route_online_debug/route_online_debug.go b/api/internal/handler/route_online_debug/route_online_debug.go index ac1c50230a..3f8da4a686 100644 --- a/api/internal/handler/route_online_debug/route_online_debug.go +++ b/api/internal/handler/route_online_debug/route_online_debug.go @@ -17,6 +17,7 @@ package route_online_debug import ( + "bytes" "encoding/json" "fmt" "io/ioutil" @@ -25,12 +26,13 @@ import ( "strings" "time" - "github.com/apisix/manager-api/internal/handler" "github.com/gin-gonic/gin" "github.com/shiningrush/droplet" "github.com/shiningrush/droplet/data" "github.com/shiningrush/droplet/wrapper" wgin "github.com/shiningrush/droplet/wrapper/gin" + + "github.com/apisix/manager-api/internal/handler" ) type Handler struct { @@ -45,16 +47,17 @@ type ProtocolSupport interface { } func (h *Handler) ApplyRoute(r *gin.Engine) { - r.POST("/apisix/admin/debug-request-forwarding", wgin.Wraps(DebugRequestForwarding, - wrapper.InputType(reflect.TypeOf(ParamsInput{})))) + r.POST("/apisix/admin/debug-request-forwarding", wgin.Wraps(h.DebugRequestForwarding, + wrapper.InputType(reflect.TypeOf(DebugOnlineInput{})))) } -type ParamsInput struct { - URL string `json:"url,omitempty"` - RequestProtocol string `json:"request_protocol,omitempty"` - BodyParams string `json:"body_params,omitempty"` - Method string `json:"method,omitempty"` - HeaderParams map[string][]string `json:"header_params,omitempty"` +type DebugOnlineInput struct { + URL string `auto_read:"online_debug_url,header"` + RequestProtocol string `auto_read:"online_debug_request_protocol,header"` + Method string `auto_read:"online_debug_method,header"` + HeaderParams string `auto_read:"online_debug_header_params,header"` + ContentType string `auto_read:"Content-Type,header"` + Body []byte `auto_read:"@body"` } type Result struct { @@ -63,66 +66,84 @@ type Result struct { Data interface{} `json:"data,omitempty"` } -func DebugRequestForwarding(c droplet.Context) (interface{}, error) { +func (h *Handler) DebugRequestForwarding(c droplet.Context) (interface{}, error) { //TODO: other Protocols, e.g: grpc, websocket - paramsInput := c.Input().(*ParamsInput) - requestProtocol := paramsInput.RequestProtocol - if requestProtocol == "" { - requestProtocol = "http" + input := c.Input().(*DebugOnlineInput) + protocol := input.RequestProtocol + if protocol == "" { + protocol = "http" } protocolMap := make(map[string]ProtocolSupport) protocolMap["http"] = &HTTPProtocolSupport{} protocolMap["https"] = &HTTPProtocolSupport{} - if v, ok := protocolMap[requestProtocol]; ok { - return v.RequestForwarding(c) - } else { - return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest}, - fmt.Errorf("Protocol unsupported %s, only http or https is allowed, but given %s", paramsInput.RequestProtocol, paramsInput.RequestProtocol) + if v, ok := protocolMap[protocol]; ok { + ret, err := v.RequestForwarding(c) + return ret, err } + + return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest}, fmt.Errorf("protocol unspported %s", protocol) } type HTTPProtocolSupport struct { } func (h *HTTPProtocolSupport) RequestForwarding(c droplet.Context) (interface{}, error) { - paramsInput := c.Input().(*ParamsInput) - bodyParams := paramsInput.BodyParams - client := &http.Client{} + input := c.Input().(*DebugOnlineInput) + url := input.URL + method := input.Method + body := input.Body + contentType := input.ContentType + + if url == "" || method == "" { + return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest}, fmt.Errorf("parameters error") + } + client := &http.Client{} client.Timeout = 5 * time.Second - req, err := http.NewRequest(strings.ToUpper(paramsInput.Method), paramsInput.URL, strings.NewReader(bodyParams)) + + var tempMap map[string][]string + err := json.Unmarshal([]byte(input.HeaderParams), &tempMap) + + if err != nil { + return &data.SpecCodeResponse{StatusCode: http.StatusInternalServerError}, err + } + + req, err := http.NewRequest(strings.ToUpper(method), url, bytes.NewReader(body)) if err != nil { return &data.SpecCodeResponse{StatusCode: http.StatusInternalServerError}, err } - for k, v := range paramsInput.HeaderParams { + + req.Header.Add("Content-Type", contentType) + for k, v := range tempMap { for _, v1 := range v { req.Header.Add(k, v1) } } + resp, err := client.Do(req) if err != nil { return &data.SpecCodeResponse{StatusCode: http.StatusInternalServerError}, err } + defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) + _body, err := ioutil.ReadAll(resp.Body) if err != nil { return &data.SpecCodeResponse{StatusCode: http.StatusInternalServerError}, err } returnData := make(map[string]interface{}) result := &Result{} - err = json.Unmarshal(body, &returnData) + err = json.Unmarshal(_body, &returnData) if err != nil { result.Code = resp.StatusCode result.Message = resp.Status - result.Data = string(body) + result.Data = string(_body) } else { result.Code = resp.StatusCode result.Message = resp.Status result.Data = returnData - } return result, nil } diff --git a/api/test/e2e/route_online_debug_test.go b/api/test/e2e/route_online_debug_test.go deleted file mode 100644 index 4665309707..0000000000 --- a/api/test/e2e/route_online_debug_test.go +++ /dev/null @@ -1,849 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package e2e - -import ( - "io/ioutil" - "net/http" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/tidwall/gjson" -) - -func TestRoute_Online_Debug_Route_Not_Exist(t *testing.T) { - tests := []HttpTestCase{ - { - Desc: "hit route that not exist", - Object: APISIXExpect(t), - Method: http.MethodGet, - Path: "/hello_", - ExpectStatus: http.StatusNotFound, - ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n", - }, - } - for _, tc := range tests { - testCaseCheck(tc, t) - } - basepath := "http://127.0.0.1:9000/apisix/admin/debug-request-forwarding" - request, _ := http.NewRequest("POST", basepath, strings.NewReader(`{"url": "`+APISIXInternalUrl+`/hello_","method": "GET","request_protocol": "http"}`)) - request.Header.Add("Authorization", token) - resp, err := http.DefaultClient.Do(request) - if err != nil { - return - } - defer resp.Body.Close() - respBody, _ := ioutil.ReadAll(resp.Body) - realBody := gjson.Get(string(respBody), "data") - assert.Equal(t, `{"code":404,"message":"404 Not Found","data":{"error_msg":"404 Route Not Found"}}`, realBody.String()) -} - -func TestRoute_Online_Debug_Route_With_Query_Params(t *testing.T) { - tests := []HttpTestCase{ - { - Desc: "hit route that not exist", - Object: APISIXExpect(t), - Method: http.MethodGet, - Path: "/hello", - ExpectStatus: http.StatusNotFound, - ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n", - Sleep: sleepTime, - }, - { - Desc: "create route with query params", - Object: ManagerApiExpect(t), - Method: http.MethodPut, - Path: "/apisix/admin/routes/r1", - Body: `{ - "uri": "/hello", - "methods": ["GET"], - "vars": [ - ["arg_name","==","aaa"] - ], - "upstream": { - "type": "roundrobin", - "nodes": [{ - "host": "172.16.238.20", - "port": 1980, - "weight": 1 - }] - } - }`, - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - { - Desc: "online debug route with query params", - Object: ManagerApiExpect(t), - Method: http.MethodPost, - Path: "/apisix/admin/debug-request-forwarding", - Body: `{ - "url": "` + APISIXInternalUrl + `/hello?name=aaa", - "request_protocol": "http", - "method": "GET" - }`, - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - Sleep: sleepTime, - }, - { - Desc: "delete route", - Object: ManagerApiExpect(t), - Method: http.MethodDelete, - Path: "/apisix/admin/routes/r1", - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - { - Desc: "verify the deleted route ", - Object: APISIXExpect(t), - Method: http.MethodGet, - Path: "/hello", - ExpectStatus: http.StatusNotFound, - ExpectBody: `{"error_msg":"404 Route Not Found"}`, - Sleep: sleepTime, - }, - } - - for _, tc := range tests { - testCaseCheck(tc, t) - } -} - -func TestRoute_Online_Debug_Route_With_Header_Params(t *testing.T) { - tests := []HttpTestCase{ - { - Desc: "make sure the route is not created ", - Object: APISIXExpect(t), - Method: http.MethodGet, - Path: "/hello", - ExpectStatus: http.StatusNotFound, - ExpectBody: `{"error_msg":"404 Route Not Found"}`, - Sleep: sleepTime, - }, - { - Desc: "create route with header params", - Object: ManagerApiExpect(t), - Method: http.MethodPut, - Path: "/apisix/admin/routes/r1", - Body: `{ - "uri": "/hello", - "methods": ["GET"], - "vars": [ - ["http_version","==","v2"] - ], - "upstream": { - "type": "roundrobin", - "nodes": [{ - "host": "172.16.238.20", - "port": 1980, - "weight": 1 - }] - } - }`, - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - Sleep: sleepTime, - }, - { - Desc: "online debug route with header params", - Object: ManagerApiExpect(t), - Method: http.MethodPost, - Path: "/apisix/admin/debug-request-forwarding", - Body: `{ - "url": "` + APISIXInternalUrl + `/hello", - "request_protocol": "http", - "method": "GET", - "header_params": { - "version": ["v2"] - } - }`, - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - { - Desc: "delete route", - Object: ManagerApiExpect(t), - Method: http.MethodDelete, - Path: "/apisix/admin/routes/r1", - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - { - Desc: "verify the deleted route ", - Object: APISIXExpect(t), - Method: http.MethodGet, - Path: "/hello", - ExpectStatus: http.StatusNotFound, - ExpectBody: `{"error_msg":"404 Route Not Found"}`, - Sleep: sleepTime, - }, - } - - for _, tc := range tests { - testCaseCheck(tc, t) - } -} - -func TestRoute_Online_Debug_Route_With_Body_Params(t *testing.T) { - tests := []HttpTestCase{ - { - Desc: "make sure the route is not created ", - Object: APISIXExpect(t), - Method: http.MethodGet, - Path: "/hello", - ExpectStatus: http.StatusNotFound, - ExpectBody: `{"error_msg":"404 Route Not Found"}`, - Sleep: sleepTime, - }, - { - Desc: "create route with method POST", - Object: ManagerApiExpect(t), - Method: http.MethodPut, - Path: "/apisix/admin/routes/r1", - Body: `{ - "uri": "/hello", - "methods": ["POST"], - "upstream": { - "type": "roundrobin", - "nodes": [{ - "host": "172.16.238.20", - "port": 1980, - "weight": 1 - }] - } - }`, - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - Sleep: sleepTime, - }, - { - Desc: "online debug route with body params", - Object: ManagerApiExpect(t), - Method: http.MethodPost, - Path: "/apisix/admin/debug-request-forwarding", - Body: `{ - "url": "` + APISIXInternalUrl + `/hello", - "request_protocol": "http", - "method": "POST", - "body_params": "{\"name\":\"test\",\"desc\":\"online debug route with body params\"}" - }`, - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - { - Desc: "delete route", - Object: ManagerApiExpect(t), - Method: http.MethodDelete, - Path: "/apisix/admin/routes/r1", - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - { - Desc: "verify the deleted route ", - Object: APISIXExpect(t), - Method: http.MethodGet, - Path: "/hello", - ExpectStatus: http.StatusNotFound, - ExpectBody: `{"error_msg":"404 Route Not Found"}`, - Sleep: sleepTime, - }, - } - - for _, tc := range tests { - testCaseCheck(tc, t) - } -} - -func TestRoute_Online_Debug_Route_With_Basic_Auth(t *testing.T) { - tests := []HttpTestCase{ - { - Desc: "make sure the route is not created ", - Object: APISIXExpect(t), - Method: http.MethodGet, - Path: "/hello", - ExpectStatus: http.StatusNotFound, - ExpectBody: `{"error_msg":"404 Route Not Found"}`, - Sleep: sleepTime, - }, - { - Desc: "create route enable basic-auth plugin", - Object: ManagerApiExpect(t), - Method: http.MethodPut, - Path: "/apisix/admin/routes/r1", - Body: `{ - "uri": "/hello", - "methods": ["GET"], - "plugins": { - "basic-auth": {} - }, - "upstream": { - "type": "roundrobin", - "nodes": [{ - "host": "172.16.238.20", - "port": 1980, - "weight": 1 - }] - } - }`, - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - Sleep: sleepTime, - }, - { - Desc: "make sure the consumer is not created", - Object: ManagerApiExpect(t), - Method: http.MethodGet, - Path: "/apisix/admin/consumers/jack", - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusNotFound, - }, - { - Desc: "create consumer", - Object: ManagerApiExpect(t), - Path: "/apisix/admin/consumers", - Method: http.MethodPut, - Body: `{ - "username": "jack", - "plugins": { - "basic-auth": { - "disable": false, - "username": "jack", - "password": "123456" - } - }, - "desc": "test description" - }`, - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - Sleep: sleepTime, - }, - { - Desc: "online debug route with username and password", - Object: ManagerApiExpect(t), - Method: http.MethodPost, - Path: "/apisix/admin/debug-request-forwarding", - Body: `{ - "url": "` + APISIXInternalUrl + `/hello", - "request_protocol": "http", - "method": "GET", - "header_params": { - "Authorization": ["Basic amFjazoxMjM0NTYKIA=="] - } - }`, - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - } - - for _, tc := range tests { - testCaseCheck(tc, t) - } - - // online debug without basic-auth - basepath := "http://127.0.0.1:9000/apisix/admin/debug-request-forwarding" - request, _ := http.NewRequest("POST", basepath, strings.NewReader(`{"url": "`+APISIXInternalUrl+`/hello","method": "GET","request_protocol":"http"}`)) - request.Header.Add("Authorization", token) - resp, err := http.DefaultClient.Do(request) - if err != nil { - return - } - defer resp.Body.Close() - respBody, _ := ioutil.ReadAll(resp.Body) - realBody := gjson.Get(string(respBody), "data") - assert.Equal(t, `{"code":401,"message":"401 Unauthorized","data":{"message":"Missing authorization in request"}}`, realBody.String()) - - // clear test data - tests = []HttpTestCase{ - { - Desc: "delete consumer", - Object: ManagerApiExpect(t), - Method: http.MethodDelete, - Path: "/apisix/admin/consumers/jack", - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - { - Desc: "verify route with the jwt token from just deleted consumer", - Object: APISIXExpect(t), - Method: http.MethodGet, - Path: "/hello", - Headers: map[string]string{"Authorization": "Basic amFjazoxMjM0NTYKIA=="}, - ExpectStatus: http.StatusUnauthorized, - ExpectBody: `{"message":"Missing related consumer"}`, - Sleep: sleepTime, - }, - { - Desc: "delete route", - Object: ManagerApiExpect(t), - Method: http.MethodDelete, - Path: "/apisix/admin/routes/r1", - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - { - Desc: "verify the deleted route ", - Object: APISIXExpect(t), - Method: http.MethodGet, - Path: "/hello", - ExpectStatus: http.StatusNotFound, - ExpectBody: `{"error_msg":"404 Route Not Found"}`, - Sleep: sleepTime, - }, - } - - for _, tc := range tests { - testCaseCheck(tc, t) - } -} - -func TestRoute_Online_Debug_Route_With_Jwt_Auth(t *testing.T) { - tests := []HttpTestCase{ - { - Desc: "make sure the route is not created ", - Object: APISIXExpect(t), - Method: http.MethodGet, - Path: "/hello", - ExpectStatus: http.StatusNotFound, - ExpectBody: `{"error_msg":"404 Route Not Found"}`, - Sleep: sleepTime, - }, - { - Desc: "create route enable jwt-auth plugin", - Object: ManagerApiExpect(t), - Method: http.MethodPut, - Path: "/apisix/admin/routes/r1", - Body: `{ - "uri": "/hello", - "methods": ["GET"], - "plugins": { - "jwt-auth": {} - }, - "upstream": { - "type": "roundrobin", - "nodes": [{ - "host": "172.16.238.20", - "port": 1980, - "weight": 1 - }] - } - }`, - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - Sleep: sleepTime, - }, - { - Desc: "make sure the consumer is not created", - Object: ManagerApiExpect(t), - Method: http.MethodGet, - Path: "/apisix/admin/consumers/jack", - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusNotFound, - Sleep: sleepTime, - }, - { - Desc: "create consumer", - Object: ManagerApiExpect(t), - Path: "/apisix/admin/consumers", - Method: http.MethodPut, - Body: `{ - "username": "jack", - "plugins": { - "jwt-auth": { - "key": "user-key", - "secret": "my-secret-key", - "algorithm": "HS256" - } - }, - "desc": "test description" - }`, - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - } - - for _, tc := range tests { - testCaseCheck(tc, t) - } - - time.Sleep(sleepTime) - - // sign jwt token - body, status, err := httpGet("http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=user-key", nil) - assert.Nil(t, err) - assert.Equal(t, http.StatusOK, status) - jwtToken := string(body) - - tests = []HttpTestCase{ - { - Desc: "online debug route with jwt token", - Object: ManagerApiExpect(t), - Method: http.MethodPost, - Path: "/apisix/admin/debug-request-forwarding", - Body: `{ - "url": "` + APISIXInternalUrl + `/hello", - "request_protocol": "http", - "method": "GET", - "header_params": { - "Authorization": ["` + jwtToken + `"] - } - }`, - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - } - - for _, tc := range tests { - testCaseCheck(tc, t) - } - - // online debug without jwt-auth - basepath := "http://127.0.0.1:9000/apisix/admin/debug-request-forwarding" - request, _ := http.NewRequest("POST", basepath, strings.NewReader(`{"url": "`+APISIXInternalUrl+`/hello","method": "GET","request_protocol":"http"}`)) - request.Header.Add("Authorization", token) - resp, err := http.DefaultClient.Do(request) - if err != nil { - return - } - defer resp.Body.Close() - respBody, _ := ioutil.ReadAll(resp.Body) - realBody := gjson.Get(string(respBody), "data") - assert.Equal(t, `{"code":401,"message":"401 Unauthorized","data":{"message":"Missing JWT token in request"}}`, realBody.String()) - - // clear test data - tests = []HttpTestCase{ - { - Desc: "delete consumer", - Object: ManagerApiExpect(t), - Method: http.MethodDelete, - Path: "/apisix/admin/consumers/jack", - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - { - Desc: "verify route with the jwt token from just deleted consumer", - Object: APISIXExpect(t), - Method: http.MethodGet, - Path: "/hello", - Headers: map[string]string{"Authorization": jwtToken}, - ExpectStatus: http.StatusUnauthorized, - ExpectBody: `{"message":"Missing related consumer"}`, - Sleep: sleepTime, - }, - { - Desc: "delete route", - Object: ManagerApiExpect(t), - Method: http.MethodDelete, - Path: "/apisix/admin/routes/r1", - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - { - Desc: "verify the deleted route ", - Object: APISIXExpect(t), - Method: http.MethodGet, - Path: "/hello", - ExpectStatus: http.StatusNotFound, - ExpectBody: `{"error_msg":"404 Route Not Found"}`, - Sleep: sleepTime, - }, - } - - for _, tc := range tests { - testCaseCheck(tc, t) - } -} - -func TestRoute_Online_Debug_Route_With_Key_Auth(t *testing.T) { - tests := []HttpTestCase{ - { - Desc: "make sure the route is not created ", - Object: APISIXExpect(t), - Method: http.MethodGet, - Path: "/hello", - ExpectStatus: http.StatusNotFound, - ExpectBody: `{"error_msg":"404 Route Not Found"}`, - }, - { - Desc: "create route enable key-auth plugin", - Object: ManagerApiExpect(t), - Method: http.MethodPut, - Path: "/apisix/admin/routes/r1", - Body: `{ - "uri": "/hello", - "methods": ["GET"], - "plugins": { - "key-auth": {} - }, - "upstream": { - "type": "roundrobin", - "nodes": [{ - "host": "172.16.238.20", - "port": 1980, - "weight": 1 - }] - } - }`, - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - { - Desc: "make sure the consumer is not created", - Object: ManagerApiExpect(t), - Method: http.MethodGet, - Path: "/apisix/admin/consumers/jack", - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusNotFound, - }, - { - Desc: "create consumer", - Object: ManagerApiExpect(t), - Path: "/apisix/admin/consumers", - Method: http.MethodPut, - Body: `{ - "username": "jack", - "plugins": { - "key-auth": { - "key": "user-key" - } - }, - "desc": "test description" - }`, - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - { - Desc: "online debug route with apikey", - Object: ManagerApiExpect(t), - Method: http.MethodPost, - Path: "/apisix/admin/debug-request-forwarding", - Body: `{ - "url": "` + APISIXInternalUrl + `/hello", - "request_protocol": "http", - "method": "GET", - "header_params": { - "apikey": ["user-key"] - } - }`, - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - } - - for _, tc := range tests { - testCaseCheck(tc, t) - } - - // online debug without key-auth - basepath := "http://127.0.0.1:9000/apisix/admin/debug-request-forwarding" - request, _ := http.NewRequest("POST", basepath, strings.NewReader(`{"url": "`+APISIXInternalUrl+`/hello","method": "GET","request_protocol": "http"}`)) - request.Header.Add("Authorization", token) - resp, err := http.DefaultClient.Do(request) - if err != nil { - return - } - defer resp.Body.Close() - respBody, _ := ioutil.ReadAll(resp.Body) - realBody := gjson.Get(string(respBody), "data") - assert.Equal(t, `{"code":401,"message":"401 Unauthorized","data":{"message":"Missing API key found in request"}}`, realBody.String()) - - // clear test data - tests = []HttpTestCase{ - { - Desc: "delete consumer", - Object: ManagerApiExpect(t), - Method: http.MethodDelete, - Path: "/apisix/admin/consumers/jack", - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - { - Desc: "verify route with the jwt token from just deleted consumer", - Object: APISIXExpect(t), - Method: http.MethodGet, - Path: "/hello", - Headers: map[string]string{"apikey": "user-key"}, - ExpectStatus: http.StatusUnauthorized, - ExpectBody: `{"message":"Missing related consumer"}`, - Sleep: sleepTime, - }, - { - Desc: "delete route", - Object: ManagerApiExpect(t), - Method: http.MethodDelete, - Path: "/apisix/admin/routes/r1", - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - { - Desc: "verify the deleted route ", - Object: APISIXExpect(t), - Method: http.MethodGet, - Path: "/hello", - ExpectStatus: http.StatusNotFound, - ExpectBody: `{"error_msg":"404 Route Not Found"}`, - Sleep: sleepTime, - }, - } - - for _, tc := range tests { - testCaseCheck(tc, t) - } -} - -func TestRoute_Online_Debug_Route_With_Query_Params_Key_Auth(t *testing.T) { - tests := []HttpTestCase{ - { - Desc: "make sure the route is not created ", - Object: APISIXExpect(t), - Method: http.MethodGet, - Path: "/hello", - ExpectStatus: http.StatusNotFound, - ExpectBody: `{"error_msg":"404 Route Not Found"}`, - }, - { - Desc: "create route enable key-auth plugin", - Object: ManagerApiExpect(t), - Method: http.MethodPut, - Path: "/apisix/admin/routes/r1", - Body: `{ - "uri": "/hello", - "methods": ["GET"], - "vars": [ - ["arg_name","==","aaa"] - ], - "plugins": { - "key-auth": {} - }, - "upstream": { - "type": "roundrobin", - "nodes": [{ - "host": "172.16.238.20", - "port": 1980, - "weight": 1 - }] - } - }`, - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - { - Desc: "make sure the consumer is not created", - Object: ManagerApiExpect(t), - Method: http.MethodGet, - Path: "/apisix/admin/consumers/jack", - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusNotFound, - }, - { - Desc: "create consumer", - Object: ManagerApiExpect(t), - Path: "/apisix/admin/consumers", - Method: http.MethodPut, - Body: `{ - "username": "jack", - "plugins": { - "key-auth": { - "key": "user-key" - } - }, - "desc": "test description" - }`, - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - { - Desc: "online debug route with apikey", - Object: ManagerApiExpect(t), - Method: http.MethodPost, - Path: "/apisix/admin/debug-request-forwarding", - Body: `{ - "url": "` + APISIXInternalUrl + `/hello?name=aaa", - "request_protocol": "http", - "method": "GET", - "header_params": { - "apikey": ["user-key"] - } - }`, - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - } - - for _, tc := range tests { - testCaseCheck(tc, t) - } - - // online debug without key-auth - basepath := "http://127.0.0.1:9000/apisix/admin/debug-request-forwarding" - request, _ := http.NewRequest("POST", basepath, strings.NewReader(`{"url": "`+APISIXInternalUrl+`/hello?name=aaa","method": "GET","request_protocol": "http"}`)) - request.Header.Add("Authorization", token) - resp, err := http.DefaultClient.Do(request) - if err != nil { - return - } - defer resp.Body.Close() - respBody, _ := ioutil.ReadAll(resp.Body) - realBody := gjson.Get(string(respBody), "data") - assert.Equal(t, `{"code":401,"message":"401 Unauthorized","data":{"message":"Missing API key found in request"}}`, realBody.String()) - - // clear test data - tests = []HttpTestCase{ - { - Desc: "delete consumer", - Object: ManagerApiExpect(t), - Method: http.MethodDelete, - Path: "/apisix/admin/consumers/jack", - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - { - Desc: "verify route with the jwt token from just deleted consumer", - Object: APISIXExpect(t), - Method: http.MethodGet, - Path: `/hello`, - Query: "name=aaa", - Headers: map[string]string{"apikey": "user-key"}, - ExpectStatus: http.StatusUnauthorized, - ExpectBody: `{"message":"Missing related consumer"}`, - Sleep: sleepTime, - }, - { - Desc: "delete route", - Object: ManagerApiExpect(t), - Method: http.MethodDelete, - Path: "/apisix/admin/routes/r1", - Headers: map[string]string{"Authorization": token}, - ExpectStatus: http.StatusOK, - }, - { - Desc: "verify the deleted route ", - Object: APISIXExpect(t), - Method: http.MethodGet, - Path: "/hello", - ExpectStatus: http.StatusNotFound, - ExpectBody: `{"error_msg":"404 Route Not Found"}`, - Sleep: sleepTime, - }, - } - - for _, tc := range tests { - testCaseCheck(tc, t) - } -} diff --git a/api/test/e2enew/base/http.go b/api/test/e2enew/base/http.go index 1ca2433c55..fda15936d8 100644 --- a/api/test/e2enew/base/http.go +++ b/api/test/e2enew/base/http.go @@ -18,12 +18,24 @@ package base import ( "bytes" + "encoding/json" + "io" "io/ioutil" + "mime/multipart" "net/http" + "net/url" + "os" + "path/filepath" + "strings" "github.com/stretchr/testify/assert" ) +type UploadFile struct { + Name string + Filepath string +} + func HttpGet(url string, headers map[string]string) ([]byte, int, error) { return httpRequest(http.MethodGet, url, headers, "") } @@ -92,3 +104,44 @@ func BatchTestServerPort(times int) map[string]int { return res } + +func GetReader(reqParams map[string]string, contentType string, files []UploadFile) (io.Reader, string, error) { + if strings.Index(contentType, "json") > -1 { + bytesData, _ := json.Marshal(reqParams) + return bytes.NewReader(bytesData), contentType, nil + } + if files != nil { + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + for _, uploadFile := range files { + file, err := os.Open(uploadFile.Filepath) + if err != nil { + return nil, "", err + } + defer file.Close() + part, err := writer.CreateFormFile(uploadFile.Name, filepath.Base(uploadFile.Filepath)) + if err != nil { + return nil, "", err + } + _, err = io.Copy(part, file) + } + for k, v := range reqParams { + if err := writer.WriteField(k, v); err != nil { + return nil, "", err + } + } + if err := writer.Close(); err != nil { + return nil, "", err + } + return body, writer.FormDataContentType(), nil + } + + urlValues := url.Values{} + for key, val := range reqParams { + urlValues.Set(key, val) + } + + reqBody := urlValues.Encode() + + return strings.NewReader(reqBody), contentType, nil +} diff --git a/api/test/e2enew/route_online_debug/route_online_debug_suite_test.go b/api/test/e2enew/route_online_debug/route_online_debug_suite_test.go new file mode 100644 index 0000000000..d92a29e0de --- /dev/null +++ b/api/test/e2enew/route_online_debug/route_online_debug_suite_test.go @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package route_online_debug + +import ( + "testing" + "time" + + "github.com/onsi/ginkgo" + + "e2enew/base" +) + +func TestRoute(t *testing.T) { + ginkgo.RunSpecs(t, "route online debug suite") +} + +var _ = ginkgo.AfterSuite(func() { + base.CleanResource("routes") + base.CleanResource("consumers") + time.Sleep(base.SleepTime) +}) + diff --git a/api/test/e2enew/route_online_debug/route_online_debug_test.go b/api/test/e2enew/route_online_debug/route_online_debug_test.go new file mode 100644 index 0000000000..0b6b935068 --- /dev/null +++ b/api/test/e2enew/route_online_debug/route_online_debug_test.go @@ -0,0 +1,822 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package route_online_debug + +import ( + "encoding/json" + "io/ioutil" + "net/http" + "path/filepath" + + "github.com/onsi/ginkgo" + "github.com/onsi/ginkgo/extensions/table" + "github.com/stretchr/testify/assert" + "github.com/tidwall/gjson" + + "e2enew/base" +) + +var upstream map[string]interface{} = map[string]interface{}{ + "type": "roundrobin", + "nodes": []map[string]interface{}{ + { + "host": base.UpstreamIp, + "port": 1980, + "weight": 1, + }, + }, +} + +var _ = ginkgo.Describe("Route_Online_Debug_Route_Not_Exist", func() { + table.DescribeTable("Route_Online_Debug_Route_Not_Exist", + func(tc base.HttpTestCase) { + base.RunTestCase(tc) + }, + table.Entry("hit route that not exist", base.HttpTestCase{ + Object: base.APISIXExpect(), + Method: http.MethodGet, + Path: "/hello_", + ExpectStatus: http.StatusNotFound, + ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n", + }), + table.Entry("online debug route with query params", base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPost, + Path: "/apisix/admin/debug-request-forwarding", + Headers: map[string]string{ + "Authorization": base.GetToken(), + "online_debug_url": base.APISIXInternalUrl + `/hello_`, + "online_debug_request_protocol": "http", + "online_debug_method": "GET", + "Content-Type": "multipart/form-data", + "online_debug_header_params": `{"test":["test1"]}`, + }, + ExpectStatus: http.StatusOK, + ExpectBody: `{"code":404,"message":"404 Not Found","data":{"error_msg":"404 Route Not Found"}}`, + Sleep: base.SleepTime, + }), + ) +}) + +var _ = ginkgo.Describe("Route_Online_Debug_Route_With_Query_Params", func() { + ginkgo.It("hit route that not exist", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.APISIXExpect(), + Method: http.MethodGet, + Path: "/hello", + ExpectStatus: http.StatusNotFound, + ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n", + }) + }) + ginkgo.It("create route with query params", func() { + t := ginkgo.GinkgoT() + var routeBody map[string]interface{} = map[string]interface{}{ + "uri": "/hello", + "methods": []string{"GET"}, + "vars": []interface{}{ + []string{"arg_name", "==", "aaa"}, + }, + "upstream": upstream, + } + _routeBody, err := json.Marshal(routeBody) + assert.Nil(t, err) + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPut, + Path: "/apisix/admin/routes/r1", + Body: string(_routeBody), + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + }) + }) + ginkgo.It("online debug route with query params", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPost, + Path: "/apisix/admin/debug-request-forwarding", + Headers: map[string]string{ + "Authorization": base.GetToken(), + "online_debug_url": base.APISIXInternalUrl + `/hello?name=aaa`, + "online_debug_request_protocol": "http", + "online_debug_method": "GET", + "Content-Type": "multipart/form-data", + "online_debug_header_params": `{"test":["test1"]}`, + }, + ExpectStatus: http.StatusOK, + ExpectBody: `"data":{"code":200,"message":"200 OK","data":"hello world`, + Sleep: base.SleepTime, + }) + }) + ginkgo.It("delete the route just created", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodDelete, + Path: "/apisix/admin/routes/r1", + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + }) + }) + ginkgo.It("hit the route just deleted", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.APISIXExpect(), + Method: http.MethodGet, + Path: "/hello", + ExpectStatus: http.StatusNotFound, + ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n", + Sleep: base.SleepTime, + }) + }) +}) + +var _ = ginkgo.Describe("Route_Online_Debug_Route_With_Header_Params", func() { + ginkgo.It("hit route that not exist", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.APISIXExpect(), + Method: http.MethodGet, + Path: "/hello", + ExpectStatus: http.StatusNotFound, + ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n", + }) + }) + ginkgo.It("create route with header params", func() { + t := ginkgo.GinkgoT() + var routeBody map[string]interface{} = map[string]interface{}{ + "uri": "/hello", + "methods": []string{"GET"}, + "vars": []interface{}{ + []string{"http_version", "==", "v2"}, + }, + "upstream": upstream, + } + _reqRouteBody, err := json.Marshal(routeBody) + assert.Nil(t, err) + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPut, + Path: "/apisix/admin/routes/r1", + Body: string(_reqRouteBody), + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + Sleep: base.SleepTime, + }) + }) + ginkgo.It("online debug route with header params", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPost, + Path: "/apisix/admin/debug-request-forwarding", + Headers: map[string]string{ + "Authorization": base.GetToken(), + "online_debug_url": base.APISIXInternalUrl + `/hello`, + "online_debug_request_protocol": "http", + "online_debug_method": "GET", + "Content-Type": "multipart/form-data", + "online_debug_header_params": `{"test":["test1"],"version":["v2"]}`, + }, + ExpectStatus: http.StatusOK, + ExpectBody: `"data":{"code":200,"message":"200 OK","data":"hello world`, + Sleep: base.SleepTime, + }) + }) + ginkgo.It("delete the route just created", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodDelete, + Path: "/apisix/admin/routes/r1", + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + }) + }) + ginkgo.It("hit the route just deleted", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.APISIXExpect(), + Method: http.MethodGet, + Path: "/hello", + ExpectStatus: http.StatusNotFound, + ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n", + Sleep: base.SleepTime, + }) + }) +}) + +var _ = ginkgo.Describe("Route_Online_Debug_Route_With_Body_Params", func() { + ginkgo.It("hit route that not exist", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.APISIXExpect(), + Method: http.MethodGet, + Path: "/hello", + ExpectStatus: http.StatusNotFound, + ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n", + }) + }) + ginkgo.It("create route with method POST", func() { + t := ginkgo.GinkgoT() + var routeBody map[string]interface{} = map[string]interface{}{ + "uri": "/hello", + "methods": []string{"POST"}, + "upstream": upstream, + } + _reqRouteBody, err := json.Marshal(routeBody) + assert.Nil(t, err) + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPut, + Path: "/apisix/admin/routes/r1", + Body: string(_reqRouteBody), + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + Sleep: base.SleepTime, + }) + }) + ginkgo.It("online debug route with body params", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPost, + Path: "/apisix/admin/debug-request-forwarding", + Body: `{ + "name": "test", + "desc": "online debug route with body params" + }`, + Headers: map[string]string{ + "Authorization": base.GetToken(), + "online_debug_url": base.APISIXInternalUrl + `/hello`, + "online_debug_request_protocol": "http", + "online_debug_method": http.MethodPost, + "Content-Type": "application/json", + "online_debug_header_params": `{"test":["test1"]}`, + }, + ExpectStatus: http.StatusOK, + ExpectBody: `"data":{"code":200,"message":"200 OK","data":"hello world`, + Sleep: base.SleepTime, + }) + }) + ginkgo.It("delete the route just created", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodDelete, + Path: "/apisix/admin/routes/r1", + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + }) + }) + ginkgo.It("hit the route just deleted", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.APISIXExpect(), + Method: http.MethodGet, + Path: "/hello", + ExpectStatus: http.StatusNotFound, + ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n", + Sleep: base.SleepTime, + }) + }) +}) +var _ = ginkgo.Describe("Route_Online_Debug_Route_With_Basic_Auth", func() { + ginkgo.It("hit route that not exist", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.APISIXExpect(), + Method: http.MethodGet, + Path: "/hello", + ExpectStatus: http.StatusNotFound, + ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n", + }) + }) + ginkgo.It("create route enable basic-auth plugin", func() { + t := ginkgo.GinkgoT() + var routeBody map[string]interface{} = map[string]interface{}{ + "uri": "/hello", + "plugins": map[string]interface{}{ + "basic-auth": map[string]interface{}{}, + }, + "methods": []string{"GET"}, + "upstream": upstream, + } + _reqRouteBody, err := json.Marshal(routeBody) + assert.Nil(t, err) + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPut, + Path: "/apisix/admin/routes/r1", + Body: string(_reqRouteBody), + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + Sleep: base.SleepTime, + }) + }) + ginkgo.It("make sure the consumer is not created", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodGet, + Path: "/apisix/admin/consumers/jack", + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusNotFound, + }) + }) + ginkgo.It("create consumer", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPut, + Path: "/apisix/admin/consumers", + Body: `{ + "username": "jack", + "plugins": { + "basic-auth": { + "disable": false, + "username": "jack", + "password": "123456" + } + }, + "desc": "test description" + }`, + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + Sleep: base.SleepTime, + }) + }) + ginkgo.It("online debug with basic-auth", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPost, + Path: "/apisix/admin/debug-request-forwarding", + Headers: map[string]string{ + "Authorization": base.GetToken(), + "online_debug_url": base.APISIXInternalUrl + `/hello`, + "online_debug_request_protocol": "http", + "online_debug_method": "GET", + "Content-Type": "multipart/form-data", + "online_debug_header_params": `{"test":["test1"],"Authorization": ["Basic amFjazoxMjM0NTYKIA=="]}`, + }, + ExpectStatus: http.StatusOK, + ExpectBody: `"data":{"code":200,"message":"200 OK","data":"hello world`, + }) + }) + ginkgo.It("online debug without basic-auth", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPost, + Path: "/apisix/admin/debug-request-forwarding", + Headers: map[string]string{ + "Authorization": base.GetToken(), + "online_debug_url": base.APISIXInternalUrl + `/hello`, + "online_debug_request_protocol": "http", + "online_debug_method": "GET", + "Content-Type": "multipart/form-data", + "online_debug_header_params": `{"test":["test1"]}`, + }, + ExpectStatus: http.StatusOK, + ExpectBody: `{"code":401,"message":"401 Unauthorized","data":{"message":"Missing authorization in request"}}`, + }) + }) + ginkgo.It("delete the route just created", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodDelete, + Path: "/apisix/admin/routes/r1", + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + }) + }) + ginkgo.It("hit the route just deleted", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.APISIXExpect(), + Method: http.MethodGet, + Path: "/hello", + ExpectStatus: http.StatusNotFound, + ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n", + Sleep: base.SleepTime, + }) + }) + ginkgo.It("delete consumer", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodDelete, + Path: "/apisix/admin/consumers/jack", + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + }) + }) +}) + +var _ = ginkgo.Describe("Route_Online_Debug_Route_With_Key_Auth", func() { + + ginkgo.It("hit route that not exist", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.APISIXExpect(), + Method: http.MethodGet, + Path: "/hello", + ExpectStatus: http.StatusNotFound, + ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n", + }) + }) + ginkgo.It("create route enable key-auth plugin", func() { + t := ginkgo.GinkgoT() + var routeBody map[string]interface{} = map[string]interface{}{ + "uri": "/hello", + "plugins": map[string]interface{}{ + "key-auth": map[string]interface{}{}, + }, + "methods": []string{"GET"}, + "upstream": upstream, + } + _reqRouteBody, err := json.Marshal(routeBody) + assert.Nil(t, err) + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPut, + Path: "/apisix/admin/routes/r1", + Body: string(_reqRouteBody), + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + Sleep: base.SleepTime, + }) + }) + ginkgo.It("make sure the consumer is not created", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodGet, + Path: "/apisix/admin/consumers/jack", + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusNotFound, + }) + }) + ginkgo.It("create consumer", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPut, + Path: "/apisix/admin/consumers", + Body: `{ + "username": "jack", + "plugins": { + "key-auth": { + "key": "user-key" + } + }, + "desc": "test description" + }`, + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + }) + }) + ginkgo.It("online debug with key-auth", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPost, + Path: "/apisix/admin/debug-request-forwarding", + Headers: map[string]string{ + "Authorization": base.GetToken(), + "online_debug_url": base.APISIXInternalUrl + `/hello`, + "online_debug_request_protocol": "http", + "online_debug_method": "GET", + "Content-Type": "multipart/form-data", + "online_debug_header_params": `{"test":["test1"],"apikey":["user-key"]}`, + }, + ExpectStatus: http.StatusOK, + ExpectBody: `"data":{"code":200,"message":"200 OK","data":"hello world`, + }) + }) + ginkgo.It("online debug without key-auth", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPost, + Path: "/apisix/admin/debug-request-forwarding", + Headers: map[string]string{ + "Authorization": base.GetToken(), + "online_debug_url": base.APISIXInternalUrl + `/hello`, + "online_debug_request_protocol": "http", + "online_debug_method": "GET", + "Content-Type": "multipart/form-data", + "online_debug_header_params": `{"test":["test1"]}`, + }, + ExpectStatus: http.StatusOK, + ExpectBody: `"data":{"code":401,"message":"401 Unauthorized","data":{"message":"Missing API key found in request"}}`, + }) + }) + ginkgo.It("delete the route just created", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodDelete, + Path: "/apisix/admin/routes/r1", + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + }) + }) + ginkgo.It("hit the route just deleted", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.APISIXExpect(), + Method: http.MethodGet, + Path: "/hello", + ExpectStatus: http.StatusNotFound, + ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n", + Sleep: base.SleepTime, + }) + }) + ginkgo.It("delete consumer", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodDelete, + Path: "/apisix/admin/consumers/jack", + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + }) + }) +}) + +var _ = ginkgo.Describe("Route_Online_Debug_Route_With_JWT_Auth", func() { + ginkgo.It("hit route that not exist", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.APISIXExpect(), + Method: http.MethodGet, + Path: "/hello", + ExpectStatus: http.StatusNotFound, + ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n", + }) + }) + ginkgo.It("create route enable jwt-auth plugin", func() { + t := ginkgo.GinkgoT() + var routeBody map[string]interface{} = map[string]interface{}{ + "uri": "/hello", + "plugins": map[string]interface{}{ + "jwt-auth": map[string]interface{}{}, + }, + "methods": []string{"GET"}, + "upstream": upstream, + } + _reqRouteBody, err := json.Marshal(routeBody) + assert.Nil(t, err) + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPut, + Path: "/apisix/admin/routes/r1", + Body: string(_reqRouteBody), + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + Sleep: base.SleepTime, + }) + }) + ginkgo.It("make sure the consumer is not created", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodGet, + Path: "/apisix/admin/consumers/jack", + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusNotFound, + }) + }) + ginkgo.It("create consumer", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPut, + Path: "/apisix/admin/consumers", + Body: `{ + "username": "jack", + "plugins": { + "jwt-auth": { + "key": "user-key", + "secret": "my-secret-key", + "algorithm": "HS256" + } + }, + "desc": "test description" + }`, + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + }) + }) + ginkgo.It("online debug with JWT-auth", func() { + t := ginkgo.GinkgoT() + jsonStr := `{"test":["test1"]}` + var _headerParams map[string]interface{} + err := json.Unmarshal([]byte(jsonStr), &_headerParams) + assert.Nil(t, err) + jwtToken := base.GetJwtToken("user-key") + l := []string{jwtToken} + _headerParams["Authorization"] = l + headerParams, err := json.Marshal(_headerParams) + assert.Nil(t, err) + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPost, + Path: "/apisix/admin/debug-request-forwarding", + Headers: map[string]string{ + "Authorization": base.GetToken(), + "online_debug_url": base.APISIXInternalUrl + `/hello`, + "online_debug_request_protocol": "http", + "online_debug_method": "GET", + "Content-Type": "multipart/form-data", + "online_debug_header_params": string(headerParams), + }, + ExpectStatus: http.StatusOK, + ExpectBody: `"data":{"code":200,"message":"200 OK","data":"hello world`, + }) + }) + ginkgo.It("online debug without JWT-auth", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPost, + Path: "/apisix/admin/debug-request-forwarding", + Headers: map[string]string{ + "Authorization": base.GetToken(), + "online_debug_url": base.APISIXInternalUrl + `/hello`, + "online_debug_request_protocol": "http", + "online_debug_method": "GET", + "Content-Type": "multipart/form-data", + "online_debug_header_params": `{"test":["test1"]}`, + }, + ExpectStatus: http.StatusOK, + ExpectBody: `"data":{"code":401,"message":"401 Unauthorized","data":{"message":"Missing JWT token in request"}}`, + }) + }) + ginkgo.It("delete the route just created", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodDelete, + Path: "/apisix/admin/routes/r1", + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + }) + }) + ginkgo.It("hit the route just deleted", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.APISIXExpect(), + Method: http.MethodGet, + Path: "/hello", + ExpectStatus: http.StatusNotFound, + ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n", + Sleep: base.SleepTime, + }) + }) + ginkgo.It("delete consumer", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodDelete, + Path: "/apisix/admin/consumers/jack", + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + }) + }) +}) + +var _ = ginkgo.Describe("Route_Online_Debug_Route_With_Files", func() { + ginkgo.It("hit route that not exist", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.APISIXExpect(), + Method: http.MethodGet, + Path: "/hello", + ExpectStatus: http.StatusNotFound, + ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n", + }) + }) + ginkgo.It("create route enable basic-auth plugin", func() { + t := ginkgo.GinkgoT() + var routeBody map[string]interface{} = map[string]interface{}{ + "uri": "/hello_", + "methods": []string{"POST"}, + "upstream": upstream, + } + _reqRouteBody, err := json.Marshal(routeBody) + assert.Nil(t, err) + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPut, + Path: "/apisix/admin/routes/r2", + Body: string(_reqRouteBody), + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + Sleep: base.SleepTime, + }) + }) + ginkgo.It("online debug with file", func() { + t := ginkgo.GinkgoT() + path, err := filepath.Abs("../../testdata/import/default.yaml") + assert.Nil(t, err) + files := []base.UploadFile{ + {Name: "file", Filepath: path}, + } + + headers := map[string]string{} + + jsonStr := `{"test":["test1"]}` + var _headerParams map[string]interface{} + err = json.Unmarshal([]byte(jsonStr), &_headerParams) + assert.Nil(t, err) + l := []string{base.GetToken()} + _headerParams["Authorization"] = l + headerParams, err := json.Marshal(_headerParams) + assert.Nil(t, err) + + basePath := base.ManagerAPIHost + "/apisix/admin/debug-request-forwarding" + requestBody, requestContentType, err := base.GetReader(headers, "multipart/form-data", files) + assert.Nil(t, err) + httpRequest, err := http.NewRequest(http.MethodPost, basePath, requestBody) + assert.Nil(t, err) + httpRequest.Header.Add("Content-Type", requestContentType) + httpRequest.Header.Add("Authorization", base.GetToken()) + httpRequest.Header.Add("online_debug_request_protocol", "http") + httpRequest.Header.Add("online_debug_url", base.ManagerAPIHost+`/apisix/admin/import/routes`) + httpRequest.Header.Add("online_debug_method", http.MethodPost) + httpRequest.Header.Add("online_debug_header_params", string(headerParams)) + client := &http.Client{} + resp, err := client.Do(httpRequest) + assert.Nil(t, err) + + defer resp.Body.Close() + + respBody, err := ioutil.ReadAll(resp.Body) + assert.Nil(t, err) + realBody := gjson.Get(string(respBody), "data") + // todo get successful result and compare + assert.Contains(t, realBody.String(), `"data":{"paths":1,"routes":1}`) + }) + + ginkgo.It("verify the route just imported and delete data", func() { + t := ginkgo.GinkgoT() + request, _ := http.NewRequest("GET", base.ManagerAPIHost+"/apisix/admin/routes", nil) + request.Header.Add("Authorization", base.GetToken()) + resp, err := http.DefaultClient.Do(request) + assert.Nil(t, err) + defer resp.Body.Close() + respBody, _ := ioutil.ReadAll(resp.Body) + list := gjson.Get(string(respBody), "data.rows").Value().([]interface{}) + + var tests []base.HttpTestCase + for _, item := range list { + route := item.(map[string]interface{}) + tc := base.HttpTestCase{ + Desc: "route patch for update status(online)", + Object: base.ManagerApiExpect(), + Method: http.MethodPatch, + Path: "/apisix/admin/routes/" + route["id"].(string), + Body: `{"status":1}`, + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + Sleep: base.SleepTime, + } + tests = append(tests, tc) + } + + // verify route + tests = append(tests, base.HttpTestCase{ + Desc: "verify the route just imported", + Object: base.APISIXExpect(), + Method: http.MethodGet, + Path: "/hello", + ExpectStatus: http.StatusOK, + ExpectBody: "hello world", + Sleep: base.SleepTime, + }) + + // delete test data + for _, item := range list { + route := item.(map[string]interface{}) + tc := base.HttpTestCase{ + Desc: "delete route", + Object: base.ManagerApiExpect(), + Method: http.MethodDelete, + Path: "/apisix/admin/routes/" + route["id"].(string), + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + } + tests = append(tests, tc) + } + + for _, tc := range tests { + base.RunTestCase(tc) + } + + }) + + ginkgo.It("hit the route just deleted", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.APISIXExpect(), + Method: http.MethodGet, + Path: "/hello", + ExpectStatus: http.StatusNotFound, + ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n", + Sleep: base.SleepTime, + }) + }) + + ginkgo.It("hit the route just deleted", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.APISIXExpect(), + Method: http.MethodGet, + Path: "/hello_", + ExpectStatus: http.StatusNotFound, + ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n", + Sleep: base.SleepTime, + }) + }) +})