Skip to content

Commit

Permalink
save progress
Browse files Browse the repository at this point in the history
  • Loading branch information
Zamralik committed May 29, 2024
1 parent c41f44a commit 8ff6640
Show file tree
Hide file tree
Showing 21 changed files with 1,305 additions and 160 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/
const enum ContentTypeEnum
{
UNKNOWN = "application/octet-stream",
BINARY = "application/octet-stream",
TEXT = "text/plain",
// eslint-disable-next-line @typescript-eslint/no-shadow -- This rule incorrectly applies to enum keys
JSON = "application/json",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
*/
const enum CookieSameSiteEnum
{
None = "None",
Lax = "Lax",
Strict = "Strict",
NONE = "None",
LAX = "Lax",
STRICT = "Strict",
}

export { CookieSameSiteEnum };
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ class RichClientRequest extends IncomingMessage
this.cookies = new Map();
this.path = undefined;
this.pathFragments = [];
this.query = {};
/* parseQuery returns a null-prototype object */
this.query = parseQuery("");
this.contentType = undefined;
this.boundary = undefined;
this.rawBody = Promise.resolve(Buffer.alloc(0));
Expand Down
68 changes: 40 additions & 28 deletions packages/architectura/src/core/server/rich-server-response.mts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { CookieSameSiteEnum } from "./definition/enum/cookie-same-site.enum.mjs"
import { ContentEncodingEnum } from "./definition/enum/content-encoding.enum.mjs";
import { LoggerProxy } from "../../service/logger/logger.proxy.mjs";
import { isErrorWithCode } from "../../predicate/is-error-with-code.mjs";
import { parseQualityValues } from "../../utility/parse-quality-values.mjs";

/**
* Rich server response.
Expand Down Expand Up @@ -106,23 +107,30 @@ class RichServerResponse extends HTTPServerResponse<RichClientRequest>

this.locked = true;

if (isNumber(parameters))
try
{
this.statusCode = parameters;
this.processCookieHeader();
await this.writePayload();
if (isNumber(parameters))
{
this.statusCode = parameters;
this.processCookieHeader();
await this.writePayload();

return;
}
return;
}

this.statusCode = parameters.status ?? HTTPStatusCodeEnum.OK;
this.statusCode = parameters.status ?? HTTPStatusCodeEnum.OK;

this.processHeaders(parameters);
this.processCookies(parameters);
this.processContentType(parameters);
this.processPayload(parameters);
this.processCookieHeader();
await this.writePayload();
this.processHeaders(parameters);
this.processCookies(parameters);
this.processContentType(parameters);
this.processPayload(parameters);
this.processCookieHeader();
await this.writePayload();
}
finally
{
this.processed = true;
}
}

/**
Expand Down Expand Up @@ -288,14 +296,19 @@ class RichServerResponse extends HTTPServerResponse<RichClientRequest>
{
this.assertUnlocked();

this.defineCookie(descriptor);
}

private defineCookie(descriptor: CookieDescriptorInterface): void
{
if (descriptor.expires !== undefined && descriptor.maxAge !== undefined)
{
throw new Error("A cookie can't have both an expires and max-age attribute.");
throw new Error("A cookie can't have both an 'Expires' and 'Max-Age' attribute.");
}

if (descriptor.sameSite === CookieSameSiteEnum.None && !(descriptor.secure ?? this.isSecure()))
if (descriptor.sameSite === CookieSameSiteEnum.NONE && !(descriptor.secure ?? this.isSecure()))
{
throw new Error("A cookie can't have the attribute SameSite equals to None without the attribute Secure.");
throw new Error("A cookie can't have the 'SameSite' attribute equals to 'None' without the 'Secure' attribute.");
}

this.cookies.set(descriptor.name, descriptor);
Expand Down Expand Up @@ -394,7 +407,7 @@ class RichServerResponse extends HTTPServerResponse<RichClientRequest>
parameters.cookies.forEach(
(descriptor: CookieDescriptorInterface): void =>
{
this.setCookie(descriptor);
this.defineCookie(descriptor);
}
);
}
Expand All @@ -420,7 +433,14 @@ class RichServerResponse extends HTTPServerResponse<RichClientRequest>
return;
}

this.setHeader("Content-Type", ContentTypeEnum.TEXT);
if (isString(parameters.payload))
{
this.setHeader("Content-Type", ContentTypeEnum.TEXT);

return;
}

this.setHeader("Content-Type", ContentTypeEnum.BINARY);
}

private processPayload(parameters: ReplyInterface): void
Expand Down Expand Up @@ -463,7 +483,6 @@ class RichServerResponse extends HTTPServerResponse<RichClientRequest>
if (this.content === undefined)
{
this.end();
this.processed = true;

return;
}
Expand All @@ -474,7 +493,6 @@ class RichServerResponse extends HTTPServerResponse<RichClientRequest>
{
this.write(this.content);
this.end();
this.processed = true;

return;
}
Expand Down Expand Up @@ -503,10 +521,6 @@ class RichServerResponse extends HTTPServerResponse<RichClientRequest>
throw error;
}
}
finally
{
this.processed = true;
}
}

private findAcceptedEncoding(): ContentEncodingEnum | undefined
Expand All @@ -518,12 +532,10 @@ class RichServerResponse extends HTTPServerResponse<RichClientRequest>
return undefined;
}

const ACCEPTED_ENCODINGS: Array<string> = ACCEPT_ENCODING_HEADER.split(", ");
const ACCEPTED_ENCODINGS: Array<string> = parseQualityValues(ACCEPT_ENCODING_HEADER);

for (const RAW_ACCEPTED_ENCODING of ACCEPTED_ENCODINGS)
for (const ACCEPTED_ENCODING of ACCEPTED_ENCODINGS)
{
const ACCEPTED_ENCODING: string | undefined = RAW_ACCEPTED_ENCODING.split(";")[0];

if (ACCEPTED_ENCODING === ContentEncodingEnum.GZIP)
{
return ContentEncodingEnum.GZIP;
Expand Down
1 change: 1 addition & 0 deletions packages/architectura/src/utility/_index.mts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./content-type/_index.mjs";
export * from "./parse-quality-values.mjs";
export * from "./singleton.mjs";
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class ContentType
*/
public static Get(extension: string): string
{
return EXTENSION_TO_MIME_TYPE_MAPPING.get(extension) ?? ContentTypeEnum.UNKNOWN;
return EXTENSION_TO_MIME_TYPE_MAPPING.get(extension) ?? ContentTypeEnum.BINARY;
}
}

