Giter Club home page Giter Club logo

sse.js's Introduction

sse.js

GitHub License NPM Downloads

sse.js is a flexible EventSource replacement for JavaScript designed to consume Server-Sent Events (SSE) streams with more control and options than the standard EventSource. The main limitations of EventSource are that it only supports no-payload GET requests, and does not support specifying additional custom headers to the HTTP request.

This package is designed to provide a usable replacement to EventSource that makes all of this possible: SSE. It is a fully compatible EventSource polyfill so you should be able to do this if you want/need to:

EventSource = SSE;

Usage

Constructor

var source = new SSE(url, options);

Getting started

The most simple way to use SSE is to create the SSE object, attach one or more listeners, and activate the stream:

var source = new SSE(url);
source.addEventListener('message', function(e) {
  // Assuming we receive JSON-encoded data payloads:
  var payload = JSON.parse(e.data);
  console.log(payload);
});

Like EventSource, SSE will automatically execute the request and start streaming. If you want to disable this behavior, and be more specific as to when the request should be triggered, you can pass the start: false option and later call the stream() method:

var source = new SSE(url, {start: false});
source.addEventListener('message', (e) => { ... });
// ... later on
source.stream();

Passing custom headers

var source = new SSE(url, {headers: {'Authorization': 'Bearer 0xdeadbeef'}});

Making a POST request and overriding the HTTP method

To make a HTTP POST request, simply specify a payload in the options:

var source = new SSE(url, {headers: {'Content-Type': 'text/plain'},
                           payload: 'Hello, world!'});

Alternatively, you can also manually override the HTTP method used to perform the request, regardless of the presence of a payload option, by specifying the method option:

var source = new SSE(url, {headers: {'Content-Type': 'text/plain'},
                           payload: 'Hello, world!',
                           method: 'GET'});

Options reference

Name Description
headers A map of additional headers to use on the HTTP request
method Override HTTP method (defaults to GET, unless a payload is given, in which case it defaults to POST)
payload An optional request payload to sent with the request
withCredentials If set to true, CORS requests will be set to include credentials
start Automatically execute the request and start streaming (defaults to true)
debug Log debug messages to the console about received chunks and dispatched events (defaults to false)

Events

SSE implements the EventTarget interface (just like EventSource) and emits fully constructed Event objects. The type of the event corresponds to the Server-Sent Event's name, and the event's timestamp is the UNIX timestamp of the reception of the event.

Additionally, the events will have the following fields:

  • id: the event ID, if present; null otherwise
  • lastEventId: the last seen event ID, or the empty string if no event with an ID was received
  • data: the event data, unparsed

SSE, like EventSource, will emit the following events:

  • open, when the first block of data is received from the event stream;
  • error, if an error occurs while making the request;
  • abort, as a response to the stream being explicitely aborted by the client;
  • readystatechange, to notify of a change in the ready state of the event source.

Note that all events dispatched by SSE will have the event target initially set to the SSE object itself.

Listening for specific event types

The Server-Sent Events specification allows for arbitrary event types, as the event field of the event. The default event type is message, so you'll most likely want to register a listener for this kind of events. If you expect another type of events, simply register your callback with the appropriate event type:

var source = new SSE(url);
source.addEventListener('status', function(e) {
  console.log('System status is now: ' + e.data);
});
source.stream();

You can also register an event listener with the on<event> style:

var source = new SSE(url);
source.onstatus = function(e) { ... };

You can mix both on<event> and addEventListener(). The on<event> handler is always called first if it is defined.

Expected response from server

It is expected that the server will return the data in the following format, as defined here:

event: <type>\n
data: <data>\n
\n

Advanced usage

withCredentials support

This EventSource polyfill supports the withCredentials option to request that the outgoing HTTP request be made with a CORS credentials mode of include, as per the HTML Living Standard.

Reconnecting after failure

SSE.js does not (yet) automatically reconnect on failure; you can listen for the abort event and decide whether to reconnect and restart the event stream by calling stream().

SSE.js will set the Last-Event-ID header on reconnection to the last seen event ID value (if any), as per the EventSource specification.

Development

TODOs and caveats

  • Internet Explorer 11 does not support arbitrary values in CustomEvents. A dependency on custom-event-polyfill is necessary for IE11 compatibility.
  • Improve XmlHttpRequest error handling and connection states

Releasing sse.js

Increment the package version with npm version, and publish to GitHub and NPM.js:

$ npm version {major,minor,patch}
$ git publish --tags
$ npm publish --otp <otp>

Then, create a new GitHub release for the new tagged version.

sse.js's People

Contributors

