vortez 5.0.0-dev.18 → 5.0.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.
Files changed (136) hide show
  1. package/.gitignore +11 -4
  2. package/README.md +699 -177
  3. package/build/Template/Compiler.d.ts +70 -0
  4. package/build/Template/Compiler.js +135 -0
  5. package/build/Template/Compiler.js.map +1 -0
  6. package/build/Template/StreamCompiler.d.ts +16 -0
  7. package/build/Template/StreamCompiler.js +54 -0
  8. package/build/Template/StreamCompiler.js.map +1 -0
  9. package/build/Template/Template.d.ts +49 -0
  10. package/build/Template/Template.js +77 -0
  11. package/build/Template/Template.js.map +1 -0
  12. package/build/Vortez.d.ts +2 -1
  13. package/build/Vortez.js +2 -1
  14. package/build/Vortez.js.map +1 -1
  15. package/build/beta/JwtManager/JwtError.d.ts +8 -0
  16. package/build/beta/JwtManager/JwtError.js +10 -0
  17. package/build/beta/JwtManager/JwtError.js.map +1 -0
  18. package/build/beta/JwtManager/JwtManager.d.ts +3 -0
  19. package/build/beta/JwtManager/JwtManager.js +24 -7
  20. package/build/beta/JwtManager/JwtManager.js.map +1 -1
  21. package/build/server/BodyParser.d.ts +6 -0
  22. package/build/server/BodyParser.js +18 -3
  23. package/build/server/BodyParser.js.map +1 -1
  24. package/build/server/Request.d.ts +6 -4
  25. package/build/server/Request.js +15 -10
  26. package/build/server/Request.js.map +1 -1
  27. package/build/server/Response.d.ts +25 -10
  28. package/build/server/Response.js +161 -75
  29. package/build/server/Response.js.map +1 -1
  30. package/build/server/Server.d.ts +4 -4
  31. package/build/server/Server.js +34 -11
  32. package/build/server/Server.js.map +1 -1
  33. package/build/server/ServerDebug.d.ts +10 -1
  34. package/build/server/ServerDebug.js +85 -17
  35. package/build/server/ServerDebug.js.map +1 -1
  36. package/build/server/config/Config.d.ts +276 -47
  37. package/build/server/config/Config.js +70 -47
  38. package/build/server/config/Config.js.map +1 -1
  39. package/build/server/config/{ConfigLoader.d.ts → Loader.d.ts} +4 -5
  40. package/build/server/config/{ConfigLoader.js → Loader.js} +8 -11
  41. package/build/server/config/Loader.js.map +1 -0
  42. package/build/server/router/Router.d.ts +88 -31
  43. package/build/server/router/Router.js +113 -51
  44. package/build/server/router/Router.js.map +1 -1
  45. package/build/server/router/algorithm/Algorithm.d.ts +39 -0
  46. package/build/server/router/algorithm/Algorithm.js +20 -0
  47. package/build/server/router/algorithm/Algorithm.js.map +1 -0
  48. package/build/server/router/algorithm/FIFO.d.ts +15 -0
  49. package/build/server/router/algorithm/FIFO.js +24 -0
  50. package/build/server/router/algorithm/FIFO.js.map +1 -0
  51. package/build/server/router/algorithm/Tree.d.ts +38 -0
  52. package/build/server/router/algorithm/Tree.js +126 -0
  53. package/build/server/router/algorithm/Tree.js.map +1 -0
  54. package/build/server/router/middleware/HttpMiddleware.js +1 -1
  55. package/build/server/router/middleware/HttpMiddleware.js.map +1 -1
  56. package/build/server/router/middleware/WsMiddleware.js +1 -1
  57. package/build/server/router/middleware/WsMiddleware.js.map +1 -1
  58. package/build/server/security/PathSecurity.d.ts +45 -0
  59. package/build/server/security/PathSecurity.js +108 -0
  60. package/build/server/security/PathSecurity.js.map +1 -0
  61. package/build/server/websocket/Websocket.js +4 -1
  62. package/build/server/websocket/Websocket.js.map +1 -1
  63. package/build/utilities/ConsoleUI.d.ts +2 -1
  64. package/build/utilities/ConsoleUI.js +2 -1
  65. package/build/utilities/ConsoleUI.js.map +1 -1
  66. package/build/utilities/DebugUI.d.ts +1 -1
  67. package/build/utilities/DebugUI.js +1 -1
  68. package/build/utilities/Encoding.d.ts +22 -0
  69. package/build/utilities/Encoding.js +26 -0
  70. package/build/utilities/Encoding.js.map +1 -0
  71. package/build/utilities/Env.js +7 -2
  72. package/build/utilities/Env.js.map +1 -1
  73. package/build/utilities/File.d.ts +10 -0
  74. package/build/utilities/File.js +19 -0
  75. package/build/utilities/File.js.map +1 -0
  76. package/build/utilities/Flatten.d.ts +73 -0
  77. package/build/utilities/Flatten.js +76 -0
  78. package/build/utilities/Flatten.js.map +1 -0
  79. package/build/utilities/Object.d.ts +16 -0
  80. package/build/utilities/Object.js +48 -0
  81. package/build/utilities/Object.js.map +1 -0
  82. package/build/utilities/Path.d.ts +31 -11
  83. package/build/utilities/Path.js +48 -15
  84. package/build/utilities/Path.js.map +1 -1
  85. package/build/utilities/Time.d.ts +21 -0
  86. package/build/utilities/Time.js +25 -0
  87. package/build/utilities/Time.js.map +1 -0
  88. package/build/utilities/Utilities.d.ts +50 -92
  89. package/build/utilities/Utilities.js +56 -71
  90. package/build/utilities/Utilities.js.map +1 -1
  91. package/build/utilities/schema/Introspection.d.ts +24 -0
  92. package/build/utilities/schema/Introspection.js +87 -0
  93. package/build/utilities/schema/Introspection.js.map +1 -0
  94. package/build/utilities/schema/JSONSchema.d.ts +68 -0
  95. package/build/utilities/schema/JSONSchema.js +13 -0
  96. package/build/utilities/schema/JSONSchema.js.map +1 -0
  97. package/build/utilities/schema/Schema.d.ts +253 -0
  98. package/build/utilities/schema/Schema.js +241 -0
  99. package/build/utilities/schema/Schema.js.map +1 -0
  100. package/build/utilities/schema/SchemaError.d.ts +10 -0
  101. package/build/utilities/schema/SchemaError.js +13 -0
  102. package/build/utilities/schema/SchemaError.js.map +1 -0
  103. package/build/utilities/schema/Validator.d.ts +94 -0
  104. package/build/utilities/schema/Validator.js +246 -0
  105. package/build/utilities/schema/Validator.js.map +1 -0
  106. package/changes.md +4 -0
  107. package/docs/ARCHITECTURE.md +142 -0
  108. package/global/style/template/error.css +29 -0
  109. package/global/style/template/folder.css +79 -0
  110. package/global/{Style/Template/Template.css → style/template/template.css} +60 -68
  111. package/global/template/error.vhtml +29 -0
  112. package/global/template/folder.vhtml +54 -0
  113. package/package.json +2 -2
  114. package/build/Template.d.ts +0 -46
  115. package/build/Template.js +0 -81
  116. package/build/Template.js.map +0 -1
  117. package/build/server/config/ConfigLoader.js.map +0 -1
  118. package/build/server/config/ConfigValidator.d.ts +0 -71
  119. package/build/server/config/ConfigValidator.js +0 -131
  120. package/build/server/config/ConfigValidator.js.map +0 -1
  121. package/examples/in-docs.js +0 -96
  122. package/global/Style/Template/Error.css +0 -30
  123. package/global/Style/Template/Folder.css +0 -77
  124. package/global/Template/Error.vhtml +0 -29
  125. package/global/Template/Folder.vhtml +0 -41
  126. package/tests/Template/template.js +0 -18
  127. package/tests/Template/template.txt +0 -13
  128. package/tests/Template/template.vhtml +0 -23
  129. package/tests/debug.js +0 -34
  130. package/tests/jwtManager/jwtManager.js +0 -342
  131. package/tests/test.js +0 -131
  132. package/tests/test.vhtml +0 -14
  133. package/tests/utilities.js +0 -28
  134. package/tests/websocket.vhtml +0 -86
  135. /package/global/{Source/Logo_960.png → source/logo_960.png} +0 -0
  136. /package/global/{Source/Logo_SM_960.png → source/logo_SM_960.png} +0 -0
