Handle serializers asynchronously (!428)

In combination with my other PR handle all the serializers asynchronously too, this should almost result in a fully asynchronous sending of responses.

Reviewed-on: https://dev.sp-tarkov.com/SPT/Server/pulls/428
Co-authored-by: Archangel <jesse@archangel.wtf>
Co-committed-by: Archangel <jesse@archangel.wtf>
This commit is contained in:
Archangel 2024-11-14 17:30:22 +00:00 committed by chomp
parent 9f8fbd181f
commit b841addd78
7 changed files with 17 additions and 18 deletions

View File

@ -1,7 +1,7 @@
import { IncomingMessage, ServerResponse } from "node:http"; import { IncomingMessage, ServerResponse } from "node:http";
export class Serializer { export class Serializer {
public serialize(sessionID: string, req: IncomingMessage, resp: ServerResponse, body: any): void { public async serialize(sessionID: string, req: IncomingMessage, resp: ServerResponse, body: any): Promise<void> {
throw new Error("Should be extended and overrode"); throw new Error("Should be extended and overrode");
} }

View File

@ -16,13 +16,13 @@ export class ImageRouter {
this.imageRouteService.addRoute(key, valueToAdd); this.imageRouteService.addRoute(key, valueToAdd);
} }
public sendImage(sessionID: string, req: IncomingMessage, resp: ServerResponse, body: any): void { public async sendImage(sessionID: string, req: IncomingMessage, resp: ServerResponse, body: any): Promise<void> {
// remove file extension // remove file extension
const url = this.vfs.stripExtension(req.url); const url = this.vfs.stripExtension(req.url);
// send image // send image
if (this.imageRouteService.existsByKey(url)) { if (this.imageRouteService.existsByKey(url)) {
this.httpFileUtil.sendFile(resp, this.imageRouteService.getByKey(url)); await this.httpFileUtil.sendFileAsync(resp, this.imageRouteService.getByKey(url));
} }
} }

View File

@ -15,7 +15,7 @@ export class BundleSerializer extends Serializer {
super(); super();
} }
public override serialize(sessionID: string, req: IncomingMessage, resp: ServerResponse, body: any): void { public override async serialize(sessionID: string, req: IncomingMessage, resp: ServerResponse, body: any): Promise<void> {
const key = decodeURI(req.url.split("/bundle/")[1]); const key = decodeURI(req.url.split("/bundle/")[1]);
const bundle = this.bundleLoader.getBundle(key); const bundle = this.bundleLoader.getBundle(key);
if (!bundle) { if (!bundle) {
@ -29,7 +29,7 @@ export class BundleSerializer extends Serializer {
return; return;
} }
this.httpFileUtil.sendFile(resp, `${bundle.modpath}/bundles/${bundle.filename}`); await this.httpFileUtil.sendFileAsync(resp, `${bundle.modpath}/bundles/${bundle.filename}`);
} }
public override canHandle(route: string): boolean { public override canHandle(route: string): boolean {

View File

@ -9,8 +9,8 @@ export class ImageSerializer extends Serializer {
super(); super();
} }
public override serialize(sessionID: string, req: IncomingMessage, resp: ServerResponse, body: any): void { public override async serialize(sessionID: string, req: IncomingMessage, resp: ServerResponse, body: any): Promise<void> {
this.imageRouter.sendImage(sessionID, req, resp, body); await this.imageRouter.sendImage(sessionID, req, resp, body);
} }
public override canHandle(route: string): boolean { public override canHandle(route: string): boolean {

View File

@ -15,7 +15,7 @@ export class NotifySerializer extends Serializer {
super(); super();
} }
public override serialize(_sessionID: string, req: IncomingMessage, resp: ServerResponse, _: any): void { public override async serialize(_sessionID: string, req: IncomingMessage, resp: ServerResponse, _: any): Promise<void> {
const splittedUrl = req.url.split("/"); const splittedUrl = req.url.split("/");
const tmpSessionID = splittedUrl[splittedUrl.length - 1].split("?last_id")[0]; const tmpSessionID = splittedUrl[splittedUrl.length - 1].split("?last_id")[0];
@ -23,7 +23,7 @@ export class NotifySerializer extends Serializer {
* Take our array of JSON message objects and cast them to JSON strings, so that they can then * Take our array of JSON message objects and cast them to JSON strings, so that they can then
* be sent to client as NEWLINE separated strings... yup. * be sent to client as NEWLINE separated strings... yup.
*/ */
this.notifierController await this.notifierController
.notifyAsync(tmpSessionID) .notifyAsync(tmpSessionID)
.then((messages: any) => messages.map((message: any) => this.jsonUtil.serialize(message)).join("\n")) .then((messages: any) => messages.map((message: any) => this.jsonUtil.serialize(message)).join("\n"))
.then((text) => this.httpServerHelper.sendTextJson(resp, text)); .then((text) => this.httpServerHelper.sendTextJson(resp, text));

View File

@ -107,7 +107,7 @@ export class SptHttpListener implements IHttpListener {
// Not debug, minority of requests need a serializer to do the job (IMAGE/BUNDLE/NOTIFY) // Not debug, minority of requests need a serializer to do the job (IMAGE/BUNDLE/NOTIFY)
const serialiser = this.serializers.find((x) => x.canHandle(output)); const serialiser = this.serializers.find((x) => x.canHandle(output));
if (serialiser) { if (serialiser) {
serialiser.serialize(sessionID, req, resp, bodyInfo); await serialiser.serialize(sessionID, req, resp, bodyInfo);
} else { } else {
// No serializer can handle the request (majority of requests dont), zlib the output and send response back // No serializer can handle the request (majority of requests dont), zlib the output and send response back
await this.sendZlibJson(resp, output, sessionID); await this.sendZlibJson(resp, output, sessionID);

View File

@ -2,21 +2,20 @@ import fs from "node:fs";
import { ServerResponse } from "node:http"; import { ServerResponse } from "node:http";
import { HttpServerHelper } from "@spt/helpers/HttpServerHelper"; import { HttpServerHelper } from "@spt/helpers/HttpServerHelper";
import { inject, injectable } from "tsyringe"; import { inject, injectable } from "tsyringe";
import { pipeline } from 'stream/promises';
@injectable() @injectable()
export class HttpFileUtil { export class HttpFileUtil {
constructor(@inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper) {} constructor(@inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper) {}
public sendFile(resp: ServerResponse, filePath: string): void { public async sendFileAsync(resp: ServerResponse, filePath: string): Promise<void> {
const pathSlic = filePath.split("/"); const pathSlice = filePath.split("/");
const type = const type =
this.httpServerHelper.getMimeText(pathSlic[pathSlic.length - 1].split(".").at(-1) ?? "") || this.httpServerHelper.getMimeText(pathSlice[pathSlice.length - 1].split(".").at(-1) ?? "") ||
this.httpServerHelper.getMimeText("txt"); this.httpServerHelper.getMimeText("txt");
const fileStream = fs.createReadStream(filePath);
fileStream.on("open", () => { resp.setHeader("Content-Type", type);
resp.setHeader("Content-Type", type);
fileStream.pipe(resp); await pipeline(fs.createReadStream(filePath), resp);
});
} }
} }