From d36ce439a7f3216b90a10661addf5dacc260caf6 Mon Sep 17 00:00:00 2001 From: Mihail Stoykov Date: Fri, 26 Feb 2021 16:29:56 +0200 Subject: [PATCH] Per VU module instance support for the http module part of #1858 --- js/internal/modules/modules.go | 12 +++++- js/modules/k6/http/file_test.go | 2 +- js/modules/k6/http/http.go | 57 ++++++++++++++++------------- js/modules/k6/http/http_test.go | 2 +- js/modules/k6/http/request.go | 6 +-- js/modules/k6/http/request_test.go | 2 +- js/modules/k6/http/response.go | 12 +++--- js/modules/k6/http/response_test.go | 4 +- 8 files changed, 56 insertions(+), 41 deletions(-) diff --git a/js/internal/modules/modules.go b/js/internal/modules/modules.go index 058fe459ae0..72cdf355052 100644 --- a/js/internal/modules/modules.go +++ b/js/internal/modules/modules.go @@ -35,7 +35,17 @@ var ( func Get(name string) interface{} { mx.RLock() defer mx.RUnlock() - return modules[name] + mod := modules[name] + if i, ok := mod.(PerTestModule); ok { + return i.NewVUModule() + } + return mod +} + +// PerTestModule is a simple interface representing a per test module object that can be used to +// make a per VU instance of the Module +type PerTestModule interface { + NewVUModule() interface{} } // Register the given mod as a JavaScript module, available diff --git a/js/modules/k6/http/file_test.go b/js/modules/k6/http/file_test.go index 45f91a18124..da0f0e3001b 100644 --- a/js/modules/k6/http/file_test.go +++ b/js/modules/k6/http/file_test.go @@ -80,7 +80,7 @@ func TestHTTPFile(t *testing.T) { assert.Equal(t, tc.expErr, fmt.Sprintf("%s", val["value"])) }() } - h := New() + h := new(GlobalHTTP).NewVUModule().(*HTTP) ctx := common.WithRuntime(context.Background(), rt) out := h.File(ctx, tc.input, tc.args...) assert.Equal(t, tc.expected, out) diff --git a/js/modules/k6/http/http.go b/js/modules/k6/http/http.go index 8aa6cdab2dc..ea6e4b0dbdb 100644 --- a/js/modules/k6/http/http.go +++ b/js/modules/k6/http/http.go @@ -30,7 +30,7 @@ import ( ) func init() { - modules.Register("k6/http", New()) + modules.Register("k6/http", new(GlobalHTTP)) } const ( @@ -46,32 +46,14 @@ const ( // ErrJarForbiddenInInitContext is used when a cookie jar was made in the init context var ErrJarForbiddenInInitContext = common.NewInitContextError("Making cookie jars in the init context is not supported") -//nolint: golint -type HTTP struct { - SSL_3_0 string `js:"SSL_3_0"` - TLS_1_0 string `js:"TLS_1_0"` - TLS_1_1 string `js:"TLS_1_1"` - TLS_1_2 string `js:"TLS_1_2"` - TLS_1_3 string `js:"TLS_1_3"` - OCSP_STATUS_GOOD string `js:"OCSP_STATUS_GOOD"` - OCSP_STATUS_REVOKED string `js:"OCSP_STATUS_REVOKED"` - OCSP_STATUS_SERVER_FAILED string `js:"OCSP_STATUS_SERVER_FAILED"` - OCSP_STATUS_UNKNOWN string `js:"OCSP_STATUS_UNKNOWN"` - OCSP_REASON_UNSPECIFIED string `js:"OCSP_REASON_UNSPECIFIED"` - OCSP_REASON_KEY_COMPROMISE string `js:"OCSP_REASON_KEY_COMPROMISE"` - OCSP_REASON_CA_COMPROMISE string `js:"OCSP_REASON_CA_COMPROMISE"` - OCSP_REASON_AFFILIATION_CHANGED string `js:"OCSP_REASON_AFFILIATION_CHANGED"` - OCSP_REASON_SUPERSEDED string `js:"OCSP_REASON_SUPERSEDED"` - OCSP_REASON_CESSATION_OF_OPERATION string `js:"OCSP_REASON_CESSATION_OF_OPERATION"` - OCSP_REASON_CERTIFICATE_HOLD string `js:"OCSP_REASON_CERTIFICATE_HOLD"` - OCSP_REASON_REMOVE_FROM_CRL string `js:"OCSP_REASON_REMOVE_FROM_CRL"` - OCSP_REASON_PRIVILEGE_WITHDRAWN string `js:"OCSP_REASON_PRIVILEGE_WITHDRAWN"` - OCSP_REASON_AA_COMPROMISE string `js:"OCSP_REASON_AA_COMPROMISE"` -} +// GlobalHTTP is a global HTTP module for a k6 instance/test run +type GlobalHTTP struct{} + +var _ modules.PerTestModule = new(GlobalHTTP) -func New() *HTTP { - //TODO: move this as an anonymous struct somewhere... - return &HTTP{ +// NewVUModule returns an HTTP instance for each VU +func (g *GlobalHTTP) NewVUModule() interface{} { // this here needs to return interface{} + return &HTTP{ // change the below fields to be not writable or not fields SSL_3_0: netext.SSL_3_0, TLS_1_0: netext.TLS_1_0, TLS_1_1: netext.TLS_1_1, @@ -94,6 +76,29 @@ func New() *HTTP { } } +//nolint: golint +type HTTP struct { + SSL_3_0 string `js:"SSL_3_0"` + TLS_1_0 string `js:"TLS_1_0"` + TLS_1_1 string `js:"TLS_1_1"` + TLS_1_2 string `js:"TLS_1_2"` + TLS_1_3 string `js:"TLS_1_3"` + OCSP_STATUS_GOOD string `js:"OCSP_STATUS_GOOD"` + OCSP_STATUS_REVOKED string `js:"OCSP_STATUS_REVOKED"` + OCSP_STATUS_SERVER_FAILED string `js:"OCSP_STATUS_SERVER_FAILED"` + OCSP_STATUS_UNKNOWN string `js:"OCSP_STATUS_UNKNOWN"` + OCSP_REASON_UNSPECIFIED string `js:"OCSP_REASON_UNSPECIFIED"` + OCSP_REASON_KEY_COMPROMISE string `js:"OCSP_REASON_KEY_COMPROMISE"` + OCSP_REASON_CA_COMPROMISE string `js:"OCSP_REASON_CA_COMPROMISE"` + OCSP_REASON_AFFILIATION_CHANGED string `js:"OCSP_REASON_AFFILIATION_CHANGED"` + OCSP_REASON_SUPERSEDED string `js:"OCSP_REASON_SUPERSEDED"` + OCSP_REASON_CESSATION_OF_OPERATION string `js:"OCSP_REASON_CESSATION_OF_OPERATION"` + OCSP_REASON_CERTIFICATE_HOLD string `js:"OCSP_REASON_CERTIFICATE_HOLD"` + OCSP_REASON_REMOVE_FROM_CRL string `js:"OCSP_REASON_REMOVE_FROM_CRL"` + OCSP_REASON_PRIVILEGE_WITHDRAWN string `js:"OCSP_REASON_PRIVILEGE_WITHDRAWN"` + OCSP_REASON_AA_COMPROMISE string `js:"OCSP_REASON_AA_COMPROMISE"` +} + func (*HTTP) XCookieJar(ctx *context.Context) *HTTPCookieJar { return newCookieJar(ctx) } diff --git a/js/modules/k6/http/http_test.go b/js/modules/k6/http/http_test.go index a7f0d81b385..f62d802b2e6 100644 --- a/js/modules/k6/http/http_test.go +++ b/js/modules/k6/http/http_test.go @@ -34,7 +34,7 @@ import ( func TestTagURL(t *testing.T) { rt := goja.New() rt.SetFieldNameMapper(common.FieldNameMapper{}) - rt.Set("http", common.Bind(rt, New(), nil)) + rt.Set("http", common.Bind(rt, new(GlobalHTTP).NewVUModule(), nil)) testdata := map[string]struct{ u, n string }{ `http://localhost/anything/`: {"http://localhost/anything/", "http://localhost/anything/"}, diff --git a/js/modules/k6/http/request.go b/js/modules/k6/http/request.go index d4a27b71b68..91fc9a73c88 100644 --- a/js/modules/k6/http/request.go +++ b/js/modules/k6/http/request.go @@ -113,7 +113,7 @@ func (h *HTTP) Request(ctx context.Context, method string, url goja.Value, args if err != nil { return nil, err } - return responseFromHttpext(resp), nil + return h.responseFromHttpext(resp), nil } //TODO break this function up @@ -377,7 +377,7 @@ func (h *HTTP) prepareBatchArray( ParsedHTTPRequest: parsedReq, Response: response, } - results[i] = &Response{response} + results[i] = h.responseFromHttpext(response) } return batchReqs, results, nil @@ -401,7 +401,7 @@ func (h *HTTP) prepareBatchObject( ParsedHTTPRequest: parsedReq, Response: response, } - results[key] = &Response{response} + results[key] = h.responseFromHttpext(response) i++ } diff --git a/js/modules/k6/http/request_test.go b/js/modules/k6/http/request_test.go index dc7cb518b30..1e927b13d51 100644 --- a/js/modules/k6/http/request_test.go +++ b/js/modules/k6/http/request_test.go @@ -169,7 +169,7 @@ func newRuntime( ctx := new(context.Context) *ctx = lib.WithState(tb.Context, state) *ctx = common.WithRuntime(*ctx, rt) - rt.Set("http", common.Bind(rt, New(), ctx)) + rt.Set("http", common.Bind(rt, new(GlobalHTTP).NewVUModule(), ctx)) return tb, state, samples, rt, ctx } diff --git a/js/modules/k6/http/response.go b/js/modules/k6/http/response.go index 8deb1b2462c..34506d7225c 100644 --- a/js/modules/k6/http/response.go +++ b/js/modules/k6/http/response.go @@ -36,11 +36,11 @@ import ( // Response is a representation of an HTTP response to be returned to the goja VM type Response struct { *httpext.Response `js:"-"` + h *HTTP } -func responseFromHttpext(resp *httpext.Response) *Response { - res := Response{resp} - return &res +func (h *HTTP) responseFromHttpext(resp *httpext.Response) *Response { + return &Response{Response: resp, h: h} } // JSON parses the body of a response as json and returns it to the goja VM @@ -159,9 +159,9 @@ func (res *Response) SubmitForm(args ...goja.Value) (*Response, error) { q.Add(k, v.String()) } requestURL.RawQuery = q.Encode() - return New().Request(res.GetCtx(), requestMethod, rt.ToValue(requestURL.String()), goja.Null(), requestParams) + return res.h.Request(res.GetCtx(), requestMethod, rt.ToValue(requestURL.String()), goja.Null(), requestParams) } - return New().Request(res.GetCtx(), requestMethod, rt.ToValue(requestURL.String()), rt.ToValue(values), requestParams) + return res.h.Request(res.GetCtx(), requestMethod, rt.ToValue(requestURL.String()), rt.ToValue(values), requestParams) } // ClickLink parses the body as an html, looks for a link and than makes a request as if the link was @@ -202,5 +202,5 @@ func (res *Response) ClickLink(args ...goja.Value) (*Response, error) { } requestURL := responseURL.ResolveReference(hrefURL) - return New().Get(res.GetCtx(), rt.ToValue(requestURL.String()), requestParams) + return res.h.Get(res.GetCtx(), rt.ToValue(requestURL.String()), requestParams) } diff --git a/js/modules/k6/http/response_test.go b/js/modules/k6/http/response_test.go index 94a5ddac42e..dbce89e0a5b 100644 --- a/js/modules/k6/http/response_test.go +++ b/js/modules/k6/http/response_test.go @@ -413,7 +413,7 @@ func BenchmarkResponseJson(b *testing.B) { tc := tc b.Run(fmt.Sprintf("Selector %s ", tc.selector), func(b *testing.B) { for n := 0; n < b.N; n++ { - resp := responseFromHttpext(&httpext.Response{Body: jsonData}) + resp := new(HTTP).responseFromHttpext(&httpext.Response{Body: jsonData}) resp.JSON(tc.selector) } }) @@ -421,7 +421,7 @@ func BenchmarkResponseJson(b *testing.B) { b.Run("Without selector", func(b *testing.B) { for n := 0; n < b.N; n++ { - resp := responseFromHttpext(&httpext.Response{Body: jsonData}) + resp := new(HTTP).responseFromHttpext(&httpext.Response{Body: jsonData}) resp.JSON() } })