@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 +163 -51
- package/examples/cookie-parsing-example.js +1 -1
- package/examples/params-parsing-example.js +1 -1
- package/examples/query-parsing-example.js +1 -1
- package/examples/uptime-example.js +1 -1
- package/package.json +2 -2
- package/src/parsers/body-parser.js +2 -2
- package/src/parsers/cookie-parser.js +2 -2
- package/src/parsers/query-parser.js +2 -2
- package/src/request-context.d.ts +15 -0
- package/src/request-context.js +38 -0
- package/src/request-context.spec.js +44 -0
- package/src/route-registry.js +1 -1
- package/src/route.js +13 -13
- package/src/route.spec.js +3 -3
- package/src/senders/error-sender.js +3 -3
- package/src/trie-router.d.ts +4 -4
- package/src/trie-router.js +5 -5
- package/src/trie-router.spec.js +20 -20
- package/src/utils/create-request-mock.d.ts +0 -1
- package/src/utils/create-request-mock.js +2 -18
- package/src/utils/create-request-mock.spec.js +4 -36
- package/src/utils/get-request-pathname.d.ts +8 -0
- package/src/utils/{get-request-path.js → get-request-pathname.js} +3 -3
- package/src/utils/{get-request-path.spec.js → get-request-pathname.spec.js} +7 -7
- package/src/utils/index.d.ts +1 -1
- package/src/utils/index.js +1 -1
- package/src/utils/get-request-path.d.ts +0 -8
package/README.md
CHANGED
|
@@ -1,80 +1,99 @@
|
|
|
1
1
|
## @e22m4u/js-trie-router
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[Trie](https://en.wikipedia.org/wiki/Trie)
|
|
3
|
+
ES-модуль HTTP роутера для Node.js, использующий
|
|
4
|
+
[Trie](https://en.wikipedia.org/wiki/Trie)
|
|
5
|
+
для разрешения маршрутов.
|
|
5
6
|
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
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
|
-
##
|
|
13
|
+
## Установка
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
16
|
npm install @e22m4u/js-trie-router
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
Для загрузки ES-модуля требуется установить `"type": "module"` в файле
|
|
20
|
+
`package.json`, или использовать `.mjs` расширение.
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
## Обзор
|
|
23
|
+
|
|
24
|
+
Базовый пример создания экземпляра роутера, объявления маршрута
|
|
25
|
+
и передачи слушателя запросов `http` серверу.
|
|
22
26
|
|
|
23
27
|
```js
|
|
24
28
|
import http from 'http';
|
|
25
|
-
import {TrieRouter} from '
|
|
26
|
-
import {HTTP_METHOD} from '../src/route.js';
|
|
29
|
+
import {TrieRouter} from '@e22m4u/js-path-trie';
|
|
27
30
|
|
|
28
|
-
const server = new http.Server(); //
|
|
29
|
-
const router = new TrieRouter(); //
|
|
31
|
+
const server = new http.Server(); // создание экземпляра HTTP сервера
|
|
32
|
+
const router = new TrieRouter(); // создание экземпляра роутера
|
|
30
33
|
|
|
31
34
|
router.defineRoute({
|
|
32
|
-
method:
|
|
33
|
-
path: '/', //
|
|
34
|
-
handler(ctx) { //
|
|
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.
|
|
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
|
-
###
|
|
48
|
+
### Контекст запроса
|
|
46
49
|
|
|
47
|
-
|
|
50
|
+
Первый параметр обработчика маршрута принимает экземпляр класса
|
|
51
|
+
`RequestContext` с набором свойств, содержащих разобранные
|
|
52
|
+
данные входящего запроса.
|
|
48
53
|
|
|
49
|
-
- `container: ServiceContainer`
|
|
50
|
-
- `req: IncomingMessage`
|
|
51
|
-
- `res: ServerResponse`
|
|
52
|
-
- `
|
|
53
|
-
- `
|
|
54
|
-
- `
|
|
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
|
-
|
|
65
|
+
Пример доступа к контексту из обработчика маршрута.
|
|
57
66
|
|
|
58
67
|
```js
|
|
59
68
|
router.defineRoute({
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
console.log(
|
|
66
|
-
console.log(
|
|
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
|
-
###
|
|
88
|
+
### Отправка ответа
|
|
73
89
|
|
|
74
|
-
|
|
90
|
+
Возвращаемое значение обработчика маршрута используется в качестве ответа
|
|
91
|
+
сервера. Тип значения влияет на представление возвращаемых данных. Например,
|
|
92
|
+
если результатом будет являться тип `object`, то такое значение автоматически
|
|
93
|
+
сериализуется в JSON.
|
|
75
94
|
|
|
76
|
-
|
|
|
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
|
-
|
|
104
|
+
Пример возвращаемого значения обработчиком маршрута.
|
|
86
105
|
|
|
87
106
|
```js
|
|
88
|
-
router.defineRoute({
|
|
107
|
+
router.defineRoute({ // регистрация маршрута
|
|
89
108
|
// ...
|
|
90
|
-
handler(ctx) {
|
|
91
|
-
//
|
|
92
|
-
return {foo: 'bar'};
|
|
109
|
+
handler(ctx) { // обработчик входящего запроса
|
|
110
|
+
return {foo: 'bar'}; // ответ будет представлен в виде JSON
|
|
93
111
|
},
|
|
94
112
|
});
|
|
95
113
|
```
|
|
96
114
|
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
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
|
-
|
|
225
|
+
Установка переменной `DEBUG` перед командой запуска включает вывод логов.
|
|
114
226
|
|
|
115
227
|
```bash
|
|
116
228
|
DEBUG=jsPathTrie* npm run test
|
|
117
229
|
```
|
|
118
230
|
|
|
119
|
-
##
|
|
231
|
+
## Тестирование
|
|
120
232
|
|
|
121
233
|
```bash
|
|
122
234
|
npm run test
|
|
123
235
|
```
|
|
124
236
|
|
|
125
|
-
##
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
30
|
+
server.on('request', router.requestListener);
|
|
31
31
|
|
|
32
32
|
// слушаем входящие запросы
|
|
33
33
|
// на указанный адрес и порт
|
package/package.json
CHANGED
|
@@ -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 = ['
|
|
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.
|
|
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 {
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
27
|
+
getRequestPathname(req),
|
|
28
28
|
);
|
|
29
29
|
}
|
|
30
30
|
return query;
|
package/src/request-context.d.ts
CHANGED
|
@@ -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
|
*
|
package/src/request-context.js
CHANGED
|
@@ -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
|
});
|
package/src/route-registry.js
CHANGED
|
@@ -61,7 +61,7 @@ export class RouteRegistry extends Service {
|
|
|
61
61
|
req.method.toUpperCase(),
|
|
62
62
|
requestPath,
|
|
63
63
|
);
|
|
64
|
-
const triePath = `${req.method.
|
|
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 {
|
|
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
|
-
*
|
|
26
|
-
* POST: '
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
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: '
|
|
34
|
-
POST: '
|
|
35
|
-
PUT: '
|
|
36
|
-
PATCH: '
|
|
37
|
-
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.
|
|
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 =
|
|
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
|
|
198
|
+
it('sets the option "method" in upper case to the "method" property', function () {
|
|
199
199
|
const route = new Route({
|
|
200
|
-
method: '
|
|
200
|
+
method: 'post',
|
|
201
201
|
path: '/',
|
|
202
202
|
handler: () => undefined,
|
|
203
203
|
});
|
|
204
|
-
expect(route.method).to.be.eq('
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
82
|
+
getRequestPathname(req),
|
|
83
83
|
);
|
|
84
84
|
}
|
|
85
85
|
}
|
package/src/trie-router.d.ts
CHANGED
|
@@ -39,7 +39,7 @@ export declare class TrieRouter extends Service {
|
|
|
39
39
|
defineRoute(routeDef: RouteDefinition): Route;
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
|
-
* Request
|
|
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.
|
|
52
|
-
* server.listen(3000);
|
|
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
|
|
57
|
+
get requestListener(): RequestListener;
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
60
|
* Add hook.
|
package/src/trie-router.js
CHANGED
|
@@ -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
|
|
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.
|
|
60
|
-
* server.listen(3000);
|
|
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
|
|
65
|
+
get requestListener() {
|
|
66
66
|
return this._handleRequest.bind(this);
|
|
67
67
|
}
|
|
68
68
|
|
package/src/trie-router.spec.js
CHANGED
|
@@ -25,10 +25,10 @@ describe('TrieRouter', function () {
|
|
|
25
25
|
});
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
describe('
|
|
28
|
+
describe('requestListener', function () {
|
|
29
29
|
it('to be a function', function () {
|
|
30
30
|
const router = new TrieRouter();
|
|
31
|
-
expect(typeof router.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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']);
|
|
@@ -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
|
|
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
|
|
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
|
|
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('
|
|
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
|
|
402
|
-
expect(req2.url).to.be.eq('/test?p1=baz&p2=qux
|
|
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 () {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {Errorf} from '@e22m4u/js-format';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Get request
|
|
4
|
+
* Get request pathname.
|
|
5
5
|
*
|
|
6
6
|
* @param {import('http').IncomingMessage} req
|
|
7
7
|
* @returns {string}
|
|
8
8
|
*/
|
|
9
|
-
export function
|
|
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 "
|
|
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 {
|
|
3
|
+
import {getRequestPathname} from './get-request-pathname.js';
|
|
4
4
|
|
|
5
|
-
describe('
|
|
5
|
+
describe('getRequestPathname', function () {
|
|
6
6
|
it('requires the argument to be an Object with "url" property', function () {
|
|
7
|
-
const throwable = v => () =>
|
|
7
|
+
const throwable = v => () => getRequestPathname(v);
|
|
8
8
|
const error = v =>
|
|
9
9
|
format(
|
|
10
|
-
'The first argument of "
|
|
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
|
|
28
|
-
const res =
|
|
29
|
-
expect(res).to.be.eq('/
|
|
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
|
});
|
package/src/utils/index.d.ts
CHANGED
|
@@ -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';
|
package/src/utils/index.js
CHANGED
|
@@ -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';
|