From a517203a19996ecb6638587c7baeb86976685a8f Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 29 Oct 2021 17:55:50 +0800 Subject: [PATCH] refactor: replace http client with net/http --- go.mod | 1 - go.sum | 2 - response.go | 21 +++++--- runner.go | 136 +++++++++++++++++++++++++++++++++++++++++----------- 4 files changed, 122 insertions(+), 38 deletions(-) diff --git a/go.mod b/go.mod index 3d6ab9ba..6ebfef1a 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ require ( github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef // indirect github.com/debugtalk/boomer v1.6.0 github.com/google/uuid v1.3.0 // indirect - github.com/imroc/req v0.3.0 github.com/jmespath/go-jmespath v0.4.0 github.com/maja42/goval v1.2.1 github.com/olekukonko/tablewriter v0.0.5 // indirect diff --git a/go.sum b/go.sum index 9e67978c..38320b86 100644 --- a/go.sum +++ b/go.sum @@ -170,8 +170,6 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imroc/req v0.3.0 h1:3EioagmlSG+z+KySToa+Ylo3pTFZs+jh3Brl7ngU12U= -github.com/imroc/req v0.3.0/go.mod h1:F+NZ+2EFSo6EFXdeIbpfE9hcC233id70kf0byW97Caw= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= diff --git a/response.go b/response.go index c471c75a..15638492 100644 --- a/response.go +++ b/response.go @@ -2,19 +2,20 @@ package hrp import ( "encoding/json" + "io/ioutil" + "net/http" "strings" "testing" - "github.com/imroc/req" "github.com/jmespath/go-jmespath" "github.com/httprunner/hrp/builtin" ) -func NewResponseObject(t *testing.T, resp *req.Resp) (*ResponseObject, error) { +func NewResponseObject(t *testing.T, resp *http.Response) (*ResponseObject, error) { // prepare response headers headers := make(map[string]string) - for k, v := range resp.Response().Header { + for k, v := range resp.Header { if len(v) > 0 { headers[k] = v[0] } @@ -22,19 +23,25 @@ func NewResponseObject(t *testing.T, resp *req.Resp) (*ResponseObject, error) { // prepare response cookies cookies := make(map[string]string) - for _, cookie := range resp.Response().Cookies() { + for _, cookie := range resp.Cookies() { cookies[cookie.Name] = cookie.Value } + // read response body + respBodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + // parse response body var body interface{} - if err := json.Unmarshal(resp.Bytes(), &body); err != nil { + if err := json.Unmarshal(respBodyBytes, &body); err != nil { // response body is not json, use raw body - body = string(resp.Bytes()) + body = string(respBodyBytes) } respObjMeta := respObjMeta{ - StatusCode: resp.Response().StatusCode, + StatusCode: resp.StatusCode, Headers: headers, Cookies: cookies, Body: body, diff --git a/runner.go b/runner.go index 7ee289e6..cebf0392 100644 --- a/runner.go +++ b/runner.go @@ -1,10 +1,16 @@ package hrp import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" "net/http" + "net/url" + "strconv" + "strings" "testing" - "github.com/imroc/req" "github.com/pkg/errors" ) @@ -17,14 +23,14 @@ func NewRunner() *Runner { return &Runner{ t: &testing.T{}, debug: false, // default to turn off debug - client: req.New(), + client: &http.Client{}, } } type Runner struct { t *testing.T debug bool - client *req.Req + client *http.Client } func (r *Runner) WithTestingT(t *testing.T) *Runner { @@ -41,7 +47,7 @@ func (r *Runner) SetDebug(debug bool) *Runner { func (r *Runner) SetProxyUrl(proxyUrl string) *Runner { log.Info().Str("proxyUrl", proxyUrl).Msg("[init] SetProxyUrl") - r.client.SetProxyUrl(proxyUrl) + // TODO return r } @@ -107,6 +113,7 @@ func (r *Runner) runStep(step IStep, config *TConfig) (stepData *StepData, err e // TODO: override testcase config stepData, err = r.runStepTestCase(tc.step) if err != nil { + log.Error().Err(err).Msg("run referenced testcase step failed") return } } else { @@ -114,6 +121,7 @@ func (r *Runner) runStep(step IStep, config *TConfig) (stepData *StepData, err e tStep := parseStep(step, config) stepData, err = r.runStepRequest(tStep) if err != nil { + log.Error().Err(err).Msg("run request step failed") return } } @@ -132,49 +140,110 @@ func (r *Runner) runStepRequest(step *TStep) (stepData *StepData, err error) { ResponseLength: 0, } - // prepare request args - var v []interface{} + rawUrl := step.Request.URL + method := step.Request.Method + req := &http.Request{ + Method: string(method), + Header: make(http.Header), + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + } + + // prepare request headers if len(step.Request.Headers) > 0 { headers, err := parseHeaders(step.Request.Headers, step.Variables) if err != nil { - return nil, err + return nil, errors.Wrap(err, "parse headers failed") + } + for key, value := range headers { + req.Header.Add(key, value) } - v = append(v, req.Header(headers)) } + if length := req.Header.Get("Content-Length"); length != "" { + if l, err := strconv.ParseInt(length, 10, 64); err == nil { + req.ContentLength = l + } + } + if host := req.Header.Get("Host"); host != "" { + req.Host = host + } + + // prepare request params + var queryParams url.Values if len(step.Request.Params) > 0 { params, err := parseData(step.Request.Params, step.Variables) if err != nil { - return nil, err + return nil, errors.Wrap(err, "parse data failed") + } + parsedParams := params.(map[string]interface{}) + if len(parsedParams) > 0 { + queryParams = make(url.Values) + for k, v := range parsedParams { + queryParams.Add(k, fmt.Sprint(v)) + } } - v = append(v, req.Param(params.(map[string]interface{}))) } - if step.Request.Body != nil { - data, err := parseData(step.Request.Body, step.Variables) - if err != nil { - return nil, err - } - switch data.(type) { - case map[string]interface{}: // post json - v = append(v, req.BodyJSON(data)) - default: // post raw data - v = append(v, data) + if queryParams != nil { + // append params to url + paramStr := queryParams.Encode() + if strings.IndexByte(rawUrl, '?') == -1 { + rawUrl = rawUrl + "?" + paramStr + } else { + rawUrl = rawUrl + "&" + paramStr } } + // prepare request cookies for cookieName, cookieValue := range step.Request.Cookies { - v = append(v, &http.Cookie{ + req.AddCookie(&http.Cookie{ Name: cookieName, Value: cookieValue, }) } - // do request action - req.Debug = r.debug - resp, err := r.client.Do(string(step.Request.Method), step.Request.URL, v...) - if err != nil { - return + // prepare request body + if step.Request.Body != nil { + data, err := parseData(step.Request.Body, step.Variables) + if err != nil { + return nil, err + } + var dataBytes []byte + switch vv := data.(type) { + case map[string]interface{}: // post json + dataBytes, err = json.Marshal(vv) + if err != nil { + return nil, err + } + setContentType(req, "application/json; charset=UTF-8") + case string: + dataBytes = []byte(vv) + case []byte: + dataBytes = vv + case bytes.Buffer: + dataBytes = vv.Bytes() + default: // unexpected body type + return nil, errors.New("unexpected request body type") + } + setBodyBytes(req, dataBytes) } - defer resp.Response().Body.Close() + + // prepare url + u, err := url.Parse(rawUrl) + if err != nil { + return nil, errors.Wrap(err, "parse url failed") + } + req.URL = u + + // do request action + // req.Debug = r.debug + // resp, err := r.client.Do(string(step.Request.Method), step.Request.URL, v...) + + resp, err := r.client.Do(req) + if err != nil { + return nil, errors.Wrap(err, "do request failed") + } + defer resp.Body.Close() // new response object respObj, err := NewResponseObject(r.t, resp) @@ -198,7 +267,7 @@ func (r *Runner) runStepRequest(step *TStep) (stepData *StepData, err error) { } stepData.Success = true - stepData.ResponseLength = resp.Response().ContentLength + stepData.ResponseLength = resp.ContentLength return } @@ -241,3 +310,14 @@ func (r *Runner) parseConfig(config *TConfig) error { func (r *Runner) GetSummary() *TestCaseSummary { return &TestCaseSummary{} } + +func setBodyBytes(req *http.Request, data []byte) { + req.Body = ioutil.NopCloser(bytes.NewReader(data)) + req.ContentLength = int64(len(data)) +} + +func setContentType(req *http.Request, contentType string) { + if req.Header.Get("Content-Type") == "" { + req.Header.Set("Content-Type", contentType) + } +}