@@ -81,19 +81,23 @@ export class UWSHTTP extends DeepstreamPlugin implements DeepstreamHTTPService {
8181 this . listenSocket = token
8282 // handle options requests
8383 this . server . options ( '/*' , ( response : uws . HttpResponse , request : uws . HttpRequest ) => {
84+ const baseHeaders : Dictionary < string > = { } ;
8485 if ( ! this . pluginOptions . allowAllOrigins ) {
85- if ( ! this . verifyOrigin ( response , request ) ) {
86- return
86+ const corsValidationHeaders = this . getVerifiedOriginHeaders ( response , request ) ;
87+ if ( ! corsValidationHeaders ) { // Verification failed and response terminated
88+ return ;
8789 }
88- this . handleOptions ( response , request )
90+ Object . assign ( baseHeaders , corsValidationHeaders ) ;
91+ this . handleOptions ( response , request , baseHeaders ) ;
8992 } else {
90- response . cork ( ( ) => {
91- response . writeHeader ( 'Access-Control-Allow-Origin' , '*' )
92- this . handleOptions ( response , request )
93- } )
93+ baseHeaders [ 'Access-Control-Allow-Origin' ] = '*' ;
94+ this . handleOptions ( response , request , baseHeaders ) ;
9495 }
9596 } )
9697
98+ // Health check path uses GET, so CORS headers will be applied by registerGetPathPrefix
99+ // The handler calls response(null), which will use sendResponse.
100+ // sendResponse will correctly order writeStatus and add Content-Type: application/json.
97101 this . registerGetPathPrefix ( this . pluginOptions . healthCheckPath , ( meta : DeepstreamHTTPMeta , response : DeepstreamHTTPResponse ) => {
98102 response ( null )
99103 } )
@@ -134,27 +138,30 @@ export class UWSHTTP extends DeepstreamPlugin implements DeepstreamHTTPService {
134138
135139 const meta = { headers : this . getHeaders ( request ) , url : request . getUrl ( ) }
136140
141+ const accumulatedHeaders : Dictionary < string > = { } ;
142+
137143 if ( ! this . pluginOptions . allowAllOrigins ) {
138- if ( ! this . verifyOrigin ( response , request ) ) {
139- return
144+ const corsHeaders = this . getVerifiedOriginHeaders ( response , request ) ;
145+ if ( ! corsHeaders ) {
146+ return ; // Response already terminated
140147 }
148+ Object . assign ( accumulatedHeaders , corsHeaders ) ;
141149 } else {
142- response . cork ( ( ) => {
143- response . writeHeader ( 'Access-Control-Allow-Origin' , '*' )
144- } )
150+ accumulatedHeaders [ 'Access-Control-Allow-Origin' ] = '*' ;
145151 }
146152
147153 readJson ( response , ( body : any ) => {
148154 handler (
149155 body ,
150156 meta ,
151- this . sendResponse . bind ( this , response )
157+ ( err , data ) => this . sendResponse ( response , err , data , accumulatedHeaders )
152158 )
153159 } , ( code : number ) => {
154160 this . terminateResponse (
155161 response ,
156162 code ,
157- HTTPStatus [ `${ code } _MESSAGE` as keyof typeof HTTPStatus ] as string
163+ HTTPStatus [ `${ code } _MESSAGE` as keyof typeof HTTPStatus ] as string ,
164+ accumulatedHeaders
158165 )
159166 } , this . pluginOptions . maxMessageSize )
160167 } )
@@ -167,20 +174,23 @@ export class UWSHTTP extends DeepstreamPlugin implements DeepstreamHTTPService {
167174 this . services . logger . warn ( EVENT . ERROR , 'get request aborted' )
168175 } )
169176
177+ const accumulatedHeaders : Dictionary < string > = { } ;
178+
170179 if ( ! this . pluginOptions . allowAllOrigins ) {
171- if ( ! this . verifyOrigin ( response , request ) ) {
172- return
180+ const corsHeaders = this . getVerifiedOriginHeaders ( response , request ) ;
181+ if ( ! corsHeaders ) {
182+ return ; // Response already terminated
173183 }
184+ Object . assign ( accumulatedHeaders , corsHeaders ) ;
174185 } else {
175- response . cork ( ( ) => {
176- response . writeHeader ( 'Access-Control-Allow-Origin' , '*' )
177- } )
186+ accumulatedHeaders [ 'Access-Control-Allow-Origin' ] = '*' ;
178187 }
179188
180189 handler (
181190 { headers : this . getHeaders ( request ) , url : request . getUrl ( ) } ,
182- this . sendResponse . bind ( this , response )
183- )
191+ // Ensure the bound sendResponse uses these accumulatedHeaders
192+ ( err , data ) => this . sendResponse ( response , err , data , accumulatedHeaders )
193+ ) ;
184194 } )
185195 }
186196
@@ -247,13 +257,21 @@ export class UWSHTTP extends DeepstreamPlugin implements DeepstreamHTTPService {
247257 } as any )
248258 }
249259
250- private terminateResponse ( response : uws . HttpResponse , code : number , message ?: string ) {
260+ private terminateResponse ( response : uws . HttpResponse , code : number , message ?: string , additionalHeaders : Dictionary < string > = { } ) {
251261 response . cork ( ( ) => {
252- response . writeHeader ( 'Content-Type' , 'text/plain; charset=utf-8' )
253262 response . writeStatus ( code . toString ( ) )
254- if ( message ) {
263+ for ( const key in additionalHeaders ) {
264+ if ( additionalHeaders . hasOwnProperty ( key ) ) {
265+ response . writeHeader ( key , additionalHeaders [ key ] )
266+ }
267+ }
268+ // Only set Content-Type if there's a message body, and not for 204/304
269+ if ( message && code !== HTTPStatus . NO_CONTENT ) {
270+ response . writeHeader ( 'Content-Type' , 'text/plain; charset=utf-8' )
255271 response . end ( `${ message } \r\n\r\n` )
256272 } else {
273+ // For 204 NO_CONTENT or other cases without a message, just end.
274+ // uWS requires end() to be called.
257275 response . end ( )
258276 }
259277 } )
@@ -262,32 +280,37 @@ export class UWSHTTP extends DeepstreamPlugin implements DeepstreamHTTPService {
262280 private sendResponse (
263281 response : uws . HttpResponse ,
264282 err : { statusCode : number , message : string } | null ,
265- data : { result : string , body : object }
283+ data : { result : string , body : object } ,
284+ additionalHeaders : Dictionary < string > = { }
266285 ) : void {
267286 if ( err ) {
268287 const statusCode = err . statusCode || HTTPStatus . BAD_REQUEST
269- this . terminateResponse ( response , statusCode , err . message )
288+ this . terminateResponse ( response , statusCode , err . message , additionalHeaders )
270289 return
271290 }
291+
272292 response . cork ( ( ) => {
273- response . writeHeader ( 'Content-Type' , 'application/json; charset=utf-8' )
274293 response . writeStatus ( HTTPStatus . OK . toString ( ) )
294+ for ( const key in additionalHeaders ) {
295+ if ( additionalHeaders . hasOwnProperty ( key ) ) {
296+ response . writeHeader ( key , additionalHeaders [ key ] )
297+ }
298+ }
299+ response . writeHeader ( 'Content-Type' , 'application/json; charset=utf-8' )
275300 if ( data ) {
276301 response . end ( `${ JSON . stringify ( data ) } \r\n\r\n` )
277302 } else {
278303 response . end ( )
279304 }
280305 } )
281306 }
282-
283307 public getHeaders ( req : uws . HttpRequest ) {
284308 const headers : Dictionary < string > = { }
285309 for ( const wantedHeader of this . pluginOptions . headers ) {
286310 headers [ wantedHeader ] = req . getHeader ( wantedHeader ) . toString ( )
287311 }
288312 return headers
289313 }
290-
291314 private getSLLParams ( options : any ) {
292315 if ( ! options . ssl ) {
293316 return null
@@ -301,7 +324,6 @@ export class UWSHTTP extends DeepstreamPlugin implements DeepstreamHTTPService {
301324 if ( ! cert_file_name ) {
302325 this . services . logger . fatal ( EVENT . PLUGIN_INITIALIZATION_ERROR , 'Must also include ssl cert in order to use SSL' )
303326 }
304-
305327 return {
306328 key_file_name,
307329 cert_file_name,
@@ -312,63 +334,71 @@ export class UWSHTTP extends DeepstreamPlugin implements DeepstreamHTTPService {
312334 return null
313335 }
314336
315- private verifyOrigin ( response : uws . HttpResponse , request : uws . HttpRequest ) : boolean {
337+ // This method now either terminates the response or returns headers for the caller to use.
338+ private getVerifiedOriginHeaders ( response : uws . HttpResponse , request : uws . HttpRequest ) : Dictionary < string > | null {
316339 const requestOriginUrl = request . getHeader ( 'origin' ) as string || request . getHeader ( 'referer' ) as string
317340 const requestHostUrl = request . getHeader ( 'host' )
318341
319342 if ( this . pluginOptions . hostUrl && requestHostUrl !== this . pluginOptions . hostUrl ) {
320343 this . terminateResponse ( response , HTTPStatus . FORBIDDEN , 'Forbidden Host.' )
321- return false
344+ return null
322345 }
323346
324347 if ( this . pluginOptions . origins ! . indexOf ( requestOriginUrl ) === - 1 ) {
325348 if ( ! requestOriginUrl ) {
326349 this . terminateResponse (
327350 response ,
328351 HTTPStatus . FORBIDDEN ,
329- 'CORS is configured for this. All requests must set a valid "Origin" header.'
352+ 'CORS is configured for this. All requests must set a valid "Origin" header.' ,
353+ // No additional headers known at this point for the error response itself
330354 )
331355 } else {
332356 this . terminateResponse (
333357 response ,
334358 HTTPStatus . FORBIDDEN ,
335- `Origin "${ requestOriginUrl } " is forbidden.`
359+ `Origin "${ requestOriginUrl } " is forbidden.` ,
360+ // No additional headers known at this point for the error response itself
336361 )
337362 }
338- return false
363+ return null
339364 }
340365
341- // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
342- response . cork ( ( ) => {
343- response . writeHeader ( 'Access-Control-Allow-Origin' , requestOriginUrl )
344- response . writeHeader ( 'Access-Control-Allow-Credentials' , 'true' )
345- response . writeHeader ( 'Vary' , 'Origin' )
346- } )
347-
348- return true
366+ // If verification is successful, return the headers to be set by the caller.
367+ return {
368+ 'Access-Control-Allow-Origin' : requestOriginUrl ,
369+ 'Access-Control-Allow-Credentials' : 'true' , // Typically needed with specific origins
370+ 'Vary' : 'Origin' // Good practice when Access-Control-Allow-Origin is dynamic
371+ } ;
349372 }
350373
351- private handleOptions ( response : uws . HttpResponse , request : uws . HttpRequest ) : void {
374+ private handleOptions ( response : uws . HttpResponse , request : uws . HttpRequest , baseCorsHeaders : Dictionary < string > ) : void {
375+ const allHeadersForResponse = { ...baseCorsHeaders } ;
376+
352377 const requestMethod = request . getHeader ( 'access-control-request-method' ) as string | undefined
353378 if ( ! requestMethod ) {
354379 this . terminateResponse (
355380 response ,
356381 HTTPStatus . BAD_REQUEST ,
357- 'Missing header "Access-Control-Request-Method".'
382+ 'Missing header "Access-Control-Request-Method".' ,
383+ allHeadersForResponse // Pass along already determined CORS headers
358384 )
359385 return
360386 }
361387 if ( this . methods . indexOf ( requestMethod ) === - 1 ) {
362388 this . terminateResponse (
363389 response ,
364390 HTTPStatus . FORBIDDEN ,
365- `Method ${ requestMethod } is forbidden. Supported methods: ${ this . methodsStr } `
391+ `Method ${ requestMethod } is forbidden. Supported methods: ${ this . methodsStr } ` ,
392+ allHeadersForResponse
366393 )
367394 return
368395 }
369396
370397 const requestHeadersRaw = request . getHeader ( 'access-control-request-headers' ) as string | undefined
371398 if ( ! requestHeadersRaw ) {
399+ // Some browsers might not send this for simple requests, but for preflight it's expected.
400+ // Depending on strictness, this could be an error or allowed.
401+ // For now, let's assume it's required for a preflight OPTIONS.
372402 this . terminateResponse (
373403 response ,
374404 HTTPStatus . BAD_REQUEST ,
@@ -382,17 +412,16 @@ export class UWSHTTP extends DeepstreamPlugin implements DeepstreamHTTPService {
382412 this . terminateResponse (
383413 response ,
384414 HTTPStatus . FORBIDDEN ,
385- `Header ${ requestHeaders [ i ] } is forbidden. Supported headers: ${ this . headersStr } `
415+ `Header ${ requestHeaders [ i ] } is forbidden. Supported headers: ${ this . headersStr } ` ,
416+ allHeadersForResponse
386417 )
387418 return
388419 }
389420 }
390421
391- response . cork ( ( ) => {
392- response . writeHeader ( 'Access-Control-Allow-Methods' , this . methodsStr )
393- response . writeHeader ( 'Access-Control-Allow-Headers' , this . headersStr )
394- this . terminateResponse ( response , HTTPStatus . NO_CONTENT )
395- } )
422+ allHeadersForResponse [ 'Access-Control-Allow-Methods' ] = this . methodsStr ;
423+ allHeadersForResponse [ 'Access-Control-Allow-Headers' ] = this . headersStr ;
424+ this . terminateResponse ( response , HTTPStatus . NO_CONTENT , undefined , allHeadersForResponse ) ;
396425 }
397426}
398427
0 commit comments