Skip to content

Commit

Permalink
fix: optimize serving static files
Browse files Browse the repository at this point in the history
Optimize serving static files
  • Loading branch information
Hage Yaapa committed Oct 16, 2018
1 parent a2ab86d commit acb0089
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 12 deletions.
28 changes: 21 additions & 7 deletions packages/rest/src/rest.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
Route,
RouteEntry,
RoutingTable,
StaticRoute,
} from './router/routing-table';

import {DefaultSequence, SequenceFunction, SequenceHandler} from './sequence';
Expand All @@ -46,6 +47,7 @@ import {
Send,
} from './types';
import {ServerOptions} from 'https';
import * as HttpErrors from 'http-errors';

const debug = require('debug')('loopback:rest:server');

Expand Down Expand Up @@ -190,6 +192,25 @@ export class RestServer extends Context implements Server, HttpServerLike {
this._setupRequestHandler();

this.bind(RestBindings.HANDLER).toDynamicValue(() => this.httpHandler);

// LB4's static assets serving router
const staticAssetsRouter = new StaticRoute(
(req: Request, res: Response) => {
return new Promise((resolve, reject) => {
const onFinished = () => resolve();
res.once('finish', onFinished);
this._routerForStaticAssets.handle(req, res, (err: Error) => {
if (err) {
return reject(err);
}
// Express router called next, which means no route was matched
return reject(new HttpErrors.NotFound());
});
});
},
);

this.httpHandler.registerRoute(staticAssetsRouter);
}

protected _setupRequestHandler() {
Expand Down Expand Up @@ -243,7 +264,6 @@ export class RestServer extends Context implements Server, HttpServerLike {
protected _setupRouterForStaticAssets() {
if (!this._routerForStaticAssets) {
this._routerForStaticAssets = express.Router();
this._expressApp.use(this._routerForStaticAssets);
}
}

Expand Down Expand Up @@ -597,12 +617,6 @@ export class RestServer extends Context implements Server, HttpServerLike {
* @param options Options for serve-static
*/
static(path: PathParams, rootDir: string, options?: ServeStaticOptions) {
const re = pathToRegExp(path, [], {end: false});
if (re.test('/')) {
throw new Error(
'Static assets cannot be mount to "/" to avoid performance penalty.',
);
}
this._routerForStaticAssets.use(path, express.static(rootDir, options));
}

Expand Down
5 changes: 5 additions & 0 deletions packages/rest/src/router/router-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ export abstract class BaseRouter implements RestRouter {
else return this.findRouteWithPathVars(request);
}

getStaticAssetsRouter() {
const route = this.routesWithoutPathVars['/get/*'];
return createResolvedRoute(route, {});
}

list() {
let routes = Object.values(this.routesWithoutPathVars);
routes = routes.concat(this.listRoutesWithPathVars());
Expand Down
37 changes: 32 additions & 5 deletions packages/rest/src/router/routing-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import {
OperationRetval,
} from '../types';

import {RestBindings} from '../keys';

import {ControllerSpec} from '@loopback/openapi-v3';

import * as assert from 'assert';
Expand Down Expand Up @@ -72,6 +74,8 @@ export interface RestRouter {
*/
find(request: Request): ResolvedRoute | undefined;

getStaticAssetsRouter(): ResolvedRoute;

/**
* List all routes
*/
Expand Down Expand Up @@ -180,10 +184,8 @@ export class RoutingTable {
return found;
}

debug('No route found for %s %s', request.method, request.path);
throw new HttpErrors.NotFound(
`Endpoint "${request.method} ${request.path}" not found.`,
);
const staticAssetsRouter = this._router.getStaticAssetsRouter();
return staticAssetsRouter;
}
}

Expand Down Expand Up @@ -238,6 +240,31 @@ export interface ResolvedRoute extends RouteEntry {
readonly schemas: SchemasObject;
}

export class StaticRoute implements RouteEntry {
public readonly verb: string = 'get';
public readonly path: string = '*';
public readonly spec: OperationObject = {responses: {}};

constructor(private _handler: Function) {}

updateBindings(requestContext: Context): void {
// no-op
}

async invokeHandler(
requestContext: Context,
args: OperationArgs,
): Promise<OperationRetval> {
const req = await requestContext.get(RestBindings.Http.REQUEST);
const res = await requestContext.get(RestBindings.Http.RESPONSE);
return this._handler(req, res);
}

describe(): string {
return 'final route to handle static assets';
}
}

/**
* Base implementation of RouteEntry
*/
Expand Down Expand Up @@ -290,7 +317,7 @@ export class Route extends BaseRoute {
verb: string,
path: string,
public readonly spec: OperationObject,
protected readonly _handler: Function,
protected readonly _handler: Function, // <-- doesn't this Function have a signature?
) {
super(verb, path, spec);
}
Expand Down

0 comments on commit acb0089

Please sign in to comment.