-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
More ArrayBuffer support #1841
More ArrayBuffer support #1841
Conversation
Codecov Report
@@ Coverage Diff @@
## master #1841 +/- ##
==========================================
+ Coverage 71.28% 71.34% +0.06%
==========================================
Files 183 181 -2
Lines 14274 14293 +19
==========================================
+ Hits 10175 10198 +23
+ Misses 3479 3474 -5
- Partials 620 621 +1
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems good to me - I will need to try using it probably.
Can you drop the support for []byte
everywhere as well so that we can see if something breaks when we no longer support it.
Kind of resolves #1841 (comment)
Set the milestone to v0.32.0. Also, a reminder that we discussed websocket binary support, which might be a part of this PR or another similar one. |
412cbd1
to
6a9ea79
Compare
OK, so I reverted the WIP changes discussed here, and pushed the agreed breaking changes for v0.32.0. Some notes:
|
Return goja.Value from open() Resolves #1841 (comment)
js/modules/k6/ws/ws.go
Outdated
@@ -292,7 +292,8 @@ func (*WS) Connect(ctx context.Context, url string, args ...goja.Value) (*WSHTTP | |||
Tags: socket.sampleTags, | |||
Value: 1, | |||
}) | |||
socket.handleEvent("message", rt.ToValue(string(readData))) | |||
ab := rt.NewArrayBuffer(readData) | |||
socket.handleEvent("message", rt.ToValue(string(readData)), rt.ToValue(&ab)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this be prohibitively expensive memory wise?
I experimented with overriding the first argument to be a String
object with an additional buffer
property, but that doesn't expose the string primitive properties (.length
, etc.) so it would be a breaking change.
Passing it as a second argument is backwards compatible, and I'm expecting it to be garbage collected if it's not used, but haven't run any benchmarks. One optimization we could do is to only pass it if the message
handler function expects a second argument, but it would be awkward to do and would require additional refactoring.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it will be better to just have binaryMessage
event or something like that instead of ... this ;)
This is not how it's done in other tools but given the current way it is done in k6 I think this is better and then we can fix it properly in the next implementation when it is more async
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, see e44fb6d.
But I also think this is a rough bandaid that adds more work for users once we move to a better implementation and deprecate it.
js/modules/k6/ws/ws.go
Outdated
func (s *Socket) Send(message interface{}) { | ||
rt := common.GetRuntime(s.ctx) | ||
|
||
writeData := []byte(message) | ||
if err := s.conn.WriteMessage(websocket.TextMessage, writeData); err != nil { | ||
var ( | ||
msgType int | ||
msg []byte | ||
) | ||
switch m := message.(type) { | ||
case string: | ||
msgType = websocket.TextMessage | ||
msg = []byte(m) | ||
case goja.ArrayBuffer: | ||
msgType = websocket.BinaryMessage | ||
msg = m.Bytes() | ||
default: | ||
err := fmt.Errorf("unsupported message type: %T, expected string or ArrayBuffer", message) | ||
common.Throw(common.GetRuntime(s.ctx), err) | ||
} | ||
|
||
if err := s.conn.WriteMessage(msgType, msg); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be done with a separate method as it was done in the original forum thread this was discussed
This IMO is a cleaner as a solution and also will mean that if a user runs a script using, they will get an error instead of it silently being converted to a string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree that it's cleaner, but here you go: b80813c.
Expanding the API to now include another method that does the same thing for a different data type is hardly a good idea. It might've worked fine for that user and it's an isolated solution, but it's another method we'll have to eventually deprecate.
@na-- WDYT?
js/modules/k6/ws/ws.go
Outdated
@@ -292,7 +292,8 @@ func (*WS) Connect(ctx context.Context, url string, args ...goja.Value) (*WSHTTP | |||
Tags: socket.sampleTags, | |||
Value: 1, | |||
}) | |||
socket.handleEvent("message", rt.ToValue(string(readData))) | |||
ab := rt.NewArrayBuffer(readData) | |||
socket.handleEvent("message", rt.ToValue(string(readData)), rt.ToValue(&ab)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it will be better to just have binaryMessage
event or something like that instead of ... this ;)
This is not how it's done in other tools but given the current way it is done in k6 I think this is better and then we can fix it properly in the next implementation when it is more async
js/modules/k6/ws/ws.go
Outdated
if _, ok := socket.eventHandlers["binaryMessage"]; ok { | ||
ab := rt.NewArrayBuffer(readData) | ||
socket.handleEvent("binaryMessage", rt.ToValue(&ab)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No matter whether it is 1 or 2 events I do think that we need to check the messagetype and call either the correct method or if we choose to have only message
events change the type of the value to ArrayBuffer or string based on it.
This will require more work in readPump
which I think should probably be better handled in a separate PR , possibly even in a following release so ... I am mostly for dropping the ws
changes for now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, we should only call the binaryMessage
handler if the messageType == BinaryMessage
, I overlooked that.
It shouldn't be much work to refactor, I'll look into it today. Don't think we should delay this because of that change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
js/modules/k6/ws/ws.go
Outdated
} | ||
} else { | ||
common.Throw(common.GetRuntime(s.ctx), | ||
fmt.Errorf("expected ArrayBuffer as argument, received: %T", msg)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally, this should be the type of message
but that will require a lot more code and practically can have a case where we can get one (if it's nil/undefined 🤔 )
I did try it and
message.ToObject(common.GetRuntime(s.ctx)).ClassName()
seems to work okay and blows up with TypeError if message
is null/undefined which is more or less what we want so ... maybe we can just add it ? But I would prefer if more testing is done that it won't panic the whole VU in some strange case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we should bother with that. Sure, it would be good to log the JS type instead, but not everything can be converted to an object. For example, (Nevermind, I'm dumb and kept the socket.sendBinary(1)
throws expected ArrayBuffer as argument, received: string
for some reason, which is more confusing than expected ArrayBuffer as argument, received: int64
%T
🤦). And we'd have to handle the null
/undefined
case as well.
I don't see any way in the Go API to call Do you think all this is worth it?typeof
so we'd have to RunString()
it.
EDIT: Actually it's not that big of a problem, will change, thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This allows us to use ToBytes to process any body type without adding the js/common dependency to httpext, and it arguably should've been part of the http module to begin with.
I experimented with overriding the first argument passed to the 'message' handler to be a String object with a `buffer` data property, but it doesn't expose the string primitive properties (.length, etc.) so it would be a breaking change. Passing the ArrayBuffer as the second argument is backwards compatible, and hopefully doesn't cause a doubling of memory usage (I'm expecting it to be garbage collected if the argument isn't used). We could detect the number of arguments defined on the handler and only pass the ArrayBuffer if expected, but this would be awkward to implement. Part of #1020
This is a more user friendly error if the user passes anything else other than an ArrayBuffer object, otherwise it would output a panic stack trace from bridge.go.
23e3683
to
9f623d8
Compare
Force-pushed because of |
These are the leftover changes to close #1020. Most of them break backwards compatibility, as discussed in the issue.
It might be easier to review by commit.