Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor new api #16

Merged
merged 9 commits into from
Sep 12, 2024
60 changes: 22 additions & 38 deletions packages/api-server/src/api-client-request.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,17 @@
import {createLogger} from '@alwatr/logger';

import type {HttpMethod, RouteHandler} from './type.js';
import type {IncomingMessage} from 'node:http';
import {NanotronServerResponse} from './api-server-response.js';

/**
* Configuration options for the Nanotron Api Client Request.
*/
export interface NanotronClientRequestConfig {
/**
* A prefix to be added to the beginning of the `url` of all defined routes.
*
* @default '/api/'
*/
prefix: `/${string}/` | '/';
}
import type {DefineRouteOption, NativeClientRequest, NativeServerResponse} from './type.js';
import type {NanotronUrl} from './url.js';
import type {Dictionary} from '@alwatr/type-helper';

export class NanotronClientRequest {
protected static versionPattern_ = new RegExp('^/v[0-9]+/');

readonly url;
readonly url: NanotronUrl;

readonly method;
readonly serverResponse: NanotronServerResponse;

readonly raw_;
readonly routeOption: DefineRouteOption | null;

/**
* A flag to indicate if the running handlers queue has been terminated.
Expand All @@ -38,33 +27,28 @@ export class NanotronClientRequest {
*/
terminatedHandlers?: true;

readonly preHandlers_: RouteHandler[] = [];
readonly sharedMeta: Dictionary = {};

protected readonly logger_;
readonly raw_: NativeClientRequest;

protected readonly config_;
protected readonly logger_;

constructor(
clientRequest: IncomingMessage,
config: NanotronClientRequestConfig,
url: NanotronUrl,
nativeClientRequest: NativeClientRequest,
nativeServerResponse: NativeServerResponse,
routeOption: DefineRouteOption | null,
) {
// Store the raw request object and configuration.
this.config_ = config;
this.raw_ = clientRequest;

// Parse request method.
this.method = (this.raw_.method ?? 'GET').toUpperCase() as HttpMethod;

// Parse request URL.
let url = this.raw_.url ?? '';
if (this.config_.prefix !== '/' && url.indexOf(this.config_.prefix) === 0) {
url = url.slice(this.config_.prefix.length - 1);
}
url = url.replace(NanotronClientRequest.versionPattern_, '/');
this.url = new URL(url, 'http://hostname/');
// Store public properties.
this.raw_ = nativeClientRequest;
this.url = url;
this.routeOption = routeOption;

// Create logger.
this.logger_ = createLogger('nt-client-request'); // TODO: add client ip
this.logger_.logMethodArgs?.('new', {method: this.method, url: this.url.pathname});
this.logger_.logMethodArgs?.('new', url.debugId);

// Create server response.
this.serverResponse = new NanotronServerResponse(this, nativeServerResponse);
}
}
54 changes: 17 additions & 37 deletions packages/api-server/src/api-server-response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import {createLogger} from '@alwatr/logger';
import {type HttpStatusCode, HttpStatusCodes, HttpStatusMessages} from './const.js';

import type {NanotronClientRequest} from './api-client-request.js';
import type {HttpResponseHeaders, ErrorResponse} from './type.js';
import type {HttpResponseHeaders, ErrorResponse, NativeServerResponse} from './type.js';
import type {Json} from '@alwatr/type-helper';
import type {ServerResponse} from 'node:http';

