You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by ju...@apache.org on 2021/02/25 15:15:15 UTC

[apisix-dashboard] branch master updated: feat(be): Online debug function adds support for transfer files (#1465)

This is an automated email from the ASF dual-hosted git repository.

juzhiyuan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-dashboard.git


The following commit(s) were added to refs/heads/master by this push:
     new e51323f  feat(be): Online debug function adds support for transfer files (#1465)
e51323f is described below

commit e51323fccf8b7bf5490f3e59319bb1c10ea03c5f
Author: JinChen <36...@users.noreply.github.com>
AuthorDate: Thu Feb 25 23:15:08 2021 +0800

    feat(be): Online debug function adds support for transfer files (#1465)
---
 .../route_online_debug/route_online_debug.go       |  77 +-
 api/test/e2e/route_online_debug_test.go            | 849 ---------------------
 api/test/e2enew/base/http.go                       |  53 ++
 .../route_online_debug_suite_test.go               |  37 +
 .../route_online_debug/route_online_debug_test.go  | 822 ++++++++++++++++++++
 5 files changed, 961 insertions(+), 877 deletions(-)

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 ac1c502..3f8da4a 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 4665309..0000000
--- 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 1ca2433..fda1593 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 0000000..d92a29e
--- /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 0000000..0b6b935
--- /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,
+		})
+	})
+})