routup 3.1.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,6 +20,7 @@ export declare enum HeaderName {
20
20
  CONTENT_LENGTH = "content-length",
21
21
  CONTENT_RANGE = "content-range",
22
22
  CONTENT_TYPE = "content-type",
23
+ CONNECTION = "connection",
23
24
  COOKIE = "cookie",
24
25
  ETag = "etag",
25
26
  HOST = "host",
@@ -34,6 +35,7 @@ export declare enum HeaderName {
34
35
  RETRY_AFTER = "retry-after",
35
36
  SET_COOKIE = "set-cookie",
36
37
  TRANSFER_ENCODING = "transfer-encoding",
38
+ X_ACCEL_BUFFERING = "x-accel-buffering",
37
39
  X_FORWARDED_HOST = "x-forwarded-host",
38
40
  X_FORWARDED_FOR = "x-forwarded-for",
39
41
  X_FORWARDED_PROTO = "x-forwarded-proto"
package/dist/index.cjs CHANGED
@@ -34,6 +34,7 @@ exports.HeaderName = void 0;
34
34
  HeaderName["CONTENT_LENGTH"] = "content-length";
35
35
  HeaderName["CONTENT_RANGE"] = "content-range";
36
36
  HeaderName["CONTENT_TYPE"] = "content-type";
37
+ HeaderName["CONNECTION"] = "connection";
37
38
  HeaderName["COOKIE"] = "cookie";
38
39
  HeaderName["ETag"] = "etag";
39
40
  HeaderName["HOST"] = "host";
@@ -48,6 +49,7 @@ exports.HeaderName = void 0;
48
49
  HeaderName["RETRY_AFTER"] = "retry-after";
49
50
  HeaderName["SET_COOKIE"] = "set-cookie";
50
51
  HeaderName["TRANSFER_ENCODING"] = "transfer-encoding";
52
+ HeaderName["X_ACCEL_BUFFERING"] = "x-accel-buffering";
51
53
  HeaderName["X_FORWARDED_HOST"] = "x-forwarded-host";
52
54
  HeaderName["X_FORWARDED_FOR"] = "x-forwarded-for";
53
55
  HeaderName["X_FORWARDED_PROTO"] = "x-forwarded-proto";
@@ -546,6 +548,10 @@ function getRequestHostName(req, options) {
546
548
  return index !== -1 ? hostname.substring(0, index) : hostname;
547
549
  }
548
550
 
551
+ function isRequestHTTP2(req) {
552
+ return typeof getRequestHeader(req, ':path') !== 'undefined' && typeof getRequestHeader(req, ':method') !== 'undefined';
553
+ }
554
+
549
555
  function getRequestIP(req, options) {
550
556
  options = options || {};
551
557
  let trustProxy;
@@ -761,6 +767,96 @@ function isResponseGone(res) {
761
767
  }
762
768
  return false;
763
769
  }
770
+ function setResponseGone(res, value) {
771
+ res[GoneSymbol] = value;
772
+ }
773
+
774
+ function serializeEventStreamMessage(message) {
775
+ let result = '';
776
+ if (message.id) {
777
+ result += `id: ${message.id}\n`;
778
+ }
779
+ if (message.event) {
780
+ result += `event: ${message.event}\n`;
781
+ }
782
+ if (typeof message.retry === 'number' && Number.isInteger(message.retry)) {
783
+ result += `retry: ${message.retry}\n`;
784
+ }
785
+ result += `data: ${message.data}\n\n`;
786
+ return result;
787
+ }
788
+
789
+ class EventStream {
790
+ open() {
791
+ this.response.req.on('close', ()=>this.end());
792
+ this.response.req.on('error', (err)=>{
793
+ this.emit('error', err);
794
+ this.end();
795
+ });
796
+ this.passThrough.on('data', (chunk)=>this.response.write(chunk));
797
+ this.passThrough.on('error', (err)=>{
798
+ this.emit('error', err);
799
+ this.end();
800
+ });
801
+ this.passThrough.on('close', ()=>this.end());
802
+ this.response.setHeader(exports.HeaderName.CONTENT_TYPE, 'text/event-stream');
803
+ this.response.setHeader(exports.HeaderName.CACHE_CONTROL, 'private, no-cache, no-store, no-transform, must-revalidate, max-age=0');
804
+ this.response.setHeader(exports.HeaderName.X_ACCEL_BUFFERING, 'no');
805
+ if (!isRequestHTTP2(this.response.req)) {
806
+ this.response.setHeader(exports.HeaderName.CONNECTION, 'keep-alive');
807
+ }
808
+ this.response.statusCode = 200;
809
+ }
810
+ write(message) {
811
+ if (typeof message === 'string') {
812
+ this.write({
813
+ data: message
814
+ });
815
+ return;
816
+ }
817
+ if (!this.passThrough.closed && this.passThrough.writable) {
818
+ this.passThrough.write(serializeEventStreamMessage(message));
819
+ }
820
+ }
821
+ end() {
822
+ if (this.flushed) return;
823
+ this.flushed = true;
824
+ if (!this.passThrough.closed) {
825
+ this.passThrough.end();
826
+ }
827
+ this.emit('close');
828
+ setResponseGone(this.response, true);
829
+ this.response.end();
830
+ }
831
+ on(event, listener) {
832
+ if (typeof this.eventHandlers[event] === 'undefined') {
833
+ this.eventHandlers[event] = [];
834
+ }
835
+ this.eventHandlers[event].push(listener);
836
+ }
837
+ emit(event, ...args) {
838
+ if (typeof this.eventHandlers[event] === 'undefined') {
839
+ return;
840
+ }
841
+ const listeners = this.eventHandlers[event].slice();
842
+ for(let i = 0; i < listeners.length; i++){
843
+ listeners[i].apply(this, args);
844
+ }
845
+ }
846
+ constructor(response){
847
+ this.response = response;
848
+ this.passThrough = new readableStream.PassThrough({
849
+ encoding: 'utf-8'
850
+ });
851
+ this.flushed = false;
852
+ this.eventHandlers = {};
853
+ this.open();
854
+ }
855
+ }
856
+
857
+ function createEventStream(response) {
858
+ return new EventStream(response);
859
+ }
764
860
 
765
861
  function appendResponseHeader(res, name, value) {
766
862
  let header = res.getHeader(name);
@@ -844,6 +940,78 @@ function setResponseHeaderContentType(res, input, ifNotExists) {
844
940
  }
845
941
  }
846
942
 
943
+ async function sendStream(res, stream, next) {
944
+ if (isWebStream(stream)) {
945
+ return stream.pipeTo(new WritableStream({
946
+ write (chunk) {
947
+ res.write(chunk);
948
+ }
949
+ })).then(()=>{
950
+ if (next) {
951
+ return next();
952
+ }
953
+ res.end();
954
+ return Promise.resolve();
955
+ }).catch((err)=>{
956
+ if (next) {
957
+ return next(err);
958
+ }
959
+ return Promise.reject(err);
960
+ });
961
+ }
962
+ return new Promise((resolve, reject)=>{
963
+ stream.on('open', ()=>{
964
+ stream.pipe(res);
965
+ });
966
+ /* istanbul ignore next */ stream.on('error', (err)=>{
967
+ if (next) {
968
+ Promise.resolve().then(()=>next(err)).then(()=>resolve()).catch((e)=>reject(e));
969
+ return;
970
+ }
971
+ res.end();
972
+ reject(err);
973
+ });
974
+ stream.on('close', ()=>{
975
+ if (next) {
976
+ Promise.resolve().then(()=>next()).then(()=>resolve()).catch((e)=>reject(e));
977
+ return;
978
+ }
979
+ res.end();
980
+ resolve();
981
+ });
982
+ });
983
+ }
984
+
985
+ async function sendWebBlob(res, blob) {
986
+ setResponseHeaderContentType(res, blob.type);
987
+ await sendStream(res, blob.stream());
988
+ }
989
+
990
+ async function sendWebResponse(res, webResponse) {
991
+ if (webResponse.redirected) {
992
+ res.setHeader(exports.HeaderName.LOCATION, webResponse.url);
993
+ }
994
+ if (webResponse.status) {
995
+ res.statusCode = webResponse.status;
996
+ }
997
+ if (webResponse.statusText) {
998
+ res.statusMessage = webResponse.statusText;
999
+ }
1000
+ webResponse.headers.forEach((value, key)=>{
1001
+ if (key === exports.HeaderName.SET_COOKIE) {
1002
+ res.appendHeader(key, splitCookiesString(value));
1003
+ } else {
1004
+ res.setHeader(key, value);
1005
+ }
1006
+ });
1007
+ if (webResponse.body) {
1008
+ await sendStream(res, webResponse.body);
1009
+ return Promise.resolve();
1010
+ }
1011
+ res.end();
1012
+ return Promise.resolve();
1013
+ }
1014
+
847
1015
  async function send(res, chunk) {
848
1016
  switch(typeof chunk){
849
1017
  case 'string':
@@ -855,11 +1023,28 @@ async function send(res, chunk) {
855
1023
  case 'number':
856
1024
  case 'object':
857
1025
  {
858
- if (buffer.Buffer.isBuffer(chunk)) {
859
- setResponseHeaderContentType(res, 'bin', true);
860
- } else if (chunk !== null) {
861
- chunk = JSON.stringify(chunk);
862
- setResponseHeaderContentType(res, 'application/json', true);
1026
+ if (chunk !== null) {
1027
+ if (chunk instanceof Error) {
1028
+ throw chunk;
1029
+ }
1030
+ if (isStream(chunk)) {
1031
+ await sendStream(res, chunk);
1032
+ return;
1033
+ }
1034
+ if (isWebBlob(chunk)) {
1035
+ await sendWebBlob(res, chunk);
1036
+ return;
1037
+ }
1038
+ if (isWebResponse(chunk)) {
1039
+ await sendWebResponse(res, chunk);
1040
+ return;
1041
+ }
1042
+ if (buffer.Buffer.isBuffer(chunk)) {
1043
+ setResponseHeaderContentType(res, 'bin', true);
1044
+ } else {
1045
+ chunk = JSON.stringify(chunk);
1046
+ setResponseHeaderContentType(res, 'application/json', true);
1047
+ }
863
1048
  }
864
1049
  break;
865
1050
  }
@@ -942,48 +1127,6 @@ function sendCreated(res, chunk) {
942
1127
  return send(res, chunk);
943
1128
  }
944
1129
 
945
- async function sendStream(res, stream, next) {
946
- if (isWebStream(stream)) {
947
- return stream.pipeTo(new WritableStream({
948
- write (chunk) {
949
- res.write(chunk);
950
- }
951
- })).then(()=>{
952
- if (next) {
953
- return next();
954
- }
955
- res.end();
956
- return Promise.resolve();
957
- }).catch((err)=>{
958
- if (next) {
959
- return next(err);
960
- }
961
- return Promise.reject(err);
962
- });
963
- }
964
- return new Promise((resolve, reject)=>{
965
- stream.on('open', ()=>{
966
- stream.pipe(res);
967
- });
968
- /* istanbul ignore next */ stream.on('error', (err)=>{
969
- if (next) {
970
- Promise.resolve().then(()=>next(err)).then(()=>resolve()).catch((e)=>reject(e));
971
- return;
972
- }
973
- res.end();
974
- reject(err);
975
- });
976
- stream.on('close', ()=>{
977
- if (next) {
978
- Promise.resolve().then(()=>next()).then(()=>resolve()).catch((e)=>reject(e));
979
- return;
980
- }
981
- res.end();
982
- resolve();
983
- });
984
- });
985
- }
986
-
987
1130
  async function sendFile(res, options, next) {
988
1131
  let stats;
989
1132
  try {
@@ -1073,36 +1216,6 @@ function sendRedirect(res, location, statusCode = 302) {
1073
1216
  return send(res, html);
1074
1217
  }
1075
1218
 
1076
- async function sendWebResponse(res, webResponse) {
1077
- if (webResponse.redirected) {
1078
- res.setHeader(exports.HeaderName.LOCATION, webResponse.url);
1079
- }
1080
- if (webResponse.status) {
1081
- res.statusCode = webResponse.status;
1082
- }
1083
- if (webResponse.statusText) {
1084
- res.statusMessage = webResponse.statusText;
1085
- }
1086
- webResponse.headers.forEach((value, key)=>{
1087
- if (key === exports.HeaderName.SET_COOKIE) {
1088
- res.appendHeader(key, splitCookiesString(value));
1089
- } else {
1090
- res.setHeader(key, value);
1091
- }
1092
- });
1093
- if (webResponse.body) {
1094
- await sendStream(res, webResponse.body);
1095
- return Promise.resolve();
1096
- }
1097
- res.end();
1098
- return Promise.resolve();
1099
- }
1100
-
1101
- async function sendWebBlob(res, blob) {
1102
- setResponseHeaderContentType(res, blob.type);
1103
- await sendStream(res, blob.stream());
1104
- }
1105
-
1106
1219
  function createResponse(request) {
1107
1220
  let output;
1108
1221
  let encoding;
@@ -1248,24 +1361,6 @@ function createResponse(request) {
1248
1361
  return writable;
1249
1362
  }
1250
1363
 
1251
- async function sendData(event, chunk) {
1252
- if (chunk instanceof Error) {
1253
- return Promise.reject(createError(chunk));
1254
- }
1255
- if (isStream(chunk)) {
1256
- await sendStream(event.response, chunk);
1257
- return Promise.resolve();
1258
- }
1259
- if (isWebBlob(chunk)) {
1260
- await sendWebBlob(event.response, chunk);
1261
- return Promise.resolve();
1262
- }
1263
- if (isWebResponse(chunk)) {
1264
- await sendWebResponse(event.response, chunk);
1265
- return Promise.resolve();
1266
- }
1267
- return send(event.response, chunk);
1268
- }
1269
1364
  function dispatch(event, target) {
1270
1365
  setRequestParams(event.request, event.params);
1271
1366
  setRequestMountPath(event.request, event.mountPath);
@@ -1299,7 +1394,7 @@ function dispatch(event, target) {
1299
1394
  handled = true;
1300
1395
  unsubscribe();
1301
1396
  if (!event.dispatched) {
1302
- await sendData(event, data);
1397
+ await send(event.response, data);
1303
1398
  }
1304
1399
  return true;
1305
1400
  };
@@ -2142,6 +2237,7 @@ class Router {
2142
2237
 
2143
2238
  exports.DispatchErrorEvent = DispatchErrorEvent;
2144
2239
  exports.DispatchEvent = DispatchEvent;
2240
+ exports.EventStream = EventStream;
2145
2241
  exports.Handler = Handler;
2146
2242
  exports.HandlerSymbol = HandlerSymbol;
2147
2243
  exports.PathMatcher = PathMatcher;
@@ -2151,6 +2247,7 @@ exports.appendResponseHeader = appendResponseHeader;
2151
2247
  exports.appendResponseHeaderDirective = appendResponseHeaderDirective;
2152
2248
  exports.coreHandler = coreHandler;
2153
2249
  exports.createError = createError;
2250
+ exports.createEventStream = createEventStream;
2154
2251
  exports.createNodeDispatcher = createNodeDispatcher;
2155
2252
  exports.createRawDispatcher = createRawDispatcher;
2156
2253
  exports.createRequest = createRequest;
@@ -2180,6 +2277,7 @@ exports.isHandlerConfig = isHandlerConfig;
2180
2277
  exports.isPath = isPath;
2181
2278
  exports.isPlugin = isPlugin;
2182
2279
  exports.isRequestCacheable = isRequestCacheable;
2280
+ exports.isRequestHTTP2 = isRequestHTTP2;
2183
2281
  exports.isResponseGone = isResponseGone;
2184
2282
  exports.matchRequestContentType = matchRequestContentType;
2185
2283
  exports.send = send;
@@ -2199,6 +2297,7 @@ exports.setRequestParams = setRequestParams;
2199
2297
  exports.setRequestRouterPath = setRequestRouterPath;
2200
2298
  exports.setResponseCacheHeaders = setResponseCacheHeaders;
2201
2299
  exports.setResponseContentTypeByFileName = setResponseContentTypeByFileName;
2300
+ exports.setResponseGone = setResponseGone;
2202
2301
  exports.setResponseHeaderAttachment = setResponseHeaderAttachment;
2203
2302
  exports.setResponseHeaderContentType = setResponseHeaderContentType;
2204
2303
  exports.transformHeaderToTuples = transformHeaderToTuples;