Skip to content

Commit

Permalink
refactor(api-server): Update NanotronClientRequest class and Nanotron…
Browse files Browse the repository at this point in the history
…ServerResponse class

- Removed unused imports and updated the imports for NanotronClientRequest and NanotronServerResponse classes in api-server.ts.
- Added import for NanotronUrl in api-server.ts.

- Updated the type definitions in type.ts to include NativeClientRequest and NativeServerResponse types.

- Updated the bodyLimit default value in NanotronApiServer class to 1MiB.

- Refactored the handleClientRequest_ method in NanotronApiServer class to use the new NativeClientRequest and NativeServerResponse types.

- Updated the getRouteOption_ method in NanotronApiServer class to use the new NanotronUrl class.

- Removed unused code and added error handling in the handleClientRequest_ method.

- Updated the comments and log messages for better clarity and readability.
  • Loading branch information
alimd committed Sep 12, 2024
1 parent 468d3d9 commit 7b587e0
Showing 1 changed file with 42 additions and 84 deletions.
126 changes: 42 additions & 84 deletions packages/api-server/src/api-server.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import {createServer, IncomingMessage, ServerResponse} from 'node:http';
import {createServer} from 'node:http';

import {createLogger} from '@alwatr/logger';

import {NanotronClientRequest} from './api-client-request.js';
import { NanotronServerResponse } from './api-server-response.js';
import {HttpStatusCodes} from './const.js';
import {NanotronUrl} from './url.js';

import type {HttpMethod, MatchType, RouteHandler} from './type.js';
import type {DefineRouteOption, MatchType, NativeClientRequest, NativeServerResponse} from './type.js';
import type {Dictionary} from '@alwatr/type-helper';
import type {Duplex} from 'node:stream';

Expand Down Expand Up @@ -71,43 +71,13 @@ export interface NanotronApiServerConfig {
* @default '/api/'
*/
prefix?: `/${string}/` | '/';
}

/**
* Configuration options for defining a route.
*/
export interface DefineRouteOption {
/**
* The HTTP method for this route.
*/
method: HttpMethod;

/**
* The URL path for this route.
*/
url: string;

/**
* Specifies how the `url` should be matched against incoming requests.
* The maximum size of the request body in bytes.
*
* @default 'exact'
*/
matchType?: MatchType;

/**
* The functions call before the main handler.
*/
preHandlers?: RouteHandler[];

/**
* The function to handle requests to this route.
* @default `1_048_576` (1MiB)
*/
handler: RouteHandler;

/**
* The functions call after the main handler.
*/
postHandlers?: RouteHandler[];
bodyLimit?: number;
}

