Skip to content

Commit a5644af

Browse files
authored
Merge pull request #50 from QuantGeekDev/feat/error-handling
feat: improve error handling for sse
2 parents 4b47edb + ba1646b commit a5644af

File tree

2 files changed

+43
-7
lines changed

2 files changed

+43
-7
lines changed

src/transports/http/server.ts

+28-5
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export class HttpStreamTransport extends AbstractTransport {
8282
cors: { ...DEFAULT_CORS_CONFIG, ...(config.cors || {}) } as Required<NonNullable<HttpStreamTransportConfig['cors']>>,
8383
auth: config.auth ?? DEFAULT_HTTP_STREAM_CONFIG.auth,
8484
headers: config.headers ?? DEFAULT_HTTP_STREAM_CONFIG.headers,
85+
enableGetSse: config.enableGetSse ?? DEFAULT_HTTP_STREAM_CONFIG.enableGetSse,
8586
};
8687

8788
if (this._config.auth?.endpoints) {
@@ -95,6 +96,7 @@ export class HttpStreamTransport extends AbstractTransport {
9596
sessionEnabled: this._config.session.enabled,
9697
resumabilityEnabled: this._config.resumability.enabled,
9798
resumabilityStore: this._config.resumability.messageStoreType,
99+
enableGetSse: this._config.enableGetSse,
98100
authEnabled: !!this._config.auth,
99101
corsOrigin: this._config.cors.allowOrigin
100102
}, null, 2)}`);
@@ -108,7 +110,9 @@ export class HttpStreamTransport extends AbstractTransport {
108110
const corsConfig = this._config.cors;
109111
const headers: Record<string, string> = {
110112
"Access-Control-Allow-Origin": corsConfig.allowOrigin || req.headers.origin || '*',
111-
"Access-Control-Allow-Methods": corsConfig.allowMethods,
113+
"Access-Control-Allow-Methods": this._config.enableGetSse ?
114+
corsConfig.allowMethods :
115+
corsConfig.allowMethods.replace(/GET,?\s*/, ''),
112116
"Access-Control-Allow-Headers": corsConfig.allowHeaders,
113117
"Access-Control-Expose-Headers": [corsConfig.exposeHeaders, this._config.session.enabled ? this._config.session.headerName : null].filter(Boolean).join(', '),
114118
"Access-Control-Allow-Credentials": "true",
@@ -181,8 +185,13 @@ export class HttpStreamTransport extends AbstractTransport {
181185
case "GET": await this.handleGet(req, res); break;
182186
case "DELETE": await this.handleDelete(req, res); break;
183187
default:
184-
res.writeHead(405, { 'Content-Type': 'text/plain', 'Allow': 'GET, POST, DELETE, OPTIONS' }); res.end("Method Not Allowed");
185-
logger.warn(`Unsupported method: ${req.method}`); break;
188+
const allowHeader = this._config.enableGetSse ?
189+
'GET, POST, DELETE, OPTIONS' :
190+
'POST, DELETE, OPTIONS';
191+
res.writeHead(405, { 'Content-Type': 'text/plain', 'Allow': allowHeader });
192+
res.end("Method Not Allowed");
193+
logger.warn(`Unsupported method: ${req.method}`);
194+
break;
186195
}
187196
} catch (error: any) {
188197
logger.error(`Error processing ${req.method} ${url.pathname}: ${error.message}`);
@@ -375,10 +384,25 @@ export class HttpStreamTransport extends AbstractTransport {
375384

376385
private async handleGet(req: IncomingMessage, res: ServerResponse): Promise<void> {
377386
logger.debug(`Handling GET request to ${this._config.endpoint}`);
387+
388+
if (!this._config.enableGetSse) {
389+
logger.debug(`GET SSE is disabled. Returning 405 Method Not Allowed.`);
390+
res.writeHead(405, {
391+
'Content-Type': 'text/plain',
392+
'Allow': 'POST, DELETE, OPTIONS'
393+
});
394+
res.end("Method Not Allowed: GET-based SSE is disabled on this server.");
395+
return;
396+
}
397+
378398
const acceptHeader = req.headers.accept || '';
379-
if (!acceptHeader.includes(SSE_CONTENT_TYPE) && !acceptHeader.includes('*/*')) throw this.httpError(406, `Not Acceptable: GET requires Accept header including ${SSE_CONTENT_TYPE}`);
399+
if (!acceptHeader.includes(SSE_CONTENT_TYPE) && !acceptHeader.includes('*/*')) {
400+
throw this.httpError(406, `Not Acceptable: GET requires Accept header including ${SSE_CONTENT_TYPE}`);
401+
}
380402

403+
const lastEventId = getRequestHeader(req.headers, "Last-Event-ID");
381404
const sessionIdHeader = getRequestHeader(req.headers, this._config.session.headerName);
405+
382406
let session: SessionData | undefined;
383407

384408
if (this._config.session.enabled && sessionIdHeader) {
@@ -393,7 +417,6 @@ export class HttpStreamTransport extends AbstractTransport {
393417
await this.handleAuthentication(req, res, `GET (sessions disabled)`, undefined);
394418
}
395419

396-
const lastEventId = getRequestHeader(req.headers, "Last-Event-ID");
397420
if (lastEventId && !this._config.resumability.enabled) {
398421
logger.warn(`Client sent Last-Event-ID (${lastEventId}) but resumability is disabled.`);
399422
}

src/transports/http/types.ts

+15-2
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,13 @@ export interface HttpStreamTransportConfig {
9999
/**
100100
* CORS configuration
101101
*/
102-
cors?: CORSConfig;
102+
cors?: {
103+
allowOrigin?: string;
104+
allowMethods?: string;
105+
allowHeaders?: string;
106+
exposeHeaders?: string;
107+
maxAge?: string;
108+
};
103109

104110
/**
105111
* Authentication configuration
@@ -141,17 +147,23 @@ export interface HttpStreamTransportConfig {
141147
historyDuration?: number;
142148
messageStoreType?: 'connection' | 'global';
143149
};
150+
151+
/**
152+
* Whether to enable GET SSE. Default: true
153+
*/
154+
enableGetSse?: boolean;
144155
}
145156

146157
/**
147158
* Internal configuration type with required fields
148159
*/
149160
export type HttpStreamTransportConfigInternal = Required<Omit<HttpStreamTransportConfig, 'headers' | 'auth' | 'cors' | 'session' | 'resumability'>> & {
161+
cors: Required<NonNullable<HttpStreamTransportConfig['cors']>>;
150162
session: Required<NonNullable<HttpStreamTransportConfig['session']>>;
151163
resumability: Required<NonNullable<HttpStreamTransportConfig['resumability']>>;
152-
cors: Required<NonNullable<CORSConfig>>;
153164
headers?: Record<string, string>;
154165
auth?: AuthConfig;
166+
enableGetSse: boolean;
155167
};
156168

157169
/**
@@ -173,6 +185,7 @@ export const DEFAULT_HTTP_STREAM_CONFIG: HttpStreamTransportConfigInternal = {
173185
historyDuration: 300000,
174186
messageStoreType: 'global',
175187
},
188+
enableGetSse: true,
176189
cors: {
177190
allowOrigin: "*",
178191
allowMethods: "GET, POST, DELETE, OPTIONS",

0 commit comments

Comments
 (0)