From cf06ae3c01b8f05484b33743783f7147d1925ac0 Mon Sep 17 00:00:00 2001 From: AviiNL Date: Sun, 19 Sep 2021 22:38:58 +0200 Subject: [PATCH] Session in controller was undefined if no cookie exists (#2) --- dist/index.js | 4 ++++ package.json | 2 +- src/Session/SessionManager.ts | 8 ++++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/dist/index.js b/dist/index.js index d3f871a..4e8a8bf 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,2 +1,6 @@ +<<<<<<< HEAD +"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("tls");require("reflect-metadata");var t=require("http"),s=require("https"),r=require("querystring"),i=require("fs"),n=require("path");function o(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var a=o(t),c=o(s),h=o(i),l=o(n);class E{constructor(e){this.keys={},this.data={},e&&this.insert(e)}get size(){return Object.keys(this.data).length}insert(e){Object.keys(e).forEach((t=>{this.set(t,e[t])}))}get(e){return this.data[e.toLowerCase()]}has(e){return void 0!==this.data[e.toLowerCase()]}set(e,t){void 0===this.keys[e]&&(this.keys[e.toLowerCase()]=e),this.data[e.toLowerCase()]=t}get all(){const e={};return Object.keys(this.data).forEach((t=>{e[this.keys[t]]=this.data[t]})),e}forEach(e){Object.keys(this.data).forEach((t=>{e(this.data[t],this.keys[t])}))}toArray(e){const t=Object.values(this.data);return e?t.sort(e):t}}const p=/^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;class u{constructor(e,t){this.name=e,this.value=t,this.maxAge=0,this.domain=null,this.path="/",this.expires=null,this.httpOnly=null,this.secure=null,this.sameSite=null}serialize(){if(!p.test(this.name))throw new Error('Failed to serialize cookie "'+this.name+'" due to invalid characters in the name.');if(!p.test(this.value))throw new Error('Failed to serialize cookie "'+this.value+'" due to invalid characters in the value.');let e=this.name+"="+encodeURIComponent(this.value);if(this.maxAge){const t=this.maxAge-0;if(isNaN(t)||!isFinite(t))throw new TypeError('maxAge of cookie "'+this.name+'" is invalid');e+="; Max-Age="+Math.floor(t)}if(this.domain){if(!p.test(this.domain))throw new Error('Failed to serialize cookie "'+this.name+'" due to invalid characters in the domain property.');e+="; Domain="+this.domain}if(this.path){if(!p.test(this.path))throw new Error('Failed to serialize cookie "'+this.name+'" due to invalid characters in the path property.');e+="; Path="+this.path}if(this.expires){if(!(this.expires instanceof Date))throw new Error('Failed to serialize cookie "'+this.name+'" because "expires" is expected to be a Date object.');e+="; Expires="+this.expires.toUTCString()}if(this.httpOnly&&(e+="; HttpOnly"),this.secure&&(e+="; Secure"),this.sameSite)switch(this.sameSite){case!0:case"strict":e+="; SameSite=Strict";break;case"lax":e+="; SameSite=Lax";break;case"none":e+="; SameSite=None"}return e}}class d{constructor(){this._cookies=new Map}set(e,t,s=86400,r=null,i="/",n=!1,o=!1,a="lax"){const c=new u(e,t);if(c.domain=r,c.path=i,c.httpOnly=n,c.secure=o,c.sameSite=a,s>0){const e=s;c.expires=new Date((new Date).getTime()+1e3*e),c.maxAge=e}this._cookies.set(e,c)}serialize(){const e=[];return this._cookies.forEach((t=>{e.push(t.serialize())})),e}}class R extends Error{constructor(e,t,s){super(t),this.title=e,this.message=t,this.statusCode=s}}class g{constructor(e,t=exports.HttpStatus.OK,s="text/plain"){this._code=exports.HttpStatus.OK,this._content="",this._isSent=!1,this._headers=new E,this._cookies=new d,this.content=e,this.contentType=s,this.statusCode=t}get cookies(){return this._cookies}get headers(){return this._headers}set statusCode(e){this._code=e}get statusCode(){return this._code}set contentType(e){this._headers.set("Content-Type",e)}set content(e){this._content=e}get content(){return this._content}get isSent(){return this._isSent}send(e){if(this._isSent)throw new Error("This response was already sent.");this._isSent=!0;const t=this._cookies.serialize();for(let s of t)e.setHeader("Set-Cookie",s);e.writeHead(this._code,this._headers.all),e.write(this._content),e.end()}}var f;(f=exports.HttpStatus||(exports.HttpStatus={}))[f.CONTINUE=100]="CONTINUE",f[f.SWITCHING_PROTOCOLS=101]="SWITCHING_PROTOCOLS",f[f.PROCESSING=102]="PROCESSING",f[f.EARLY_HINTS=103]="EARLY_HINTS",f[f.OK=200]="OK",f[f.CREATED=201]="CREATED",f[f.ACCEPTED=202]="ACCEPTED",f[f.NON_AUTHORITATIVE_INFORMATION=203]="NON_AUTHORITATIVE_INFORMATION",f[f.NO_CONTENT=204]="NO_CONTENT",f[f.RESET_CONTENT=205]="RESET_CONTENT",f[f.PARTIAL_CONTENT=206]="PARTIAL_CONTENT",f[f.MULTI_STATUS=207]="MULTI_STATUS",f[f.ALREADY_REPORTED=208]="ALREADY_REPORTED",f[f.IM_USED=226]="IM_USED",f[f.MULTIPLE_CHOICES=300]="MULTIPLE_CHOICES",f[f.MOVED_PERMANENTLY=301]="MOVED_PERMANENTLY",f[f.FOUND=302]="FOUND",f[f.SEE_OTHER=303]="SEE_OTHER",f[f.NOT_MODIFIED=304]="NOT_MODIFIED",f[f.USE_PROXY=305]="USE_PROXY",f[f.RESERVED=306]="RESERVED",f[f.TEMPORARY_REDIRECT=307]="TEMPORARY_REDIRECT",f[f.PERMANENTLY_REDIRECT=308]="PERMANENTLY_REDIRECT",f[f.BAD_REQUEST=400]="BAD_REQUEST",f[f.UNAUTHORIZED=401]="UNAUTHORIZED",f[f.PAYMENT_REQUIRED=402]="PAYMENT_REQUIRED",f[f.FORBIDDEN=403]="FORBIDDEN",f[f.NOT_FOUND=404]="NOT_FOUND",f[f.METHOD_NOT_ALLOWED=405]="METHOD_NOT_ALLOWED",f[f.NOT_ACCEPTABLE=406]="NOT_ACCEPTABLE",f[f.PROXY_AUTHENTICATION_REQUIRED=407]="PROXY_AUTHENTICATION_REQUIRED",f[f.REQUEST_TIMEOUT=408]="REQUEST_TIMEOUT",f[f.CONFLICT=409]="CONFLICT",f[f.GONE=410]="GONE",f[f.LENGTH_REQUIRED=411]="LENGTH_REQUIRED",f[f.PRECONDITION_FAILED=412]="PRECONDITION_FAILED",f[f.REQUEST_ENTITY_TOO_LARGE=413]="REQUEST_ENTITY_TOO_LARGE",f[f.REQUEST_URI_TOO_LONG=414]="REQUEST_URI_TOO_LONG",f[f.UNSUPPORTED_MEDIA_TYPE=415]="UNSUPPORTED_MEDIA_TYPE",f[f.REQUESTED_RANGE_NOT_SATISFIABLE=416]="REQUESTED_RANGE_NOT_SATISFIABLE",f[f.EXPECTATION_FAILED=417]="EXPECTATION_FAILED",f[f.I_AM_A_TEAPOT=418]="I_AM_A_TEAPOT",f[f.MISDIRECTED_REQUEST=421]="MISDIRECTED_REQUEST",f[f.UNPROCESSABLE_ENTITY=422]="UNPROCESSABLE_ENTITY",f[f.LOCKED=423]="LOCKED",f[f.FAILED_DEPENDENCY=424]="FAILED_DEPENDENCY",f[f.TOO_EARLY=425]="TOO_EARLY",f[f.UPGRADE_REQUIRED=426]="UPGRADE_REQUIRED",f[f.PRECONDITION_REQUIRED=428]="PRECONDITION_REQUIRED",f[f.TOO_MANY_REQUESTS=429]="TOO_MANY_REQUESTS",f[f.REQUEST_HEADER_FIELDS_TOO_LARGE=431]="REQUEST_HEADER_FIELDS_TOO_LARGE",f[f.UNAVAILABLE_FOR_LEGAL_REASONS=451]="UNAVAILABLE_FOR_LEGAL_REASONS",f[f.INTERNAL_SERVER_ERROR=500]="INTERNAL_SERVER_ERROR",f[f.NOT_IMPLEMENTED=501]="NOT_IMPLEMENTED",f[f.BAD_GATEWAY=502]="BAD_GATEWAY",f[f.SERVICE_UNAVAILABLE=503]="SERVICE_UNAVAILABLE",f[f.GATEWAY_TIMEOUT=504]="GATEWAY_TIMEOUT",f[f.VERSION_NOT_SUPPORTED=505]="VERSION_NOT_SUPPORTED",f[f.VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL=506]="VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL",f[f.INSUFFICIENT_STORAGE=507]="INSUFFICIENT_STORAGE",f[f.LOOP_DETECTED=508]="LOOP_DETECTED",f[f.NOT_EXTENDED=510]="NOT_EXTENDED",f[f.NETWORK_AUTHENTICATION_REQUIRED=511]="NETWORK_AUTHENTICATION_REQUIRED";class T extends R{constructor(e="An internal server error occurred."){super("Internal Server Error",e,exports.HttpStatus.INTERNAL_SERVER_ERROR)}}class _ extends R{constructor(e="The requested resource could not be found."){super("Not Found",e,exports.HttpStatus.NOT_FOUND)}}class m{setResponse(e){this.response=e}hasResponse(){return this.response instanceof g}getResponse(){return this.response}}class O extends m{constructor(e,t,s,r){super(),this.error=e,this.request=t,this.controller=s,this.methodName=r}}class S extends m{constructor(e,t,s,r,i){super(),this.options=e,this.request=t,this.session=s,this.templateFile=r,this.data=i}}class A extends m{constructor(e,t,s){super(),this.request=e,this.route=t,this.session=s}}class L{constructor(e,t,s,r){this.request=e,this.route=t,this.response=s,this.session=r}}class N extends m{constructor(e,t,s){super(),this.request=e,this.fileName=t,this.mimeType=s}}class I{constructor(e,t,s,r){this.request=e,this.response=t,this.fileName=s,this.mimeType=r}}const y=/\((.*?)\)/g,v=/(\(\?)?:\w+/g,x=/[\-{}\[\]+?.,\\^$|#\s]/g,w=/\*/g;class b{constructor(t,s){this.r=t;const r=new URL(t.url,"http://localhost/"),i={};r.searchParams.forEach(((e,t)=>i[t]=e)),this._clientIp=t.headers["x-forwarded-for"]||t.socket.remoteAddress,this._method=t.method.toUpperCase(),this._path=r.pathname.replace(/(\/)$/g,"")||"/",this._headers=new E(t.headers),this._cookies=new E(t.headers.cookie?this._parseCookies(t.headers.cookie):{}),this._query=new E(i),this._parameters=new E,this._post=s.fields,this._files=s.files,this._body=s.raw,this._isSecure=t.socket instanceof e.TLSSocket&&t.socket.encrypted}get(e){return this._post.has(e)?this._post.get(e):this._files.has(e)?this._files.get(e):this._headers.has(e)?this._headers.get(e):this._cookies.has(e)?this._cookies.get(e):this._parameters.has(e)?this._parameters.get(e):this._query.has(e)?this._query.get(e):void 0}get clientIp(){return this._clientIp}get isSecure(){return this._isSecure}get path(){return this._path}get method(){return this._method}get cookies(){return this._cookies}get headers(){return this._headers}get query(){return this._query}get parameters(){return this._parameters}get post(){return this._post}get files(){return this._files}get json(){try{return JSON.parse(this._body.toString())}catch(e){return null}}get body(){return this._body}isMatchingRoute(e){if(this.method!==e.method)return!1;e._matcher||(e._matcher=this._parsePattern(e.path));let t=e._matcher.regExp.exec(this.path);return!!t&&(t=t.slice(1,-1),t.reduce(((t,s,r)=>(s&&this._parameters.set(e._matcher.namedParams[r],s),t)),{}),!0)}_parsePattern(e){let t=[];return e=e.replace(x,"\\$&").replace(y,"(?:$1)?").replace(v,(function(e,s){return t.push(e.slice(1)),s?e:"([^/?]+)"})).replace(w,(function(){return t.push("path"),"([^?]*?)"})),{regExp:new RegExp("^"+e+"(?:\\?([\\s\\S]*))?$"),namedParams:t}}_parseCookies(e){if("string"!=typeof e)return{};const t={},s=e.split(/; */);for(let e=0;e{e.prototype.__ROUTES__[t].forEach((s=>{(Array.isArray(s.method)?s.method:[s.method]).forEach((r=>{this.routes.push({path:s.path,method:r,signature:s.signature,_controller:[e,t]})}))}))})):console.warn("Given controller has no registered routes.")}findByRequest(e){const t=this.routes.find((t=>e.isMatchingRoute(t)));if(t)return t}}class C{constructor(e,t,s){this.request=e,this.socket=t,this.session=s}}class k{onServerError(e){let t='\n\n\n Error\n \n \n\n\n\n
\n
\n
Something went wrong
\n
\n
\n {{ message }}\n
{{ trace }}
\n
\n
\n {{ title }}\n
\n
\n\n\n',s=e.error.title||e.error.constructor.name;e.error.statusCode&&(s=" "+e.error.statusCode+" - "+s),t=t.replace("{{ title }}",s),t=t.replace("{{ message }}",e.error.message),t=t.replace("{{ trace }}",e.error.stack),e.setResponse(new D(t,exports.HttpStatus.NOT_FOUND))}}class M{constructor(e,t){this.raw=e,this.fields=new E,this.files=new E;for(let e of t)this.addPartToBag(e,e.filename?this.files:this.fields,!!e.filename)}addPartToBag(e,t,s){const r=e.name;r.endsWith("[]")?(t.has(r)||t.set(r,[]),s?t.get(r).push({name:r,fileName:e.filename,mimeType:e.type,data:e.data}):t.get(r).push(e.data.toString())):s?t.set(r,{name:r,fileName:e.filename,mimeType:e.type,data:e.data}):t.set(r,e.data.toString())}}function q(e){const t=e.header.split(";"),s=t[2];let r={};if(s){r=function(e){const t=e.split("="),s=t[0].trim(),r=JSON.parse(t[1].trim()),i={};return Object.defineProperty(i,s,{value:r,writable:!0,enumerable:!0,configurable:!0}),i}(s);const t=e.info.split(":")[1].trim();Object.defineProperty(r,"type",{value:t,writable:!0,enumerable:!0,configurable:!0})}return Object.defineProperty(r,"name",{value:t[1].split("=")[1].replace(/"/g,""),writable:!0,enumerable:!0,configurable:!0}),Object.defineProperty(r,"data",{value:Buffer.from(e.part),writable:!0,enumerable:!0,configurable:!0}),r}class P{constructor(e){this.maxUploadSize=e}async decode(e,t){const s=await this.getRequestBody(e,t),r=e.headers["content-type"]||"";return r.toLowerCase().startsWith("multipart/form-data")?this.parseMultipartFormData(t,r,s):r.toLowerCase().startsWith("application/x-www-form-urlencoded")?this.parseFormUrlEncoded(t,s):new M(s,[])}parseMultipartFormData(e,t,s){return new M(s,function(e,t){let s="",r="",i="",n=0,o=[];const a=[];for(let c=0;c0?e[c-1]:null,E=10===h&&13===l;if(10===h||13===h||(s+=String.fromCharCode(h)),0===n&&E)"--"+t===s&&(n=1),s="";else if(1===n&&E)r=s,n=2,-1===r.indexOf("filename")&&(n=3),s="";else if(2===n&&E)i=s,n=3,s="";else if(3===n&&E)n=4,o=[],s="";else if(4===n){if(s.length>t.length+4&&(s=""),"--"+t===s){const e=o.length-s.length,t={header:r,info:i,part:o.slice(0,e-1)};a.push(q(t)),o=[],s="",n=5,r="",i=""}else o.push(h);E&&(s="")}else 5===n&&E&&(n=1)}return a}(s,function(e){const t=e.split(";");if(t)for(let e=0;e=0){const e=s.split("=");return new String(e[1]).trim()}}return""}(t)))}parseFormUrlEncoded(e,t){try{const e=r.decode(t.toString()),s=[];return Object.keys(e).forEach((t=>{s.push({name:t,data:Buffer.from(e[t])})})),new M(t,s)}catch(t){e.writeHead(413),e.end()}}getRequestBody(e,t){let s=[],r=0;return new Promise(((i,n)=>{e.on("data",(i=>{if(r+=i.length,r>this.maxUploadSize)throw t.writeHead(413),t.end(),e.socket.destroy(),new Error("Upload size too big.");s.push(i)})).on("end",(()=>{i(Buffer.concat(s))})).on("error",(e=>{n(e)}))}))}}class Y{constructor(){this.storage=new Map}delete(e){this.storage.delete(e)}gc(){const e=(new Date).getTime(),t=[];this.storage.forEach(((s,r)=>{e>s.ttl&&t.push(r)})),t.forEach((e=>this.storage.delete(e)))}get(e){return this.storage.has(e)?this.storage.get(e).data:void 0}set(e,t){this.storage.set(e,{data:t,ttl:(new Date).getTime()+864e5})}}class H{constructor(e){try{this.data=JSON.parse(e)||{}}catch(e){this.data={}}}has(e){return void 0!==this.data[e]}get(e,t){return this.has(e)?this.data[e]:t}set(e,t){this.data[e]=t}delete(e){delete this.data[e]}toString(){return JSON.stringify(this.data)}}class F{constructor(e,t){this.sessionStorage=e,this.cookieName=t,this.sessionData=new Map}onRequest(e){let t=e.request.cookies.get(this.cookieName);t||(t=this.generateSessionId(),e.request.cookies.set(this.cookieName,t)),this.sessionData.set(t,new H(t?this.sessionStorage.get(t):"{}")),e.session=this.sessionData.get(t)}onResponse(e){const t=e.request.cookies.get(this.cookieName);this.sessionStorage.set(t,this.sessionData.get(t).toString()),e.response.cookies.set(this.cookieName,t,0)}getSessionByRequest(e){const t=e.cookies.get(this.cookieName);if(t)return this.sessionData.get(t)}generateSessionId(){const e=[...Array(64)].map((e=>(~~(36*Math.random())).toString(36))).join("");return this.sessionData.has(e)?this.generateSessionId():e}}class j{constructor(e,t){this.directories=e,this.requestEventListeners=[],this.responseEventListeners=[],this.defaultMimeTypes={txt:"text/plain",html:"text/html",js:"text/javascript",css:"text/css",gif:"image/gif",png:"image/png",svg:"image/svg",json:"application/json"},t||(t=e=>this.defaultMimeTypes[e]||"application/octet-stream"),this.lookupMimeType=t}registerStaticRequestEventListener(e){this.requestEventListeners.push(e),this.requestEventListeners=this.requestEventListeners.sort(((e,t)=>e.priority>t.priority?-1:1))}registerStaticResponseEventListener(e){this.responseEventListeners.push(e),this.responseEventListeners=this.responseEventListeners.sort(((e,t)=>e.priority>t.priority?-1:1))}async onErrorEvent(e){if(!(e.error instanceof _))return;const t=this.lookup(e.request.path);if(!t)return;const s=await this.lookupMimeType(l.default.extname(t).replace(/^\./,"").toLowerCase()),r=new N(e.request,t,s);let i=!1;for(let t of this.requestEventListeners)if(i=!1===await t.callback(r),r.hasResponse()&&!e.hasResponse()&&e.setResponse(r.getResponse()),i)break;const n=new g(h.default.readFileSync(t),exports.HttpStatus.OK,s),o=new I(e.request,n,t,s);i=!1;for(let t of this.responseEventListeners)if(i=!1===await t.callback(o),r.hasResponse()&&!e.hasResponse()&&e.setResponse(r.getResponse()),i)break;e.hasResponse()||e.setResponse(n)}lookup(e){for(let t of this.directories){let s=l.default.resolve(t,e.replace(/^\//,""));if(!1!==s.toLowerCase().startsWith(t.toLowerCase())&&(h.default.existsSync(s)&&!h.default.statSync(s).isDirectory()))return s}}}class G{constructor(e){this.templateDirectories=e,this.renderEventListeners=[]}registerRenderEventListener(e){this.renderEventListeners.push(e),this.renderEventListeners=this.renderEventListeners.sort(((e,t)=>e.priority>t.priority?-1:1))}async render(e,t,s,r){r=r||{};const i=this.findTemplateFile(s.name);if(!i)throw new _('The template "'+s.name+'" could not be found. Looked in "'+this.templateDirectories.join('", "')+'".');const n=new S(s.options,e,t,i,r);for(let e of this.renderEventListeners)if(await e.callback(n),n.hasResponse())return n.getResponse();throw new T('No available template renderer is able to render "'+s.name+'".')}findTemplateFile(e){for(let t of this.templateDirectories){const s=l.default.resolve(t,e);if(i.existsSync(s))return s}}}exports.AccessDeniedError=class extends R{constructor(e="Access to the requested resource is denied."){super("Access denied",e,exports.HttpStatus.FORBIDDEN)}},exports.Bag=E,exports.Cookie=u,exports.CookieBag=d,exports.ErrorEvent=O,exports.Harmony=class{constructor(e){this.options=e,this.errorEventListeners=[],this.requestEventListeners=[],this.responseEventListeners=[],this.upgradeEventListeners=[],this.typedControllerArguments=new Map,this.router=new U,this.server=e.enableHttps?c.default.createServer(e.httpsOptions,this.handle.bind(this)):a.default.createServer(this.handle.bind(this)),this.requestDecoder=new P(e.maxUploadSize||1048576),e.sni&&"object"==typeof e.sni&&(!1===e.enableHttps?console.warn("SNI configuration is ignored because HTTPS is disabled."):Object.keys(e.sni).forEach((t=>{this.server.addContext(t,e.sni[t])}))),this.registerErrorEventListener((new k).onServerError,-1/0),e.controllers&&e.controllers.forEach((e=>this.registerController(e))),e.errorEventListeners&&e.errorEventListeners.forEach((e=>this.registerErrorEventListener(e.callback,e.priority))),e.requestEventListeners&&e.requestEventListeners.forEach((e=>this.registerRequestEventListener(e.callback,e.priority))),e.responseEventListeners&&e.responseEventListeners.forEach((e=>this.registerResponseEventListener(e.callback,e.priority))),e.upgradeEventListeners&&e.upgradeEventListeners.forEach((e=>this.registerUpgradeEventListener(e.callback,e.priority))),e.enableSession&&(e.session=e.session||{},this.sessionManager=new F(e.session.storage||new Y,e.session.cookieName||"HARMONY_SID"),this.registerRequestEventListener((e=>this.sessionManager.onRequest(e)),1/0),this.registerResponseEventListener((e=>this.sessionManager.onResponse(e)),-1/0)),e.static=e.static||{},e.static.publicDirectories&&e.static.publicDirectories.length>0&&(this.staticAssetHandler=new j(e.static.publicDirectories,e.static.lookupMimeType),this.registerErrorEventListener((e=>this.staticAssetHandler.onErrorEvent(e)),1/0),Array.isArray(e.static.staticRequestEventListeners)&&e.static.staticRequestEventListeners.length>0&&e.static.staticRequestEventListeners.forEach((e=>{this.staticAssetHandler.registerStaticRequestEventListener(e)})),Array.isArray(e.static.staticResponseEventListeners)&&e.static.staticResponseEventListeners.length>0&&e.static.staticResponseEventListeners.forEach((e=>{this.staticAssetHandler.registerStaticResponseEventListener(e)}))),e.templating&&e.templating.templateDirectories&&(this.templateManager=new G(e.templating.templateDirectories),Array.isArray(e.templating.renderEventListeners)&&e.templating.renderEventListeners.length>0&&e.templating.renderEventListeners.forEach((e=>{this.templateManager.registerRenderEventListener(e)}))),this.server.on("error",(()=>{})),this.server.on("connection",(e=>{e.on("error",(()=>{}))})),this.server.on("upgrade",((e,t)=>{try{const s=new b(e,new M(Buffer.from(""),[])),r=new C(s,t,this.sessionManager?this.sessionManager.getSessionByRequest(s):void 0);for(let e of this.upgradeEventListeners)if(e.callback(r))return}catch(e){console.warn(e)}}))}start(){this.server.listen(this.options.port||8e3)}use(e){e.install(this)}get httpServer(){return this.server}addServerNameIdentificationContext(e,t){if(!(this.server instanceof c.default.Server))throw new Error("Unable to add SNI configuration while HTTPS is disabled.");this.server.addContext(e,t)}getSessionByRequest(e){if(!this.sessionManager)throw new Error("Session management is disabled.");return this.sessionManager.getSessionByRequest(e)}registerController(e){this.router.registerController(e)}registerTypedControllerArgument(e,t){this.typedControllerArguments.set(e,t)}registerErrorEventListener(e,t=0){this.errorEventListeners.push({callback:e,priority:t}),this.errorEventListeners=this.errorEventListeners.sort(((e,t)=>e.priority>t.priority?-1:1))}registerUpgradeEventListener(e,t=0){this.upgradeEventListeners.push({callback:e,priority:t}),this.upgradeEventListeners=this.upgradeEventListeners.sort(((e,t)=>e.priority>t.priority?-1:1))}registerRequestEventListener(e,t=0){this.requestEventListeners.push({callback:e,priority:t}),this.requestEventListeners=this.requestEventListeners.sort(((e,t)=>e.priority>t.priority?-1:1))}registerResponseEventListener(e,t=0){this.responseEventListeners.push({callback:e,priority:t}),this.responseEventListeners=this.responseEventListeners.sort(((e,t)=>e.priority>t.priority?-1:1))}registerStaticRequestEventListener(e,t=0){if(void 0===this.staticAssetHandler)throw new Error("Unable to register a static request event listener because static resource handling is disabled. Please ensure at least one public directory is configured.");this.staticAssetHandler.registerStaticRequestEventListener({callback:e,priority:t})}registerStaticResponseEventListener(e,t=0){if(void 0===this.staticAssetHandler)throw new Error("Unable to register a static response event listener because static resource handling is disabled. Please ensure at least one public directory is configured.");this.staticAssetHandler.registerStaticResponseEventListener({callback:e,priority:t})}registerRenderTemplateListener(e,t=0){if(void 0===this.templateManager)throw new Error("Unable to register a render template event listener because templating is currently disabled. Please ensure that at least one template directory is configured.");this.templateManager.registerRenderEventListener({callback:e,priority:t})}async handle(e,t){let s,r,i,n;try{s=await this.requestDecoder.decode(e,t),r=new b(e,s),i=this.router.findByRequest(r)}catch(e){return}try{if(!i)throw new _;if(n=this.options.serviceContainer?this.options.serviceContainer.get(i._controller[0]):new i._controller[0],"function"!=typeof n[i._controller[1]])throw new T('Method "'+i._controller[1]+'" is not an accessible method in this controller.');const e=new A(r,i,this.sessionManager?this.sessionManager.getSessionByRequest(r):void 0);let s,o=!1;for(let t of this.requestEventListeners)if(o=!1===await t.callback(e),s||!e.hasResponse()||e.getResponse().isSent||(s=e.getResponse()),o)break;if(!(s||(s=await this.handleControllerAction(n,i._controller[1],i,r),s instanceof g))){if(n.__TEMPLATES__&&n.__TEMPLATES__[i._controller[1]]){const e=this.sessionManager?this.sessionManager.getSessionByRequest(r):void 0;s=await this.templateManager.render(r,e,n.__TEMPLATES__[i._controller[1]],s)}if(!(s instanceof g))throw new T('Method "'+i._controller[1]+'" did not return a Response object.')}const a=new L(r,i,s,this.sessionManager?this.sessionManager.getSessionByRequest(r):void 0);for(let e of this.responseEventListeners)if(!1===e.callback(a))break;s.isSent||s.send(t)}catch(e){const s=new O(e,r,n,i?i._controller[1]:void 0);let o=!1;for(let e of this.errorEventListeners){const r=!1===await e.callback(s);if(!1===o&&s.hasResponse()){const e=s.getResponse();!1===e.isSent&&(e.send(t),o=!0)}if(r)return}}}async handleControllerAction(e,t,s,r){const i=e[t],n=[],o=Object.values(r.parameters.all),a=new Map;a.set(b,(e=>e)),a.set(H,(e=>{if(!this.sessionManager)throw new T("Controller attempted to access Session, but sessions are disabled.");return this.sessionManager.getSessionByRequest(r)})),this.typedControllerArguments.forEach(((e,t)=>{a.set(t,e)}));for(let e=0;ee.trim())):[]).map((e=>e.split("=")[0].trim()));if(c)for(let e=0;ee})}if(void 0!==s.__TEMPLATES__[r])throw new Error('Multiple @Template annotations found for method "'+r+'".');s.__TEMPLATES__[r]={name:e,options:t}}}; +======= "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("tls");require("reflect-metadata");var t=require("http"),s=require("https"),r=require("querystring"),i=require("fs"),n=require("path");function o(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var a=o(t),c=o(s),h=o(i),l=o(n);class E{constructor(e){this.keys={},this.data={},e&&this.insert(e)}get size(){return Object.keys(this.data).length}insert(e){Object.keys(e).forEach((t=>{this.set(t,e[t])}))}get(e){return this.data[e.toLowerCase()]}has(e){return void 0!==this.data[e.toLowerCase()]}set(e,t){void 0===this.keys[e]&&(this.keys[e.toLowerCase()]=e),this.data[e.toLowerCase()]=t}get all(){const e={};return Object.keys(this.data).forEach((t=>{e[this.keys[t]]=this.data[t]})),e}forEach(e){Object.keys(this.data).forEach((t=>{e(this.data[t],this.keys[t])}))}toArray(e){const t=Object.values(this.data);return e?t.sort(e):t}}const p=/^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;class u{constructor(e,t){this.name=e,this.value=t,this.maxAge=0,this.domain=null,this.path="/",this.expires=null,this.httpOnly=null,this.secure=null,this.sameSite=null}serialize(){if(!p.test(this.name))throw new Error('Failed to serialize cookie "'+this.name+'" due to invalid characters in the name.');if(!p.test(this.value))throw new Error('Failed to serialize cookie "'+this.value+'" due to invalid characters in the value.');let e=this.name+"="+encodeURIComponent(this.value);if(this.maxAge){const t=this.maxAge-0;if(isNaN(t)||!isFinite(t))throw new TypeError('maxAge of cookie "'+this.name+'" is invalid');e+="; Max-Age="+Math.floor(t)}if(this.domain){if(!p.test(this.domain))throw new Error('Failed to serialize cookie "'+this.name+'" due to invalid characters in the domain property.');e+="; Domain="+this.domain}if(this.path){if(!p.test(this.path))throw new Error('Failed to serialize cookie "'+this.name+'" due to invalid characters in the path property.');e+="; Path="+this.path}if(this.expires){if(!(this.expires instanceof Date))throw new Error('Failed to serialize cookie "'+this.name+'" because "expires" is expected to be a Date object.');e+="; Expires="+this.expires.toUTCString()}if(this.httpOnly&&(e+="; HttpOnly"),this.secure&&(e+="; Secure"),this.sameSite)switch(this.sameSite){case!0:case"strict":e+="; SameSite=Strict";break;case"lax":e+="; SameSite=Lax";break;case"none":e+="; SameSite=None"}return e}}class d{constructor(){this._cookies=new Map}set(e,t,s=86400,r=null,i="/",n=!1,o=!1,a="lax"){const c=new u(e,t);if(c.domain=r,c.path=i,c.httpOnly=n,c.secure=o,c.sameSite=a,s>0){const e=s;c.expires=new Date((new Date).getTime()+1e3*e),c.maxAge=e}this._cookies.set(e,c)}serialize(){const e=[];return this._cookies.forEach((t=>{e.push(t.serialize())})),e}}class R extends Error{constructor(e,t,s){super(t),this.title=e,this.message=t,this.statusCode=s}}class g{constructor(e,t=exports.HttpStatus.OK,s="text/plain"){this._code=exports.HttpStatus.OK,this._content="",this._isSent=!1,this._headers=new E,this._cookies=new d,this.content=e,this.contentType=s,this.statusCode=t}get cookies(){return this._cookies}get headers(){return this._headers}set statusCode(e){this._code=e}get statusCode(){return this._code}set contentType(e){this._headers.set("Content-Type",e)}set content(e){this._content=e}get content(){return this._content}get isSent(){return this._isSent}send(e){if(this._isSent)throw new Error("This response was already sent.");this._isSent=!0;const t=this._cookies.serialize();for(let s of t)e.setHeader("Set-Cookie",s);e.writeHead(this._code,this._headers.all),e.write(this._content),e.end()}}var f;(f=exports.HttpStatus||(exports.HttpStatus={}))[f.CONTINUE=100]="CONTINUE",f[f.SWITCHING_PROTOCOLS=101]="SWITCHING_PROTOCOLS",f[f.PROCESSING=102]="PROCESSING",f[f.EARLY_HINTS=103]="EARLY_HINTS",f[f.OK=200]="OK",f[f.CREATED=201]="CREATED",f[f.ACCEPTED=202]="ACCEPTED",f[f.NON_AUTHORITATIVE_INFORMATION=203]="NON_AUTHORITATIVE_INFORMATION",f[f.NO_CONTENT=204]="NO_CONTENT",f[f.RESET_CONTENT=205]="RESET_CONTENT",f[f.PARTIAL_CONTENT=206]="PARTIAL_CONTENT",f[f.MULTI_STATUS=207]="MULTI_STATUS",f[f.ALREADY_REPORTED=208]="ALREADY_REPORTED",f[f.IM_USED=226]="IM_USED",f[f.MULTIPLE_CHOICES=300]="MULTIPLE_CHOICES",f[f.MOVED_PERMANENTLY=301]="MOVED_PERMANENTLY",f[f.FOUND=302]="FOUND",f[f.SEE_OTHER=303]="SEE_OTHER",f[f.NOT_MODIFIED=304]="NOT_MODIFIED",f[f.USE_PROXY=305]="USE_PROXY",f[f.RESERVED=306]="RESERVED",f[f.TEMPORARY_REDIRECT=307]="TEMPORARY_REDIRECT",f[f.PERMANENTLY_REDIRECT=308]="PERMANENTLY_REDIRECT",f[f.BAD_REQUEST=400]="BAD_REQUEST",f[f.UNAUTHORIZED=401]="UNAUTHORIZED",f[f.PAYMENT_REQUIRED=402]="PAYMENT_REQUIRED",f[f.FORBIDDEN=403]="FORBIDDEN",f[f.NOT_FOUND=404]="NOT_FOUND",f[f.METHOD_NOT_ALLOWED=405]="METHOD_NOT_ALLOWED",f[f.NOT_ACCEPTABLE=406]="NOT_ACCEPTABLE",f[f.PROXY_AUTHENTICATION_REQUIRED=407]="PROXY_AUTHENTICATION_REQUIRED",f[f.REQUEST_TIMEOUT=408]="REQUEST_TIMEOUT",f[f.CONFLICT=409]="CONFLICT",f[f.GONE=410]="GONE",f[f.LENGTH_REQUIRED=411]="LENGTH_REQUIRED",f[f.PRECONDITION_FAILED=412]="PRECONDITION_FAILED",f[f.REQUEST_ENTITY_TOO_LARGE=413]="REQUEST_ENTITY_TOO_LARGE",f[f.REQUEST_URI_TOO_LONG=414]="REQUEST_URI_TOO_LONG",f[f.UNSUPPORTED_MEDIA_TYPE=415]="UNSUPPORTED_MEDIA_TYPE",f[f.REQUESTED_RANGE_NOT_SATISFIABLE=416]="REQUESTED_RANGE_NOT_SATISFIABLE",f[f.EXPECTATION_FAILED=417]="EXPECTATION_FAILED",f[f.I_AM_A_TEAPOT=418]="I_AM_A_TEAPOT",f[f.MISDIRECTED_REQUEST=421]="MISDIRECTED_REQUEST",f[f.UNPROCESSABLE_ENTITY=422]="UNPROCESSABLE_ENTITY",f[f.LOCKED=423]="LOCKED",f[f.FAILED_DEPENDENCY=424]="FAILED_DEPENDENCY",f[f.TOO_EARLY=425]="TOO_EARLY",f[f.UPGRADE_REQUIRED=426]="UPGRADE_REQUIRED",f[f.PRECONDITION_REQUIRED=428]="PRECONDITION_REQUIRED",f[f.TOO_MANY_REQUESTS=429]="TOO_MANY_REQUESTS",f[f.REQUEST_HEADER_FIELDS_TOO_LARGE=431]="REQUEST_HEADER_FIELDS_TOO_LARGE",f[f.UNAVAILABLE_FOR_LEGAL_REASONS=451]="UNAVAILABLE_FOR_LEGAL_REASONS",f[f.INTERNAL_SERVER_ERROR=500]="INTERNAL_SERVER_ERROR",f[f.NOT_IMPLEMENTED=501]="NOT_IMPLEMENTED",f[f.BAD_GATEWAY=502]="BAD_GATEWAY",f[f.SERVICE_UNAVAILABLE=503]="SERVICE_UNAVAILABLE",f[f.GATEWAY_TIMEOUT=504]="GATEWAY_TIMEOUT",f[f.VERSION_NOT_SUPPORTED=505]="VERSION_NOT_SUPPORTED",f[f.VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL=506]="VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL",f[f.INSUFFICIENT_STORAGE=507]="INSUFFICIENT_STORAGE",f[f.LOOP_DETECTED=508]="LOOP_DETECTED",f[f.NOT_EXTENDED=510]="NOT_EXTENDED",f[f.NETWORK_AUTHENTICATION_REQUIRED=511]="NETWORK_AUTHENTICATION_REQUIRED";class T extends R{constructor(e="An internal server error occurred."){super("Internal Server Error",e,exports.HttpStatus.INTERNAL_SERVER_ERROR)}}class _ extends R{constructor(e="The requested resource could not be found."){super("Not Found",e,exports.HttpStatus.NOT_FOUND)}}class m{setResponse(e){this.response=e}hasResponse(){return this.response instanceof g}getResponse(){return this.response}}class O extends m{constructor(e,t,s,r){super(),this.error=e,this.request=t,this.controller=s,this.methodName=r}}class S extends m{constructor(e,t,s,r,i){super(),this.options=e,this.request=t,this.session=s,this.templateFile=r,this.data=i}}class A extends m{constructor(e,t,s){super(),this.request=e,this.route=t,this.session=s}}class L{constructor(e,t,s,r){this.request=e,this.route=t,this.response=s,this.session=r}}class N extends m{constructor(e,t,s){super(),this.request=e,this.fileName=t,this.mimeType=s}}class I{constructor(e,t,s,r){this.request=e,this.response=t,this.fileName=s,this.mimeType=r}}const y=/\((.*?)\)/g,v=/(\(\?)?:\w+/g,x=/[\-{}\[\]+?.,\\^$|#\s]/g,w=/\*/g;class b{constructor(t,s){this.r=t;const r=new URL(t.url,"http://localhost/"),i={};r.searchParams.forEach(((e,t)=>i[t]=e)),this._clientIp=t.headers["x-forwarded-for"]||t.socket.remoteAddress,this._method=t.method.toUpperCase(),this._path=r.pathname.replace(/(\/)$/g,"")||"/",this._headers=new E(t.headers),this._cookies=new E(t.headers.cookie?this._parseCookies(t.headers.cookie):{}),this._query=new E(i),this._parameters=new E,this._post=s.fields,this._files=s.files,this._body=s.raw,this._isSecure=t.socket instanceof e.TLSSocket&&t.socket.encrypted}get(e){return this._post.has(e)?this._post.get(e):this._files.has(e)?this._files.get(e):this._headers.has(e)?this._headers.get(e):this._cookies.has(e)?this._cookies.get(e):this._parameters.has(e)?this._parameters.get(e):this._query.has(e)?this._query.get(e):void 0}get clientIp(){return this._clientIp}get isSecure(){return this._isSecure}get path(){return this._path}get method(){return this._method}get cookies(){return this._cookies}get headers(){return this._headers}get query(){return this._query}get parameters(){return this._parameters}get post(){return this._post}get files(){return this._files}get json(){try{return JSON.parse(this._body.toString())}catch(e){return null}}get body(){return this._body}isMatchingRoute(e){if(this.method!==e.method)return!1;e._matcher||(e._matcher=this._parsePattern(e.path));let t=e._matcher.regExp.exec(this.path);return!!t&&(t=t.slice(1,-1),t.reduce(((t,s,r)=>(s&&this._parameters.set(e._matcher.namedParams[r],s),t)),{}),!0)}_parsePattern(e){let t=[];return e=e.replace(x,"\\$&").replace(y,"(?:$1)?").replace(v,(function(e,s){return t.push(e.slice(1)),s?e:"([^/?]+)"})).replace(w,(function(){return t.push("path"),"([^?]*?)"})),{regExp:new RegExp("^"+e+"(?:\\?([\\s\\S]*))?$"),namedParams:t}}_parseCookies(e){if("string"!=typeof e)return{};const t={},s=e.split(/; */);for(let e=0;e{e.prototype.__ROUTES__[t].forEach((s=>{(Array.isArray(s.method)?s.method:[s.method]).forEach((r=>{this.routes.push({path:s.path,method:r,signature:s.signature,_controller:[e,t]})}))}))})):console.warn("Given controller has no registered routes.")}findByRequest(e){const t=this.routes.find((t=>e.isMatchingRoute(t)));if(t)return t}}class C{constructor(e,t,s){this.request=e,this.socket=t,this.session=s}}class M{onServerError(e){let t='\n\n\n Error\n \n \n\n\n\n
\n
\n
Something went wrong
\n
\n
\n {{ message }}\n
{{ trace }}
\n
\n
\n {{ title }}\n
\n
\n\n\n',s=e.error.title||e.error.constructor.name;e.error.statusCode&&(s=" "+e.error.statusCode+" - "+s),t=t.replace("{{ title }}",s),t=t.replace("{{ message }}",e.error.message),t=t.replace("{{ trace }}",e.error.stack),e.setResponse(new D(t,exports.HttpStatus.NOT_FOUND))}}class k{constructor(e,t){this.raw=e,this.fields=new E,this.files=new E;for(let e of t)this.addPartToBag(e,e.filename?this.files:this.fields,!!e.filename)}addPartToBag(e,t,s){const r=e.name;r.endsWith("[]")?(t.has(r)||t.set(r,[]),s?t.get(r).push({name:r,fileName:e.filename,mimeType:e.type,data:e.data}):t.get(r).push(e.data.toString())):s?t.set(r,{name:r,fileName:e.filename,mimeType:e.type,data:e.data}):t.set(r,e.data.toString())}}function q(e){const t=e.header.split(";"),s=t[2];let r={};if(s){r=function(e){const t=e.split("="),s=t[0].trim(),r=JSON.parse(t[1].trim()),i={};return Object.defineProperty(i,s,{value:r,writable:!0,enumerable:!0,configurable:!0}),i}(s);const t=e.info.split(":")[1].trim();Object.defineProperty(r,"type",{value:t,writable:!0,enumerable:!0,configurable:!0})}return Object.defineProperty(r,"name",{value:t[1].split("=")[1].replace(/"/g,""),writable:!0,enumerable:!0,configurable:!0}),Object.defineProperty(r,"data",{value:Buffer.from(e.part),writable:!0,enumerable:!0,configurable:!0}),r}class P{constructor(e){this.maxUploadSize=e}async decode(e,t){const s=await this.getRequestBody(e,t),r=e.headers["content-type"]||"";return r.toLowerCase().startsWith("multipart/form-data")?this.parseMultipartFormData(t,r,s):r.toLowerCase().startsWith("application/x-www-form-urlencoded")?this.parseFormUrlEncoded(t,s):new k(s,[])}parseMultipartFormData(e,t,s){return new k(s,function(e,t){let s="",r="",i="",n=0,o=[];const a=[];for(let c=0;c0?e[c-1]:null,E=10===h&&13===l;if(10===h||13===h||(s+=String.fromCharCode(h)),0===n&&E)"--"+t===s&&(n=1),s="";else if(1===n&&E)r=s,n=2,-1===r.indexOf("filename")&&(n=3),s="";else if(2===n&&E)i=s,n=3,s="";else if(3===n&&E)n=4,o=[],s="";else if(4===n){if(s.length>t.length+4&&(s=""),"--"+t===s){const e=o.length-s.length,t={header:r,info:i,part:o.slice(0,e-1)};a.push(q(t)),o=[],s="",n=5,r="",i=""}else o.push(h);E&&(s="")}else 5===n&&E&&(n=1)}return a}(s,function(e){const t=e.split(";");if(t)for(let e=0;e=0){const e=s.split("=");return new String(e[1]).trim()}}return""}(t)))}parseFormUrlEncoded(e,t){try{const e=r.decode(t.toString()),s=[];return Object.keys(e).forEach((t=>{s.push({name:t,data:Buffer.from(e[t])})})),new k(t,s)}catch(t){e.writeHead(413),e.end()}}getRequestBody(e,t){let s=[],r=0;return new Promise(((i,n)=>{e.on("data",(i=>{if(r+=i.length,r>this.maxUploadSize)throw t.writeHead(413),t.end(),e.socket.destroy(),new Error("Upload size too big.");s.push(i)})).on("end",(()=>{i(Buffer.concat(s))})).on("error",(e=>{n(e)}))}))}}class Y{constructor(){this.storage=new Map}delete(e){this.storage.delete(e)}gc(){const e=(new Date).getTime(),t=[];this.storage.forEach(((s,r)=>{e>s.ttl&&t.push(r)})),t.forEach((e=>this.storage.delete(e)))}get(e){return this.storage.has(e)?this.storage.get(e).data:void 0}set(e,t){this.storage.set(e,{data:t,ttl:(new Date).getTime()+864e5})}}class H{constructor(e){try{this.data=JSON.parse(e)||{}}catch(e){this.data={}}}has(e){return void 0!==this.data[e]}get(e,t){return this.has(e)?this.data[e]:t}set(e,t){this.data[e]=t}delete(e){delete this.data[e]}toString(){return JSON.stringify(this.data)}}class F{constructor(e,t){this.sessionStorage=e,this.cookieName=t,this.sessionData=new Map}onRequest(e){const t=e.request.cookies.get(this.cookieName);this.sessionData.set(t,new H(t?this.sessionStorage.get(t):"{}")),e.session=this.sessionData.get(t)}onResponse(e){const t=e.request.cookies.get(this.cookieName)||this.generateSessionId();this.sessionStorage.set(t,this.sessionData.get(t).toString()),e.response.cookies.set(this.cookieName,t,0)}getSessionByRequest(e){const t=e.cookies.get(this.cookieName);if(t)return this.sessionData.get(t)}generateSessionId(){const e=[...Array(64)].map((e=>(~~(36*Math.random())).toString(36))).join("");return this.sessionData.has(e)?this.generateSessionId():e}}class j{constructor(e,t){this.directories=e,this.requestEventListeners=[],this.responseEventListeners=[],this.defaultMimeTypes={txt:"text/plain",html:"text/html",js:"text/javascript",css:"text/css",gif:"image/gif",png:"image/png",svg:"image/svg",json:"application/json"},t||(t=e=>this.defaultMimeTypes[e]||"application/octet-stream"),this.lookupMimeType=t}registerStaticRequestEventListener(e){this.requestEventListeners.push(e),this.requestEventListeners=this.requestEventListeners.sort(((e,t)=>e.priority>t.priority?-1:1))}registerStaticResponseEventListener(e){this.responseEventListeners.push(e),this.responseEventListeners=this.responseEventListeners.sort(((e,t)=>e.priority>t.priority?-1:1))}async onErrorEvent(e){if(!(e.error instanceof _))return;const t=this.lookup(e.request.path);if(!t)return;const s=await this.lookupMimeType(l.default.extname(t).replace(/^\./,"").toLowerCase()),r=new N(e.request,t,s);let i=!1;for(let t of this.requestEventListeners)if(i=!1===await t.callback(r),r.hasResponse()&&!e.hasResponse()&&e.setResponse(r.getResponse()),i)break;const n=new g(h.default.readFileSync(t),exports.HttpStatus.OK,s),o=new I(e.request,n,t,s);i=!1;for(let t of this.responseEventListeners)if(i=!1===await t.callback(o),r.hasResponse()&&!e.hasResponse()&&e.setResponse(r.getResponse()),i)break;e.hasResponse()||e.setResponse(n)}lookup(e){for(let t of this.directories){let s=l.default.resolve(t,e.replace(/^\//,""));if(!1!==s.toLowerCase().startsWith(t.toLowerCase())&&(h.default.existsSync(s)&&!h.default.statSync(s).isDirectory()))return s}}}class G{constructor(e){this.templateDirectories=e,this.renderEventListeners=[]}registerRenderEventListener(e){this.renderEventListeners.push(e),this.renderEventListeners=this.renderEventListeners.sort(((e,t)=>e.priority>t.priority?-1:1))}async render(e,t,s,r){r=r||{};const i=this.findTemplateFile(s.name);if(!i)throw new _('The template "'+s.name+'" could not be found. Looked in "'+this.templateDirectories.join('", "')+'".');const n=new S(s.options,e,t,i,r);for(let e of this.renderEventListeners)if(await e.callback(n),n.hasResponse())return n.getResponse();throw new T('No available template renderer is able to render "'+s.name+'".')}findTemplateFile(e){for(let t of this.templateDirectories){const s=l.default.resolve(t,e);if(i.existsSync(s))return s}}}exports.AccessDeniedError=class extends R{constructor(e="Access to the requested resource is denied."){super("Access denied",e,exports.HttpStatus.FORBIDDEN)}},exports.Bag=E,exports.Cookie=u,exports.CookieBag=d,exports.ErrorEvent=O,exports.Harmony=class{constructor(e){this.options=e,this.errorEventListeners=[],this.requestEventListeners=[],this.responseEventListeners=[],this.upgradeEventListeners=[],this.typedControllerArguments=new Map,this.router=new U,this.server=e.enableHttps?c.default.createServer(e.httpsOptions,this.handle.bind(this)):a.default.createServer(this.handle.bind(this)),this.requestDecoder=new P(e.maxUploadSize||1048576),e.sni&&"object"==typeof e.sni&&(!1===e.enableHttps?console.warn("SNI configuration is ignored because HTTPS is disabled."):Object.keys(e.sni).forEach((t=>{this.server.addContext(t,e.sni[t])}))),this.registerErrorEventListener((new M).onServerError,-1/0),e.controllers&&e.controllers.forEach((e=>this.registerController(e))),e.errorEventListeners&&e.errorEventListeners.forEach((e=>this.registerErrorEventListener(e.callback,e.priority))),e.requestEventListeners&&e.requestEventListeners.forEach((e=>this.registerRequestEventListener(e.callback,e.priority))),e.responseEventListeners&&e.responseEventListeners.forEach((e=>this.registerResponseEventListener(e.callback,e.priority))),e.upgradeEventListeners&&e.upgradeEventListeners.forEach((e=>this.registerUpgradeEventListener(e.callback,e.priority))),e.enableSession&&(e.session=e.session||{},this.sessionManager=new F(e.session.storage||new Y,e.session.cookieName||"HARMONY_SID"),this.registerRequestEventListener((e=>this.sessionManager.onRequest(e)),1/0),this.registerResponseEventListener((e=>this.sessionManager.onResponse(e)),-1/0)),e.static=e.static||{},e.static.publicDirectories&&e.static.publicDirectories.length>0&&(this.staticAssetHandler=new j(e.static.publicDirectories,e.static.lookupMimeType),this.registerErrorEventListener((e=>this.staticAssetHandler.onErrorEvent(e)),1/0),Array.isArray(e.static.staticRequestEventListeners)&&e.static.staticRequestEventListeners.length>0&&e.static.staticRequestEventListeners.forEach((e=>{this.staticAssetHandler.registerStaticRequestEventListener(e)})),Array.isArray(e.static.staticResponseEventListeners)&&e.static.staticResponseEventListeners.length>0&&e.static.staticResponseEventListeners.forEach((e=>{this.staticAssetHandler.registerStaticResponseEventListener(e)}))),e.templating&&e.templating.templateDirectories&&(this.templateManager=new G(e.templating.templateDirectories),Array.isArray(e.templating.renderEventListeners)&&e.templating.renderEventListeners.length>0&&e.templating.renderEventListeners.forEach((e=>{this.templateManager.registerRenderEventListener(e)}))),this.server.on("error",(()=>{})),this.server.on("connection",(e=>{e.on("error",(()=>{}))})),this.server.on("upgrade",((e,t)=>{try{const s=new b(e,new k(Buffer.from(""),[])),r=new C(s,t,this.sessionManager?this.sessionManager.getSessionByRequest(s):void 0);for(let e of this.upgradeEventListeners)if(e.callback(r))return}catch(e){console.warn(e)}}))}start(){this.server.listen(this.options.port||8e3)}use(e){e.install(this)}get httpServer(){return this.server}addServerNameIdentificationContext(e,t){if(!(this.server instanceof c.default.Server))throw new Error("Unable to add SNI configuration while HTTPS is disabled.");this.server.addContext(e,t)}getSessionByRequest(e){if(!this.sessionManager)throw new Error("Session management is disabled.");return this.sessionManager.getSessionByRequest(e)}registerController(e){this.router.registerController(e)}registerTypedControllerArgument(e,t){this.typedControllerArguments.set(e,t)}registerErrorEventListener(e,t=0){this.errorEventListeners.push({callback:e,priority:t}),this.errorEventListeners=this.errorEventListeners.sort(((e,t)=>e.priority>t.priority?-1:1))}registerUpgradeEventListener(e,t=0){this.upgradeEventListeners.push({callback:e,priority:t}),this.upgradeEventListeners=this.upgradeEventListeners.sort(((e,t)=>e.priority>t.priority?-1:1))}registerRequestEventListener(e,t=0){this.requestEventListeners.push({callback:e,priority:t}),this.requestEventListeners=this.requestEventListeners.sort(((e,t)=>e.priority>t.priority?-1:1))}registerResponseEventListener(e,t=0){this.responseEventListeners.push({callback:e,priority:t}),this.responseEventListeners=this.responseEventListeners.sort(((e,t)=>e.priority>t.priority?-1:1))}registerStaticRequestEventListener(e,t=0){if(void 0===this.staticAssetHandler)throw new Error("Unable to register a static request event listener because static resource handling is disabled. Please ensure at least one public directory is configured.");this.staticAssetHandler.registerStaticRequestEventListener({callback:e,priority:t})}registerStaticResponseEventListener(e,t=0){if(void 0===this.staticAssetHandler)throw new Error("Unable to register a static response event listener because static resource handling is disabled. Please ensure at least one public directory is configured.");this.staticAssetHandler.registerStaticResponseEventListener({callback:e,priority:t})}registerRenderTemplateListener(e,t=0){if(void 0===this.templateManager)throw new Error("Unable to register a render template event listener because templating is currently disabled. Please ensure that at least one template directory is configured.");this.templateManager.registerRenderEventListener({callback:e,priority:t})}async handle(e,t){let s,r,i,n;try{s=await this.requestDecoder.decode(e,t),r=new b(e,s),i=this.router.findByRequest(r)}catch(e){return}try{if(!i)throw new _;if(n=this.options.serviceContainer?this.options.serviceContainer.get(i._controller[0]):new i._controller[0],"function"!=typeof n[i._controller[1]])throw new T('Method "'+i._controller[1]+'" is not an accessible method in this controller.');const e=new A(r,i,this.sessionManager?this.sessionManager.getSessionByRequest(r):void 0);let s,o=!1;for(let t of this.requestEventListeners)if(o=!1===await t.callback(e),s||!e.hasResponse()||e.getResponse().isSent||(s=e.getResponse()),o)break;if(!(s||(s=await this.handleControllerAction(n,i._controller[1],i,r),s instanceof g))){if(n.__TEMPLATES__&&n.__TEMPLATES__[i._controller[1]]){const e=this.sessionManager?this.sessionManager.getSessionByRequest(r):void 0;s=await this.templateManager.render(r,e,n.__TEMPLATES__[i._controller[1]],s)}if(!(s instanceof g))throw new T('Method "'+i._controller[1]+'" did not return a Response object.')}const a=new L(r,i,s,this.sessionManager?this.sessionManager.getSessionByRequest(r):void 0);for(let e of this.responseEventListeners)if(!1===e.callback(a))break;s.isSent||s.send(t)}catch(e){const s=new O(e,r,n,i?i._controller[1]:void 0);let o=!1;for(let e of this.errorEventListeners){const r=!1===await e.callback(s);if(!1===o&&s.hasResponse()){const e=s.getResponse();!1===e.isSent&&(e.send(t),o=!0)}if(r)return}}}async handleControllerAction(e,t,s,r){const i=e[t],n=[],o=Object.values(r.parameters.all),a=new Map;a.set(b,(e=>e)),a.set(H,(e=>{if(!this.sessionManager)throw new T("Controller attempted to access Session, but sessions are disabled.");return this.sessionManager.getSessionByRequest(r)})),this.typedControllerArguments.forEach(((e,t)=>{a.set(t,e)}));for(let e=0;ee.trim())):[]).map((e=>e.split("=")[0].trim()));if(c)for(let e=0;ee})}if(void 0!==s.__TEMPLATES__[r])throw new Error('Multiple @Template annotations found for method "'+r+'".');s.__TEMPLATES__[r]={name:e,options:t}}}; +>>>>>>> 316a8bb8be07a7acaa9403e354a0a620a6bec063 //# sourceMappingURL=index.js.map diff --git a/package.json b/package.json index 1371f2a..a06d21c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@byteshift/harmony", - "version": "1.2.6", + "version": "1.2.7", "description": "A component based HTTP server micro-framework", "author": "Harold Iedema ", "devDependencies": { diff --git a/src/Session/SessionManager.ts b/src/Session/SessionManager.ts index 3e18405..0110aec 100644 --- a/src/Session/SessionManager.ts +++ b/src/Session/SessionManager.ts @@ -31,7 +31,11 @@ export class SessionManager */ public onRequest(event: RequestEvent): void { - const _id = event.request.cookies.get(this.cookieName); + let _id = event.request.cookies.get(this.cookieName); + if (!_id) { + _id = this.generateSessionId(); + event.request.cookies.set(this.cookieName, _id); + } this.sessionData.set(_id, new Session(_id ? this.sessionStorage.get(_id) : '{}')); (event as any).session = this.sessionData.get(_id); } @@ -43,7 +47,7 @@ export class SessionManager */ public onResponse(event: ResponseEvent): void { - const sessionId = event.request.cookies.get(this.cookieName) || this.generateSessionId(); + const sessionId = event.request.cookies.get(this.cookieName); this.sessionStorage.set(sessionId, this.sessionData.get(sessionId).toString()); event.response.cookies.set(this.cookieName, sessionId, 0);