Skip to content

Commit

Permalink
Merge pull request #1841 from k6io/feat/1020-arraybuffer-breaking
Browse files Browse the repository at this point in the history
More ArrayBuffer support
  • Loading branch information
Ivan Mirić authored Apr 29, 2021
2 parents 14c6cd9 + 9f623d8 commit af1e032
Show file tree
Hide file tree
Showing 16 changed files with 417 additions and 176 deletions.
14 changes: 14 additions & 0 deletions js/common/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,17 @@ func ToBytes(data interface{}) ([]byte, error) {
return nil, fmt.Errorf("invalid type %T, expected string, []byte or ArrayBuffer", data)
}
}

// ToString tries to return a string from compatible types.
func ToString(data interface{}) (string, error) {
switch dt := data.(type) {
case []byte:
return string(dt), nil
case string:
return dt, nil
case goja.ArrayBuffer:
return string(dt.Bytes()), nil
default:
return "", fmt.Errorf("invalid type %T, expected string, []byte or ArrayBuffer", data)
}
}
29 changes: 29 additions & 0 deletions js/common/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func TestThrow(t *testing.T) {
}

func TestToBytes(t *testing.T) {
t.Parallel()
rt := goja.New()
b := []byte("hello")
testCases := []struct {
Expand All @@ -70,3 +71,31 @@ func TestToBytes(t *testing.T) {
})
}
}

func TestToString(t *testing.T) {
t.Parallel()
rt := goja.New()
s := "hello"
testCases := []struct {
in interface{}
expOut, expErr string
}{
{s, s, ""},
{"hello", s, ""},
{rt.NewArrayBuffer([]byte(s)), s, ""},
{struct{}{}, "", "invalid type struct {}, expected string, []byte or ArrayBuffer"},
}

for _, tc := range testCases { //nolint: paralleltest // false positive: https://github.com/kunwardeep/paralleltest/issues/8
tc := tc
t.Run(fmt.Sprintf("%T", tc.in), func(t *testing.T) {
t.Parallel()
out, err := ToString(tc.in)
if tc.expErr != "" {
assert.EqualError(t, err, tc.expErr)
return
}
assert.Equal(t, tc.expOut, out)
})
}
}
8 changes: 5 additions & 3 deletions js/initcontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,9 @@ func (i *InitContext) compileImport(src, filename string) (*goja.Program, error)
return pgm, err
}

// Open implements open() in the init context and will read and return the contents of a file.
// If the second argument is "b" it returns the data as a binary array, otherwise as a string.
// Open implements open() in the init context and will read and return the
// contents of a file. If the second argument is "b" it returns an ArrayBuffer
// instance, otherwise a string representation.
func (i *InitContext) Open(ctx context.Context, filename string, args ...string) (goja.Value, error) {
if lib.GetState(ctx) != nil {
return nil, errors.New(openCantBeUsedOutsideInitContextMsg)
Expand Down Expand Up @@ -242,7 +243,8 @@ func (i *InitContext) Open(ctx context.Context, filename string, args ...string)
}

if len(args) > 0 && args[0] == "b" {
return i.runtime.ToValue(data), nil
ab := i.runtime.NewArrayBuffer(data)
return i.runtime.ToValue(&ab), nil
}
return i.runtime.ToValue(string(data)), nil
}
32 changes: 16 additions & 16 deletions js/initcontext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,24 +239,22 @@ func TestInitContextRequire(t *testing.T) {
})
}

