api/internal/handler/route_online_debug/route_online_debug.go (129 lines of code) (raw):
/*
* 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 (
"bytes"
"compress/gzip"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"reflect"
"strings"
"time"
"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 {
}
func NewHandler() (handler.RouteRegister, error) {
return &Handler{}, nil
}
type ProtocolSupport interface {
RequestForwarding(c droplet.Context) (interface{}, error)
}
func (h *Handler) ApplyRoute(r *gin.Engine) {
r.POST("/apisix/admin/debug-request-forwarding", wgin.Wraps(h.DebugRequestForwarding,
wrapper.InputType(reflect.TypeOf(DebugOnlineInput{}))))
}
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 {
Code int `json:"code,omitempty"`
Header interface{} `json:"header,omitempty"`
Message string `json:"message,omitempty"`
Data interface{} `json:"data,omitempty"`
}
func (h *Handler) DebugRequestForwarding(c droplet.Context) (interface{}, error) {
//TODO: other Protocols, e.g: grpc, websocket
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[protocol]; ok {
ret, err := v.RequestForwarding(c)
return ret, err
}
return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest}, fmt.Errorf("protocol unsupported %s", protocol)
}
type HTTPProtocolSupport struct {
}
func (h *HTTPProtocolSupport) RequestForwarding(c droplet.Context) (interface{}, error) {
input := c.Input().(*DebugOnlineInput)
url := input.URL
method := input.Method
body := input.Body
contentType := input.ContentType
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.DisableCompression = true
client := &http.Client{
Transport: transport,
Timeout: 5 * time.Second,
}
var tempMap map[string][]string
err := json.Unmarshal([]byte(input.HeaderParams), &tempMap)
if err != nil {
return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest}, fmt.Errorf("can not get header")
}
req, err := http.NewRequest(strings.ToUpper(method), url, bytes.NewReader(body))
if err != nil {
return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest}, err
}
req.Header.Add("Content-Type", contentType)
for k, v := range tempMap {
for _, v1 := range v {
if !strings.EqualFold(k, "Content-Type") {
req.Header.Add(k, v1)
} else {
req.Header.Set(k, v1)
}
}
}
resp, err := client.Do(req)
if err != nil {
return &data.SpecCodeResponse{StatusCode: http.StatusInternalServerError}, err
}
defer resp.Body.Close()
// handle gzip content encoding
var reader io.ReadCloser
switch resp.Header.Get("Content-Encoding") {
case "gzip":
reader, err = gzip.NewReader(resp.Body)
if err != nil {
return &data.SpecCodeResponse{StatusCode: http.StatusInternalServerError}, err
}
defer reader.Close()
default:
reader = resp.Body
}
_body, err := ioutil.ReadAll(reader)
if err != nil {
return &data.SpecCodeResponse{StatusCode: http.StatusInternalServerError}, err
}
returnData := make(map[string]interface{})
result := &Result{}
err = json.Unmarshal(_body, &returnData)
if err != nil {
result.Code = resp.StatusCode
result.Header = resp.Header
result.Message = resp.Status
result.Data = string(_body)
} else {
result.Code = resp.StatusCode
result.Header = resp.Header
result.Message = resp.Status
result.Data = returnData
}
return result, nil
}