From a36a4a365b30ff4f331f5b9e5e08b5c98257aff5 Mon Sep 17 00:00:00 2001 From: Shulhan Date: Mon, 5 Feb 2024 01:07:44 +0700 Subject: all: add type to customize how to dump HTTP request and response The HTTPRequestDumper define an handler to convert [http.Request] into [RunResponse] DumpRequest. The HTTPResponseDumper define an handler to convert [http.Response] into [RunResponse] DumpResponse. --- example/example.go | 8 ++++---- http_request_dumper.go | 29 +++++++++++++++++++++++++++++ http_response_dumper.go | 29 +++++++++++++++++++++++++++++ http_target.go | 19 ++++++++++++++++++- run_request.go | 3 +++ run_response.go | 9 ++++----- trunks.go | 7 +++++-- 7 files changed, 92 insertions(+), 12 deletions(-) create mode 100644 http_request_dumper.go create mode 100644 http_response_dumper.go diff --git a/example/example.go b/example/example.go index 128c78c..17e24eb 100644 --- a/example/example.go +++ b/example/example.go @@ -460,7 +460,7 @@ func (ex *Example) runExampleGet(req *trunks.RunRequest) (res *trunks.RunRespons return nil, err } - err = res.SetHTTPRequest(httpRequest) + err = res.SetHTTPRequest(req.HTTPTarget.RequestDumper, httpRequest) if err != nil { return nil, err } @@ -472,7 +472,7 @@ func (ex *Example) runExampleGet(req *trunks.RunRequest) (res *trunks.RunRespons return nil, err } - err = res.SetHTTPResponse(httpResponse) + err = res.SetHTTPResponse(req.HTTPTarget.ResponseDumper, httpResponse) if err != nil { return nil, err } @@ -553,7 +553,7 @@ func (ex *Example) runExamplePostForm(req *trunks.RunRequest) (res *trunks.RunRe return nil, err } - err = res.SetHTTPRequest(httpRequest) + err = res.SetHTTPRequest(req.HTTPTarget.RequestDumper, httpRequest) if err != nil { return nil, err } @@ -563,7 +563,7 @@ func (ex *Example) runExamplePostForm(req *trunks.RunRequest) (res *trunks.RunRe return nil, err } - err = res.SetHTTPResponse(httpResponse) + err = res.SetHTTPResponse(req.HTTPTarget.ResponseDumper, httpResponse) if err != nil { return nil, err } diff --git a/http_request_dumper.go b/http_request_dumper.go new file mode 100644 index 0000000..e82d4e6 --- /dev/null +++ b/http_request_dumper.go @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2024 M. Shulhan +// SPDX-License-Identifier: GPL-3.0-or-later + +package trunks + +import ( + "bytes" + "fmt" + "net/http" + "net/http/httputil" +) + +// HTTPRequestDumper define an handler to convert [http.Request] into +// [RunResponse] DumpRequest. +type HTTPRequestDumper func(req *http.Request) ([]byte, error) + +// DumpHTTPRequest define default [HTTPRequestDumper] that convert +// [http.Request] with its body to stream of bytes using +// [httputil.DumpRequest]. +// +// The returned dump have CRLF ("\r\n") replaced with single LF ("\n"). +func DumpHTTPRequest(req *http.Request) (raw []byte, err error) { + raw, err = httputil.DumpRequestOut(req, true) + if err != nil { + return nil, fmt.Errorf(`DumpHTTPRequest: %w`, err) + } + raw = bytes.ReplaceAll(raw, []byte{'\r', '\n'}, []byte{'\n'}) + return raw, nil +} diff --git a/http_response_dumper.go b/http_response_dumper.go new file mode 100644 index 0000000..541970c --- /dev/null +++ b/http_response_dumper.go @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2024 M. Shulhan +// SPDX-License-Identifier: GPL-3.0-or-later + +package trunks + +import ( + "bytes" + "fmt" + "net/http" + "net/http/httputil" +) + +// HTTPResponseDumper define an handler to convert [http.Response] into +// [RunResponse] DumpResponse. +type HTTPResponseDumper func(resp *http.Response) ([]byte, error) + +// DumpHTTPResponse define default [HTTPResponseDumper] that convert +// [http.Response] with its body to stream of bytes using +// [httputil.DumpResponse]. +// +// The returned dump have CRLF ("\r\n") replaced with single LF ("\n"). +func DumpHTTPResponse(resp *http.Response) (raw []byte, err error) { + raw, err = httputil.DumpResponse(resp, true) + if err != nil { + return nil, fmt.Errorf(`DumpHTTPResponse: %w`, err) + } + raw = bytes.ReplaceAll(raw, []byte{'\r', '\n'}, []byte{'\n'}) + return raw, nil +} diff --git a/http_target.go b/http_target.go index 62ba582..be5919a 100644 --- a/http_target.go +++ b/http_target.go @@ -45,6 +45,16 @@ type HTTPTarget struct { PreAttack HTTPPreAttackHandler `json:"-"` Attack HTTPAttackHandler `json:"-"` + // RequestDumper define the handler to store [http.Request] after + // Run into [RunRequest] DumpRequest. + // Default to [DumpHTTPRequest] if its nil. + RequestDumper HTTPRequestDumper `json:"-"` + + // ResponseDumper define the handler to store [http.Response] after + // Run into [RunRequest] DumpResponse. + // Default to [DumpHTTPResponse] if its nil. + ResponseDumper HTTPResponseDumper `json:"-"` + // ID of target, optional. // If its empty, it will generated using value from Name. ID string @@ -78,6 +88,8 @@ func (ht *HTTPTarget) clone(src *HTTPTarget) { ht.Run = src.Run ht.PreAttack = src.PreAttack ht.Attack = src.Attack + ht.RequestDumper = src.RequestDumper + ht.ResponseDumper = src.ResponseDumper ht.ID = src.ID ht.Name = src.Name ht.Hint = src.Hint @@ -92,7 +104,6 @@ func (ht *HTTPTarget) clone(src *HTTPTarget) { func (ht *HTTPTarget) init() (err error) { if len(ht.Name) == 0 { return fmt.Errorf(`HTTPTarget.Name is empty`) - } if len(ht.ID) == 0 { ht.ID = generateID(ht.Name) @@ -106,6 +117,12 @@ func (ht *HTTPTarget) init() (err error) { if len(ht.Path) == 0 { ht.Path = "/" } + if ht.RequestDumper == nil { + ht.RequestDumper = DumpHTTPRequest + } + if ht.ResponseDumper == nil { + ht.ResponseDumper = DumpHTTPResponse + } return nil } diff --git a/run_request.go b/run_request.go index 7c426cc..2f18098 100644 --- a/run_request.go +++ b/run_request.go @@ -57,6 +57,9 @@ func generateRunRequest( outrr.HTTPTarget.Params = req.HTTPTarget.Params outrr.HTTPTarget.paramsToPath() + outrr.HTTPTarget.RequestDumper = origHTTPTarget.RequestDumper + outrr.HTTPTarget.ResponseDumper = origHTTPTarget.ResponseDumper + return outrr } diff --git a/run_response.go b/run_response.go index 0586c6e..5dffd16 100644 --- a/run_response.go +++ b/run_response.go @@ -7,7 +7,6 @@ import ( "fmt" "io" "net/http" - "net/http/httputil" libhttp "github.com/shuLhan/share/lib/http" ) @@ -27,8 +26,8 @@ type RunResponse struct { // SetHTTPRequest 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) +func (rres *RunResponse) SetHTTPRequest(dumper HTTPRequestDumper, req *http.Request) (err error) { + rres.DumpRequest, err = dumper(req) if err != nil { return fmt.Errorf(`SetHTTPRequest: %w`, err) } @@ -37,13 +36,13 @@ func (rres *RunResponse) SetHTTPRequest(req *http.Request) (err error) { // SetHTTPResponse dump the HTTP response including body into the DumpResponse // field. -func (rres *RunResponse) SetHTTPResponse(res *http.Response) (err error) { +func (rres *RunResponse) SetHTTPResponse(dumper HTTPResponseDumper, res *http.Response) (err error) { var logp = `SetHTTPResponse` rres.ResponseStatus = res.Status rres.ResponseStatusCode = res.StatusCode - rres.DumpResponse, err = httputil.DumpResponse(res, true) + rres.DumpResponse, err = dumper(res) if err != nil { return fmt.Errorf("%s: %w", logp, err) } diff --git a/trunks.go b/trunks.go index b90df42..5ae3656 100644 --- a/trunks.go +++ b/trunks.go @@ -190,6 +190,9 @@ func (trunks *Trunks) RunHTTP(req *RunRequest) (res *RunResponse, err error) { req.Target.BaseURL = origTarget.BaseURL req.Target.Name = origTarget.Name req.HTTPTarget.ConvertParams = origHTTPTarget.ConvertParams + req.HTTPTarget.RequestDumper = origHTTPTarget.RequestDumper + req.HTTPTarget.ResponseDumper = origHTTPTarget.ResponseDumper + res, err = trunks.runHTTPTarget(req) } else { req = generateRunRequest(trunks.Env, req, origTarget, origHTTPTarget) @@ -371,7 +374,7 @@ func (trunks *Trunks) runHTTPTarget(rr *RunRequest) (res *RunResponse, err error return nil, fmt.Errorf("%s: %w", logp, err) } - err = res.SetHTTPRequest(httpRequest) + err = res.SetHTTPRequest(rr.HTTPTarget.RequestDumper, httpRequest) if err != nil { return nil, fmt.Errorf("%s: %w", logp, err) } @@ -381,7 +384,7 @@ func (trunks *Trunks) runHTTPTarget(rr *RunRequest) (res *RunResponse, err error return nil, fmt.Errorf("%s: %w", logp, err) } - err = res.SetHTTPResponse(httpResponse) + err = res.SetHTTPResponse(rr.HTTPTarget.ResponseDumper, httpResponse) if err != nil { return nil, fmt.Errorf("%s: %w", logp, err) } -- cgit v1.3