func createAndReadFile(t *testing.T, file string, content []byte, expectedLength int, binary bool) (*BundleInstance, error) {
func createAndReadFile(t *testing.T, file string, content []byte, expectedLength int, binary string) (*BundleInstance, error) {
t.Helper()
fs := afero.NewMemMapFs()
assert.NoError(t, fs.MkdirAll("/path/to", 0o755))
assert.NoError(t, afero.WriteFile(fs, "/path/to/"+file, content, 0o644))

binaryArg := ""
if binary {
binaryArg = ",\"b\""
}

data := fmt.Sprintf(`
export let data = open("/path/to/%s"%s);
let binArg = "%s";
export let data = open("/path/to/%s", binArg);
var expectedLength = %d;
if (data.length != expectedLength) {
throw new Error("Length not equal, expected: " + expectedLength + ", actual: " + data.length);
var len = binArg === "b" ? "byteLength" : "length";
if (data[len] != expectedLength) {
throw new Error("Length not equal, expected: " + expectedLength + ", actual: " + data[len]);
}
export default function() {}
`, file, binaryArg, expectedLength)
`, binary, file, expectedLength)
b, err := getSimpleBundle(t, "/path/to/script.js", data, fs)

if !assert.NoError(t, err) {
Expand All @@ -282,8 +280,9 @@ func TestInitContextOpen(t *testing.T) {
//{[]byte{00, 36, 32, 127}, "utf-16", 2}, // $€
}
for _, tc := range testCases {
tc := tc
t.Run(tc.file, func(t *testing.T) {
bi, err := createAndReadFile(t, tc.file, tc.content, tc.length, false)
bi, err := createAndReadFile(t, tc.file, tc.content, tc.length, "")
if !assert.NoError(t, err) {
return
}
Expand All @@ -292,12 +291,12 @@ func TestInitContextOpen(t *testing.T) {
}

t.Run("Binary", func(t *testing.T) {
bi, err := createAndReadFile(t, "/path/to/file.bin", []byte("hi!\x0f\xff\x01"), 6, true)
bi, err := createAndReadFile(t, "/path/to/file.bin", []byte("hi!\x0f\xff\x01"), 6, "b")
if !assert.NoError(t, err) {
return
}
bytes := []byte{104, 105, 33, 15, 255, 1}
assert.Equal(t, bytes, bi.Runtime.Get("data").Export())
buf := bi.Runtime.NewArrayBuffer([]byte{104, 105, 33, 15, 255, 1})
assert.Equal(t, buf, bi.Runtime.Get("data").Export())
})

testdata := map[string]string{
Expand All @@ -306,8 +305,9 @@ func TestInitContextOpen(t *testing.T) {
}

for name, loadPath := range testdata {
loadPath := loadPath
t.Run(name, func(t *testing.T) {
_, err := createAndReadFile(t, loadPath, []byte("content"), 7, false)
_, err := createAndReadFile(t, loadPath, []byte("content"), 7, "")
if !assert.NoError(t, err) {
return
}
Expand Down Expand Up @@ -409,7 +409,7 @@ func TestRequestWithBinaryFile(t *testing.T) {

v, err := bi.exports[consts.DefaultFn](goja.Undefined())
assert.NoError(t, err)
assert.NotNil(t, v)
require.NotNil(t, v)
assert.Equal(t, true, v.Export())

<-ch
Expand Down
18 changes: 12 additions & 6 deletions js/modules/k6/crypto/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ import (
"golang.org/x/crypto/md4"
"golang.org/x/crypto/ripemd160"

"github.com/dop251/goja"

"github.com/loadimpact/k6/js/common"
)

Expand All @@ -52,16 +54,18 @@ func New() *Crypto {
}

// RandomBytes returns random data of the given size.
func (*Crypto) RandomBytes(ctx context.Context, size int) []byte {
func (*Crypto) RandomBytes(ctx context.Context, size int) *goja.ArrayBuffer {
rt := common.GetRuntime(ctx)
if size < 1 {
common.Throw(common.GetRuntime(ctx), errors.New("invalid size"))
common.Throw(rt, errors.New("invalid size"))
}
bytes := make([]byte, size)
_, err := rand.Read(bytes)
if err != nil {
common.Throw(common.GetRuntime(ctx), err)
common.Throw(rt, err)
}
return bytes
ab := rt.NewArrayBuffer(bytes)
return &ab
}

// Md4 returns the MD4 hash of input in the given encoding.
Expand Down Expand Up @@ -171,6 +175,7 @@ func (hasher *Hasher) Update(input interface{}) {
// Digest returns the hash value in the given encoding.
func (hasher *Hasher) Digest(outputEncoding string) interface{} {
sum := hasher.hash.Sum(nil)
rt := common.GetRuntime(hasher.ctx)

switch outputEncoding {
case "base64":
Expand All @@ -186,11 +191,12 @@ func (hasher *Hasher) Digest(outputEncoding string) interface{} {
return hex.EncodeToString(sum)

case "binary":
return sum
ab := rt.NewArrayBuffer(sum)
return &ab

default:
err := errors.New("Invalid output encoding: " + outputEncoding)
common.Throw(common.GetRuntime(hasher.ctx), err)
common.Throw(rt, err)
}

return ""
Expand Down
8 changes: 4 additions & 4 deletions js/modules/k6/crypto/crypto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ func TestCryptoAlgorithms(t *testing.T) {

t.Run("RandomBytesSuccess", func(t *testing.T) {
_, err := rt.RunString(`
var bytes = crypto.randomBytes(5);
if (bytes.length !== 5) {
throw new Error("Incorrect size: " + bytes.length);
var buf = crypto.randomBytes(5);
if (buf.byteLength !== 5) {
throw new Error("Incorrect size: " + buf.byteLength);
}`)

assert.NoError(t, err)
Expand Down Expand Up @@ -303,7 +303,7 @@ func TestOutputEncoding(t *testing.T) {
return true;
}
var resultBinary = hasher.digest("binary");
var resultBinary = new Uint8Array(hasher.digest("binary"));
if (!arraysEqual(resultBinary, correctBinary)) {
throw new Error("Binary encoding mismatch: " + JSON.stringify(resultBinary));
}
Expand Down
18 changes: 14 additions & 4 deletions js/modules/k6/encoding/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ func (e *Encoding) B64encode(ctx context.Context, input interface{}, encoding st
}

// B64decode returns the decoded data of the base64 encoded input string using
// the given encoding.
func (e *Encoding) B64decode(ctx context.Context, input string, encoding string) string {
// the given encoding. If format is "s" it returns the data as a string,
// otherwise as an ArrayBuffer.
func (e *Encoding) B64decode(ctx context.Context, input, encoding, format string) interface{} {
var output []byte
var err error

Expand All @@ -73,9 +74,18 @@ func (e *Encoding) B64decode(ctx context.Context, input string, encoding string)
output, err = base64.StdEncoding.DecodeString(input)
}

rt := common.GetRuntime(ctx) //nolint: ifshort
if err != nil {
common.Throw(common.GetRuntime(ctx), err)
common.Throw(rt, err)
}

var out interface{}
if format == "s" {
out = string(output)
} else {
ab := rt.NewArrayBuffer(output)
out = &ab
}

return string(output)
return out
}
37 changes: 27 additions & 10 deletions js/modules/k6/encoding/encoding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
)

func TestEncodingAlgorithms(t *testing.T) {
t.Parallel()
if testing.Short() {
return
}
Expand All @@ -54,9 +55,12 @@ func TestEncodingAlgorithms(t *testing.T) {
t.Run("DefaultDec", func(t *testing.T) {
_, err := rt.RunString(`
var correct = "hello world";
var decoded = encoding.b64decode("aGVsbG8gd29ybGQ=");
if (decoded !== correct) {
throw new Error("Decoding mismatch: " + decoded);
var decBin = encoding.b64decode("aGVsbG8gd29ybGQ=");
var decText = String.fromCharCode.apply(null, new Uint8Array(decBin));
decText = decodeURIComponent(escape(decText));
if (decText !== correct) {
throw new Error("Decoding mismatch: " + decText);
}`)
assert.NoError(t, err)
})
Expand All @@ -70,6 +74,17 @@ func TestEncodingAlgorithms(t *testing.T) {
}`)
assert.NoError(t, err)
})
t.Run("DefaultArrayBufferDec", func(t *testing.T) { //nolint: paralleltest // weird that it triggers here, and these tests can't be parallel
_, err := rt.RunString(`
var exp = "hello";
var decBin = encoding.b64decode("aGVsbG8=");
var decText = String.fromCharCode.apply(null, new Uint8Array(decBin));
decText = decodeURIComponent(escape(decText));
if (decText !== exp) {
throw new Error("Decoding mismatch: " + decText);
}`)
assert.NoError(t, err)
})
t.Run("DefaultUnicodeEnc", func(t *testing.T) {
_, err := rt.RunString(`
var correct = "44GT44KT44Gr44Gh44Gv5LiW55WM";
Expand All @@ -82,9 +97,11 @@ func TestEncodingAlgorithms(t *testing.T) {
t.Run("DefaultUnicodeDec", func(t *testing.T) {
_, err := rt.RunString(`
var correct = "こんにちは世界";
var decoded = encoding.b64decode("44GT44KT44Gr44Gh44Gv5LiW55WM");
if (decoded !== correct) {
throw new Error("Decoding mismatch: " + decoded);
var decBin = encoding.b64decode("44GT44KT44Gr44Gh44Gv5LiW55WM");
var decText = String.fromCharCode.apply(null, new Uint8Array(decBin));
decText = decodeURIComponent(escape(decText));
if (decText !== correct) {
throw new Error("Decoding mismatch: " + decText);
}`)
assert.NoError(t, err)
})
Expand All @@ -100,7 +117,7 @@ func TestEncodingAlgorithms(t *testing.T) {
t.Run("StdDec", func(t *testing.T) {
_, err := rt.RunString(`
var correct = "hello world";
var decoded = encoding.b64decode("aGVsbG8gd29ybGQ=", "std");
var decoded = encoding.b64decode("aGVsbG8gd29ybGQ=", "std", "s");
if (decoded !== correct) {
throw new Error("Decoding mismatch: " + decoded);
}`)
Expand All @@ -118,7 +135,7 @@ func TestEncodingAlgorithms(t *testing.T) {
t.Run("RawStdDec", func(t *testing.T) {
_, err := rt.RunString(`
var correct = "hello world";
var decoded = encoding.b64decode("aGVsbG8gd29ybGQ", "rawstd");
var decoded = encoding.b64decode("aGVsbG8gd29ybGQ", "rawstd", "s");
if (decoded !== correct) {
throw new Error("Decoding mismatch: " + decoded);
}`)
Expand All @@ -136,7 +153,7 @@ func TestEncodingAlgorithms(t *testing.T) {
t.Run("URLDec", func(t *testing.T) {
_, err := rt.RunString(`
var correct = "小飼弾..";
var decoded = encoding.b64decode("5bCP6aO85by-Li4=", "url");
var decoded = encoding.b64decode("5bCP6aO85by-Li4=", "url", "s");
if (decoded !== correct) {
throw new Error("Decoding mismatch: " + decoded);
}`)
Expand All @@ -154,7 +171,7 @@ func TestEncodingAlgorithms(t *testing.T) {
t.Run("RawURLDec", func(t *testing.T) {
_, err := rt.RunString(`
var correct = "小飼弾..";
var decoded = encoding.b64decode("5bCP6aO85by-Li4", "rawurl");
var decoded = encoding.b64decode("5bCP6aO85by-Li4", "rawurl", "s");
if (decoded !== correct) {
throw new Error("Decoding mismatch: " + decoded);
}`)
Expand Down
2 changes: 2 additions & 0 deletions js/modules/k6/http/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ func (h *HTTP) Request(ctx context.Context, method string, url goja.Value, args
if err != nil {
return nil, err
}
processResponse(ctx, resp, req.ResponseType)
return h.responseFromHttpext(resp), nil
}

Expand Down Expand Up @@ -450,6 +451,7 @@ func (h *HTTP) Batch(ctx context.Context, reqsV goja.Value) (goja.Value, error)
errs := httpext.MakeBatchRequests(
ctx, batchReqs, reqCount,
int(state.Options.Batch.Int64), int(state.Options.BatchPerHost.Int64),
processResponse,
)

for i := 0; i < reqCount; i++ {
Expand Down
Loading

0 comments on commit af1e032

Please sign in to comment.