Expand Down
60 changes: 60 additions & 0 deletions packages/architectura/src/utility/parse-quality-values.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { splitOnce } from "@vitruvius-labs/toolbox";

/**
* Parses a string of quality values.
*
* @remarks
* - Order values by weight in descending order.
* - Preserve order of values with the same weight.
* - Default weight is 1.
*/
function parseQualityValues(input: string): Array<string>
{
interface QualityValueInterface
{
value: string;
weight: number;
index: number;
}

if (input === "")
{
return [];
}

return input
.split(",")
.map(
(serialized_value: string, index: number): QualityValueInterface =>
{
const TRIMMED_VALUE: string = serialized_value.trim();

const [VALUE, WEIGHT]: [string, string] = splitOnce(";q=", TRIMMED_VALUE);

return {
value: VALUE,
weight: WEIGHT === "" ? 1 : parseFloat(WEIGHT),
index: index,
};
}
)
.sort(
(a: QualityValueInterface, z: QualityValueInterface): number =>
{
if (a.weight === z.weight)
{
return a.index - z.index;
}

return z.weight - a.weight;
}
)
.map(
(quality_value: QualityValueInterface): string =>
{
return quality_value.value;
}
);
}

export { parseQualityValues };
Loading

0 comments on commit 8ff6640

Please sign in to comment.