Skip to content

Commit 311c920

Browse files
fix!: remove default stream close event (#81)
* fix!: remove default stream close event BREAKING CHANGE: there is no "end" event when stream is closing in case you depended on it The current implementation sends its own event when an async iterable ends, this is bad as the implemented SSE protocol doesn't expect that message, it's an application level thing. It should be up to the application to send such a message or just close the SSE stream. Remove that event (Possibly a breaking change if anyone relied on it). For example, that caused an error in our project from our handler receving that message and crashing cause it's an unknown message type whose body is not even JSON as our application expects. Contributed on behalf of [Swimm](https://swimm.io/) * fix tests Signed-off-by: Marin Petrunic <marin.petrunic@gmail.com> * update ci nodejs version Signed-off-by: Marin Petrunic <marin.petrunic@gmail.com> --------- Signed-off-by: Marin Petrunic <marin.petrunic@gmail.com> Co-authored-by: Marin Petrunic <marin.petrunic@gmail.com>
1 parent 3495dbc commit 311c920

File tree

4 files changed

+34
-38
lines changed

4 files changed

+34
-38
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
runs-on: ubuntu-latest
1414
strategy:
1515
matrix:
16-
node-version: [16, 18]
16+
node-version: [18, 20]
1717
steps:
1818
- uses: actions/checkout@v3
1919
- name: Use Node.js ${{ matrix.node-version }}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"name": "fastify-sse-v2",
33
"version": "3.1.2",
4+
"packageManager": "yarn@1.22.19",
45
"description": "Fastify plugin for sending server side events.",
56
"main": "lib/index.js",
67
"types": "lib/index.d.ts",

src/sse.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ export async function* transformAsyncIterable(
66
for await (const message of source) {
77
yield serializeSSEEvent(message);
88
}
9-
yield serializeSSEEvent({ event: "end", data: "Stream closed" });
109
}
1110

1211
export function serializeSSEEvent(chunk: EventMessage): string {

test/fastify4/fastify4.spec.ts

Lines changed: 32 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import {expect} from "chai";
2-
import {FastifyInstance, EventMessage, RouteHandler} from "fastify";
3-
import {getEventSource, getFastifyServer, getBaseUrl} from "./utils";
4-
import pushable, {Pushable} from "it-pushable";
1+
import { get } from "http";
2+
import { expect } from "chai";
3+
import { FastifyInstance, EventMessage, RouteHandler } from "fastify";
4+
import EventSource from "eventsource";
5+
import pushable, { Pushable } from "it-pushable";
56
import sinon from "sinon";
6-
import {get} from "http";
7+
import { getEventSource, getFastifyServer, getBaseUrl } from "./utils";
78

89
describe("Fastify - Test SSE plugin", function () {
9-
1010
let server: FastifyInstance;
1111
let source: Pushable<EventMessage>;
1212

@@ -17,7 +17,7 @@ describe("Fastify - Test SSE plugin", function () {
1717

1818
afterEach(async function () {
1919
source.end();
20-
if(server) {
20+
if (server) {
2121
await server.close();
2222
}
2323
});
@@ -33,12 +33,12 @@ describe("Fastify - Test SSE plugin", function () {
3333

3434
it("should set plugin headers", function (done) {
3535
try {
36-
get(getBaseUrl(server), {timeout: 100}, (res) => {
36+
get(getBaseUrl(server), { timeout: 100 }, (res) => {
3737
expect(res.headers["x-test-header2"]).to.be.deep.equal("test2");
3838
res.destroy();
3939
done();
4040
});
41-
} catch(e) {
41+
} catch (e) {
4242
done(e);
4343
}
4444
});
@@ -65,85 +65,81 @@ describe("Fastify - Test SSE plugin", function () {
6565
eventsource.close();
6666
throw "shouldn't be called";
6767
});
68-
eventsource.addEventListener("end", function (e: Event) {
69-
expect(e.type).to.be.equal("end");
70-
// @ts-ignore
71-
expect(e.data).to.be.equal("Stream closed");
72-
eventsource.close();
73-
done();
68+
eventsource.addEventListener("error", function (e) {
69+
if (e.data == undefined) {
70+
// Connection closed by server
71+
eventsource.close();
72+
done();
73+
} else {
74+
throw "shouldn't happen";
75+
}
7476
});
7577
});
7678

7779
it("should send single event", function (done) {
7880
const eventsource = getEventSource(server);
79-
source.push({data: "Something", id: "1", event: "message"});
80-
eventsource.onmessage = (evt => {
81+
source.push({ data: "Something", id: "1", event: "message" });
82+
eventsource.onmessage = (evt) => {
8183
expect(evt.data).equal("Something");
8284
expect(evt.type).equal("message");
8385
expect(evt.lastEventId).equal("1");
8486
eventsource.close();
8587
done();
86-
});
87-
88+
};
8889
});
8990

9091
it("should send multiple events without async iterable", function (done) {
9192
const handler: RouteHandler = async (req, resp): Promise<void> => {
92-
for await( const event of source) {
93+
for await (const event of source) {
9394
resp.sse(event);
9495
return resp;
9596
}
96-
9797
};
9898
getFastifyServer(handler).then((server2) => {
9999
const eventsource = getEventSource(server2);
100-
source.push({id: "1", event: "message", data: "Something"});
101-
eventsource.onmessage = (evt => {
100+
source.push({ id: "1", event: "message", data: "Something" });
101+
eventsource.onmessage = (evt) => {
102102
expect(evt.data).equal("Something");
103103
expect(evt.type).equal("message");
104104
expect(evt.lastEventId).equal("1");
105105
eventsource.close();
106106
server2.close();
107107
done();
108-
});
108+
};
109109
});
110-
111110
});
112111

113112
it("should send event after headers has been sent by user", function (done) {
114113
const handler: RouteHandler = async (req, resp): Promise<void> => {
115114
resp.header("Content-Type", "text/event-stream");
116115
resp.raw.flushHeaders();
117-
resp.sse({id: "1", event: "message", data: "Something"});
116+
resp.sse({ id: "1", event: "message", data: "Something" });
118117
return resp;
119118
};
120119
getFastifyServer(handler).then((server2) => {
121120
const eventsource = getEventSource(server2);
122-
eventsource.onmessage = (evt => {
121+
eventsource.onmessage = (evt) => {
123122
expect(evt.data).equal("Something");
124123
expect(evt.type).equal("message");
125124
expect(evt.lastEventId).equal("1");
126125
eventsource.close();
127126
server2.close();
128127
done();
129-
});
128+
};
130129
});
131-
132130
});
133131

134132
it("should send multiple events", function (done) {
135133
const eventsource = getEventSource(server);
136-
source.push({id: "1", event: "message", data: "Something"});
137-
source.push({id: "2", event: "message", data: "Something"});
134+
source.push({ id: "1", event: "message", data: "Something" });
135+
source.push({ id: "2", event: "message", data: "Something" });
138136
source.end();
139137
const spy = sinon.spy();
140-
eventsource.onmessage = (() => spy());
141-
eventsource.onerror = (() => {
138+
eventsource.onmessage = () => spy();
139+
eventsource.onerror = () => {
142140
expect(spy.callCount).to.be.equal(2);
143141
eventsource.close();
144142
done();
145-
});
146-
143+
};
147144
});
148-
149145
});

0 commit comments

Comments
 (0)