diff --git a/js/modules/k6/http/http.go b/js/modules/k6/http/http.go index ddb8d13f621..19e32f2473a 100644 --- a/js/modules/k6/http/http.go +++ b/js/modules/k6/http/http.go @@ -128,21 +128,25 @@ func (*HTTP) CookieJar(ctx context.Context) *HTTPCookieJar { return &HTTPCookieJar{state.CookieJar, &ctx} } -func (*HTTP) setRequestCookies(req *http.Request, jar *cookiejar.Jar, reqCookies map[string]*HTTPRequestCookie) { - jarCookies := make(map[string][]*http.Cookie) +func (*HTTP) mergeCookies(req *http.Request, jar *cookiejar.Jar, reqCookies map[string]*HTTPRequestCookie) map[string][]*HTTPRequestCookie { + allCookies := make(map[string][]*HTTPRequestCookie) for _, c := range jar.Cookies(req.URL) { - jarCookies[c.Name] = append(jarCookies[c.Name], c) + allCookies[c.Name] = append(allCookies[c.Name], &HTTPRequestCookie{Name: c.Name, Value: c.Value}) } for key, reqCookie := range reqCookies { - if jc := jarCookies[key]; jc != nil && reqCookie.Replace { - jarCookies[key] = []*http.Cookie{{Name: key, Value: reqCookie.Value}} + if jc := allCookies[key]; jc != nil && reqCookie.Replace { + allCookies[key] = []*HTTPRequestCookie{{Name: key, Value: reqCookie.Value}} } else { - jarCookies[key] = append(jarCookies[key], &http.Cookie{Name: key, Value: reqCookie.Value}) + allCookies[key] = append(allCookies[key], &HTTPRequestCookie{Name: key, Value: reqCookie.Value}) } } - for _, cookies := range jarCookies { + return allCookies +} + +func (*HTTP) setRequestCookies(req *http.Request, reqCookies map[string][]*HTTPRequestCookie) { + for _, cookies := range reqCookies { for _, c := range cookies { - req.AddCookie(c) + req.AddCookie(&http.Cookie{Name: c.Name, Value: c.Value}) } } } diff --git a/js/modules/k6/http/http_request.go b/js/modules/k6/http/http_request.go index c26fee0a1dc..1d39aa78a58 100644 --- a/js/modules/k6/http/http_request.go +++ b/js/modules/k6/http/http_request.go @@ -44,6 +44,14 @@ import ( null "gopkg.in/guregu/null.v3" ) +type HTTPRequest struct { + Method string + URL string + Headers map[string][]string + Body string + Cookies map[string][]*HTTPRequestCookie +} + func (http *HTTP) Get(ctx context.Context, url goja.Value, args ...goja.Value) (*HTTPResponse, error) { // The body argument is always undefined for GETs and HEADs. args = append([]goja.Value{goja.Undefined()}, args...) @@ -113,9 +121,14 @@ func (h *HTTP) request(ctx context.Context, rt *goja.Runtime, state *common.Stat URL: url.URL, Header: make(http.Header), } + respReq := &HTTPRequest{ + Method: req.Method, + URL: req.URL.String(), + } if bodyBuf != nil { req.Body = ioutil.NopCloser(bodyBuf) req.ContentLength = int64(bodyBuf.Len()) + respReq.Body = bodyBuf.String() } if contentType != "" { req.Header.Set("Content-Type", contentType) @@ -231,7 +244,9 @@ func (h *HTTP) request(ctx context.Context, rt *goja.Runtime, state *common.Stat } if activeJar != nil { - h.setRequestCookies(req, activeJar, reqCookies) + mergedCookies := h.mergeCookies(req, activeJar, reqCookies) + respReq.Cookies = mergedCookies + h.setRequestCookies(req, mergedCookies) } // Check rate limit *after* we've prepared a request; no need to wait with that part. @@ -241,7 +256,9 @@ func (h *HTTP) request(ctx context.Context, rt *goja.Runtime, state *common.Stat } } - resp := &HTTPResponse{ctx: ctx, URL: url.URLString} + respReq.Headers = req.Header + + resp := &HTTPResponse{ctx: ctx, URL: url.URLString, Request: *respReq} client := http.Client{ Transport: state.HTTPTransport, Timeout: timeout, @@ -252,7 +269,9 @@ func (h *HTTP) request(ctx context.Context, rt *goja.Runtime, state *common.Stat activeJar.SetCookies(req.URL, respCookies) } req.Header.Del("Cookie") - h.setRequestCookies(req, activeJar, reqCookies) + mergedCookies := h.mergeCookies(req, activeJar, reqCookies) + + h.setRequestCookies(req, mergedCookies) } if l := len(via); int64(l) > redirects.Int64 { diff --git a/js/modules/k6/http/http_request_test.go b/js/modules/k6/http/http_request_test.go index a96ac889ff3..0ba8d1db68a 100644 --- a/js/modules/k6/http/http_request_test.go +++ b/js/modules/k6/http/http_request_test.go @@ -862,4 +862,37 @@ func TestRequestAndBatch(t *testing.T) { assertRequestMetricsEmitted(t, state.Samples, "PUT", "https://httpbin.org/put", "", 200, "") }) }) + + t.Run("HTTPRequest", func(t *testing.T) { + t.Run("EmptyBody", func(t *testing.T) { + _, err := common.RunString(rt, ` + let reqUrl = "https://httpbin.org/cookies" + let res = http.get(reqUrl); + let jar = new http.CookieJar(); + + jar.set("https://httpbin.org/cookies", "key", "value"); + res = http.request("GET", "https://httpbin.org/cookies", null, { cookies: { key2: "value2" }, jar: jar }); + + if (res.json().cookies.key != "value") { throw new Error("wrong cookie value: " + res.json().cookies.key); } + + if (res.status != 200) { throw new Error("wrong status: " + res.status); } + if (res.request["method"] !== "GET") { throw new Error("http request method was not \"GET\": " + JSON.stringify(res.request)) } + if (res.request["body"].length != 0) { throw new Error("http request body was not null: " + JSON.stringify(res.request["body"])) } + if (res.request["url"] != reqUrl) { + throw new Error("wrong http request url: " + JSON.stringify(res.request)) + } + if (res.request["cookies"]["key2"][0].name != "key2") { throw new Error("wrong http request cookies: " + JSON.stringify(JSON.stringify(res.request["cookies"]["key2"]))) } + if (res.request["headers"]["User-Agent"][0] != "TestUserAgent") { throw new Error("wrong http request headers: " + JSON.stringify(res.request)) } + `) + assert.NoError(t, err) + }) + t.Run("NonEmptyBody", func(t *testing.T) { + _, err := common.RunString(rt, ` + let res = http.post("https://httpbin.org/post", {a: "a", b: 2}, {headers: {"Content-Type": "application/x-www-form-urlencoded; charset=utf-8"}}); + if (res.status != 200) { throw new Error("wrong status: " + res.status); } + if (res.request["body"] != "a=a&b=2") { throw new Error("http request body was not set properly: " + JSON.stringify(res.request))} + `) + assert.NoError(t, err) + }) + }) } diff --git a/js/modules/k6/http/response.go b/js/modules/k6/http/response.go index 1a2d22b7b06..021f8da93e9 100644 --- a/js/modules/k6/http/response.go +++ b/js/modules/k6/http/response.go @@ -62,6 +62,7 @@ type HTTPResponse struct { TLSCipherSuite string OCSP OCSP `js:"ocsp"` Error string + Request HTTPRequest cachedJSON goja.Value }