Skip to content
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

sse: support Server Sent Event #3639

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions docs/design/021-sse-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Introduce an SSE API module for k6

| | |
|:---------------------|:-------------------------------------------------------------|
| **author** | @phymbert |
| **status** | 🔧 proposal |
| **revisions** | [initial](https://github.com/grafana/k6/pull/3639) |
| **Proof of concept** | [branch](https://github.com/phymbert/k6/tree/hp/feature/sse) |
| **references** | [#746](https://github.com/grafana/k6/issues/746) |

## Problem definition

The current version of k6 reads the full http response body before returning to the client,
which make impossible testing [Server-Sent Event](https://fr.wikipedia.org/wiki/Server-sent_events).

We propose to introduce a new `sse` module.
This module is intended to offer an intuitive and user-friendly API for SSE interactions within k6 scripts.

## Proposed solution

We suggest to implement a minimalist, experimental (`sse`) module based on the go http client.
The new module will allow users to interact with SSE.
The module will provide an `open` which will allow the user to pass a setup function to configure an `event` callback as it is done in the `ws` module with `message`.

### Limitation
The module will not support async io and the javascript main loop will be blocked during the http request duration.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Async support is becoming more and more important, so integrating a new module without async support is questionable. This is just my personal opinion, @mstoykov is more competent in this...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that SSE is by default an asynchronous communication I find that this must be asynchronous as well.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by asynchronous ? it's one way server to client data flow on top of HTTP 1.1 w/ single connection. The event loop is blocked while waiting for next event, but on each new event the client callback is triggered.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take a look at xk6-mock extension code base, especially in the szkiba/muxpress library. You will find example there how to support callback running on eventloop.


### Example usage

```javascript
import sse from 'k6/experimental/sse';

var url = "https://echo.websocket.org/.sse";
var params = {"tags": {"my_tag": "hello"}};

var response = sse.open(url, params, function (client) {
client.on('open', function open() {
console.log('connected');
});

client.on('event', function (event) {
console.log(`event id=${event.id}, name=${event.name}, data=${event.data}`);
});

client.on('error', function (e) {
console.log('An unexpected error occurred: ', e.error());
});
});

check(response, {"status is 200": (r) => r && r.status === 200});
```

### Conclusion

We believe the [proof of concept](https://github.com/grafana/k6/blob/d5cd1010ecb2381376188c8a47ab861cf8b5dc3d/js/modules/k6/experimental/sse/sse.go) developed with this proposal illustrates the feasibility and benefits of developing such an API.
23 changes: 23 additions & 0 deletions examples/sse.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import sse from "k6/experimental/sse";
import {check} from "k6";

export default function () {
var url = "https://echo.websocket.org/.sse";
var params = {"tags": {"my_tag": "hello"}};

var response = sse.open(url, params, function (client) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An SSE connection is usually long-lived. We do not test normal operation by opening a new connection in each iteration.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

client.on('open', function open() {
console.log('connected');
});

client.on('event', function (event) {
console.log(`event id=${event.id}, name=${event.name}, data=${event.data}`);
});

client.on('error', function (e) {
console.log('An unexpected error occurred: ', e.error());
});
});

check(response, {"status is 200": (r) => r && r.status === 200});
};
2 changes: 2 additions & 0 deletions js/jsmodules.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package js

import (
"go.k6.io/k6/js/modules/k6/experimental/sse"
"sync"

"go.k6.io/k6/ext"
Expand Down Expand Up @@ -45,6 +46,7 @@ func getInternalJSModules() map[string]interface{} {
"k6/experimental/tracing": tracing.New(),
"k6/experimental/browser": browser.New(),
"k6/experimental/fs": fs.New(),
"k6/experimental/sse": sse.New(),
"k6/net/grpc": grpc.New(),
"k6/html": html.New(),
"k6/http": http.New(),
Expand Down
Loading