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 }