@e22m4u/js-trie-router 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,80 +1,99 @@
1
1
  ## @e22m4u/js-trie-router
2
2
 
3
- A pure ES-module of the Node.js HTTP router that uses the
4
- [Trie](https://en.wikipedia.org/wiki/Trie) for routing.
3
+ ES-модуль HTTP роутера для Node.js, использующий
4
+ [Trie](https://en.wikipedia.org/wiki/Trie)
5
+ для разрешения маршрутов.
5
6
 
6
- - Uses [path-to-regexp](https://github.com/pillarjs/path-to-regexp) syntax.
7
- - Supports path parameters.
8
- - Parses JSON-body automatically.
9
- - Parses a query string and the Cookie header.
10
- - Supports `preHandler` and `postHandler` hooks.
11
- - Asynchronous request handler.
7
+ - Поддержка [path-to-regexp](https://github.com/pillarjs/path-to-regexp) синтаксиса.
8
+ - Автоматический парсинг JSON-тела запроса.
9
+ - Парсинг строки запроса и заголовка `cookie`.
10
+ - Поддержка `preHandler` и `postHandler` хуков.
11
+ - Позволяет использовать асинхронные обработчики.
12
12
 
13
- ## Installation
13
+ ## Установка
14
14
 
15
15
  ```bash
16
16
  npm install @e22m4u/js-trie-router
17
17
  ```
18
18
 
19
- ## Overview
19
+ Для загрузки ES-модуля требуется установить `"type": "module"` в файле
20
+ `package.json`, или использовать `.mjs` расширение.
20
21
 
21
- A basic "Hello world." example.
22
+ ## Обзор
23
+
24
+ Базовый пример создания экземпляра роутера, объявления маршрута
25
+ и передачи слушателя запросов `http` серверу.
22
26
 
23
27
  ```js
24
28
  import http from 'http';
25
- import {TrieRouter} from '../src/index.js';
26
- import {HTTP_METHOD} from '../src/route.js';
29
+ import {TrieRouter} from '@e22m4u/js-path-trie';
27
30
 
28
- const server = new http.Server(); // A Node.js HTTP server.
29
- const router = new TrieRouter(); // A TrieRouter instance.
31
+ const server = new http.Server(); // создание экземпляра HTTP сервера
32
+ const router = new TrieRouter(); // создание экземпляра роутера
30
33
 
31
34
  router.defineRoute({
32
- method: HTTP_METHOD.GET, // Request method.
33
- path: '/', // Path template like "/user/:id".
34
- handler(ctx) { // Request handler.
35
+ method: 'GET', // метод запроса "GET", "POST" и т.д.
36
+ path: '/', // шаблон пути, пример "/user/:id"
37
+ handler(ctx) { // обработчик маршрута
35
38
  return 'Hello world!';
36
39
  },
37
40
  });
38
41
 
39
- server.on('request', router.requestHandler);
40
- server.listen(3000, 'localhost');
42
+ server.on('request', router.requestListener); // подключение роутера
43
+ server.listen(3000, 'localhost'); // прослушивание запросов
41
44
 
42
45
  // Open in browser http://localhost:3000
43
46
  ```
44
47
 
45
- ### RequestContext
48
+ ### Контекст запроса
46
49
 
47
- The first parameter of the `Router` handler is the `RequestContext` instance.
50
+ Первый параметр обработчика маршрута принимает экземпляр класса
51
+ `RequestContext` с набором свойств, содержащих разобранные
52
+ данные входящего запроса.
48
53
 
49
- - `container: ServiceContainer`
50
- - `req: IncomingMessage`
51
- - `res: ServerResponse`
52
- - `query: ParsedQuery`
53
- - `headers: ParsedHeaders`
54
- - `cookie: ParsedCookie`
54
+ - `container: ServiceContainer` экземпляр [сервис-контейнера](https://npmjs.com/package/@e22m4u/js-service)
55
+ - `req: IncomingMessage` нативный поток запроса модуля `http`
56
+ - `res: ServerResponse` нативный поток ответа модуля `http`
57
+ - `params: ParsedParams` объект ключ-значение с параметрами пути
58
+ - `query: ParsedQuery` объект ключ-значение с параметрами строки запроса
59
+ - `headers: ParsedHeaders` объект ключ-значение с заголовками запроса
60
+ - `cookie: ParsedCookie` объект ключ-значение разобранного заголовка `cookie`
61
+ - `method: string` метод запроса в верхнем регистре, например `GET`, `POST` и т.д.
62
+ - `path: string` путь включающий строку запроса, например `/myPath?foo=bar`
63
+ - `pathname: string` путь запроса, например `/myMath`
55
64
 
56
- The `RequestContext` can be destructured.
65
+ Пример доступа к контексту из обработчика маршрута.
57
66
 
58
67
  ```js
59
68
  router.defineRoute({
60
- // ...
61
- handler({req, res, query, headers, cookie}) {
62
- console.log(req); // IncomingMessage
63
- console.log(res); // ServerResponse
64
- console.log(query); // {id: '10', ...}
65
- console.log(headers); // {'cookie': 'foo=bar', ...}
66
- console.log(cookie); // {foo: 'bar', ...}
69
+ method: 'GET',
70
+ path: '/users/:id',
71
+ handler(ctx) {
72
+ // GET /users/10?include=city
73
+ // Cookie: foo=bar; baz=qux;
74
+ console.log(ctx.req); // IncomingMessage
75
+ console.log(ctx.res); // ServerResponse
76
+ console.log(ctx.params); // {id: 10}
77
+ console.log(ctx.query); // {include: 'city'}
78
+ console.log(ctx.headers); // {cookie: 'foo=bar; baz=qux;'}
79
+ console.log(ctx.cookie); // {foo: 'bar', baz: 'qux'}
80
+ console.log(ctx.method); // "GET"
81
+ console.log(ctx.path); // "/users/10?include=city"
82
+ console.log(ctx.pathname); // "/users/10"
67
83
  // ...
68
84
  },
69
85
  });
70
86
  ```
71
87
 
72
- ### Sending response
88
+ ### Отправка ответа
73
89
 
74
- Return values of the `Route` handler will be sent as described below.
90
+ Возвращаемое значение обработчика маршрута используется в качестве ответа
91
+ сервера. Тип значения влияет на представление возвращаемых данных. Например,
92
+ если результатом будет являться тип `object`, то такое значение автоматически
93
+ сериализуется в JSON.
75
94
 
76
- | type | content-type |
77
- |---------|--------------------------|
95
+ | value | content-type |
96
+ |-----------|--------------------------|
78
97
  | `string` | text/plain |
79
98
  | `number` | application/json |
80
99
  | `boolean` | application/json |
@@ -82,20 +101,19 @@ Return values of the `Route` handler will be sent as described below.
82
101
  | `Buffer` | application/octet-stream |
83
102
  | `Stream` | application/octet-stream |
84
103
 
85
- Here is an example of a JSON response.
104
+ Пример возвращаемого значения обработчиком маршрута.
86
105
 
87
106
  ```js
88
- router.defineRoute({
107
+ router.defineRoute({ // регистрация маршрута
89
108
  // ...
90
- handler(ctx) {
91
- // sends "application/json"
92
- return {foo: 'bar'};
109
+ handler(ctx) { // обработчик входящего запроса
110
+ return {foo: 'bar'}; // ответ будет представлен в виде JSON
93
111
  },
94
112
  });
95
113
  ```
96
114
 
97
- If the `ServerResponse` has been sent manually, then the return
98
- value will be ignored.
115
+ Контекст запроса `ctx` содержит нативный экземпляр класса `ServerResponse`,
116
+ который может быть использован для ручного управления ответом.
99
117
 
100
118
  ```js
101
119
  router.defineRoute({
@@ -108,20 +126,114 @@ router.defineRoute({
108
126
  });
109
127
  ```
110
128
 
111
- ## Debug
129
+ ### Хуки маршрута
130
+
131
+ Определение маршрута методом `defineRoute` позволяет задать хуки
132
+ для отслеживания и перехвата входящего запроса и ответа
133
+ конкретного маршрута.
134
+
135
+ - `preHandler` выполняется перед вызовом обработчика
136
+ - `postHandler` выполняется после вызова обработчика
137
+
138
+ #### preHandler
139
+
140
+ Перед вызовом обработчика маршрута может потребоваться выполнение
141
+ таких операции как авторизация и проверка параметров запроса. Для
142
+ этого можно использовать хук `preHandler`.
143
+
144
+ ```js
145
+ router.defineRoute({ // регистрация маршрута
146
+ // ...
147
+ preHandler(ctx) {
148
+ // вызывается перед обработчиком
149
+ console.log(`Incoming request ${ctx.method} ${ctx.path}`);
150
+ // Incoming request GET /myPath
151
+ },
152
+ handler(ctx) {
153
+ return 'Hello world!';
154
+ },
155
+ });
156
+ ```
157
+
158
+ Если хук `preHandler` возвращает значение отличное от `undefined` и `null`,
159
+ то такое значение будет использовано в качестве ответа сервера, а вызов
160
+ обработчика маршрута будет пропущен.
161
+
162
+ ```js
163
+ router.defineRoute({ // регистрация маршрута
164
+ // ...
165
+ preHandler(ctx) {
166
+ // возвращение ответа сервера
167
+ return 'Are you authorized?';
168
+ },
169
+ handler(ctx) {
170
+ // данный обработчик не вызывается, так как
171
+ // хук "preHandler" уже отправил ответ
172
+ },
173
+ });
174
+ ```
175
+
176
+ #### postHandler
177
+
178
+ Возвращаемое значение обработчика маршрута передается вторым аргументом
179
+ хука `postHandler`. По аналогии с `preHandler`, если возвращаемое
180
+ значение отличается от `undefined` и `null`, то такое значение будет
181
+ использовано в качестве ответа сервера. Это может быть полезно для
182
+ модификации возвращаемого ответа.
183
+
184
+ ```js
185
+ router.defineRoute({
186
+ // ...
187
+ handler(ctx) {
188
+ return 'Hello world!';
189
+ },
190
+ postHandler(ctx, data) {
191
+ // выполняется после обработчика маршрута
192
+ return data.toUpperCase(); // HELLO WORLD!
193
+ },
194
+ });
195
+ ```
196
+
197
+ ### Глобальные хуки
198
+
199
+ Экземпляр роутера `TrieRouter` позволяет задать глобальные хуки, которые
200
+ имеют более высокий приоритет перед хуками маршрута, и вызываются
201
+ в первую очередь.
202
+
203
+ - `preHandler` выполняется перед вызовом обработчика
204
+ - `postHandler` выполняется после вызова обработчика
205
+
206
+ Добавить глобальные хуки можно методом `addHook` экземпляра роутера,
207
+ где первым параметром передается название хука, а вторым его функция.
208
+
209
+ ```js
210
+ router.addHook('preHandler', (ctx) => {
211
+ // вызов перед обработчиком маршрута
212
+ });
213
+
214
+ router.addHook('postHandler', (ctx, data) => {
215
+ // вызов после обработчика маршрута
216
+ });
217
+ ```
218
+
219
+ Аналогично хукам маршрута, если глобальный хук возвращает значение
220
+ отличное от `undefined` и `null`, то такое значение будет использовано
221
+ как ответ сервера.
222
+
223
+ ## Отладка
112
224
 
113
- Set environment variable `DEBUG=jsTrieRouter*` before start.
225
+ Установка переменной `DEBUG` перед командой запуска включает вывод логов.
114
226
 
115
227
  ```bash
116
228
  DEBUG=jsPathTrie* npm run test
117
229
  ```
118
230
 
119
- ## Testing
231
+ ## Тестирование
120
232
 
121
233
  ```bash
122
234
  npm run test
123
235
  ```
124
236
 
125
- ## License
237
+ ## Лицензия
126
238
 
127
239
  MIT
@@ -15,7 +15,7 @@ router.defineRoute({
15
15
  // создаем экземпляр HTTP сервера
16
16
  // и подключаем обработчик запросов
17
17
  const server = new http.Server();
18
- server.on('request', router.requestHandler);
18
+ server.on('request', router.requestListener);
19
19
 
20
20
  // слушаем входящие запросы
21
21
  // на указанный адрес и порт
@@ -15,7 +15,7 @@ router.defineRoute({
15
15
  // создаем экземпляр HTTP сервера
16
16
  // и подключаем обработчик запросов
17
17
  const server = new http.Server();
18
- server.on('request', router.requestHandler);
18
+ server.on('request', router.requestListener);
19
19
 
20
20
  // слушаем входящие запросы
21
21
  // на указанный адрес и порт
@@ -15,7 +15,7 @@ router.defineRoute({
15
15
  // создаем экземпляр HTTP сервера
16
16
  // и подключаем обработчик запросов
17
17
  const server = new http.Server();
18
- server.on('request', router.requestHandler);
18
+ server.on('request', router.requestListener);
19
19
 
20
20
  // слушаем входящие запросы
21
21
  // на указанный адрес и порт
@@ -27,7 +27,7 @@ router.defineRoute({
27
27
  // создаем экземпляр HTTP сервера
28
28
  // и подключаем обработчик запросов
29
29
  const server = new http.Server();
30
- server.on('request', router.requestHandler);
30
+ server.on('request', router.requestListener);
31
31
 
32
32
  // слушаем входящие запросы
33
33
  // на указанный адрес и порт
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@e22m4u/js-trie-router",
3
- "version": "0.0.1",
4
- "description": "Trie-based router for Node.js",
3
+ "version": "0.0.3",
4
+ "description": "Trie-роутер для Node.js",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
7
7
  "scripts": {
@@ -11,7 +11,7 @@ import {parseContentType} from '../utils/parse-content-type.js';
11
11
  *
12
12
  * @type {string[]}
13
13
  */
14
- export const METHODS_WITH_BODY = ['post', 'put', 'patch', 'delete'];
14
+ export const METHODS_WITH_BODY = ['POST', 'PUT', 'PATCH', 'DELETE'];
15
15
 
16
16
  /**
17
17
  * Unparsable media types.
@@ -100,7 +100,7 @@ export class BodyParser extends Service {
100
100
  * @returns {Promise<*>|undefined}
101
101
  */
102
102
  parse(req) {
103
- if (!METHODS_WITH_BODY.includes(req.method.toLowerCase())) {
103
+ if (!METHODS_WITH_BODY.includes(req.method.toUpperCase())) {
104
104
  this.debug(
105
105
  'Body parsing was skipped for the %s request.',
106
106
  req.method.toUpperCase(),
@@ -1,6 +1,6 @@
1
1
  import {Service} from '../service.js';
2
2
  import {parseCookie} from '../utils/index.js';
3
- import {getRequestPath} from '../utils/index.js';
3
+ import {getRequestPathname} from '../utils/index.js';
4
4
 
5
5
  /**
6
6
  * Cookie parser.
@@ -24,7 +24,7 @@ export class CookieParser extends Service {
24
24
  this.debug(
25
25
  'The request %s %v has no cookie.',
26
26
  req.method,
27
- getRequestPath(req),
27
+ getRequestPathname(req),
28
28
  );
29
29
  }
30
30
  return cookie;
@@ -1,6 +1,6 @@
1
1
  import querystring from 'querystring';
2
2
  import {Service} from '../service.js';
3
- import {getRequestPath} from '../utils/index.js';
3
+ import {getRequestPathname} from '../utils/index.js';
4
4
 
5
5
  /**
6
6
  * Query parser.
@@ -24,7 +24,7 @@ export class QueryParser extends Service {
24
24
  this.debug(
25
25
  'The request %s %v has no query.',
26
26
  req.method,
27
- getRequestPath(req),
27
+ getRequestPathname(req),
28
28
  );
29
29
  }
30
30
  return query;
@@ -39,6 +39,21 @@ export declare class RequestContext {
39
39
  */
40
40
  cookie: ParsedCookie;
41
41
 
42
+ /**
43
+ * Method.
44
+ */
45
+ get method(): string;
46
+
47
+ /**
48
+ * Path.
49
+ */
50
+ get path(): string;
51
+
52
+ /**
53
+ * Pathname.
54
+ */
55
+ get pathname(): string;
56
+
42
57
  /**
43
58
  * Constructor.
44
59
  *
@@ -2,6 +2,7 @@ import {Errorf} from '@e22m4u/js-format';
2
2
  import {isReadableStream} from './utils/index.js';
3
3
  import {isWritableStream} from './utils/index.js';
4
4
  import {ServiceContainer} from '@e22m4u/js-service';
5
+ import {getRequestPathname} from './utils/index.js';
5
6
 
6
7
  /**
7
8
  * Request context.
@@ -63,6 +64,43 @@ export class RequestContext {
63
64
  */
64
65
  cookie = {};
65
66
 
67
+ /**
68
+ * Method.
69
+ *
70
+ * @returns {string}
71
+ */
72
+ get method() {
73
+ return this.req.method.toUpperCase();
74
+ }
75
+
76
+ /**
77
+ * Path.
78
+ *
79
+ * @returns {string}
80
+ */
81
+ get path() {
82
+ return this.req.url;
83
+ }
84
+
85
+ /**
86
+ * Pathname.
87
+ *
88
+ * @type {string|undefined}
89
+ * @private
90
+ */
91
+ _pathname = undefined;
92
+
93
+ /**
94
+ * Pathname.
95
+ *
96
+ * @returns {string}
97
+ */
98
+ get pathname() {
99
+ if (this._pathname != null) return this._pathname;
100
+ this._pathname = getRequestPathname(this.req);
101
+ return this._pathname;
102
+ }
103
+
66
104
  /**
67
105
  * Constructor.
68
106
  *
@@ -86,4 +86,48 @@ describe('RequestContext', function () {
86
86
  expect(ctx.res).to.be.eq(res);
87
87
  });
88
88
  });
89
+
90
+ describe('method', function () {
91
+ it('returns the method name in upper case', function () {
92
+ const req = createRequestMock({method: 'post'});
93
+ const res = createResponseMock();
94
+ const cnt = new ServiceContainer();
95
+ const ctx = new RequestContext(cnt, req, res);
96
+ expect(ctx.method).to.be.eq('POST');
97
+ });
98
+ });
99
+
100
+ describe('path', function () {
101
+ it('returns the request pathname with the query string', function () {
102
+ const req = createRequestMock({path: '/pathname?foo=bar'});
103
+ const res = createResponseMock();
104
+ const cnt = new ServiceContainer();
105
+ const ctx = new RequestContext(cnt, req, res);
106
+ expect(req.url).to.be.eq('/pathname?foo=bar');
107
+ expect(ctx.path).to.be.eq('/pathname?foo=bar');
108
+ });
109
+ });
110
+
111
+ describe('pathname', function () {
112
+ it('returns the request pathname without the query string', function () {
113
+ const req = createRequestMock({path: '/pathname?foo=bar'});
114
+ const res = createResponseMock();
115
+ const cnt = new ServiceContainer();
116
+ const ctx = new RequestContext(cnt, req, res);
117
+ expect(req.url).to.be.eq('/pathname?foo=bar');
118
+ expect(ctx.pathname).to.be.eq('/pathname');
119
+ });
120
+
121
+ it('sets the cache to the "_pathname" property and uses is for next accesses', function () {
122
+ const req = createRequestMock({path: '/pathname'});
123
+ const res = createResponseMock();
124
+ const cnt = new ServiceContainer();
125
+ const ctx = new RequestContext(cnt, req, res);
126
+ expect(ctx._pathname).to.be.undefined;
127
+ expect(ctx.pathname).to.be.eq('/pathname');
128
+ expect(ctx._pathname).to.be.eq('/pathname');
129
+ ctx._pathname = '/overridden';
130
+ expect(ctx.pathname).to.be.eq('/overridden');
131
+ });
132
+ });
89
133
  });
@@ -61,7 +61,7 @@ export class RouteRegistry extends Service {
61
61
  req.method.toUpperCase(),
62
62
  requestPath,
63
63
  );
64
- const triePath = `${req.method.toLowerCase()}/${requestPath}`;
64
+ const triePath = `${req.method.toUpperCase()}/${requestPath}`;
65
65
  const resolved = this._trie.match(triePath);
66
66
  if (resolved) {
67
67
  const route = resolved.value;
package/src/route.js CHANGED
@@ -2,7 +2,7 @@ import {Errorf} from '@e22m4u/js-format';
2
2
  import {HOOK_NAME} from './hooks/index.js';
3
3
  import {HookRegistry} from './hooks/index.js';
4
4
  import {createDebugger} from './utils/index.js';
5
- import {getRequestPath} from './utils/index.js';
5
+ import {getRequestPathname} from './utils/index.js';
6
6
 
7
7
  /**
8
8
  * @typedef {import('./request-context.js').RequestContext} RequestContext
@@ -22,19 +22,19 @@ import {getRequestPath} from './utils/index.js';
22
22
  * Http method.
23
23
  *
24
24
  * @type {{
25
- * DELETE: 'delete',
26
- * POST: 'post',
27
- * GET: 'get',
28
- * PUT: 'put',
29
- * PATCH: 'patch',
25
+ * GET: 'GET',
26
+ * POST: 'POST',
27
+ * PUT: 'PUT',
28
+ * PATCH: 'PATCH',
29
+ * DELETE: 'DELETE',
30
30
  * }}
31
31
  */
32
32
  export const HTTP_METHOD = {
33
- GET: 'get',
34
- POST: 'post',
35
- PUT: 'put',
36
- PATCH: 'patch',
37
- DELETE: 'delete',
33
+ GET: 'GET',
34
+ POST: 'POST',
35
+ PUT: 'PUT',
36
+ PATCH: 'PATCH',
37
+ DELETE: 'DELETE',
38
38
  };
39
39
 
40
40
  /**
@@ -134,7 +134,7 @@ export class Route {
134
134
  'a non-empty String, but %v given.',
135
135
  routeDef.method,
136
136
  );
137
- this._method = routeDef.method.toLowerCase();
137
+ this._method = routeDef.method.toUpperCase();
138
138
  if (typeof routeDef.path !== 'string')
139
139
  throw new Errorf(
140
140
  'The option "path" of the Route should be ' + 'a String, but %v given.',
@@ -173,7 +173,7 @@ export class Route {
173
173
  * @returns {*}
174
174
  */
175
175
  handle(context) {
176
- const requestPath = getRequestPath(context.req);
176
+ const requestPath = getRequestPathname(context.req);
177
177
  debug(
178
178
  'Invoking the Route handler for the request %s %v.',
179
179
  this.method.toUpperCase(),
package/src/route.spec.js CHANGED
@@ -195,13 +195,13 @@ describe('Route', function () {
195
195
  throwable2(() => undefined)();
196
196
  });
197
197
 
198
- it('sets the option "method" in lowercase to the "method" property', function () {
198
+ it('sets the option "method" in upper case to the "method" property', function () {
199
199
  const route = new Route({
200
- method: 'POST',
200
+ method: 'post',
201
201
  path: '/',
202
202
  handler: () => undefined,
203
203
  });
204
- expect(route.method).to.be.eq('post');
204
+ expect(route.method).to.be.eq('POST');
205
205
  });
206
206
 
207
207
  it('sets the option "path" to the "path" property', function () {
@@ -1,7 +1,7 @@
1
1
  import {inspect} from 'util';
2
2
  import {Service} from '../service.js';
3
3
  import getStatusMessage from 'statuses';
4
- import {getRequestPath} from '../utils/index.js';
4
+ import {getRequestPathname} from '../utils/index.js';
5
5
 
6
6
  /**
7
7
  * Exposed error properties.
@@ -61,7 +61,7 @@ export class ErrorSender extends Service {
61
61
  'The %s error is sent for the request %s %v.',
62
62
  statusCode,
63
63
  req.method,
64
- getRequestPath(req),
64
+ getRequestPathname(req),
65
65
  );
66
66
  }
67
67
 
@@ -79,7 +79,7 @@ export class ErrorSender extends Service {
79
79
  this.debug(
80
80
  'The 404 error is sent for the request %s %v.',
81
81
  req.method,
82
- getRequestPath(req),
82
+ getRequestPathname(req),
83
83
  );
84
84
  }
85
85
  }
@@ -39,7 +39,7 @@ export declare class TrieRouter extends Service {
39
39
  defineRoute(routeDef: RouteDefinition): Route;
40
40
 
41
41
  /**
42
- * Request handler.
42
+ * Request listener.
43
43
  *
44
44
  * Example:
45
45
  * ```
@@ -48,13 +48,13 @@ export declare class TrieRouter extends Service {
48
48
  *
49
49
  * const router = new TrieRouter();
50
50
  * const server = new http.Server();
51
- * server.on('request', router.requestHandler); // Sets the request handler.
52
- * server.listen(3000); // Starts listening for connections.
51
+ * server.on('request', router.requestListener); // Sets the request listener.
52
+ * server.listen(3000); // Starts listening for connections.
53
53
  * ```
54
54
  *
55
55
  * @returns {Function}
56
56
  */
57
- get requestHandler(): RequestListener;
57
+ get requestListener(): RequestListener;
58
58
 
59
59
  /**
60
60
  * Add hook.
@@ -35,7 +35,7 @@ export class TrieRouter extends Service {
35
35
  * path: '/users/:id', // The path template may have parameters.
36
36
  * preHandler(ctx) { ... }, // The "preHandler" is executed before a route handler.
37
37
  * handler(ctx) { ... }, // Request handler function.
38
- * postHandler(ctx, data) { ... }, // The "postHandler" is executed after a route handler
38
+ * postHandler(ctx, data) { ... }, // The "postHandler" is executed after a route handler.
39
39
  * });
40
40
  * ```
41
41
  *
@@ -47,7 +47,7 @@ export class TrieRouter extends Service {
47
47
  }
48
48
 
49
49
  /**
50
- * Request handler.
50
+ * Request listener.
51
51
  *
52
52
  * Example:
53
53
  * ```
@@ -56,13 +56,13 @@ export class TrieRouter extends Service {
56
56
  *
57
57
  * const router = new TrieRouter();
58
58
  * const server = new http.Server();
59
- * server.on('request', router.requestHandler); // Sets the request handler.
60
- * server.listen(3000); // Starts listening for connections.
59
+ * server.on('request', router.requestListener); // Sets the request listener.
60
+ * server.listen(3000); // Starts listening for connections.
61
61
  * ```
62
62
  *
63
63
  * @returns {Function}
64
64
  */
65
- get requestHandler() {
65
+ get requestListener() {
66
66
  return this._handleRequest.bind(this);
67
67
  }
68
68
 
@@ -25,10 +25,10 @@ describe('TrieRouter', function () {
25
25
  });
26
26
  });
27
27
 
28
- describe('requestHandler', function () {
28
+ describe('requestListener', function () {
29
29
  it('to be a function', function () {
30
30
  const router = new TrieRouter();
31
- expect(typeof router.requestHandler).to.be.eq('function');
31
+ expect(typeof router.requestListener).to.be.eq('function');
32
32
  });
33
33
 
34
34
  it('passes request context to the route handler', function (done) {
@@ -43,7 +43,7 @@ describe('TrieRouter', function () {
43
43
  });
44
44
  const req = createRequestMock({path: '/test'});
45
45
  const res = createResponseMock();
46
- router.requestHandler(req, res);
46
+ router.requestListener(req, res);
47
47
  });
48
48
 
49
49
  it('passes path parameters to the request context', function (done) {
@@ -58,7 +58,7 @@ describe('TrieRouter', function () {
58
58
  });
59
59
  const req = createRequestMock({path: '/foo-bar'});
60
60
  const res = createResponseMock();
61
- router.requestHandler(req, res);
61
+ router.requestListener(req, res);
62
62
  });
63
63
 
64
64
  it('passes query parameters to the request context', function (done) {
@@ -73,7 +73,7 @@ describe('TrieRouter', function () {
73
73
  });
74
74
  const req = createRequestMock({path: '?p1=foo&p2=bar'});
75
75
  const res = createResponseMock();
76
- router.requestHandler(req, res);
76
+ router.requestListener(req, res);
77
77
  });
78
78
 
79
79
  it('passes parsed cookie to the request context', function (done) {
@@ -88,7 +88,7 @@ describe('TrieRouter', function () {
88
88
  });
89
89
  const req = createRequestMock({headers: {cookie: 'p1=foo; p2=bar;'}});
90
90
  const res = createResponseMock();
91
- router.requestHandler(req, res);
91
+ router.requestListener(req, res);
92
92
  });
93
93
 
94
94
  it('passes plain text body to the request context', function (done) {
@@ -104,7 +104,7 @@ describe('TrieRouter', function () {
104
104
  });
105
105
  const req = createRequestMock({method: HTTP_METHOD.POST, body});
106
106
  const res = createResponseMock();
107
- router.requestHandler(req, res);
107
+ router.requestListener(req, res);
108
108
  });
109
109
 
110
110
  it('passes parsed JSON body to the request context', function (done) {
@@ -120,7 +120,7 @@ describe('TrieRouter', function () {
120
120
  });
121
121
  const req = createRequestMock({method: HTTP_METHOD.POST, body: data});
122
122
  const res = createResponseMock();
123
- router.requestHandler(req, res);
123
+ router.requestListener(req, res);
124
124
  });
125
125
 
126
126
  it('passes headers to the request context', function (done) {
@@ -138,7 +138,7 @@ describe('TrieRouter', function () {
138
138
  });
139
139
  const req = createRequestMock({headers: {foo: 'bar'}});
140
140
  const res = createResponseMock();
141
- router.requestHandler(req, res);
141
+ router.requestListener(req, res);
142
142
  });
143
143
 
144
144
  it('uses DataSender to send the response', function (done) {
@@ -158,7 +158,7 @@ describe('TrieRouter', function () {
158
158
  done();
159
159
  },
160
160
  });
161
- router.requestHandler(req, res);
161
+ router.requestListener(req, res);
162
162
  });
163
163
 
164
164
  it('uses ErrorSender to send the response', function (done) {
@@ -181,7 +181,7 @@ describe('TrieRouter', function () {
181
181
  done();
182
182
  },
183
183
  });
184
- router.requestHandler(req, res);
184
+ router.requestListener(req, res);
185
185
  });
186
186
 
187
187
  describe('hooks', function () {
@@ -207,7 +207,7 @@ describe('TrieRouter', function () {
207
207
  });
208
208
  const req = createRequestMock();
209
209
  const res = createResponseMock();
210
- router.requestHandler(req, res);
210
+ router.requestListener(req, res);
211
211
  const result = await res.getBody();
212
212
  expect(result).to.be.eq(body);
213
213
  expect(order).to.be.eql(['preHandler1', 'preHandler2', 'handler']);
@@ -235,7 +235,7 @@ describe('TrieRouter', function () {
235
235
  });
236
236
  const req = createRequestMock();
237
237
  const res = createResponseMock();
238
- router.requestHandler(req, res);
238
+ router.requestListener(req, res);
239
239
  const result = await res.getBody();
240
240
  expect(result).to.be.eq(body);
241
241
  expect(order).to.be.eql(['handler', 'postHandler1', 'postHandler2']);
@@ -266,7 +266,7 @@ describe('TrieRouter', function () {
266
266
  });
267
267
  const req = createRequestMock();
268
268
  const res = createResponseMock();
269
- router.requestHandler(req, res);
269
+ router.requestListener(req, res);
270
270
  const result = await res.getBody();
271
271
  expect(result).to.be.eq(body);
272
272
  expect(order).to.be.eql(['preHandler1', 'preHandler2', 'handler']);
@@ -301,7 +301,7 @@ describe('TrieRouter', function () {
301
301
  });
302
302
  const req = createRequestMock();
303
303
  const res = createResponseMock();
304
- router.requestHandler(req, res);
304
+ router.requestListener(req, res);
305
305
  const result = await res.getBody();
306
306
  expect(result).to.be.eq(body);
307
307
  expect(order).to.be.eql(['handler', 'postHandler1', 'postHandler2']);
@@ -331,7 +331,7 @@ describe('TrieRouter', function () {
331
331
  });
332
332
  const req = createRequestMock();
333
333
  const res = createResponseMock();
334
- router.requestHandler(req, res);
334
+ router.requestListener(req, res);
335
335
  const result = await res.getBody();
336
336
  expect(result).to.be.eq(body);
337
337
  expect(order).to.be.eql(['preHandler1', 'preHandler2', 'handler']);
@@ -361,7 +361,7 @@ describe('TrieRouter', function () {
361
361
  });
362
362
  const req = createRequestMock();
363
363
  const res = createResponseMock();
364
- router.requestHandler(req, res);
364
+ router.requestListener(req, res);
365
365
  const result = await res.getBody();
366
366
  expect(result).to.be.eq(body);
367
367
  expect(order).to.be.eql(['handler', 'postHandler1', 'postHandler2']);
@@ -391,7 +391,7 @@ describe('TrieRouter', function () {
391
391
  });
392
392
  const req = createRequestMock();
393
393
  const res = createResponseMock();
394
- router.requestHandler(req, res);
394
+ router.requestListener(req, res);
395
395
  const result = await res.getBody();
396
396
  expect(result).to.be.eq(preHandlerBody);
397
397
  expect(result).not.to.be.eq(handlerBody);
@@ -421,7 +421,7 @@ describe('TrieRouter', function () {
421
421
  });
422
422
  const req = createRequestMock();
423
423
  const res = createResponseMock();
424
- router.requestHandler(req, res);
424
+ router.requestListener(req, res);
425
425
  const result = await res.getBody();
426
426
  expect(result).not.to.be.eq(handlerBody);
427
427
  expect(result).to.be.eq(postHandlerBody);
@@ -448,7 +448,7 @@ describe('TrieRouter', function () {
448
448
  });
449
449
  const req = createRequestMock();
450
450
  const res = createResponseMock();
451
- router.requestHandler(req, res);
451
+ router.requestListener(req, res);
452
452
  const result = await res.getBody();
453
453
  expect(result).to.be.eq(body);
454
454
  expect(order).to.be.eql(['preHandler', 'handler', 'postHandler']);
@@ -10,7 +10,6 @@ type RequestPatch = {
10
10
  secure?: boolean;
11
11
  path?: string;
12
12
  query?: object;
13
- hash?: string;
14
13
  cookie?: object;
15
14
  headers?: object;
16
15
  body?: string;
@@ -14,7 +14,6 @@ import {BUFFER_ENCODING_LIST} from './fetch-request-body.js';
14
14
  * secure?: boolean;
15
15
  * path?: string;
16
16
  * query?: object;
17
- * hash?: string;
18
17
  * cookie?: object;
19
18
  * headers?: object;
20
19
  * body?: string;
@@ -74,12 +73,6 @@ export function createRequestMock(patch) {
74
73
  patch.query,
75
74
  );
76
75
  }
77
- if (patch.hash != null && typeof patch.hash !== 'string')
78
- throw new Errorf(
79
- 'The parameter "hash" of "createRequestMock" ' +
80
- 'should be a String, but %v given.',
81
- patch.hash,
82
- );
83
76
  if (
84
77
  (patch.cookie != null &&
85
78
  typeof patch.cookie !== 'string' &&
@@ -143,7 +136,7 @@ export function createRequestMock(patch) {
143
136
  const req =
144
137
  patch.stream ||
145
138
  createRequestStream(patch.secure, patch.body, patch.encoding);
146
- req.url = createRequestUrl(patch.path || '/', patch.query, patch.hash);
139
+ req.url = createRequestUrl(patch.path || '/', patch.query);
147
140
  req.headers = createRequestHeaders(
148
141
  patch.host,
149
142
  patch.secure,
@@ -199,10 +192,9 @@ function createRequestStream(secure, body, encoding) {
199
192
  *
200
193
  * @param {string} path
201
194
  * @param {string|object|null|undefined} query
202
- * @param {string|null|undefined} hash
203
195
  * @returns {string}
204
196
  */
205
- function createRequestUrl(path, query, hash) {
197
+ function createRequestUrl(path, query) {
206
198
  if (typeof path !== 'string')
207
199
  throw new Errorf(
208
200
  'The parameter "path" of "createRequestUrl" ' +
@@ -219,12 +211,6 @@ function createRequestUrl(path, query, hash) {
219
211
  query,
220
212
  );
221
213
  }
222
- if (hash != null && typeof hash !== 'string')
223
- throw new Errorf(
224
- 'The parameter "hash" of "createRequestUrl" ' +
225
- 'should be a String, but %v given.',
226
- path,
227
- );
228
214
  let url = ('/' + path).replace('//', '/');
229
215
  if (typeof query === 'object') {
230
216
  const qs = queryString.stringify(query);
@@ -232,8 +218,6 @@ function createRequestUrl(path, query, hash) {
232
218
  } else if (typeof query === 'string') {
233
219
  url += `?${query.replace(/^\?/, '')}`;
234
220
  }
235
- hash = (hash || '').replace('#', '');
236
- if (hash) url += `#${hash}`;
237
221
  return url;
238
222
  }
239
223
 
@@ -128,26 +128,6 @@ describe('createRequestMock', function () {
128
128
  throwable(null)();
129
129
  });
130
130
 
131
- it('requires the parameter "hash" to be a String', function () {
132
- const throwable = v => () => createRequestMock({hash: v});
133
- const error = v =>
134
- format(
135
- 'The parameter "hash" of "createRequestMock" ' +
136
- 'should be a String, but %s given.',
137
- v,
138
- );
139
- expect(throwable(10)).to.throw(error('10'));
140
- expect(throwable(0)).to.throw(error('0'));
141
- expect(throwable(true)).to.throw(error('true'));
142
- expect(throwable(false)).to.throw(error('false'));
143
- expect(throwable([])).to.throw(error('Array'));
144
- expect(throwable({})).to.throw(error('Object'));
145
- throwable('str')();
146
- throwable('')();
147
- throwable(undefined)();
148
- throwable(null)();
149
- });
150
-
151
131
  it('requires the parameter "cookie" to be a String or Object', function () {
152
132
  const throwable = v => () => createRequestMock({cookie: v});
153
133
  const error = v =>
@@ -289,7 +269,7 @@ describe('createRequestMock', function () {
289
269
  expect(req.socket).to.be.instanceof(Socket);
290
270
  });
291
271
 
292
- it('uses the default path "/" without query and hash', function () {
272
+ it('uses the default path "/" without a query string', function () {
293
273
  const req = createRequestMock();
294
274
  expect(req.url).to.be.eq('/');
295
275
  });
@@ -377,29 +357,17 @@ describe('createRequestMock', function () {
377
357
  expect(req.url).to.be.eq('/?p1=foo&p2=bar');
378
358
  });
379
359
 
380
- it('sets the hash value to the request url', async function () {
381
- const req = createRequestMock({hash: 'myHash'});
382
- expect(req.url).to.be.eq('/#myHash');
383
- });
384
-
385
- it('sets the hash value to the request url with the prefix "#"', async function () {
386
- const req = createRequestMock({hash: '#myHash'});
387
- expect(req.url).to.be.eq('/#myHash');
388
- });
389
-
390
- it('set parameters "path", "query" and "hash" to the request url', function () {
360
+ it('set parameters "path" and "query" to the request url', function () {
391
361
  const req1 = createRequestMock({
392
362
  path: 'test',
393
363
  query: 'p1=foo&p2=bar',
394
- hash: 'myHash1',
395
364
  });
396
365
  const req2 = createRequestMock({
397
366
  path: '/test',
398
367
  query: {p1: 'baz', p2: 'qux'},
399
- hash: '#myHash2',
400
368
  });
401
- expect(req1.url).to.be.eq('/test?p1=foo&p2=bar#myHash1');
402
- expect(req2.url).to.be.eq('/test?p1=baz&p2=qux#myHash2');
369
+ expect(req1.url).to.be.eq('/test?p1=foo&p2=bar');
370
+ expect(req2.url).to.be.eq('/test?p1=baz&p2=qux');
403
371
  });
404
372
 
405
373
  it('sets the parameter "method" in uppercase', async function () {
@@ -0,0 +1,8 @@
1
+ import {IncomingMessage} from 'http';
2
+
3
+ /**
4
+ * Get request pathname.
5
+ *
6
+ * @param req
7
+ */
8
+ export declare function getRequestPathname(req: IncomingMessage): string;
@@ -1,12 +1,12 @@
1
1
  import {Errorf} from '@e22m4u/js-format';
2
2
 
3
3
  /**
4
- * Get request path.
4
+ * Get request pathname.
5
5
  *
6
6
  * @param {import('http').IncomingMessage} req
7
7
  * @returns {string}
8
8
  */
9
- export function getRequestPath(req) {
9
+ export function getRequestPathname(req) {
10
10
  if (
11
11
  !req ||
12
12
  typeof req !== 'object' ||
@@ -14,7 +14,7 @@ export function getRequestPath(req) {
14
14
  typeof req.url !== 'string'
15
15
  ) {
16
16
  throw new Errorf(
17
- 'The first argument of "getRequestPath" should be ' +
17
+ 'The first argument of "getRequestPathname" should be ' +
18
18
  'an instance of IncomingMessage, but %v given.',
19
19
  req,
20
20
  );
@@ -1,13 +1,13 @@
1
1
  import {expect} from '../chai.js';
2
2
  import {format} from '@e22m4u/js-format';
3
- import {getRequestPath} from './get-request-path.js';
3
+ import {getRequestPathname} from './get-request-pathname.js';
4
4
 
5
- describe('getRequestPath', function () {
5
+ describe('getRequestPathname', function () {
6
6
  it('requires the argument to be an Object with "url" property', function () {
7
- const throwable = v => () => getRequestPath(v);
7
+ const throwable = v => () => getRequestPathname(v);
8
8
  const error = v =>
9
9
  format(
10
- 'The first argument of "getRequestPath" should be ' +
10
+ 'The first argument of "getRequestPathname" should be ' +
11
11
  'an instance of IncomingMessage, but %s given.',
12
12
  v,
13
13
  );
@@ -24,8 +24,8 @@ describe('getRequestPath', function () {
24
24
  throwable({url: ''})();
25
25
  });
26
26
 
27
- it('returns the request path without query parameters', function () {
28
- const res = getRequestPath({url: '/test?foo=bar'});
29
- expect(res).to.be.eq('/test');
27
+ it('returns the request path without the query string', function () {
28
+ const res = getRequestPathname({url: '/pathname?foo=bar'});
29
+ expect(res).to.be.eq('/pathname');
30
30
  });
31
31
  });
@@ -4,8 +4,8 @@ export * from './create-error.js';
4
4
  export * from './to-camel-case.js';
5
5
  export * from './create-debugger.js';
6
6
  export * from './is-response-sent.js';
7
- export * from './get-request-path.js';
8
7
  export * from './is-readable-stream.js';
9
8
  export * from './is-writable-stream.js';
10
9
  export * from './fetch-request-body.js';
11
10
  export * from './create-cookie-string.js';
11
+ export * from './get-request-pathname.js';
@@ -4,8 +4,8 @@ export * from './create-error.js';
4
4
  export * from './to-camel-case.js';
5
5
  export * from './create-debugger.js';
6
6
  export * from './is-response-sent.js';
7
- export * from './get-request-path.js';
8
7
  export * from './is-readable-stream.js';
9
8
  export * from './is-writable-stream.js';
10
9
  export * from './fetch-request-body.js';
11
10
  export * from './create-cookie-string.js';
11
+ export * from './get-request-pathname.js';
@@ -1,8 +0,0 @@
1
- import {IncomingMessage} from 'http';
2
-
3
- /**
4
- * Get request path.
5
- *
6
- * @param req
7
- */
8
- export declare function getRequestPath(req: IncomingMessage): string;