export class NanotronApiServer {
Expand All @@ -120,9 +90,10 @@ export class NanotronApiServer {
healthRoute: true,
allowAllOrigin: false,
prefix: '/api/',
bodyLimit: 1_048_576, // 1MiB
};

protected readonly config_;
readonly config_;
protected readonly logger_;

readonly httpServer;
Expand Down Expand Up @@ -181,26 +152,26 @@ export class NanotronApiServer {
this.httpServer.close();
}

protected getRouteOption_(option: Required<Pick<DefineRouteOption, 'method' | 'url'>>): Required<DefineRouteOption> | null {
this.logger_.logMethodArgs?.('getRouteOption_', option);
protected getRouteOption_(url: NanotronUrl): Required<DefineRouteOption> | null {
this.logger_.logMethod?.('getRouteOption_');

if (
Object.hasOwn(this.routeHandlerList__.exact, option.method) &&
Object.hasOwn(this.routeHandlerList__.exact[option.method], option.url)
Object.hasOwn(this.routeHandlerList__.exact, url.method) &&
Object.hasOwn(this.routeHandlerList__.exact[url.method], url.pathname)
) {
return this.routeHandlerList__.exact[option.method][option.url];
return this.routeHandlerList__.exact[url.method][url.pathname];
}

if (Object.hasOwn(this.routeHandlerList__.startsWith, option.method)) {
const routeList = this.routeHandlerList__.startsWith[option.method];
for (const url in routeList) {
if (url.indexOf(option.url) === 0) {
return routeList[url];
if (Object.hasOwn(this.routeHandlerList__.startsWith, url.method)) {
const routeList = this.routeHandlerList__.startsWith[url.method];
for (const pathname in routeList) {
if (pathname.indexOf(url.pathname) === 0) {
return routeList[pathname];
}
}
}

this.logger_.incident?.('getRouteOption_', 'route_not_found', option);
this.logger_.incident?.('getRouteOption_', 'route_not_found', {method: url.method, url: url.pathname});
return null;
}

Expand All @@ -224,6 +195,7 @@ export class NanotronApiServer {
matchType: 'exact',
preHandlers: [],
postHandlers: [],
bodyLimit: this.config_.bodyLimit,
...option,
};
this.logger_.logMethodArgs?.('defineRoute', {...option_, handler: 'function'});
Expand All @@ -247,68 +219,54 @@ export class NanotronApiServer {
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
}

protected handleHttpError_(serverResponse: NanotronServerResponse, error?: unknown): void {
this.logger_.logMethod?.('handleHttpError_');
// TODO: custom error template by the user.
serverResponse.replyError(error);
}

protected async handleClientRequest_(clientRequest: IncomingMessage, serverResponse: ServerResponse): Promise<void> {
protected async handleClientRequest_(
nativeClientRequest: NativeClientRequest,
nativeServerResponse: NativeServerResponse,
): Promise<void> {
this.logger_.logMethod?.('handleClientRequest_');

if (clientRequest.url === undefined) {
if (nativeClientRequest.url === undefined) {
this.logger_.accident('handleClientRequest_', 'http_server_url_undefined');
return;
}

if (clientRequest.method === undefined) {
if (nativeClientRequest.method === undefined) {
this.logger_.accident('handleClientRequest_', 'http_server_method_undefined');
return;
}

const nanotronClientRequest = new NanotronClientRequest(clientRequest, {prefix: this.config_.prefix});
const nanotronServerResponse = new NanotronServerResponse(serverResponse, {clientRequest: nanotronClientRequest});
const url = new NanotronUrl(nativeClientRequest, this.config_.prefix);

const routeOption = this.getRouteOption_({
method: nanotronClientRequest.method,
url: nanotronClientRequest.url.pathname,
});
const routeOption = this.getRouteOption_(url);

const connection = new NanotronClientRequest(url, nativeClientRequest, nativeServerResponse, routeOption);

if (routeOption === null) {
nanotronServerResponse.statusCode = HttpStatusCodes.Error_Client_404_Not_Found;
return this.handleHttpError_(nanotronServerResponse);
connection.serverResponse.statusCode = HttpStatusCodes.Error_Client_404_Not_Found;
connection.serverResponse.replyError();
return;
}

const sharedMeta = {};

try {
for (const handler of nanotronClientRequest.preHandlers_) {
if (nanotronClientRequest.terminatedHandlers === true) return;
await handler(nanotronClientRequest, nanotronServerResponse, sharedMeta);
}

for (const handler of routeOption.preHandlers) {
if (nanotronClientRequest.terminatedHandlers === true) return;
await handler(nanotronClientRequest, nanotronServerResponse, sharedMeta);
if (connection.terminatedHandlers === true) return;
await handler(connection, connection.serverResponse, connection.sharedMeta);
}

await routeOption.handler(nanotronClientRequest, nanotronServerResponse, sharedMeta);
await routeOption.handler(connection, connection.serverResponse, connection.sharedMeta);

for (const handler of routeOption.postHandlers) {
if (nanotronClientRequest.terminatedHandlers === true) return;
await handler(nanotronClientRequest, nanotronServerResponse, sharedMeta);
if (connection.terminatedHandlers === true) return;
await handler(connection, connection.serverResponse, connection.sharedMeta);
}
}
catch (error) {
this.logger_.error('handleClientRequest_', 'route_handler_error', error, {
url: nanotronClientRequest.url.pathname,
method: nanotronClientRequest.method,
});
this.logger_.error('handleClientRequest_', 'route_handler_error', error, url.debugId);

if (nanotronServerResponse.statusCode < HttpStatusCodes.Error_Client_400_Bad_Request) {
nanotronServerResponse.statusCode = HttpStatusCodes.Error_Server_500_Internal_Server_Error;
if (connection.serverResponse.statusCode < HttpStatusCodes.Error_Client_400_Bad_Request) {
connection.serverResponse.statusCode = HttpStatusCodes.Error_Server_500_Internal_Server_Error;
}
this.handleHttpError_(nanotronServerResponse, error);
connection.serverResponse.replyError(error);
}

// TODO: handled open remained connections.
Expand Down

0 comments on commit 7b587e0

Please sign in to comment.