Orange-OpenSource / hurl

Hurl, run and test HTTP requests with plain text.
https://hurl.dev
Apache License 2.0
12.94k stars 484 forks source link

Websocket support and documentation? #1096

Open nahuakang opened 1 year ago

nahuakang commented 1 year ago

Problem to solve

I am trying to see if hurl can test websocket requests.

Proposal

I'd like to know if hurl can test websocket requests and, if yes, can we add some documentation on how to do so?

Additional context and resources

N/A

Tasks to complete

N/A

fabricereix commented 1 year ago

Hi @nahuakang, we haven't done it so far. We have only used Hurl with the http/https protocols. Not sure whether it's easy or not to add Websocket support. We can have a look at it. How would you write the Hurl file? It's also useful to have a concrete example.

nahuakang commented 1 year ago

@fabricereix Thanks so much for your response. While I have a concrete use case (which is currently done in Postman), I don't have a concrete example on how to write it in Hurl style. If it helps, I can think about it for a bit.

ShaddyDC commented 6 months ago

I've got an example, though I'm not sure how I'd want to write it in Hurl style. Basically, I'm writing a graphql API, where I can submit jobs and track their progress with a subscription, which uses websockets. I haven't quite settled on what API I want yet, but traffic looks something like this atm (> for sent and < for received items):

> {"type":"connection_init","payload":{}}
< {"type":"connection_ack"}
> {"id":"4b8d2a04-0a56-4565-8f34-41067e2b3cb6","type":"subscribe","payload":{"query":"jobUpdate(jobId: \"lU1TG2v79Q0ynYMv1FsZs\") {\n    state\n    progress {\n      current total\n    }\n  }\n}"}}
< {"type":"next","id":"4b8d2a04-0a56-4565-8f34-41067e2b3cb6","payload":{"data":{"jobUpdate":{"state":"STARTED","progress":{"current":1,"total":3}}}}}
< {"type":"next","id":"4b8d2a04-0a56-4565-8f34-41067e2b3cb6","payload":{"data":{"jobUpdate":{"state":"STARTED","progress":{"current":2,"total":3}}}}}
< {"type":"next","id":"4b8d2a04-0a56-4565-8f34-41067e2b3cb6","payload":{"data":{"jobUpdate":{"state":"DONE","progress":null}}}}
< {"type":"complete","id":"4b8d2a04-0a56-4565-8f34-41067e2b3cb6"}

There can also be failure updates, so I guess what I'd be interested in would be a way to write a graphql subscription request like any other request and then assert that at some point a packet with a specific payload returns and that the connection completes within a certain time frame.

Maybe something along these lines? Very rough sketch, I haven't thought about this much, and it would be nice if we could check that a specific subsequence of updates appeared in order, though getting that right is probably very difficult.

WS http://localhost:8000/ws
```graphql
subscription {
  jobUpdate(
      jobId: "{{job_id}}"
    ) {
    state
    progress { current total }
  }
}

HTTP 200 [Asserts] any jsonpath "$.data.jobUpdate.state" == "DONE" any jsonpath "$.data.jobUpdate.progress.current" == 2

jcamiel commented 6 months ago

With JetBrains HTTP client, the WebSocket syntax is something like:

WEBSOCKET ws://localhost:8080/websocket
Content-Type: application-json // Used for content highlighting only

// Request body, for example:
{
  "message": "First message sent on connection"
}
===  // message separator
{
  "message": "Second message" // will be sent right after the previous one
}
=== wait-for-server  // keyword used to wait for the server response
{
  "message": "Send this after the server response"
}

With === // message separator as a sent message seperator, and === wait-for-server as a wait message from server

linkdd commented 5 months ago

My 2 cents:

HTTP http://example.com/ws
Connection: upgrade
Upgrade: websocket
[Options]
connection-close: false

HTTP 101
[Asserts]
# typical response assertions
[SendData]
> {
>   "action": "send-me-cats",
>   "#comment": "the > is for multiline, like a blockquote in markdown"
> }
[AssertReceiveData]
jsonpath "$.cats" count == 49
[SendData]
> {
>   "action": "send-me-cats",
>   "#comment": "the > is for multiline, like a blockquote in markdown"
> }
[AssertReceiveData]
jsonpath "$.cats" count == 49
[ConnectionClose]

Basically, a websocket is "just" an HTTP connection that is not closed.

Upon receiving the status code 101 Switching Protocol we are in "websocket mode".

We could then have a series of section [SendData] and/or [AssertReceiveData], ended by a [ConnectionClose].

This would be more in line with the actual hurl file syntax AND the actual flow of the data on the socket.


Note that this syntax would work the same for Server-Sent-Events:

GET http://example.com/sse
[Options]
connection-close: false

HTTP 200
[Asserts]
header "Content-Type" == "text/event-stream"
[AssertReceiveData]
# ...
[AssertReceiveData]
# ...
[AssertReceiveData]
# ...
[ConnectionClose]
jcamiel commented 5 months ago

Good ideas, I also like JetBrains's HTTP Client format for web socket:

WEBSOCKET ws://localhost:8080/websocket
Content-Type: application-json // Used for content highlighting only

// Request body, for example:
{
  "message": "First message sent on connection"
}
===  // message separator
{
  "message": "Second message" // will be sent right after the previous one
}
=== wait-for-server  // keyword used to wait for the server response
{
  "message": "Send this after the server response"
}

https://www.jetbrains.com/help/idea/http-client-in-product-code-editor.html#multiple_messages