/**
* Configuration options for the Nanotron Api Server Response.
Expand All @@ -15,30 +14,27 @@ export interface NanotronServerResponseConfig {
}

export class NanotronServerResponse {
readonly raw_;
readonly clientRequest: NanotronClientRequest;

readonly raw_: NativeServerResponse;

readonly headers: HttpResponseHeaders;

protected readonly logger_;

protected readonly config_;

protected hasBeenSent_ = false;
get hasBeenSent(): boolean {
return this.hasBeenSent_;
}

constructor(
serverResponse: ServerResponse,
config: NanotronServerResponseConfig,
) {
// Store the raw request object and configuration.
this.config_ = config;
this.raw_ = serverResponse;
constructor(nanotronClientRequest: NanotronClientRequest, nativeServerResponse: NativeServerResponse) {
// Store public properties.
this.clientRequest = nanotronClientRequest;
this.raw_ = nativeServerResponse;

// Create logger.
this.logger_ = createLogger('nt-server-response'); // TODO: add client ip
this.logger_.logMethod?.('new');
this.logger_.logMethodArgs?.('new', this.clientRequest.url.debugId);

// Set default reply headers.
this.headers = {
Expand All @@ -64,14 +60,14 @@ export class NanotronServerResponse {

replyErrorResponse(errorResponse: ErrorResponse): void {
this.logger_.logMethod?.('replyErrorResponse');
this.config_.clientRequest.terminatedHandlers = true;
this.clientRequest.terminatedHandlers = true;
this.replyJson(errorResponse);
}

replyError(error?: Error | string | Json | unknown): void {
this.logger_.logMethodArgs?.('replyError', {error});

this.config_.clientRequest.terminatedHandlers = true;
this.clientRequest.terminatedHandlers = true;
let statusCode = this.statusCode;

if (statusCode < HttpStatusCodes.Error_Client_400_Bad_Request) {
Expand All @@ -81,30 +77,25 @@ export class NanotronServerResponse {
if (error instanceof Error) {
this.replyJson({
ok: false,
errorCode: error.name === 'Error'
? ('error_' + statusCode) as Lowercase<string>
: (error.name + '').toLowerCase(),
errorCode: error.name === 'Error' ? (('error_' + statusCode) as Lowercase<string>) : (error.name + '').toLowerCase(),
errorMessage: error.message,
});
}

else if (typeof error === 'string') {
this.replyJson({
ok: false,
errorCode: ('error_' + statusCode) as Lowercase<string>,
errorMessage: error,
});
}

else if (typeof error === 'object' && error !== null) {
this.replyJson(error as Json);
}

else {
this.replyJson({
ok: false,
errorCode: ('error_' + statusCode) as Lowercase<string>,
errorMessage: HttpStatusMessages[statusCode]
errorMessage: HttpStatusMessages[statusCode],
} as ErrorResponse);
}
}
Expand All @@ -117,10 +108,7 @@ export class NanotronServerResponse {
responseString = JSON.stringify(responseJson);
}
catch (error) {
this.logger_.error('replyJson', 'reply_json_stringify_failed', error, {
url: this.config_.clientRequest.url.pathname,
method: this.config_.clientRequest.method,
});
this.logger_.error('replyJson', 'reply_json_stringify_failed', error, this.clientRequest.url.debugId);
this.statusCode = HttpStatusCodes.Error_Server_500_Internal_Server_Error;
responseString = JSON.stringify({
ok: false,
Expand All @@ -134,10 +122,7 @@ export class NanotronServerResponse {
}

reply(context: string | Buffer): void {
this.logger_.logMethodArgs?.('reply', {
url: this.config_.clientRequest.url.pathname,
method: this.config_.clientRequest.method,
});
this.logger_.logMethodArgs?.('reply', this.clientRequest.url.debugId);

if (this.raw_.writableFinished && this.hasBeenSent_ === false) {
// The response has already been sent by direct access to the server api.
Expand All @@ -147,8 +132,7 @@ export class NanotronServerResponse {

if (this.hasBeenSent_) {
this.logger_.accident('reply', 'reply_already_sent', {
url: this.config_.clientRequest.url.pathname,
method: this.config_.clientRequest.method,
url: this.clientRequest.url.debugId,
replySent: this.hasBeenSent_,
writableFinished: this.raw_.writableFinished,
});
Expand All @@ -166,13 +150,9 @@ export class NanotronServerResponse {

this.applyHeaders_();
this.raw_.end(context, 'binary');

}
catch (error) {
this.logger_.error('reply', 'server_response_error', error, {
url: this.config_.clientRequest.url.pathname,
method: this.config_.clientRequest.method,
});
this.logger_.error('reply', 'server_response_error', error, this.clientRequest.url.debugId);
this.hasBeenSent_ = false;
}
}
Expand Down
Loading