caugner avatar cwelton avatar dedalusmohantyma avatar janpaepke avatar jonahbron avatar mpetazzoni avatar patataman avatar rodrigovr avatar toucanbran avatar valadaptive avatar vijeetgv avatar vishwajeet-pandey-exa avatar vsantele avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sse.js's Issues

Receiving all events at once in Angular, not when they are sent by Jersey SSE

I generate SSE events with a Java app developed with Jersey 2.32 and running on Tomcat 9.
Once the get request is received, an event is generated every 500 milliseconds, and I have checked it works fine by outputting the event data on the Java console.
The client is an Angular 12 app. I have picked up your library as I need to add an authentification header to my request.
The client does receive the messages, but it receives them all at once, instead of receiving them as soon as they are sent by the server.
I have attached files with my code. Any help really appreciated.
angular-client-excerpt.txt
batch-process-monitoring.service.txt
server-side-events.service.txt
jersey-sse.txt

Make importable as an ES module?

I see that this module currently exports in CommonJS format for Node.js, but I'd like to import it as an ES module in browser JavaScript. Is it possible to make this library compatible with both import/export styles?

On my own fork, I was able to simply add export { SSE }; to the end of sse.js and everything works great for me, but I don't know if that would impact usage in Node at all.

removed constructor in 2.4.0? can not build

Hi, I can not build my project after auto-update to 2.4.0. If I'm watching correctly, you removed constructor, so var source = new SSE(url, options); is not possible to call. Was the change really intended?

lastEventId not implemented?

The spec says that server-sent events should have a lastEventId property, which is set to the value of the last id field that was sent through the stream (or the empty string if there was none). It doesn't necessarily have to belong to the current event; it's just the last event ID witnessed within that stream.

This library does something vaguely similar, but with some deviations:

  • The property is named id, not lastEventId
  • It's reset with each new event sent, rather than persisting
  • It's set to null by default, whereas lastEventId defaults to the empty string
  • lastEventId forbids null bytes; the id property allows them

