@@ -2,6 +2,8 @@ const express = require('express');
22const { HTTP } = require ( "cloudevents" ) ;
33const jsonata = require ( 'jsonata' ) ;
44const fs = require ( 'node:fs' ) ;
5+ const http = require ( 'http' ) ;
6+ const https = require ( 'https' ) ;
57const fsPromises = require ( 'node:fs' ) . promises ;
68const { buffer} = require ( 'node:stream/consumers' ) ;
79
@@ -31,10 +33,18 @@ const {ZipkinExporter} = require('@opentelemetry/exporter-zipkin');
3133const { W3CTraceContextPropagator, CompositePropagator} = require ( "@opentelemetry/core" ) ;
3234const { B3InjectEncoding, B3Propagator} = require ( "@opentelemetry/propagator-b3" ) ;
3335
34- const port = process . env . PORT = process . env . PORT || 8080 ;
36+ const httpPort = process . env . HTTP_PORT || 8080 ;
37+ const httpsPort = process . env . HTTPS_PORT || 8443 ;
38+ const httpsCertPath = process . env . HTTPS_CERT_PATH ;
39+ const httpsKeyPath = process . env . HTTPS_KEY_PATH ;
40+ const disableHTTPServer = process . env . DISABLE_HTTP_SERVER === 'true' ;
3541const k_sink = process . env . K_SINK || undefined ;
3642const jsonata_transform_file_name = process . env . JSONATA_TRANSFORM_FILE_NAME || undefined ;
3743
44+ if ( disableHTTPServer && ( ! httpsKeyPath || ! fs . existsSync ( httpsKeyPath ) || ! httpsCertPath || ! fs . existsSync ( httpsCertPath ) ) ) {
45+ throw new Error ( `HTTP and HTTPS server are both disabled, disableHTTPServer='${ disableHTTPServer } ', httpsKeyPath='${ httpsKeyPath } ', httpsCertPath=${ httpsCertPath } ` ) ;
46+ }
47+
3848// Allow transforming the response received by the endpoint defined by K_SINK
3949const jsonata_response_transform_file_name = process . env . JSONATA_RESPONSE_TRANSFORM_FILE_NAME || undefined ;
4050
@@ -250,7 +260,7 @@ app.post("/", async (req, res) => {
250260 const k_sink_url = new URL ( k_sink )
251261 const kSinkSendSpan = tracer . startSpan ( 'k_sink_send' , {
252262 attributes : {
253- [ ATTR_URL_SCHEME ] : k_sink_url . protocol . endsWith ( ':' ) ? k_sink_url . protocol . substring ( 0 , k_sink_url . protocol . length - 1 ) : k_sink_url . protocol ,
263+ [ ATTR_URL_SCHEME ] : k_sink_url . protocol . endsWith ( ':' ) ? k_sink_url . protocol . substring ( 0 , k_sink_url . protocol . length - 1 ) : k_sink_url . protocol ,
254264 [ ATTR_SERVER_ADDRESS ] : k_sink_url . hostname ,
255265 [ ATTR_SERVER_PORT ] : k_sink_url . port ,
256266 }
@@ -267,6 +277,7 @@ app.post("/", async (req, res) => {
267277 headers : k_sink_request_headers ,
268278 body : JSON . stringify ( transformed ) ,
269279 redirect : 'error' ,
280+ signal : req . signal ,
270281 } )
271282 kSinkSendSpan . setAttributes ( {
272283 'http.status_code' : result . status ,
@@ -384,19 +395,71 @@ app.get('/readyz', (req, res) => {
384395
385396app . disable ( 'x-powered-by' ) ;
386397
387- const server = app . listen ( port , ( ) => {
388- console . log ( `Jsonata server listening on port ${ port } ` )
389- } )
398+ let httpServer = null
399+ let httpsServer = null
400+
401+ if ( ! disableHTTPServer ) {
402+ httpServer = http . createServer ( app )
403+ . listen ( httpPort , ( ) => {
404+ console . log ( `Jsonata HTTP server listening on port ${ httpPort } ` )
405+ } )
406+ }
407+
408+ if ( httpsCertPath && httpsKeyPath ) {
409+ const httpsServerOptions = {
410+ cert : fs . readFileSync ( httpsCertPath ) ,
411+ key : fs . readFileSync ( httpsKeyPath ) ,
412+
413+ // TLS Versions
414+ minVersion : 'TLSv1.2' , // Minimum TLS version (avoid older, less secure protocols)
415+ maxVersion : 'TLSv1.3' , // Maximum TLS version
416+
417+ // Cipher Suites
418+ ciphers : [
419+ 'ECDHE-ECDSA-AES128-GCM-SHA256' ,
420+ 'ECDHE-RSA-AES128-GCM-SHA256' ,
421+ 'ECDHE-ECDSA-AES256-GCM-SHA384' ,
422+ 'ECDHE-RSA-AES256-GCM-SHA384' ,
423+ 'ECDHE-ECDSA-CHACHA20-POLY1305' ,
424+ 'ECDHE-RSA-CHACHA20-POLY1305' ,
425+ 'DHE-RSA-AES128-GCM-SHA256' ,
426+ 'DHE-RSA-AES256-GCM-SHA384'
427+ ] . join ( ':' ) ,
428+
429+ // Attempt to use server cipher suite preference instead of clients
430+ honorCipherOrder : true ,
431+
432+ // Additional security options
433+ secureOptions :
434+ require ( 'constants' ) . SSL_OP_NO_TLSv1 |
435+ require ( 'constants' ) . SSL_OP_NO_TLSv1_1 |
436+ require ( 'constants' ) . SSL_OP_NO_COMPRESSION ,
437+ }
438+
439+ httpsServer = https . createServer ( httpsServerOptions , app )
440+ . listen ( httpsPort , ( ) => {
441+ console . log ( `Jsonata HTTPS server listening on port ${ httpsPort } ` )
442+ } )
443+ }
390444
391445process . on ( 'SIGINT' , shutDown ) ;
392446process . on ( 'SIGTERM' , shutDownNow ) ;
393447
394448let connections = [ ] ;
395449
396- server . on ( 'connection' , connection => {
397- connections . push ( connection ) ;
398- connection . on ( 'close' , ( ) => connections = connections . filter ( curr => curr !== connection ) ) ;
399- } ) ;
450+ if ( httpServer ) {
451+ httpServer . on ( 'connection' , connection => {
452+ connections . push ( connection ) ;
453+ connection . on ( 'close' , ( ) => connections = connections . filter ( curr => curr !== connection ) ) ;
454+ } ) ;
455+ }
456+
457+ if ( httpsServer ) {
458+ httpsServer . on ( 'connection' , connection => {
459+ connections . push ( connection ) ;
460+ connection . on ( 'close' , ( ) => connections = connections . filter ( curr => curr !== connection ) ) ;
461+ } ) ;
462+ }
400463
401464function shutDown ( ) {
402465 console . log ( 'Received interrupt signal, shutting down gracefully' ) ;
@@ -413,14 +476,30 @@ function shutDown() {
413476function shutDownNow ( ) {
414477 console . log ( 'Shutting down gracefully' ) ;
415478
416- server . close ( ( ) => {
417- console . log ( 'Closed out remaining connections' ) ;
479+ const closePromises = [ ]
480+ if ( httpServer ) {
481+ closePromises . push ( new Promise ( ( resolve , _ ) => {
482+ httpServer . close ( ( ) => {
483+ console . log ( 'Closed out remaining HTTP connections' ) ;
484+ resolve ( )
485+ } ) ;
486+ } ) )
487+ }
488+ if ( httpsServer ) {
489+ closePromises . push ( new Promise ( ( resolve , _ ) => {
490+ httpsServer . close ( ( ) => {
491+ console . log ( 'Closed out remaining HTTPS connections' ) ;
492+ resolve ( )
493+ } ) ;
494+ } ) )
495+ }
418496
497+ Promise . all ( closePromises ) . then ( ( ) => {
419498 console . log ( 'Shutting down tracing...' ) ;
420499 batchSpanProcessor . shutdown ( ) . then ( ( ) => {
421500 process . exit ( 0 ) ;
422501 } ) ;
423- } ) ;
502+ } )
424503
425504 setTimeout ( ( ) => {
426505 console . error ( 'Could not close connections in time, forcefully shutting down' ) ;
0 commit comments