diff --git a/src/lib/App.ts b/src/lib/App.ts index 12efa0a..f865b81 100644 --- a/src/lib/App.ts +++ b/src/lib/App.ts @@ -56,7 +56,7 @@ export default class App implements IApp { public handle(event: IRawEvent, callback: IRawCallback): void { if (event.isHttp) { - const req: IHttpRequest = new HttpRequest(event); + const req: IHttpRequest = new HttpRequest(this, event); const res: IHttpResponse = new HttpResponse(this, req, callback); const done = httpFinalHandler(req, res, { env: this.get(configuration.ENVIRONMENT), diff --git a/src/lib/http/HttpRequest.ts b/src/lib/http/HttpRequest.ts index f285733..ae7a3d5 100644 --- a/src/lib/http/HttpRequest.ts +++ b/src/lib/http/HttpRequest.ts @@ -1,12 +1,16 @@ import * as accepts from "accepts"; import * as fresh from "fresh"; +import configuration from "./../configuration/configuration"; +import ICookie from "./../types/http/ICookie"; import IHttpRequest from "./../types/http/IHttpRequest"; import IHttpResponse from "./../types/http/IHttpResponse"; import IHttpRoute from "./../types/http/IHttpRoute"; import IHttpUploadedFile from "./../types/http/IHttpUploadedFile"; +import IApp from "./../types/IApp"; import INext from "./../types/INext"; import IRawEvent from "./../types/IRawEvent"; -import { mergeParams, normalizeType } from "./../utils/utils"; +import { getCookiesFromHeader, mergeParams, normalizeType } from "./../utils/utils"; +import Cookie from "./Cookie"; /** * A incoming request created when the event is APIGatewayEvent. @@ -24,8 +28,9 @@ export default class HttpRequest implements IHttpRequest { private _event: IRawEvent; private _headers: { [name: string]: string }; private _context: { [name: string]: any }; + private _cookies: { [name: string]: ICookie }; - constructor(event: IRawEvent) { + constructor(app: IApp, event: IRawEvent) { this.body = event.body; // Default body this._event = event; this._context = {}; @@ -35,6 +40,8 @@ export default class HttpRequest implements IHttpRequest { for (const key of Object.keys(this._event.headers)) { this._headers[key.toLowerCase()] = this._event.headers[key]; } + + this._cookies = getCookiesFromHeader(this._headers.Cookie, app.get(configuration.COOKIE_SECRET)); } get headers(): { [name: string]: string } { @@ -85,6 +92,10 @@ export default class HttpRequest implements IHttpRequest { return this._context; } + get cookies(): { [name: string]: ICookie } { + return this._cookies; + } + public header(key: string): string { return this.headers[key.toLowerCase()]; } @@ -162,4 +173,8 @@ export default class HttpRequest implements IHttpRequest { return !this.fresh(response); } + public cookie(name: string): ICookie { + return this._cookies[name]; + } + } diff --git a/src/lib/http/HttpResponse.ts b/src/lib/http/HttpResponse.ts index b293b52..22380f8 100644 --- a/src/lib/http/HttpResponse.ts +++ b/src/lib/http/HttpResponse.ts @@ -1,4 +1,4 @@ -import { parse, serialize } from "cookie"; +import { serialize } from "cookie"; import { sign } from "cookie-signature"; import * as encodeUrl from "encodeurl"; import * as escapeHtml from "escape-html"; diff --git a/src/lib/types/http/IHttpRequest.ts b/src/lib/types/http/IHttpRequest.ts index 64e105d..e97ecc8 100644 --- a/src/lib/types/http/IHttpRequest.ts +++ b/src/lib/types/http/IHttpRequest.ts @@ -1,4 +1,5 @@ import INext from "./../INext"; +import ICookie from "./ICookie"; import IHttpResponse from "./IHttpResponse"; import IHttpRoute from "./IHttpRoute"; import IHttpUploadedFile from "./IHttpUploadedFile"; @@ -94,6 +95,13 @@ export default interface IHttpRequest { */ readonly context: { [name: string]: any }; + /** + * Returns the all the cookies retrieving their values and + * options from the HTTP request header. The key will be the name of + * the cookie and the value the object representing the cookie. + */ + readonly cookies: {[name: string]: ICookie}; + /** * Return request header. * @@ -180,4 +188,13 @@ export default interface IHttpRequest { */ stale(response: IHttpResponse): boolean; + /** + * Returns the cookie with the given `name` retrieving the value and + * options from the HTTP request header. + * + * @param {string} name + * @return {ICookie} + */ + cookie(name: string): ICookie; + } diff --git a/src/lib/utils/utils.ts b/src/lib/utils/utils.ts index 0d11f56..cc05152 100644 --- a/src/lib/utils/utils.ts +++ b/src/lib/utils/utils.ts @@ -1,5 +1,9 @@ -import { format, parse } from "content-type"; +import { format, parse as parseContentType } from "content-type"; +import { parse as parseCookie } from "cookie"; +import { unsign } from "cookie-signature"; import { lookup } from "mime-types"; +import Cookie from "./../http/Cookie"; +import ICookie from "./../types/http/ICookie"; import IRawEvent from "./../types/IRawEvent"; /** @@ -12,7 +16,7 @@ export function setCharset(contentType: string, charset: string): string { } // parse type - const parsed = parse(contentType); + const parsed = parseContentType(contentType); // set charset parsed.parameters.charset = charset; @@ -71,3 +75,33 @@ export function normalizeType(type: string): string { ? lookup(type) : type; } + +const parseCookieValue = (rawValue: string, secret: string): string | { [name: string]: any } => { + let result: string | { [name: string]: any } = rawValue; + + if (result.startsWith("s:")) { + result = unsign(result.substr(2), secret) as string; + } + + if (result.startsWith("j:")) { + result = JSON.parse(result.substr(2)); + } + + return result; +}; + +export function getCookiesFromHeader(header: string, secret: string): { [name: string]: ICookie } { + const result: { [name: string]: ICookie } = {}; + + if (header) { + const cookiesAsObject: { [name: string]: string } = parseCookie(header); + + for (const cookieName of Object.keys(cookiesAsObject)) { + const cookieValue: string | { [name: string]: any } = parseCookieValue(cookiesAsObject[cookieName], secret); + + result[cookieName] = new Cookie(cookieName, cookieValue); + } + } + + return result; +}