@@ -41,8 +41,9 @@ export class BodyParser {
41
41
  this.httpRequest.on('end', () => resolve(Buffer.concat(chunks)));
42
42
  this.httpRequest.on('error', (error) => reject(Error('fail parsing request body', { cause: error })));
43
43
  this.httpRequest.on('data', (chunk) => {
44
- if (chunks.length > 1e+8)
45
- return void this.httpRequest.destroy();
44
+ const size = chunks.reduce((total, current) => total + current.length, 0) + chunk.length;
45
+ if (size > 1e+8)
46
+ return void this.httpRequest.destroy(Error('Request body is too large'));
46
47
  chunks.push(chunk);
47
48
  });
48
49
  });
@@ -90,7 +91,8 @@ export class BodyParser {
90
91
  const fragments = decoded.split('&');
91
92
  fragments.forEach((pair) => {
92
93
  const [key, value] = pair.split('=');
93
- content[decodeURIComponent(key)] = decodeURIComponent(value);
94
+ const normalizedKey = this.safeDecodeURIComponent(key.replace(/\+/g, ' '));
95
+ content[normalizedKey] = this.safeDecodeURIComponent((value ?? '').replace(/\+/g, ' '));
94
96
  });
95
97
  return {
96
98
  mimeType: 'application/x-www-form-urlencoded',
@@ -157,6 +159,19 @@ export class BodyParser {
157
159
  return null;
158
160
  return result[1];
159
161
  }
162
+ /**
163
+ * Safely decodes a URI component, returning the original value if decoding fails.
164
+ * @param value - The value to decode.
165
+ * @returns The decoded value or the original if decoding fails.
166
+ */
167
+ safeDecodeURIComponent(value) {
168
+ try {
169
+ return decodeURIComponent(value);
170
+ }
171
+ catch {
172
+ return value;
173
+ }
174
+ }
160
175
  }
161
176
  export default BodyParser;
162
177
  //# sourceMappingURL=BodyParser.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BodyParser.js","sourceRoot":"","sources":["../../src/server/BodyParser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,MAAM,OAAO,UAAU;IAGI;IACA;IAHf,MAAM,CAAC,uBAAuB,GAAG,oHAAoH,CAAC;IAC9J,YACuB,OAAiC,EACjC,WAAiC;QADjC,YAAO,GAAP,OAAO,CAA0B;QACjC,gBAAW,GAAX,WAAW,CAAsB;IACrD,CAAC;IACJ;;;;OAIA;IACO,KAAK,CAAC,KAAK;QACd,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC;QACzD,IAAI,CAAC,WAAW;YAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QACxE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QACtC,MAAM,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3D,QAAO,MAAM,EAAE,CAAC;YACrB,KAAK,YAAY,CAAC,CAAC,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YACjD,KAAK,kBAAkB,CAAC,CAAC,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YACvD,KAAK,mCAAmC,CAAC,CAAC,OAAO,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAC9E,KAAK,qBAAqB,CAAC,CAAC,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACvE,OAAO,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QACrE,CAAC;IACC,CAAC;IACD;;;;OAIA;IACQ,KAAK,CAAC,WAAW;QACrB,MAAM,MAAM,GAAU,EAAE,CAAC;QACzB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACnC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACjE,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,2BAA2B,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;YACtG,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;gBAClC,IAAG,MAAM,CAAC,MAAM,GAAG,IAAI;oBAAE,OAAO,KAAK,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;gBAChE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACvB,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;IACP,CAAC;IACD;;;;OAIA;IACQ,WAAW,CAAC,IAAY;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACpC,OAAO;YACH,QAAQ,EAAE,YAAY;YACtB,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,IAAI;SACd,CAAC;IACN,CAAC;IACD;;;;;OAKA;IACQ,WAAW,CAAC,IAAY;QAC5B,IAAI,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YACpC,OAAO;gBACH,QAAQ,EAAE,kBAAkB;gBAC5B,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;gBACzB,KAAK,EAAE,IAAI;aACd,CAAC;QACN,CAAC;QAAC,OAAM,KAAK,EAAE,CAAC;YAAC,MAAM,IAAI,KAAK,CAAC,2BAA2B,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAAC,CAAC;IACtF,CAAC;IACD;;;;OAIA;IACQ,iBAAiB,CAAC,IAAY;QAClC,MAAM,OAAO,GAA4B,EAAE,CAAC;QAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACrC,SAAS,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;YACvB,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACrC,OAAO,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACjE,CAAC,CAAC,CAAC;QACG,OAAO;YACH,QAAQ,EAAE,mCAAmC;YAC7C,OAAO,EAAE,OAAO;YAChB,KAAK,EAAE,IAAI;SACd,CAAA;IACL,CAAC;IACD;;;;;;OAMA;IACQ,eAAe,CAAC,IAAY,EAAE,UAAoB,EAAE;QACxD,MAAM,OAAO,GAA4B,EAAE,CAAC;QAC5C,MAAM,KAAK,GAA6B,EAAE,CAAC;QACjD,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,mBAAmB,EAAE,CAAC,MAAM,EAAE,QAAgB,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC;QACxG,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACjF,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/C,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAC3C,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;YAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YAC7C,IAAI,IAAI,IAAI,IAAI;gBAAE,OAAO;YACzB,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI;gBAAE,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;YACzF,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG;gBAClB,IAAI,EAAE,IAAI,CAAC,QAAQ;gBACnB,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;gBACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,SAAS;gBACpC,OAAO,EAAE,IAAI,CAAC,OAAO;aACxB,CAAA;QACL,CAAC,CAAC,CAAC;QACH,OAAO;YACH,QAAQ,EAAE,qBAAqB;YAC/B,OAAO,EAAE,KAAK;SACjB,CAAC;IACN,CAAC;IACD;;;;OAIA;IACQ,gBAAgB,CAAC,IAAY;QACjC,MAAM,IAAI,GAAG,UAAU,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAClE,IAAI,IAAI,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;QAC9B,MAAM,CAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,IAAI,EAAE,QAAQ,GAAG,IAAI,EAAE,OAAO,GAAG,EAAE,CAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACxF,OAAO;YACH,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAC,QAAQ,EAAE;YACzD,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC;YAC9C,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI;YACtF,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI;SACzF,CAAC;IACN,CAAC;IACJ;;;OAGG;IACK,aAAa,CAAC,IAAY;QACjC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAC9D,IAAI,MAAM,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;QAChC,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;;AAuEF,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"BodyParser.js","sourceRoot":"","sources":["../../src/server/BodyParser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,MAAM,OAAO,UAAU;IAGI;IACA;IAHf,MAAM,CAAC,uBAAuB,GAAG,oHAAoH,CAAC;IAC9J,YACuB,OAAiC,EACjC,WAAiC;QADjC,YAAO,GAAP,OAAO,CAA0B;QACjC,gBAAW,GAAX,WAAW,CAAsB;IACrD,CAAC;IACJ;;;;OAIA;IACO,KAAK,CAAC,KAAK;QACd,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC;QACzD,IAAI,CAAC,WAAW;YAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QACxE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QACtC,MAAM,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3D,QAAO,MAAM,EAAE,CAAC;YACrB,KAAK,YAAY,CAAC,CAAC,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YACjD,KAAK,kBAAkB,CAAC,CAAC,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YACvD,KAAK,mCAAmC,CAAC,CAAC,OAAO,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAC9E,KAAK,qBAAqB,CAAC,CAAC,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACvE,OAAO,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QACrE,CAAC;IACC,CAAC;IACD;;;;OAIA;IACQ,KAAK,CAAC,WAAW;QACrB,MAAM,MAAM,GAAU,EAAE,CAAC;QACzB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACnC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACjE,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,2BAA2B,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;YACtG,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;gBAClC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;gBACzF,IAAI,IAAI,GAAG,IAAI;oBAAE,OAAO,KAAK,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC,CAAC;gBAC1F,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACvB,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;IACP,CAAC;IACD;;;;OAIA;IACQ,WAAW,CAAC,IAAY;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACpC,OAAO;YACH,QAAQ,EAAE,YAAY;YACtB,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,IAAI;SACd,CAAC;IACN,CAAC;IACD;;;;;OAKA;IACQ,WAAW,CAAC,IAAY;QAC5B,IAAI,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YACpC,OAAO;gBACH,QAAQ,EAAE,kBAAkB;gBAC5B,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;gBACzB,KAAK,EAAE,IAAI;aACd,CAAC;QACN,CAAC;QAAC,OAAM,KAAK,EAAE,CAAC;YAAC,MAAM,IAAI,KAAK,CAAC,2BAA2B,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAAC,CAAC;IACtF,CAAC;IACD;;;;OAIA;IACQ,iBAAiB,CAAC,IAAY;QAClC,MAAM,OAAO,GAA4B,EAAE,CAAC;QAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACrC,SAAS,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;YACvB,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACrC,MAAM,aAAa,GAAG,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;YAC3E,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,sBAAsB,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;QAC5F,CAAC,CAAC,CAAC;QACG,OAAO;YACH,QAAQ,EAAE,mCAAmC;YAC7C,OAAO,EAAE,OAAO;YAChB,KAAK,EAAE,IAAI;SACd,CAAA;IACL,CAAC;IACD;;;;;;OAMA;IACQ,eAAe,CAAC,IAAY,EAAE,UAAoB,EAAE;QACxD,MAAM,OAAO,GAA4B,EAAE,CAAC;QAC5C,MAAM,KAAK,GAA6B,EAAE,CAAC;QACjD,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,mBAAmB,EAAE,CAAC,MAAM,EAAE,QAAgB,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC;QACxG,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACjF,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/C,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAC3C,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;YAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YAC7C,IAAI,IAAI,IAAI,IAAI;gBAAE,OAAO;YACzB,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI;gBAAE,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;YACzF,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG;gBAClB,IAAI,EAAE,IAAI,CAAC,QAAQ;gBACnB,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;gBACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,SAAS;gBACpC,OAAO,EAAE,IAAI,CAAC,OAAO;aACxB,CAAA;QACL,CAAC,CAAC,CAAC;QACH,OAAO;YACH,QAAQ,EAAE,qBAAqB;YAC/B,OAAO,EAAE,KAAK;SACjB,CAAC;IACN,CAAC;IACD;;;;OAIA;IACQ,gBAAgB,CAAC,IAAY;QACjC,MAAM,IAAI,GAAG,UAAU,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAClE,IAAI,IAAI,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;QAC9B,MAAM,CAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,IAAI,EAAE,QAAQ,GAAG,IAAI,EAAE,OAAO,GAAG,EAAE,CAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACxF,OAAO;YACH,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAC,QAAQ,EAAE;YACzD,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC;YAC9C,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI;YACtF,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI;SACzF,CAAC;IACN,CAAC;IACJ;;;OAGG;IACK,aAAa,CAAC,IAAY;QACjC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAC9D,IAAI,MAAM,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;QAChC,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACE;;;;OAIA;IACQ,sBAAsB,CAAC,KAAa;QACxC,IAAI,CAAC;YAAC,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAAC,CAAC;QACzC,MAAM,CAAC;YAAC,OAAO,KAAK,CAAC;QAAC,CAAC;IAC3B,CAAC;;AAuEL,eAAe,UAAU,CAAC"}
@@ -13,15 +13,15 @@ export declare class Request {
13
13
  headers: Request.Headers;
14
14
  /** Contains the request cookies. */
15
15
  cookies: Cookie;
16
- /** Contains the POST data sent. */
16
+ /** Contains the query string parameters sent. */
17
17
  searchParams: Request.SearchParams;
18
18
  /** Contains the IP address of the requester. */
19
19
  ip?: string | string[];
20
20
  /** Contains the request method. */
21
21
  method: Request.Method;
22
- /** Contains the POSTParser. */
23
- session: Session;
24
22
  /** Contains the session of the device that made the request. */
23
+ session: Session;
24
+ /** Contains the request body parser. */
25
25
  private body;
26
26
  /** Contains the HTTP request received by the server. */
27
27
  private httpRequest;
@@ -45,6 +45,7 @@ export declare class Request {
45
45
  * @param Url - The URL received from the HTTP request.
46
46
  */
47
47
  private getSearchParams;
48
+ private normalizeUrl;
48
49
  }
49
50
  export declare namespace Request {
50
51
  export import BodyParser = _BodyParser;
@@ -58,6 +59,7 @@ export declare namespace Request {
58
59
  }
59
60
  interface SearchParams extends Document {
60
61
  }
61
- type Method = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'ALL';
62
+ type KnownMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'ALL';
63
+ type Method = KnownMethod | (string & {});
62
64
  }
63
65
  export default Request;
@@ -13,15 +13,15 @@ export class Request {
13
13
  headers;
14
14
  /** Contains the request cookies. */
15
15
  cookies;
16
- /** Contains the POST data sent. */
16
+ /** Contains the query string parameters sent. */
17
17
  searchParams;
18
18
  /** Contains the IP address of the requester. */
19
19
  ip;
20
20
  /** Contains the request method. */
21
21
  method;
22
- /** Contains the POSTParser. */
23
- session;
24
22
  /** Contains the session of the device that made the request. */
23
+ session;
24
+ /** Contains the request body parser. */
25
25
  body;
26
26
  /** Contains the HTTP request received by the server. */
27
27
  httpRequest;
@@ -37,12 +37,12 @@ export class Request {
37
37
  const forwardedIP = httpRequest.headers['x-forwarded-for'];
38
38
  const remoteIP = httpRequest.socket.remoteAddress;
39
39
  const method = httpRequest.method ?? 'GET';
40
- const url = httpRequest.url ?? '/';
40
+ let url = httpRequest.url ?? '/';
41
41
  this.httpRequest = httpRequest;
42
42
  this.ip = forwardedIP ? forwardedIP : remoteIP ? remoteIP : '0.0.0.0';
43
43
  this.method = this.getMethod(method);
44
44
  this.url = url.split('?')[0];
45
- this.url = decodeURI(this.url.endsWith('/') ? this.url : this.url + '/');
45
+ this.url = this.normalizeUrl(this.url);
46
46
  this.headers = httpRequest.headers;
47
47
  this.cookies = new Cookie(this.headers.cookie);
48
48
  this.session = Session.get(this.cookies);
@@ -55,11 +55,9 @@ export class Request {
55
55
  * @param method - The method used for the request.
56
56
  */
57
57
  getMethod(method) {
58
- return method == 'POST'
59
- ? 'POST'
60
- : method == 'PUT' ? 'PUT'
61
- : method == 'DELETE' ? 'DELETE'
62
- : 'GET';
58
+ method = method.trim().toUpperCase();
59
+ method = method.length > 0 ? method : 'GET';
60
+ return method;
63
61
  }
64
62
  /**
65
63
  * Retrieves the data sent via URL QUERY.
@@ -71,6 +69,13 @@ export class Request {
71
69
  UrlObject.searchParams.forEach((value, name) => searchParams[name] = value);
72
70
  return searchParams;
73
71
  }
72
+ normalizeUrl(url) {
73
+ url = url.split('?')[0];
74
+ url = decodeURI(url.endsWith('/') ? url : url + '/');
75
+ url = url.length > 1 && url.endsWith('/') ? url.slice(0, -1) : url;
76
+ url = url.startsWith('/') ? url : '/' + url;
77
+ return url;
78
+ }
74
79
  }
75
80
  (function (Request) {
76
81
  Request.BodyParser = _BodyParser;
@@ -1 +1 @@
1
- {"version":3,"file":"Request.js","sourceRoot":"","sources":["../../src/server/Request.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Request.js","sourceRoot":"","sources":["../../src/server/Request.ts"],"names":[],"mappings":"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"}
@@ -5,9 +5,13 @@
5
5
  */
6
6
  import HTTP from 'http';
7
7
  import FS from 'fs';
8
+ import { Readable } from 'stream';
8
9
  import Request from './Request.js';
9
10
  import Config from './config/Config.js';
10
11
  export declare class Response {
12
+ static readonly contentTypeMap: Response.contentTypeMap;
13
+ static readonly acceptRangeFormats: string[];
14
+ private static readonly version;
11
15
  /** Contains the request received by the server. */
12
16
  request: Request;
13
17
  /** Contains the list of server response templates. */
@@ -21,7 +25,7 @@ export declare class Response {
21
25
  * @param httpResponse - The response to be sent by the server.
22
26
  * @param templates - The list of server response templates.
23
27
  */
24
- constructor(request: Request, httpResponse: HTTP.ServerResponse, templates?: Config.Templates);
28
+ constructor(request: Request, httpResponse: HTTP.ServerResponse, templates: Config['data']['templates']);
25
29
  /** Checks if the response has been sent. */
26
30
  get isSended(): boolean;
27
31
  /**
@@ -29,7 +33,7 @@ export declare class Response {
29
33
  * More types will be supported over time.
30
34
  * @param extension - The file extension.
31
35
  */
32
- generateHeaders(extension: string): Request.Headers;
36
+ generateHeaders(extension: string): HTTP.OutgoingHttpHeaders;
33
37
  /**
34
38
  * Sends response headers.
35
39
  * @param code - The HTTP status code.
@@ -41,7 +45,17 @@ export declare class Response {
41
45
  * @param data - The data to be sent.
42
46
  * @param encode - The encoding used for the response.
43
47
  */
44
- send(data: Response.data, options?: Response.options): void;
48
+ send(data: Response.data, options?: Response.options): Promise<void>;
49
+ /**
50
+ * Sends a readable stream as a response.
51
+ * @param data - The readable stream to be sent.
52
+ * @param options - The response options.
53
+ * @throws If an error occurs while sending the stream.
54
+ * @returns A promise that resolves when the stream has been sent.
55
+ * @remarks This method is used for sending large data, such as files or templates, without loading them entirely into memory.
56
+ * It handles backpressure and ensures efficient streaming of data to the client.
57
+ */
58
+ private sendReadable;
45
59
  /**
46
60
  * Sends a file as a response.
47
61
  * @param path - The file path to send.
@@ -51,11 +65,11 @@ export declare class Response {
51
65
  sendFile(path: string, options?: Response.options): Promise<void>;
52
66
  /**
53
67
  * Sends the listing of a folder as a response.
54
- * @param basePath - The routing rule base path.
55
- * @param relativePath - The relative path received in the request.
68
+ * @param base - The routing rule base path.
69
+ * @param plus - The relative path received in the request.
56
70
  * @throws If the folder does not exist or is invalid.
57
71
  */
58
- sendFolder(basePath: string, relativePath: string): Promise<void>;
72
+ sendFolder(base: string, plus?: string): Promise<void>;
59
73
  /**
60
74
  * Sends a `.vhtml` template as a response.
61
75
  * @param path - The template file path.
@@ -69,25 +83,26 @@ export declare class Response {
69
83
  * @param data - The data to send.
70
84
  * @param options - The response options.
71
85
  */
72
- sendJson(data: any, options?: Response.options): void;
86
+ sendJson(data: any, options?: Response.options): Promise<void>;
73
87
  /**
74
88
  * Sends an error as a response.
75
89
  * @param status - The HTTP status code of the error.
76
90
  * @param message - The error message.
77
91
  */
78
92
  sendError(status: number, message: string): Promise<void>;
79
- private fileExist;
93
+ private getFsErrorStatus;
94
+ private static loadVersion;
80
95
  }
81
96
  export declare namespace Response {
82
97
  interface options {
83
98
  status?: number;
84
- headers?: Request.Headers;
99
+ headers?: HTTP.OutgoingHttpHeaders;
85
100
  encode?: BufferEncoding;
86
101
  }
87
102
  interface contentTypeMap {
88
103
  [key: string]: string | undefined;
89
104
  }
90
- type data = string | Buffer | FS.ReadStream;
105
+ type data = string | Buffer | Readable | FS.ReadStream;
91
106
  type Extensions = ('HTML' | 'JS' | 'CSS' | 'JSON' | 'XML' | 'TXT' | 'SVG' | 'PNG' | 'JPG' | 'JPEG' | 'MP3' | 'WAV' | 'MP4');
92
107
  }
93
108
  export default Response;
@@ -5,11 +5,33 @@
5
5
  */
6
6
  import FS from 'fs';
7
7
  import PATH from 'path';
8
+ import { Readable } from 'stream';
9
+ import { pipeline } from 'stream/promises';
8
10
  import Logger from '../logger/Logger.js';
9
11
  import Utilities from '../utilities/Utilities.js';
10
- import Template from '../Template.js';
12
+ import Template from '../Template/Template.js';
13
+ import PathSecurity from './security/PathSecurity.js';
11
14
  const logger = new Logger({ prefix: 'Response' });
12
15
  export class Response {
16
+ static contentTypeMap = {
17
+ 'html': 'text/html',
18
+ 'js': 'text/javascript',
19
+ 'css': 'text/css',
20
+ 'json': 'application/json',
21
+ 'xml': 'application/xml',
22
+ 'txt': 'text/plain',
23
+ 'svg': 'image/svg+xml',
24
+ 'png': 'image/png',
25
+ 'jpg': 'image/jpeg',
26
+ 'jpeg': 'image/jpeg',
27
+ 'mp3': 'audio/mpeg',
28
+ 'wav': 'audio/x-wav',
29
+ 'mp4': 'video/mp4',
30
+ };
31
+ static acceptRangeFormats = [
32
+ 'svg', 'png', 'jpg', 'jpeg', 'mp3', 'wav', 'mp4'
33
+ ];
34
+ static version = Response.loadVersion();
13
35
  /** Contains the request received by the server. */
14
36
  request;
15
37
  /** Contains the list of server response templates. */
@@ -23,12 +45,12 @@ export class Response {
23
45
  * @param httpResponse - The response to be sent by the server.
24
46
  * @param templates - The list of server response templates.
25
47
  */
26
- constructor(request, httpResponse, templates = {}) {
48
+ constructor(request, httpResponse, templates) {
27
49
  this.request = request;
28
50
  this.templates = templates;
29
51
  this.httpResponse = httpResponse;
30
52
  this.httpResponse.setHeader('X-Powered-By', 'MyNetFeez-Labs Vortez');
31
- this.httpResponse.setHeader('X-Version', '3.7.0-dev.18');
53
+ this.httpResponse.setHeader('X-Version', Response.version);
32
54
  }
33
55
  /** Checks if the response has been sent. */
34
56
  get isSended() { return this._isSended || this.httpResponse.writableEnded || this.httpResponse.headersSent; }
@@ -41,28 +63,10 @@ export class Response {
41
63
  extension = extension.startsWith('.') ? extension.slice(1) : extension;
42
64
  extension = extension.toLowerCase();
43
65
  const headers = {};
44
- const contentTypeMap = {
45
- 'html': 'text/html',
46
- 'js': 'text/javascript',
47
- 'css': 'text/css',
48
- 'json': 'application/json',
49
- 'xml': 'application/xml',
50
- 'txt': 'text/plain',
51
- 'svg': 'image/svg+xml',
52
- 'png': 'image/png',
53
- 'jpg': 'image/jpeg',
54
- 'jpeg': 'image/jpeg',
55
- 'mp3': 'audio/mpeg',
56
- 'wav': 'audio/x-wav',
57
- 'mp4': 'video/mp4',
58
- };
59
- const acceptRangeFormats = [
60
- 'svg', 'png', 'jpg', 'jpeg', 'mp3', 'wav', 'mp4'
61
- ];
62
- const type = contentTypeMap[extension];
66
+ const type = Response.contentTypeMap[extension];
63
67
  if (type)
64
- headers['Content-Type'] = type;
65
- if (acceptRangeFormats.includes(extension)) {
68
+ headers['Content-Type'] = type ?? 'application/octet-stream';
69
+ if (Response.acceptRangeFormats.includes(extension)) {
66
70
  headers['Accept-Ranges'] = 'bytes';
67
71
  }
68
72
  return headers;
@@ -83,16 +87,32 @@ export class Response {
83
87
  * @param data - The data to be sent.
84
88
  * @param encode - The encoding used for the response.
85
89
  */
86
- send(data, options = {}) {
90
+ async send(data, options = {}) {
91
+ if (data instanceof Readable)
92
+ return await this.sendReadable(data, options);
87
93
  this._isSended = true;
88
94
  const status = options.status ?? 200;
89
95
  const encode = options.encode || 'utf-8';
90
96
  const headers = options.headers || this.generateHeaders('txt');
91
97
  this.sendHeaders(status, headers);
92
- if (data instanceof FS.ReadStream)
93
- return void data.pipe(this.httpResponse);
94
98
  this.httpResponse.end(data, encode);
95
99
  }
100
+ /**
101
+ * Sends a readable stream as a response.
102
+ * @param data - The readable stream to be sent.
103
+ * @param options - The response options.
104
+ * @throws If an error occurs while sending the stream.
105
+ * @returns A promise that resolves when the stream has been sent.
106
+ * @remarks This method is used for sending large data, such as files or templates, without loading them entirely into memory.
107
+ * It handles backpressure and ensures efficient streaming of data to the client.
108
+ */
109
+ async sendReadable(data, options) {
110
+ this._isSended = true;
111
+ const status = options.status ?? 200;
112
+ const headers = options.headers || this.generateHeaders('txt');
113
+ await this.sendHeaders(status, headers);
114
+ await pipeline(data, this.httpResponse);
115
+ }
96
116
  /**
97
117
  * Sends a file as a response.
98
118
  * @param path - The file path to send.
@@ -104,76 +124,118 @@ export class Response {
104
124
  try {
105
125
  const details = await FS.promises.stat(path);
106
126
  if (!details.isFile())
107
- return this.sendError(500, '[Response Error] - Provided path is not a file.');
127
+ return await this.sendError(500, '[Response Error] - Provided path is not a file.');
108
128
  if (!this.request.headers.range) {
109
129
  const stream = FS.createReadStream(path);
110
130
  const headers = this.generateHeaders(PATH.extname(path));
111
131
  headers['content-length'] = details.size.toString();
112
- this.send(stream, { status: 200, headers });
132
+ await this.send(stream, { status: 200, headers });
113
133
  }
114
134
  else {
115
- const info = /bytes=(\d*)?-?(\d*)?/i.exec(this.request.headers.range);
135
+ const rangeHeader = this.request.headers.range;
136
+ const info = /^bytes=(\d*)-(\d*)$/i.exec(rangeHeader);
116
137
  if (!info)
117
- return this.sendError(416, 'Requested range exceeds file size');
118
- const [startString, endString] = info.slice(1);
119
- if (!startString)
120
- return this.sendError(416, 'Requested range exceeds file size');
121
- const start = Number(startString);
122
- const maxSize = start + 1024 * 1000;
123
- const end = endString
124
- ? Number(endString)
125
- : maxSize >= details.size
126
- ? details.size - 1
127
- : maxSize;
128
- if (start > details.size || end > details.size)
129
- return this.sendError(416, 'Requested range exceeds file size');
138
+ return await this.sendError(416, 'Requested range exceeds file size');
139
+ const [, startString, endString] = info;
140
+ let start;
141
+ let end;
142
+ if (startString && endString) {
143
+ start = Number(startString);
144
+ end = Number(endString);
145
+ }
146
+ else if (startString) {
147
+ start = Number(startString);
148
+ const maxSize = start + 1024 * 1000;
149
+ end = maxSize >= details.size ? details.size - 1 : maxSize;
150
+ }
151
+ else if (endString) {
152
+ const suffixSize = Number(endString);
153
+ if (!Number.isInteger(suffixSize) || suffixSize <= 0) {
154
+ return await this.sendError(416, 'Requested range exceeds file size');
155
+ }
156
+ start = Math.max(details.size - suffixSize, 0);
157
+ end = details.size - 1;
158
+ }
159
+ else
160
+ return await this.sendError(416, 'Requested range exceeds file size');
161
+ if (!Number.isInteger(start) ||
162
+ !Number.isInteger(end) ||
163
+ start < 0 ||
164
+ end < start ||
165
+ start >= details.size ||
166
+ end >= details.size)
167
+ return await this.sendError(416, 'Requested range exceeds file size');
130
168
  const size = end - start + 1;
131
169
  const stream = FS.createReadStream(path, { start, end });
132
170
  const headers = this.generateHeaders(PATH.extname(path));
133
171
  headers['content-length'] = size.toString();
134
172
  headers['content-range'] = `bytes ${start}-${end}/${details.size}`;
135
- this.send(stream, { status: 206, headers });
173
+ await this.send(stream, { status: 206, headers });
136
174
  }
137
175
  }
138
176
  catch (error) {
139
- this.sendError(500, error instanceof Error ? error.message : '[Response Error] - File does not exist.');
177
+ const status = this.getFsErrorStatus(error);
178
+ const message = status === 404
179
+ ? 'The requested URL was not found'
180
+ : status === 403
181
+ ? 'Access denied to requested resource'
182
+ : error instanceof Error
183
+ ? error.message
184
+ : '[Response Error] - File does not exist.';
185
+ await this.sendError(status, message);
140
186
  logger.error(`error sending file ${this.request.session.id}`, error);
141
187
  }
142
188
  }
143
189
  /**
144
190
  * Sends the listing of a folder as a response.
145
- * @param basePath - The routing rule base path.
146
- * @param relativePath - The relative path received in the request.
191
+ * @param base - The routing rule base path.
192
+ * @param plus - The relative path received in the request.
147
193
  * @throws If the folder does not exist or is invalid.
148
194
  */
149
- async sendFolder(basePath, relativePath) {
150
- basePath = basePath.endsWith('/') ? basePath : basePath + '/';
151
- relativePath = relativePath.endsWith('/') ? relativePath.slice(0, -1) : relativePath;
152
- const path = Utilities.Path.normalize(basePath + relativePath);
195
+ async sendFolder(base, plus = '') {
196
+ const basePath = Utilities.Path.resolve(base || Utilities.Path.rootDir);
197
+ const path = await PathSecurity.resolveInsideBase(basePath, plus);
198
+ if (!path) {
199
+ logger.warn(`&C2[Vortez Security] &C3Intento de Path Traversal bloqueado:`);
200
+ logger.warn(` &C3- IP: &C6${this.request.ip}`);
201
+ logger.warn(` &C3- Session ID: &C6${this.request.session.id}`);
202
+ logger.warn(` &C3- URL: &C6${this.request.url}`);
203
+ logger.warn(` &C3- Base: &C6${basePath}`);
204
+ logger.warn(` &C3- Intento: &C6${plus}`);
205
+ return void await this.sendError(403, 'Forbidden: Outside of sandbox');
206
+ }
153
207
  try {
154
- if (!await this.fileExist(path))
155
- return void this.sendError(404, 'The requested URL was not fount');
208
+ if (!await Utilities.File.exists(path))
209
+ return void await this.sendError(404, 'The requested URL was not found');
156
210
  const details = await FS.promises.stat(path);
157
211
  if (details.isFile())
158
- return this.sendFile(path);
212
+ return await this.sendFile(path);
159
213
  if (!details.isDirectory())
160
- return this.sendError(404, 'The requested URL was not fount');
214
+ return await this.sendError(404, 'The requested URL was not found');
161
215
  const folder = await FS.promises.readdir(path);
162
216
  if (this.templates.folder) {
163
- this.sendTemplate(this.templates.folder, {
217
+ await this.sendTemplate(this.templates.folder, {
164
218
  Url: this.request.url,
165
219
  folder
166
220
  });
167
221
  }
168
222
  else {
169
- this.sendTemplate(Utilities.Path.relative('./global/Template/Folder.vhtml'), {
223
+ await this.sendTemplate(Utilities.Path.module('global/template/folder.vhtml'), {
170
224
  Url: this.request.url,
171
225
  folder
172
226
  });
173
227
  }
174
228
  }
175
229
  catch (error) {
176
- this.sendError(500, error instanceof Error ? error.message : '[Response Error] - File/Directory does not exist.');
230
+ const status = this.getFsErrorStatus(error);
231
+ const message = status === 404
232
+ ? 'The requested URL was not found'
233
+ : status === 403
234
+ ? 'Access denied to requested resource'
235
+ : error instanceof Error
236
+ ? error.message
237
+ : '[Response Error] - File/Directory does not exist.';
238
+ await this.sendError(status, message);
177
239
  logger.error(`error sending folder ${this.request.session.id}`, error);
178
240
  }
179
241
  }
@@ -187,13 +249,23 @@ export class Response {
187
249
  async sendTemplate(path, data, options = {}) {
188
250
  path = Utilities.Path.normalize(path);
189
251
  try {
190
- const template = await Template.load(path, data);
252
+ const template = await Template.stream(path, data);
191
253
  const status = options.status ?? 200;
192
254
  const headers = options.headers || this.generateHeaders('html');
193
- this.send(template, { status, headers });
255
+ await this.send(template, { status, headers, encode: options.encode });
194
256
  }
195
257
  catch (error) {
196
- this.sendError(500, error instanceof Error ? error.message : '[Response Error] - Template does not exist.');
258
+ const status = this.getFsErrorStatus(error);
259
+ const message = status === 404
260
+ ? 'Template not found'
261
+ : status === 403
262
+ ? 'Access denied to template resource'
263
+ : error instanceof Error
264
+ ? error.message
265
+ : '[Response Error] - Template does not exist.';
266
+ if (!this.httpResponse.headersSent && !this.httpResponse.writableEnded) {
267
+ await this.sendError(status, message);
268
+ }
197
269
  logger.error(`error sending template ${this.request.session.id}`, error);
198
270
  }
199
271
  }
@@ -202,15 +274,15 @@ export class Response {
202
274
  * @param data - The data to send.
203
275
  * @param options - The response options.
204
276
  */
205
- sendJson(data, options = {}) {
277
+ async sendJson(data, options = {}) {
206
278
  try {
207
279
  const json = JSON.stringify(data);
208
280
  const status = options.status ?? 200;
209
281
  const headers = options.headers || this.generateHeaders('json');
210
- this.send(json, { status, headers });
282
+ await this.send(json, { status, headers });
211
283
  }
212
284
  catch (error) {
213
- this.sendError(500, error instanceof Error ? error.message : '[Response Error] - Data cannot be converted to JSON.');
285
+ await this.sendError(500, error instanceof Error ? error.message : '[Response Error] - Data cannot be converted to JSON.');
214
286
  logger.error(`error sending json ${this.request.session.id}`, error);
215
287
  }
216
288
  }
@@ -226,31 +298,45 @@ export class Response {
226
298
  status, message
227
299
  });
228
300
  const headers = this.generateHeaders('html');
229
- this.send(template, { status: status, headers });
301
+ await this.send(template, { status: status, headers });
230
302
  }
231
303
  else {
232
- const template = await Template.load(Utilities.Path.relative('./global/Template/Error.vhtml'), {
304
+ const template = await Template.load(Utilities.Path.module('global/template/error.vhtml'), {
233
305
  status, message
234
306
  });
235
307
  const headers = this.generateHeaders('html');
236
- this.send(template, { status: status, headers });
308
+ await this.send(template, { status: status, headers });
237
309
  }
238
310
  }
239
311
  catch (error) {
240
- console.error(error);
241
312
  const headers = this.generateHeaders('txt');
242
- this.send(`Error: ${status} -> ${message}`, { status: status, headers });
313
+ await this.send(`Error: ${status} -> ${message}`, { status: status, headers });
243
314
  logger.error(`error sending error: ${this.request.session.id}`, error);
244
315
  }
245
316
  }
246
- async fileExist(path) {
317
+ getFsErrorStatus(error) {
318
+ if (!(error instanceof Error))
319
+ return 500;
320
+ const withCode = error;
321
+ if (withCode.code === 'ENOENT' || withCode.code === 'ENOTDIR')
322
+ return 404;
323
+ if (withCode.code === 'EACCES' || withCode.code === 'EPERM')
324
+ return 403;
325
+ return 500;
326
+ }
327
+ static loadVersion() {
328
+ const envVersion = process.env.npm_package_version;
329
+ if (envVersion)
330
+ return envVersion;
247
331
  try {
248
- await FS.promises.stat(path);
249
- return true;
250
- }
251
- catch (error) {
252
- return false;
332
+ const packagePath = Utilities.Path.module('package.json');
333
+ const raw = FS.readFileSync(packagePath, 'utf8');
334
+ const data = JSON.parse(raw);
335
+ if (typeof data.version === 'string' && data.version.length > 0)
336
+ return data.version;
253
337
  }
338
+ catch { }
339
+ return 'unknown';
254
340
  }
255
341
  }
256
342
  export default Response;