From 593d02333d978eec5297e94b35da3e8ac15ef034 Mon Sep 17 00:00:00 2001 From: k1LoW Date: Thu, 26 Oct 2023 16:48:34 +0900 Subject: [PATCH 1/3] Fix handling of http headers --- http.go | 19 ++++++++++++++----- http_test.go | 16 ++++++++-------- parse.go | 15 ++++++++++++--- parse_test.go | 10 +++++----- testdata/book/http.yml | 3 +++ testdata/http.yml.debugger.golden | Bin 7346 -> 7378 bytes testdata/http.yml.runbook.golden | 1 + testdata/pick_step.http.yml.8.golden | 20 ++++++++++---------- 8 files changed, 53 insertions(+), 31 deletions(-) diff --git a/http.go b/http.go index 8af97363..c4659ef0 100644 --- a/http.go +++ b/http.go @@ -63,7 +63,7 @@ type httpRunner struct { type httpRequest struct { path string method string - headers map[string]string + headers http.Header mediaType string body any useCookie *bool @@ -430,9 +430,12 @@ func (rnr *httpRunner) run(ctx context.Context, r *httpRequest, s *step) error { return err } for k, v := range r.headers { - req.Header.Set(k, v) - if k == "Host" { - req.Host = v + req.Header.Del(k) + for _, vv := range v { + req.Header.Add(k, vv) + if k == "Host" { + req.Host = vv + } } } @@ -453,7 +456,13 @@ func (rnr *httpRunner) run(ctx context.Context, r *httpRequest, s *step) error { req.Header.Set("Content-Type", r.mediaType) } for k, v := range r.headers { - req.Header.Set(k, v) + req.Header.Del(k) + for _, vv := range v { + req.Header.Add(k, vv) + if k == "Host" { + req.Host = vv + } + } } o.capturers.captureHTTPRequest(rnr.name, req) diff --git a/http_test.go b/http_test.go index c79eb055..9f0e397f 100644 --- a/http_test.go +++ b/http_test.go @@ -33,8 +33,8 @@ func TestHTTPRunnerRunUsingGitHubAPI(t *testing.T) { path: "/users/k1LoW", method: http.MethodGet, mediaType: MediaTypeApplicationJSON, - headers: map[string]string{ - "Authorization": fmt.Sprintf("token %s", os.Getenv("GITHUB_TOKEN")), + headers: http.Header{ + "Authorization": []string{fmt.Sprintf("token %s", os.Getenv("GITHUB_TOKEN"))}, }, }, true, @@ -45,8 +45,8 @@ func TestHTTPRunnerRunUsingGitHubAPI(t *testing.T) { path: "/invalid/endpoint", method: http.MethodGet, mediaType: MediaTypeApplicationJSON, - headers: map[string]string{ - "Authorization": fmt.Sprintf("token %s", os.Getenv("GITHUB_TOKEN")), + headers: http.Header{ + "Authorization": []string{fmt.Sprintf("token %s", os.Getenv("GITHUB_TOKEN"))}, }, }, false, @@ -507,7 +507,7 @@ func TestNotFollowRedirect(t *testing.T) { &httpRequest{ path: "/redirect", method: http.MethodGet, - headers: map[string]string{}, + headers: http.Header{}, }, false, http.StatusNotFound, @@ -516,7 +516,7 @@ func TestNotFollowRedirect(t *testing.T) { &httpRequest{ path: "/redirect", method: http.MethodGet, - headers: map[string]string{}, + headers: http.Header{}, }, true, http.StatusFound, @@ -576,7 +576,7 @@ func TestHTTPCerts(t *testing.T) { req := &httpRequest{ path: "/users/1", method: http.MethodGet, - headers: map[string]string{}, + headers: http.Header{}, } for i, tt := range tests { t.Run(fmt.Sprintf("%v", i), func(t *testing.T) { @@ -624,7 +624,7 @@ func TestHTTPRunnerInitializeWithCerts(t *testing.T) { req := &httpRequest{ path: "/users/1", method: http.MethodGet, - headers: map[string]string{}, + headers: http.Header{}, } for i, tt := range tests { t.Run(fmt.Sprintf("%v", i), func(t *testing.T) { diff --git a/parse.go b/parse.go index 833cc1aa..5315709b 100644 --- a/parse.go +++ b/parse.go @@ -2,6 +2,7 @@ package runn import ( "fmt" + "net/http" "regexp" "strings" "time" @@ -14,7 +15,7 @@ import ( func parseHTTPRequest(v map[string]any) (*httpRequest, error) { v = trimDelimiter(v) req := &httpRequest{ - headers: map[string]string{}, + headers: http.Header{}, } part, err := yaml.Marshal(v) if err != nil { @@ -45,8 +46,14 @@ func parseHTTPRequest(v map[string]any) (*httpRequest, error) { return nil, fmt.Errorf("invalid request: %s", string(part)) } for k, v := range hm { - req.headers[k], ok = v.(string) - if !ok { + switch v := v.(type) { + case string: + req.headers.Add(k, v) + case []any: + for _, vv := range v { + req.headers.Add(k, vv.(string)) + } + default: return nil, fmt.Errorf("invalid request: %s", string(part)) } } @@ -178,6 +185,8 @@ func parseGrpcRequest(v map[string]any, expand func(any) (any, error)) (*grpcReq for _, vv := range v { req.headers.Append(k, vv.(string)) } + default: + return nil, fmt.Errorf("invalid request: %s", string(part)) } } } diff --git a/parse_test.go b/parse_test.go index ba360a17..6ceadf62 100644 --- a/parse_test.go +++ b/parse_test.go @@ -30,7 +30,7 @@ func TestParseHTTPRequest(t *testing.T) { path: "/login", method: http.MethodPost, mediaType: MediaTypeApplicationJSON, - headers: map[string]string{}, + headers: http.Header{}, body: map[string]any{ "key": "value", }, @@ -47,7 +47,7 @@ func TestParseHTTPRequest(t *testing.T) { path: "/users/k1LoW", method: http.MethodGet, mediaType: "", - headers: map[string]string{}, + headers: http.Header{}, body: nil, useCookie: nil, trace: nil, @@ -82,7 +82,7 @@ func TestParseHTTPRequest(t *testing.T) { path: "/users/k1LoW", method: http.MethodGet, mediaType: "", - headers: map[string]string{}, + headers: http.Header{}, body: nil, useCookie: &use, trace: nil, @@ -100,7 +100,7 @@ func TestParseHTTPRequest(t *testing.T) { path: "/users/k1LoW", method: http.MethodGet, mediaType: "", - headers: map[string]string{}, + headers: http.Header{}, body: nil, useCookie: nil, trace: &use, @@ -130,7 +130,7 @@ func TestParseHTTPRequest(t *testing.T) { path: "/users/k1LoW?page=2", method: http.MethodGet, mediaType: "", - headers: map[string]string{}, + headers: http.Header{}, body: nil, useCookie: ¬Use, trace: ¬Use, diff --git a/testdata/book/http.yml b/testdata/book/http.yml index 72343d73..99db68fd 100644 --- a/testdata/book/http.yml +++ b/testdata/book/http.yml @@ -70,6 +70,9 @@ steps: get: headers: Authorization: 'Bearer xxxxx' + Multivalues: + - a + - b body: application/json: null diff --git a/testdata/http.yml.debugger.golden b/testdata/http.yml.debugger.golden index 360c770f554c0fff97c55fe5a371b44e468b03c4..432f9c26c02a95c5d19028f956fdc0213df0adbc 100644 GIT binary patch delta 47 rcmdmFdC77^0Ee7!X--LISz=CUYO$3 Date: Thu, 26 Oct 2023 17:03:39 +0900 Subject: [PATCH 2/3] bonsai --- http.go | 29 +++++++++++++++-------------- http_test.go | 12 ++++-------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/http.go b/http.go index c4659ef0..2e144243 100644 --- a/http.go +++ b/http.go @@ -302,7 +302,7 @@ func (r *httpRequest) setCookieHeader(req *http.Request, cookies map[string]map[ } } -func (r *httpRequest) setTraceHeader(req *http.Request, s *step) error { +func (r *httpRequest) setTraceHeader(s *step) error { if r.trace == nil || !*r.trace { return nil } @@ -314,7 +314,7 @@ func (r *httpRequest) setTraceHeader(req *http.Request, s *step) error { return err } // Set Trace in the header - req.Header.Set("X-Runn-Trace", string(tj)) + r.headers.Set("X-Runn-Trace", string(tj)) return nil } @@ -361,6 +361,19 @@ func (rnr *httpRunner) run(ctx context.Context, r *httpRequest, s *step) error { return err } + // Override useCookie + if r.useCookie == nil && rnr.useCookie != nil && *rnr.useCookie { + r.useCookie = rnr.useCookie + } + + // Override trace + if r.trace == nil && rnr.trace != nil && *rnr.trace { + r.trace = rnr.trace + } + if err := r.setTraceHeader(s); err != nil { + return err + } + var ( req *http.Request res *http.Response @@ -416,19 +429,7 @@ func (rnr *httpRunner) run(ctx context.Context, r *httpRequest, s *step) error { return err } r.setContentTypeHeader(req) - - // Override useCookie - if r.useCookie == nil && rnr.useCookie != nil && *rnr.useCookie { - r.useCookie = rnr.useCookie - } r.setCookieHeader(req, o.store.cookies) - // Override trace - if r.trace == nil && rnr.trace != nil && *rnr.trace { - r.trace = rnr.trace - } - if err := r.setTraceHeader(req, s); err != nil { - return err - } for k, v := range r.headers { req.Header.Del(k) for _, vv := range v { diff --git a/http_test.go b/http_test.go index 9f0e397f..e21ae82e 100644 --- a/http_test.go +++ b/http_test.go @@ -817,15 +817,11 @@ func TestSetTraceHeader(t *testing.T) { for _, tt := range tests { t.Run(fmt.Sprintf("trace:%v", *tt.trace), func(t *testing.T) { r := &httpRequest{ - trace: tt.trace, - } - req := &http.Request{ - Method: http.MethodPost, - Header: http.Header{"Content-Type": []string{"application/json"}}, + headers: http.Header{}, + trace: tt.trace, } - - r.setTraceHeader(req, tt.step) - got := req.Header.Get("X-Runn-Trace") + r.setTraceHeader(tt.step) + got := r.headers.Get("X-Runn-Trace") if got != tt.want { t.Errorf("got %v\nwant %v", got, tt.want) From 4dc2a0408ff0c3a5012bb9a1dc649c2b825272bf Mon Sep 17 00:00:00 2001 From: k1LoW Date: Thu, 26 Oct 2023 17:15:45 +0900 Subject: [PATCH 3/3] Fix lint warn --- http_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/http_test.go b/http_test.go index e21ae82e..b58a82ea 100644 --- a/http_test.go +++ b/http_test.go @@ -820,9 +820,10 @@ func TestSetTraceHeader(t *testing.T) { headers: http.Header{}, trace: tt.trace, } - r.setTraceHeader(tt.step) + if err := r.setTraceHeader(tt.step); err != nil { + t.Error(err) + } got := r.headers.Get("X-Runn-Trace") - if got != tt.want { t.Errorf("got %v\nwant %v", got, tt.want) }