github.com/Tyktechnologies/tyk@v2.9.5+incompatible/gateway/tracing.go (about)

     1  package gateway
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"net/http/httputil"
     9  	"strings"
    10  
    11  	"github.com/gorilla/mux"
    12  	"github.com/sirupsen/logrus"
    13  
    14  	"github.com/TykTechnologies/tyk/apidef"
    15  )
    16  
    17  type traceHttpRequest struct {
    18  	Method  string      `json:"method"`
    19  	Path    string      `json:"path"`
    20  	Body    string      `json:"body"`
    21  	Headers http.Header `json:"headers"`
    22  }
    23  
    24  func (tr *traceHttpRequest) toRequest() *http.Request {
    25  	r := httptest.NewRequest(tr.Method, tr.Path, strings.NewReader(tr.Body))
    26  	// It sets example.com by default. Setting it to empty will not show a value because it is not necessary.
    27  	r.Host = ""
    28  
    29  	for key, values := range tr.Headers {
    30  		for _, v := range values {
    31  			r.Header.Add(key, v)
    32  		}
    33  	}
    34  
    35  	ctxSetTrace(r)
    36  
    37  	return r
    38  }
    39  
    40  // TraceRequest is for tracing an HTTP request
    41  // swagger:model TraceRequest
    42  type traceRequest struct {
    43  	Request *traceHttpRequest     `json:"request"`
    44  	Spec    *apidef.APIDefinition `json:"spec"`
    45  }
    46  
    47  // TraceResponse is for tracing an HTTP response
    48  // swagger:model TraceResponse
    49  type traceResponse struct {
    50  	Message  string `json:"message"`
    51  	Response string `json:"response"`
    52  	Logs     string `json:"logs"`
    53  }
    54  
    55  // Tracing request
    56  // Used to test API definition by sending sample request,
    57  // and analysisng output of both response and logs
    58  //
    59  //---
    60  // requestBody:
    61  //   content:
    62  //     application/json:
    63  //       schema:
    64  //         "$ref": "#/definitions/traceRequest"
    65  //       examples:
    66  //         request:
    67  //           method: GET
    68  //           path: /get
    69  //           headers:
    70  //              Authorization: key
    71  //         spec:
    72  //           api_name: "Test"
    73  // responses:
    74  //   200:
    75  //     description: Success tracing request
    76  //     schema:
    77  //       "$ref": "#/definitions/traceResponse"
    78  //     examples:
    79  //       message: "ok"
    80  //       response:
    81  //         code: 200
    82  //         headers:
    83  //           Header: value
    84  //         body: body-value
    85  //       logs: {...}\n{...}
    86  func traceHandler(w http.ResponseWriter, r *http.Request) {
    87  	var traceReq traceRequest
    88  	if err := json.NewDecoder(r.Body).Decode(&traceReq); err != nil {
    89  		log.Error("Couldn't decode trace request: ", err)
    90  
    91  		doJSONWrite(w, http.StatusBadRequest, apiError("Request malformed"))
    92  		return
    93  	}
    94  
    95  	if traceReq.Spec == nil {
    96  		log.Error("Spec field is missing")
    97  		doJSONWrite(w, http.StatusBadRequest, apiError("Spec field is missing"))
    98  		return
    99  	}
   100  
   101  	if traceReq.Request == nil {
   102  		log.Error("Request field is missing")
   103  		doJSONWrite(w, http.StatusBadRequest, apiError("Request field is missing"))
   104  		return
   105  	}
   106  
   107  	var logStorage bytes.Buffer
   108  	logger := logrus.New()
   109  	logger.Formatter = &logrus.JSONFormatter{}
   110  	logger.Level = logrus.DebugLevel
   111  	logger.Out = &logStorage
   112  
   113  	gs := prepareStorage()
   114  	subrouter := mux.NewRouter()
   115  
   116  	loader := &APIDefinitionLoader{}
   117  	spec := loader.MakeSpec(traceReq.Spec, logrus.NewEntry(logger))
   118  
   119  	chainObj := processSpec(spec, nil, &gs, subrouter, logrus.NewEntry(logger))
   120  	spec.middlewareChain = chainObj
   121  
   122  	if chainObj.ThisHandler == nil {
   123  		doJSONWrite(w, http.StatusBadRequest, traceResponse{Message: "error", Logs: logStorage.String()})
   124  		return
   125  	}
   126  
   127  	wr := httptest.NewRecorder()
   128  	tr := traceReq.Request.toRequest()
   129  	chainObj.ThisHandler.ServeHTTP(wr, tr)
   130  
   131  	var response string
   132  	if dump, err := httputil.DumpResponse(wr.Result(), true); err == nil {
   133  		response = string(dump)
   134  	} else {
   135  		response = err.Error()
   136  	}
   137  
   138  	var request string
   139  	if dump, err := httputil.DumpRequest(tr, true); err == nil {
   140  		request = string(dump)
   141  	} else {
   142  		request = err.Error()
   143  	}
   144  
   145  	requestDump := "====== Request ======\n" + request + "\n====== Response ======\n" + response
   146  
   147  	doJSONWrite(w, http.StatusOK, traceResponse{Message: "ok", Response: requestDump, Logs: logStorage.String()})
   148  }