fix(core): router resolver - malformed URI error handling
MODIFY
packages/core/src/http/router/http.router.resolver.v2.ts
packages/core/src/http/router/http.router.resolver.v2.ts
@@ -2,7 +2,7 @@ import { Subject, of, fromEvent, Observable } from 'rxjs';
import { takeUntil, share, take, mergeMap, map, catchError } from 'rxjs/operators';
import { pipe } from 'fp-ts/lib/pipeable';
import { EffectContext } from '../../effects/effects.interface';
-import { HttpServer, HttpRequest } from '../http.interface';
+import { HttpServer, HttpRequest, HttpStatus } from '../http.interface';
import { defaultError$ } from '../error/http.error.effect';
import { HttpEffectResponse, HttpErrorEffect, HttpOutputEffect } from '../effects/http.effects.interface';
import {
@@ -11,6 +11,7 @@ import {
errorNotBoundToRequestErrorFactory,
responseNotBoundToRequestErrorFactory,
isHttpRequestError,
+ HttpError
} from '../error/http.error.model';
import { useContext } from '../../context/context.hook';
import { LoggerToken, LoggerTag, LoggerLevel } from '../../logger';
@@ -134,6 +135,8 @@ export const resolveRouting = (
const resolve = (req: HttpRequest) => {
const [urlPath, urlQuery] = req.url.split('?');
+
+ try {
const resolvedRoute = find(urlPath, req.method);
if (!resolvedRoute) {
@@ -147,6 +150,13 @@ export const resolveRouting = (
resolvedRoute.subject.next(req);
requestBus.next(req);
+ } catch (error) {
+ if (error.name === 'URIError') {
+ return errorSubject.next({ req, error: new HttpError(error.message, HttpStatus.BAD_REQUEST) });
+ }
+
+ return errorSubject.next({ req, error: new HttpError(`Internal server error: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR) });
+ }
};
return {

MODIFY
packages/core/src/http/router/specs/http.router.resolver.v2.spec.ts
packages/core/src/http/router/specs/http.router.resolver.v2.spec.ts
import { of, merge } from 'rxjs';
-import { mapTo, take, toArray, delay, mergeMap, map } from 'rxjs/operators';
+import { mapTo, take, toArray, delay, mergeMap, map, first } from 'rxjs/operators';
import { createMockEffectContext, createHttpRequest, createHttpResponse, createTestRoute } from '../../../+internal/testing';
import { HttpEffect } from '../../effects/http.effects.interface';
import { Routing } from '../http.router.interface';
import { resolveRouting } from '../http.router.resolver.v2';
import { factorizeRegExpWithParams } from '../http.router.params.factory';
+import { HttpError } from '../../error/http.error.model';
+import { HttpStatus } from '../../http.interface';
describe('#resolveRouting', () => {
test('resolves routes inside collection', async (done) => {
@@ -67,7 +69,7 @@ describe('#resolveRouting', () => {
resolve(req5);
});
- test('returns undefined if route cannot be resolved', () => {
+ test(`returns ${HttpStatus.NOT_FOUND} (Not Found) response if route cannot be resolved`, async () => {
// given
const ctx = createMockEffectContext();
const response = createHttpResponse();
@@ -83,10 +85,44 @@ describe('#resolveRouting', () => {
}];
// when
- const subject = resolveRouting(routing, ctx)().resolve(req);
+ const { resolve, errorSubject } = resolveRouting(routing, ctx)();
+ const errorPromise = errorSubject.pipe(first()).toPromise();
+
+ resolve(req);
// then
- expect(subject).toBeUndefined();
+ await expect(errorPromise).resolves.toEqual({
+ req,
+ error: new HttpError('Route not found', HttpStatus.NOT_FOUND),
+ });
+ });
+
+ test(`returns ${HttpStatus.BAD_REQUEST} (Bad Request) response in case of malformed URL`, async () => {
+ // given
+ const ctx = createMockEffectContext();
+ const response = createHttpResponse();
+ const req = createHttpRequest(({ url: '/group/%test', method: 'GET', response }));
+ const path = factorizeRegExpWithParams('/group/:id');
+
+ const effect$: HttpEffect = req$ => req$.pipe(mapTo({ body: 'test' }));
+
+ const routing: Routing = [{
+ regExp: path.regExp,
+ path: path.path,
+ methods: { GET: { effect: effect$, middlewares: [], parameters: path.parameters } },
+ }];
+
+ // when
+ const { resolve, errorSubject } = resolveRouting(routing, ctx)();
+ const errorPromise = errorSubject.pipe(first()).toPromise();
+
+ resolve(req);
+
+ // then
+ await expect(errorPromise).resolves.toEqual({
+ req,
+ error: new HttpError('URI malformed', HttpStatus.BAD_REQUEST),
+ });
});
test('handles concurrent requests for the same path', done => {
@@ -179,7 +215,7 @@ describe('#resolveRouting', () => {
createTestRoute({ delay: 20, throwError: true }), // [1] GET /delay_20
createTestRoute({ delay: 30 }), // [2] GET /delay_30
createTestRoute({ delay: 40 }), // [3] GET /delay_40
- ]
+ ];
const routing: Routing = testData.map(route => route.item);

fix(core): router resolver - malformed URI error handling