I want to bring this in line with the spec, but I'm concerned about backwards compatibility with previous versions of the library. There are a few options:

  • Leave id as-is and document the differences.
  • Add lastEventId as a separate property, but keep id and its behavior. This would complicate the codebase, but would allow for better standards compatibility and backwards compatibility.
  • Do a major version bump, and fix this along with some other spec deviations (e.g. firing CustomEvents instead of MessageEvents; not setting the event's origin; lack of reconnection logic).

Event stream parsing fails when \n\n sequence is split among two network packets

I opened the following pull request #35 with a new parser implementation that solves this bug.

The bug was found on the following (real) case:

1f50: 0a 0d 0a 35 32 0d 0a 69 64 3a 20 70 72 6f 67 72 ...52..id: progr
1f60: 65 73 73 0a 64 61 74 61 3a 20 7b 22 63 6f 75 6e ess.data: {"coun
1f70: 74 22 3a 33 33 2c 22 74 6f 74 61 6c 22 3a 31 33 t":33,"total":13
1f80: 31 2c 22 74 69 6d 65 73 74 61 6d 70 22 3a 31 36 1,"timestamp":16
1f90: 39 31 31 39 36 39 38 30 7d 0a 74 79 70 65 3a 20 91196980}.type:
1fa0: 6d 65 73 73 61 67 65 0a 0a 0d 0a 35 32 0d 0a 69 message....52..i
1fb0: 64 3a 20 70 72 6f 67 72 65 73 73 0a 64 61 74 61 d: progress.data
1fc0: 3a 20 7b 22 63 6f 75 6e 74 22 3a 33 34 2c 22 74 : {"count":34,"t
1fd0: 6f 74 61 6c 22 3a 31 33 31 2c 22 74 69 6d 65 73 otal":131,"times
1fe0: 74 61 6d 70 22 3a 31 36 39 31 31 39 36 39 38 30 tamp":1691196980
1ff0: 7d 0a 74 79 70 65 3a 20 6d 65 73 73 61 67 65 0a }.type: message.
== Info: TLSv1.2 (IN), TLS header, Supplemental data (23):
<= Recv SSL data, 5 bytes (0x5)
0000: 17 03 03 00 19                                  .....
<= Recv data, 1 bytes (0x1)
0000: 0a                                              .
== Info: TLSv1.2 (IN), TLS header, Supplemental data (23):
<= Recv SSL data, 5 bytes (0x5)
0000: 17 03 03 00 1a                                  .....
<= Recv data, 2 bytes (0x2)
0000: 0d 0a                                           ..
== Info: TLSv1.2 (IN), TLS header, Supplemental data (23):
<= Recv SSL data, 5 bytes (0x5)
0000: 17 03 03 20 18                                  ... .
<= Recv data, 8192 bytes (0x2000)
0000: 35 32 0d 0a 69 64 3a 20 70 72 6f 67 72 65 73 73 52..id: progress
0010: 0a 64 61 74 61 3a 20 7b 22 63 6f 75 6e 74 22 3a .data: {"count":
0020: 33 35 2c 22 74 6f 74 61 6c 22 3a 31 33 31 2c 22 35,"total":131,"
0030: 74 69 6d 65 73 74 61 6d 70 22 3a 31 36 39 31 31 timestamp":16911
0040: 39 36 39 38 30 7d 0a 74 79 70 65 3a 20 6d 65 73 96980}.type: mes
0050: 73 61 67 65 0a 0a 0d 0a 35 32 0d 0a 69 64 3a 20 sage....52..id:
0060: 70 72 6f 67 72 65 73 73 0a 64 61 74 61 3a 20 7b progress.data: {
0070: 22 63 6f 75 6e 74 22 3a 33 36 2c 22 74 6f 74 61 "count":36,"tota

As you can see, there's a single line break (\n) on an single packet.
This causes the parser to merge two adjacent events that should not be merged.

Spec deviations with regards to trailing whitespace and field names without colons

I've noticed two ways in which this library's parsing behavior deviates from the official event stream specification:

  • Trailing whitespace is trimmed from each data block. While the last trailing newline should indeed be trimmed, the spec doesn't say to remove any other whitespace, and testing with Firefox's native EventSource confirms that trailing whitespace should be preserved.
  • Lines which don't contain a colon (field separator) are ignored. The spec says to treat the entire line as the name of the field in that case, meaning that a line containing only data\n should result in a newline being added to the data buffer (confirmed via in-browser EventSource behavior).

EDIT: I've also fixed a third spec deviation:

  • A blank "event" field should set the event type to an empty string, which should result in the actual dispatched event being of type "message".

readystatechange

version 1.0.0

state from -1 to 0 or 1, this version can't change...

Issue related to Firefox

It appears Firefox keeps loading the page and never actually completely loads the page. For instance, we have dropdown side menus that would properly drop down when this script is disabled. When enabled, they do no drop down. Any ideas?

Happens only with Firefox, Chrome and Safari work.

is sse.js on npm?

I couldn't find it on npm but it seems strange that it would not be published there.

application/json payload not working

As soon as I change Content-Type to application/json it stops to receive events from the server:

const payload = JSON.stringify({ prompt: "test" });
console.log(payload);

var subscription = new SSE("http://localhost:3002/api/process", {
        headers: { "Content-Type": "application/json" },
        payload: payload,
});

On backend, I'm using express with app.use(express.json()); and I see the payload with console.log(req.body.prompt);, so that's not the issue.

plain text also works as expected, but in that case I can't read req.body (also as expected).

So the problem is really that my callbacks aren't getting called.

Any ideas?

SSE Timeout after 20 mins

I see there's no timeout set from the client side, yet we're getting a SSE timeout after 20 mins. On the server side, we've no timeout.

Is there a way to increase this?

doesn't work with IE 11

I am using angular 9. I get a JS error (attached) on IE. My browserslist supports IE 9-11 and the target is set to es5 on tsconfig.json. I've included all the polyfills for IE on polyfill.ts.

Does this library work on IE 11 ?

image

image
image
image

How to catch payload in php?

I found out that the {payload: value} is not getting into the php file. can you give an example of how to catch POST value into the server via EventSource?

EventStream empty in Chrome Dev Tools?

Does anyone have any idea why Chrome isn't showing any events in the EventStream (Chrome DevTools/Network) when using POST with SSE? It might have to do with the non-standard use of POST of course, but it does actually show the EventStream tab, so it did correctly detect that we're dealing with a stream.

It's not important, because everything works just fine, but I was just curious if anyone was seeing the same thing and if there was some kind of fix.

Improve XmlHttpRequest error handling and connection states

I rewrote and simplified XHR connection/event handling mechanism for my purposes as proposed in TODO list. It seems error and abort listeners are not needed at all, if readystatechange does all the work in the end, but I also directly followed EventSource event model, where error event announces changes to ready state. Since my aim was to enable reconnecting, this is also part of the code here:

Please have a look and if this seems usable, I can create pull requests for some parts.

Detect close

Hi
Is it possible to detect when the SSE is closed?
Thanks!

Faulty line splitting logic for part detection with \r\n

This code does not work as intended: https://github.com/mpetazzoni/sse.js/blob/master/lib/sse.js#L114
data.split(/(\r\n|\r|\n){2}/g).forEach(function(part) {

If a stream uses \r\n as the separator, then this will not work because \r\n{2} is not matched but \r followed by \n is matched since you're doing an OR regex.

The regex needs to be /(\r\n\r\n|\r\r|\n\n)/g if you want this to behave as intended.

Otherwise you end up with this incoming data:

event: foo\r\n
data: bar\r\n
\r\n

being interpreted as a single event with no data as you get ['event: foo', '', 'data: bar']

Consequently I think the code at https://github.com/mpetazzoni/sse.js/blob/master/lib/sse.js#L141
chunk.split(/\n|\r\n|\r/).forEach(function(line) {

may need to be reviewed also.. it should probably prioritize \r\n to avoid such issues, i.e. \r\n|\n|\r

CORS error is missing in the preflight call

hello! we re getting a CORS error is missing, even though we added the nedded headers. after investigating we saw that in the preflight call the headers look like this:

OPTIONS /url... Access-Control-Request-Method: GET Access-Control-Request-Headers: access-control-allow-origin,authentication-source,authority,authorization,cache-control

if you see the Access-Control-Request-Headers become comma separated and the values are missing.. and the same happens in the normal call that follows.. Any idea if this has happened before or what we can do?

Thanks a lot!

License

Hi,
Thank you for releasing SSE.js! It's licensed under the Apache license, which requires including a copy of it, but is it acceptable to simply include the copyright notice included at the top of the file and a link to the GitHub repo?
Thanks!

License Issue

sse.js is copyrighted "all rights reserved", but the LICENSE file is an apache license.

Perhaps remove the sse.js copyright?

Corrupt json emitted in observeSSEMessages call

In my use of this library users are creating dynamic queries against our api. We then use SSE to stream to the client where the JSON is parsed and processed. However for certain queries corrupt 'data' events are being returned. ie a truncated message is being emitted which throws an exception when JSON.parse(message.data) is called. Using curl I am able to make the same query, output to a file, and verify that a well formed message was emitted from the same api. The truncation can occurring in the middle of a variable name, not just values, implying it's not escape character related. I can only assume that the corruption is occurring either in the SSE library, or chrome. Are you aware of any limitations to SSE or your library which could cause such truncation? I don't think this is memory related as chrome doesn't blow up and subsequent processing of uncorrupt rows can continue.

Low level compatibility?

I believe that the real EventSource sets the TCP keepalive of the connection so it can idle for longer. I'm not sure if this can be set just on the server side.

I wonder how using sse.js impacts the hard problems with streams, namely knowing if you are still connected, and reconnecting automatically.

I am looking for a nice way to do GraphQL subscriptions over HTTP/2, and websockets seem too complex. Much simpler to return an event stream for each POST subscription and letting HTTP/2 multiplex them. Automatic reconnecting would be wonderful.

How to close connection in sse.js

I have created connection with sse.js

let url = "https://XXXXXXXX/stream"; let requestJson = { "id" : "123456" }; var source = new SSE(url, { headers: { 'Content-Type': 'application/json', 'Authorization': 'wefergre735rb23v23j23vj3vh43v43', }, payload: JSON.stringify(requestJson), method: 'POST' });

And Get Stream data with below code

source.addEventListener('message', function(e) { payload = JSON.parse(e.data); }); source.stream();

And i want to close connection with below code but it's not working

source.close();

Thanks

withCredentials support

Hello,

EventSource supports { withCredentials: true } . Would it be possible to support this switch in sse.js as well? Or maybe it is possible to set it already?

Thanks,
Piotr

POST issue in SSE

Hi,
I try post method in sse but,unfortunately it shown error
"Response for preflight has invalid HTTP status code 403 ". Can you rectify this error.

screenshot from 2016-11-25 17 56 43
Thanks.

CORS error on post

I keep getting a CORS error, it doesn't even send the request to my backend on a different server. (I already have these set in my backend:
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")

I'm guessing its a CORS error from SSE itself

this._checkStreamClosed Is Receiving A Null xhr in Firefox Only

Line 165 of sse.js throws an error in firefox only because this.xhr is null and it's trying to get the readyState.

I think it's happening because line 192 sets this.xhr to null and that's triggering a readystatechange event which hits the checkStreamClosed callback. Why it's happening only in firefox, I have no clue.

Browser Web Developer Tool not showing the events in the EventStream tab

While I do receive the Server-Side events and the eventListeners pick them up, when I look for those events in the different browsers' (Chrome, Firefox) Web Developer Tools, the EventStream tab does not show them.

This is not a critical thing, but it would be useful for troubleshooting to be able to see these in the web developer tools.

See an example below:
image

Any way to catch HTTP error status code?

When there is a server-side error, it would be nice if there would be a way to catch the HTTP error code thrown.

Currently I can catch the error via an event listener like the following, but can't tell the HTTP status code (like 500 or 401, etc):
addEventListener("error", function(e) {

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.