From eaffbf8780e370ecec4e9bbc534694b8a815a86a Mon Sep 17 00:00:00 2001 From: Shulhan Date: Sat, 27 Mar 2021 21:57:57 +0700 Subject: all: change the Run result to return dump of HTTP request and response Previously, we display the actual response of the HTTP target only on the user interface. This changes make the Run function to display the actual HTTP request and response that being send and received on HTTP target. --- _www/index.js | 18 ++------ example/example.go | 70 ++++++++++++++++++++++++---- go.mod | 4 +- go.sum | 4 +- http_target.go | 2 +- run_response.go | 44 ++++++++++++++++++ trunks.go | 131 ++++++++++++++++++----------------------------------- 7 files changed, 159 insertions(+), 114 deletions(-) create mode 100644 run_response.go diff --git a/_www/index.js b/_www/index.js index 5512b14..a902ad1 100644 --- a/_www/index.js +++ b/_www/index.js @@ -184,7 +184,8 @@ function renderHttpTargets(target) {

Parameters

-

Run response

+

Run output

+

 				

 
 				

Attack results

@@ -397,19 +398,8 @@ async function run(targetID, httpTargetID) { return } - let elResponse = document.getElementById(httpTargetID + "_response") - let m = _requestMethods[req.HttpTarget.Method] - switch (m) { - case "GET": - case "POST": - case "PUT": - case "DELETE": - elResponse.innerHTML = JSON.stringify(res, null, 2) - break - default: - elResponse.innerHTML = atob(res.data) - break - } + document.getElementById(httpTargetID + "_request").innerHTML = atob(res.data.DumpRequest) + document.getElementById(httpTargetID + "_response").innerHTML = atob(res.data.DumpResponse) } async function runWebSocket(targetID, wstID) { diff --git a/example/example.go b/example/example.go index ba2dada..cbdb840 100644 --- a/example/example.go +++ b/example/example.go @@ -233,18 +233,43 @@ func (ex *Example) pathExamplePostForm(epr *libhttp.EndpointRequest) ([]byte, er return json.Marshal(&res) } -func (ex *Example) runExampleGet(req *trunks.RunRequest) ([]byte, error) { +func (ex *Example) runExampleGet(req *trunks.RunRequest) (res *trunks.RunResponse, err error) { if req.Target.HttpClient == nil { req.Target.HttpClient = libhttp.NewClient(req.Target.BaseUrl, nil, true) } - _, resbody, err := req.Target.HttpClient.Get( + + res = &trunks.RunResponse{} + + headers := req.HttpTarget.Headers.ToHttpHeader() + params := req.HttpTarget.Params.ToUrlValues() + + httpRequest, err := req.Target.HttpClient.GenerateHttpRequest( + req.HttpTarget.Method, req.HttpTarget.Path, - req.HttpTarget.Headers.ToHttpHeader(), - req.HttpTarget.Params.ToUrlValues()) + req.HttpTarget.RequestType, + headers, + params, + ) + if err != nil { + return nil, err + } + + err = res.SetHttpRequest(httpRequest) + if err != nil { + return nil, err + } + + httpResponse, _, err := req.Target.HttpClient.Do(httpRequest) + if err != nil { + return nil, err + } + + err = res.SetHttpResponse(httpResponse) if err != nil { return nil, err } - return resbody, nil + + return res, nil } func (ex *Example) preattackExampleGet(rr *trunks.RunRequest) { @@ -271,18 +296,43 @@ func (ex *Example) attackExampleGet(rr *trunks.RunRequest) vegeta.Targeter { } } -func (ex *Example) runExamplePostForm(req *trunks.RunRequest) ([]byte, error) { +func (ex *Example) runExamplePostForm(req *trunks.RunRequest) (res *trunks.RunResponse, err error) { if req.Target.HttpClient == nil { req.Target.HttpClient = libhttp.NewClient(req.Target.BaseUrl, nil, true) } - _, resbody, err := req.Target.HttpClient.PostForm( + + res = &trunks.RunResponse{} + + headers := req.HttpTarget.Headers.ToHttpHeader() + params := req.HttpTarget.Params.ToUrlValues() + + httpRequest, err := req.Target.HttpClient.GenerateHttpRequest( + req.HttpTarget.Method, req.HttpTarget.Path, - req.HttpTarget.Headers.ToHttpHeader(), - req.HttpTarget.Params.ToUrlValues()) + req.HttpTarget.RequestType, + headers, + params, + ) + if err != nil { + return nil, err + } + + err = res.SetHttpRequest(httpRequest) + if err != nil { + return nil, err + } + + httpResponse, _, err := req.Target.HttpClient.Do(httpRequest) + if err != nil { + return nil, err + } + + err = res.SetHttpResponse(httpResponse) if err != nil { return nil, err } - return resbody, nil + + return res, nil } func (ex *Example) preattackExamplePostForm(rr *trunks.RunRequest) { diff --git a/go.mod b/go.mod index acf15f8..b4c4688 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,10 @@ require ( github.com/dgryski/go-gk v0.0.0-20200319235926-a69029f61654 // indirect github.com/influxdata/tdigest v0.0.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/shuLhan/share v0.24.1-0.20210321092144-d4f1f279b631 + github.com/shuLhan/share v0.24.1-0.20210327143930-4cdd6b01c14b github.com/tsenart/vegeta/v12 v12.8.4 golang.org/x/net v0.0.0-20210315170653-34ac3e1c2000 // indirect golang.org/x/text v0.3.5 // indirect ) + +//replace github.com/shuLhan/share => ../share diff --git a/go.sum b/go.sum index 288305d..8e75b1c 100644 --- a/go.sum +++ b/go.sum @@ -26,8 +26,8 @@ github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7 github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/miekg/dns v1.1.17/go.mod h1:WgzbA6oji13JREwiNsRDNfl7jYdPnmz+VEuLrA+/48M= -github.com/shuLhan/share v0.24.1-0.20210321092144-d4f1f279b631 h1:/aIE4Q48KUHC4NufZhx9fNyCdPJxB5nX53jlXbKQGZc= -github.com/shuLhan/share v0.24.1-0.20210321092144-d4f1f279b631/go.mod h1:c6xnA1EctNz2KGVlekfydyXoUjpTPjyUHUIyWSELN/c= +github.com/shuLhan/share v0.24.1-0.20210327143930-4cdd6b01c14b h1:NykUKIze94kWmnUGhxcDvUewYHqiSUVx+dYM9AVXA5U= +github.com/shuLhan/share v0.24.1-0.20210327143930-4cdd6b01c14b/go.mod h1:c6xnA1EctNz2KGVlekfydyXoUjpTPjyUHUIyWSELN/c= github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25 h1:7z3LSn867ex6VSaahyKadf4WtSsJIgne6A1WLOAGM8A= github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25/go.mod h1:lbP8tGiBjZ5YWIc2fzuRpTaz0b/53vT6PEs3QuAWzuU= github.com/tsenart/go-tsz v0.0.0-20180814232043-cdeb9e1e981e/go.mod h1:SWZznP1z5Ki7hDT2ioqiFKEse8K9tU2OUvaRI0NeGQo= diff --git a/http_target.go b/http_target.go index f21df13..79057bb 100644 --- a/http_target.go +++ b/http_target.go @@ -19,7 +19,7 @@ import ( // HttpRunHandler define the function type that will be called when client // send request to run the HTTP target. // -type HttpRunHandler func(rr *RunRequest) ([]byte, error) +type HttpRunHandler func(rr *RunRequest) (runres *RunResponse, err error) // // HttpAttackHandler define the function type that will be called when client diff --git a/run_response.go b/run_response.go new file mode 100644 index 0000000..de12105 --- /dev/null +++ b/run_response.go @@ -0,0 +1,44 @@ +// Copyright 2021, Shulhan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package trunks + +import ( + "fmt" + "net/http" + "net/http/httputil" +) + +// +// RunResponse contains the raw request and response when running HTTP or +// WebSocket target. +// +type RunResponse struct { + DumpRequest []byte + DumpResponse []byte +} + +// +// SetHttpResponse dump the HTTP request including body into the DumpRequest +// field. +// +func (rres *RunResponse) SetHttpRequest(req *http.Request) (err error) { + rres.DumpRequest, err = httputil.DumpRequest(req, true) + if err != nil { + return fmt.Errorf("SetHttpRequest: %w", err) + } + return nil +} + +// +// SetHttpResponse dump the HTTP response including body into the DumpResponse +// field. +// +func (rres *RunResponse) SetHttpResponse(res *http.Response) (err error) { + rres.DumpResponse, err = httputil.DumpResponse(res, true) + if err != nil { + return fmt.Errorf("SetHttpResponse: %w", err) + } + return nil +} diff --git a/trunks.go b/trunks.go index 919de36..5cb747b 100644 --- a/trunks.go +++ b/trunks.go @@ -8,7 +8,6 @@ import ( "encoding/json" "fmt" "net/http" - "net/http/httputil" "os" "sort" "strings" @@ -405,15 +404,25 @@ func (trunks *Trunks) apiTargetRunHttp(epr *libhttp.EndpointRequest) ([]byte, er return nil, errInvalidHttpTarget(req.HttpTarget.ID) } - if req.HttpTarget.Run != nil { + var res *RunResponse + + if req.HttpTarget.Run == nil { + req.Target.BaseUrl = origTarget.BaseUrl + req.Target.Name = origTarget.Name + res, err = trunks.runHttpTarget(req) + } else { req.mergeHttpTarget(trunks.Env, origTarget, origHttpTarget) - return req.HttpTarget.Run(req) + res, err = req.HttpTarget.Run(req) + } + if err != nil { + return nil, errInternal(err) } - req.Target.BaseUrl = origTarget.BaseUrl - req.Target.Name = origTarget.Name + epres := libhttp.EndpointResponse{} + epres.Code = http.StatusOK + epres.Data = res - return trunks.runHttpTarget(req) + return json.Marshal(&epres) } func (trunks *Trunks) apiTargetRunWebSocket(epr *libhttp.EndpointRequest) ([]byte, error) { @@ -503,99 +512,49 @@ func (trunks *Trunks) getTargetByResultFilename(name string) (t *Target, ht *Htt return t, ht } -func (trunks *Trunks) runHttpTarget(rr *RunRequest) (resbody []byte, err error) { +func (trunks *Trunks) runHttpTarget(rr *RunRequest) (res *RunResponse, err error) { var ( - httpRes *http.Response - res = libhttp.EndpointResponse{} + logp = "runHttpTarget" httpc = libhttp.NewClient(rr.Target.BaseUrl, nil, true) headers = rr.HttpTarget.Headers.ToHttpHeader() - params = rr.HttpTarget.Params.ToUrlValues() + params interface{} ) - switch rr.HttpTarget.Method { - case libhttp.RequestMethodGet: - httpRes, resbody, err = httpc.Get(rr.HttpTarget.Path, headers, params) - - case libhttp.RequestMethodConnect, - libhttp.RequestMethodDelete, - libhttp.RequestMethodHead, - libhttp.RequestMethodOptions, - libhttp.RequestMethodPatch, - libhttp.RequestMethodTrace: - - httpReq, err := http.NewRequest(rr.HttpTarget.Method.String(), - fmt.Sprintf("%s%s", rr.Target.BaseUrl, rr.HttpTarget.Path), - nil) - if err != nil { - mlog.Errf("runHttpTarget: %s %s: %s\n", - rr.HttpTarget.Method.String(), - rr.HttpTarget.Path, err) - return nil, errInternal(err) - } - - httpRes, err = httpc.Do(httpReq) - if err != nil { - mlog.Errf("runHttpTarget: %s %s: %s\n", - rr.HttpTarget.Method.String(), - rr.HttpTarget.Path, err) - return nil, errInternal(err) - } + if rr.HttpTarget.RequestType == libhttp.RequestTypeJSON { + params = rr.HttpTarget.Params + } else { + params = rr.HttpTarget.Params.ToUrlValues() + } - dumpres, err := httputil.DumpResponse(httpRes, true) - if err != nil { - mlog.Errf("runHttpTarget: %s %s: %s\n", - rr.HttpTarget.Method.String(), - rr.HttpTarget.Path, err) - return nil, errInternal(err) - } + res = &RunResponse{} - res.Code = http.StatusOK - res.Data = dumpres - return json.Marshal(res) - - case libhttp.RequestMethodPost: - switch rr.HttpTarget.RequestType { - case libhttp.RequestTypeNone, - libhttp.RequestTypeQuery: - httpRes, resbody, err = httpc.Post( - rr.HttpTarget.Path, headers, params) - - case libhttp.RequestTypeForm: - httpRes, resbody, err = httpc.PostForm( - rr.HttpTarget.Path, headers, params) - - case libhttp.RequestTypeMultipartForm: - httpRes, resbody, err = httpc.PostFormData( - rr.HttpTarget.Path, headers, - rr.HttpTarget.Params.ToMultipartFormData()) - - case libhttp.RequestTypeJSON: - httpRes, resbody, err = httpc.PostJSON( - rr.HttpTarget.Path, headers, - rr.HttpTarget.Params) - } + httpRequest, err := httpc.GenerateHttpRequest( + rr.HttpTarget.Method, + rr.HttpTarget.Path, + rr.HttpTarget.RequestType, + headers, + params, + ) + if err != nil { + return nil, fmt.Errorf("%s: %w", logp, err) + } - case libhttp.RequestMethodPut: - if rr.HttpTarget.RequestType == libhttp.RequestTypeJSON { - httpRes, resbody, err = httpc.PutJSON( - rr.HttpTarget.Path, headers, - rr.HttpTarget.Params) - } else { - httpRes, resbody, err = httpc.Put( - rr.HttpTarget.Path, headers, nil) - } + err = res.SetHttpRequest(httpRequest) + if err != nil { + return nil, fmt.Errorf("%s: %w", logp, err) } + + httpResponse, _, err := httpc.Do(httpRequest) if err != nil { - return nil, errInternal(err) + return nil, fmt.Errorf("%s: %w", logp, err) } - if httpRes.StatusCode != http.StatusOK { - res.Code = httpRes.StatusCode - res.Message = httpRes.Status - res.Data = resbody - return json.Marshal(res) + + err = res.SetHttpResponse(httpResponse) + if err != nil { + return nil, fmt.Errorf("%s: %w", logp, err) } - return resbody, err + return res, nil } // -- cgit v1.3