Skip to content

Commit

Permalink
fix: Add compatibility mode option to disable Content-Type header che…
Browse files Browse the repository at this point in the history
…cking

Add relevant information to migration guide so users are aware of the breaking change.
  • Loading branch information
alh-solidatus committed Jan 27, 2025
1 parent 340ec41 commit 2791b8c
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 4 deletions.
12 changes: 12 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,15 @@ The default reconnect timeout is now 3 seconds - up from 1 second in v1/v2. This
Redirect handling now matches Chrome/Safari. On disconnects, we will always reconnect to the _original_ URL. In v1/v2, only HTTP 307 would reconnect to the original, while 301 and 302 would both redirect to the _destination_.

While the _ideal_ behavior would be for 301 and 308 to reconnect to the redirect _destination_, and 302/307 to reconnect to the _original_ URL, this is not possible to do cross-platform (cross-origin requests in browsers do not allow reading location headers, and redirect handling will have to be done manually).

#### Strict checking of Content-Type header

The Content-Type header is now checked. It's value must be `text/event-stream`, and the connection will be failed otherwise.

To maintain the previous behaviour, set `strictContentType: false` when constructing EventSources:

```ts
const es = new EventSource('https://my-server.com/sse', {
strictContentType: false
})
```
19 changes: 15 additions & 4 deletions src/EventSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ export class EventSource extends EventTarget {
this.#reconnectInterval = 3000
this.#fetch = eventSourceInitDict?.fetch ?? globalThis.fetch
this.#withCredentials = eventSourceInitDict?.withCredentials ?? false
this.#strictContentType = eventSourceInitDict?.strictContentType ?? true

this.#connect()
}
Expand Down Expand Up @@ -253,6 +254,13 @@ export class EventSource extends EventTarget {
*/
#withCredentials: boolean

/**
* Whether to enforce `Content-Type: text/event-stream` header presence on responses.
*
* @internal
*/
#strictContentType: boolean

/**
* The fetch implementation to use
*
Expand Down Expand Up @@ -373,10 +381,13 @@ export class EventSource extends EventTarget {
}

// [spec] …or if res's `Content-Type` is not `text/event-stream`, then fail the connection.
const contentType = headers.get('content-type') || ''
if (!contentType.startsWith('text/event-stream')) {
this.#failConnection('Invalid content type, expected "text/event-stream"', status)
return
// ... excepting if switched off by our compatibility-mode option.
if (this.#strictContentType) {
const contentType = headers.get('content-type') || ''
if (!contentType.startsWith('text/event-stream')) {
this.#failConnection('Invalid content type, expected "text/event-stream"', status)
return
}
}

// [spec] …if the readyState attribute is set to a value other than CLOSED…
Expand Down
6 changes: 6 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ export interface EventSourceInit {
*/
withCredentials?: boolean

/**
* A boolean value, defaulting to `true`, indicating if strict enforcement of 'Content-Type' header should be enabled.
* If this is true, connections will fail and not reconnect unless a `Content-Type: text/event-stream` header is returned.
*/
strictContentType?: boolean

/**
* Optional fetch implementation to use. Defaults to `globalThis.fetch`.
* Can also be used for advanced use cases like mocking, proxying, custom certs etc.
Expand Down

0 comments on commit 2791b8c

Please sign in to comment.