@httpland/compression-middleware 1.0.0-beta.1
Sign up to get free protection for your applications and to get access to all the features.
- package/LICENSE +20 -0
- package/README.md +208 -0
- package/esm/deps/deno.land/std@0.180.0/http/_negotiation/common.js +40 -0
- package/esm/deps/deno.land/std@0.180.0/http/_negotiation/encoding.js +121 -0
- package/esm/deps/deno.land/std@0.180.0/http/_negotiation/language.js +110 -0
- package/esm/deps/deno.land/std@0.180.0/http/_negotiation/media_type.js +174 -0
- package/esm/deps/deno.land/std@0.180.0/http/negotiation.js +36 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/_db.js +33 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/_util.js +122 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/content_type.js +54 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/extension.js +25 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/extensions_by_type.js +30 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/format_media_type.js +60 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/get_charset.js +36 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/mod.js +22 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/parse_media_type.js +121 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/type_by_extension.js +23 -0
- package/esm/deps/deno.land/std@0.180.0/media_types/vendor/mime-db.v1.52.0.js +8555 -0
- package/esm/deps/deno.land/x/vary@1.0.0/mod.js +108 -0
- package/esm/deps.js +8 -0
- package/esm/encoders/deflate.js +11 -0
- package/esm/encoders/gzip.js +11 -0
- package/esm/encoders/utils.js +7 -0
- package/esm/middleware.js +55 -0
- package/esm/mod.js +3 -0
- package/esm/package.json +3 -0
- package/esm/transform.js +23 -0
- package/esm/types.js +3 -0
- package/esm/utils.js +19 -0
- package/package.json +55 -0
- package/script/deps/deno.land/std@0.180.0/http/_negotiation/common.js +45 -0
- package/script/deps/deno.land/std@0.180.0/http/_negotiation/encoding.js +125 -0
- package/script/deps/deno.land/std@0.180.0/http/_negotiation/language.js +114 -0
- package/script/deps/deno.land/std@0.180.0/http/_negotiation/media_type.js +178 -0
- package/script/deps/deno.land/std@0.180.0/http/negotiation.js +42 -0
- package/script/deps/deno.land/std@0.180.0/media_types/_db.js +39 -0
- package/script/deps/deno.land/std@0.180.0/media_types/_util.js +132 -0
- package/script/deps/deno.land/std@0.180.0/media_types/content_type.js +58 -0
- package/script/deps/deno.land/std@0.180.0/media_types/extension.js +29 -0
- package/script/deps/deno.land/std@0.180.0/media_types/extensions_by_type.js +34 -0
- package/script/deps/deno.land/std@0.180.0/media_types/format_media_type.js +64 -0
- package/script/deps/deno.land/std@0.180.0/media_types/get_charset.js +40 -0
- package/script/deps/deno.land/std@0.180.0/media_types/mod.js +38 -0
- package/script/deps/deno.land/std@0.180.0/media_types/parse_media_type.js +125 -0
- package/script/deps/deno.land/std@0.180.0/media_types/type_by_extension.js +27 -0
- package/script/deps/deno.land/std@0.180.0/media_types/vendor/mime-db.v1.52.0.js +8557 -0
- package/script/deps/deno.land/x/vary@1.0.0/mod.js +113 -0
- package/script/deps.js +22 -0
- package/script/encoders/deflate.js +14 -0
- package/script/encoders/gzip.js +14 -0
- package/script/encoders/utils.js +10 -0
- package/script/middleware.js +61 -0
- package/script/mod.js +7 -0
- package/script/package.json +3 -0
- package/script/transform.js +27 -0
- package/script/types.js +4 -0
- package/script/utils.js +24 -0
- package/types/deps/deno.land/std@0.180.0/http/_negotiation/common.d.ts +37 -0
- package/types/deps/deno.land/std@0.180.0/http/_negotiation/encoding.d.ts +33 -0
- package/types/deps/deno.land/std@0.180.0/http/_negotiation/language.d.ts +30 -0
- package/types/deps/deno.land/std@0.180.0/http/_negotiation/media_type.d.ts +30 -0
- package/types/deps/deno.land/std@0.180.0/http/negotiation.d.ts +126 -0
- package/types/deps/deno.land/std@0.180.0/media_types/_db.d.ts +5 -0
- package/types/deps/deno.land/std@0.180.0/media_types/_util.d.ts +21 -0
- package/types/deps/deno.land/std@0.180.0/media_types/content_type.d.ts +38 -0
- package/types/deps/deno.land/std@0.180.0/media_types/extension.d.ts +17 -0
- package/types/deps/deno.land/std@0.180.0/media_types/extensions_by_type.d.ts +20 -0
- package/types/deps/deno.land/std@0.180.0/media_types/format_media_type.d.ts +16 -0
- package/types/deps/deno.land/std@0.180.0/media_types/get_charset.d.ts +15 -0
- package/types/deps/deno.land/std@0.180.0/media_types/mod.d.ts +20 -0
- package/types/deps/deno.land/std@0.180.0/media_types/parse_media_type.d.ts +37 -0
- package/types/deps/deno.land/std@0.180.0/media_types/type_by_extension.d.ts +17 -0
- package/types/deps/deno.land/std@0.180.0/media_types/vendor/mime-db.v1.52.0.d.ts +8518 -0
- package/types/deps/deno.land/x/vary@1.0.0/mod.d.ts +23 -0
- package/types/deps.d.ts +7 -0
- package/types/encoders/deflate.d.ts +3 -0
- package/types/encoders/gzip.d.ts +3 -0
- package/types/encoders/utils.d.ts +4 -0
- package/types/middleware.d.ts +28 -0
- package/types/mod.d.ts +2 -0
- package/types/transform.d.ts +3 -0
- package/types/types.d.ts +15 -0
- package/types/utils.d.ts +2 -0
package/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2023 httpland
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
# compression-middleware
|
2
|
+
|
3
|
+
[![deno land](http://img.shields.io/badge/available%20on-deno.land/x-lightgrey.svg?logo=deno)](https://deno.land/x/compression_middleware)
|
4
|
+
[![deno doc](https://doc.deno.land/badge.svg)](https://doc.deno.land/https/deno.land/x/compression_middleware/mod.ts)
|
5
|
+
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/httpland/compression-middleware)](https://github.com/httpland/compression-middleware/releases)
|
6
|
+
[![codecov](https://codecov.io/github/httpland/compression-middleware/branch/main/graph/badge.svg)](https://codecov.io/gh/httpland/compression-middleware)
|
7
|
+
[![GitHub](https://img.shields.io/github/license/httpland/compression-middleware)](https://github.com/httpland/compression-middleware/blob/main/LICENSE)
|
8
|
+
|
9
|
+
[![test](https://github.com/httpland/compression-middleware/actions/workflows/test.yaml/badge.svg)](https://github.com/httpland/compression-middleware/actions/workflows/test.yaml)
|
10
|
+
[![NPM](https://nodei.co/npm/@httpland/compression-middleware.png?mini=true)](https://nodei.co/npm/@httpland/compression-middleware/)
|
11
|
+
|
12
|
+
HTTP compression middleware.
|
13
|
+
|
14
|
+
Compresses HTTP Content(body).
|
15
|
+
|
16
|
+
Compliant with
|
17
|
+
[RFC 9110, 8.4. Content-Encoding](https://www.rfc-editor.org/rfc/rfc9110.html#section-8.4)
|
18
|
+
and
|
19
|
+
[RFC 9110, 12.5.3. Accept-Encoding](https://www.rfc-editor.org/rfc/rfc9110.html#name-accept-encoding)
|
20
|
+
.
|
21
|
+
|
22
|
+
## Middleware
|
23
|
+
|
24
|
+
For a definition of Universal HTTP middleware, see the
|
25
|
+
[http-middleware](https://github.com/httpland/http-middleware) project.
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
Middleware convert message body and adds the `Content-Encoding` header to the
|
30
|
+
response.
|
31
|
+
|
32
|
+
Also, safely add `Accept-Encoding` to the `vary` header in response.
|
33
|
+
|
34
|
+
```ts
|
35
|
+
import { compression } from "https://deno.land/x/compression_middleware@$VERSION/mod.ts";
|
36
|
+
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
|
37
|
+
|
38
|
+
const middleware = compression();
|
39
|
+
const request = new Request("test:", {
|
40
|
+
headers: {
|
41
|
+
"accept-encoding": "deflate;q=0.5, gzip;q=1.0",
|
42
|
+
},
|
43
|
+
});
|
44
|
+
|
45
|
+
const response = await middleware(
|
46
|
+
request,
|
47
|
+
() => new Response("<body>"),
|
48
|
+
);
|
49
|
+
|
50
|
+
assertEquals(await response.text(), "<gzip:body>");
|
51
|
+
assertEquals(response.headers.get("content-encoding"), "gzip");
|
52
|
+
```
|
53
|
+
|
54
|
+
yield:
|
55
|
+
|
56
|
+
```http
|
57
|
+
Content-Encoding: <encoding>
|
58
|
+
Vary: accept-encoding
|
59
|
+
```
|
60
|
+
|
61
|
+
## Built-in encoding
|
62
|
+
|
63
|
+
Middleware supports the following encodings by default:
|
64
|
+
|
65
|
+
- gzip
|
66
|
+
- deflate
|
67
|
+
|
68
|
+
## Additional encoding
|
69
|
+
|
70
|
+
You can add supported encoding.
|
71
|
+
|
72
|
+
There are two ways to add encodings:
|
73
|
+
|
74
|
+
- Encoding map
|
75
|
+
- Encoder list
|
76
|
+
|
77
|
+
In either style, the result is the same.
|
78
|
+
|
79
|
+
When encoding is added, a shallow merge is performed in favor of user-defined.
|
80
|
+
|
81
|
+
## Encoding map
|
82
|
+
|
83
|
+
Encoding map defines a map of encoding formats and
|
84
|
+
[encode functions](#encode-api) .
|
85
|
+
|
86
|
+
Example of adding `brotli` encoding:
|
87
|
+
|
88
|
+
```ts
|
89
|
+
import {
|
90
|
+
compression,
|
91
|
+
type Encode,
|
92
|
+
type EncodingMap,
|
93
|
+
} from "https://deno.land/x/compression_middleware@$VERSION/mod.ts";
|
94
|
+
|
95
|
+
declare const encodeBr: Encode;
|
96
|
+
const encodingMap: EncodingMap = { br: encodeBr };
|
97
|
+
|
98
|
+
const middleware = compression(encodingMap);
|
99
|
+
```
|
100
|
+
|
101
|
+
## Encoder list
|
102
|
+
|
103
|
+
Encoder list defines a list of `Encoder`.
|
104
|
+
|
105
|
+
`Encoder` is a following structured object:
|
106
|
+
|
107
|
+
| Name | Type | Description |
|
108
|
+
| -------- | --------------------- | ---------------- |
|
109
|
+
| encoding | `string` | Encoding format. |
|
110
|
+
| encode | [Encode](#encode-api) | Encode stream. |
|
111
|
+
|
112
|
+
Example of adding `brotli` encoding:
|
113
|
+
|
114
|
+
```ts
|
115
|
+
import {
|
116
|
+
compression,
|
117
|
+
type Encode,
|
118
|
+
type Encoder,
|
119
|
+
} from "https://deno.land/x/compression_middleware@$VERSION/mod.ts";
|
120
|
+
|
121
|
+
declare const encodeBr: Encode;
|
122
|
+
const Br: Encoder = { encoding: "br", encode: encodeBr };
|
123
|
+
|
124
|
+
const middleware = compression([Br]);
|
125
|
+
```
|
126
|
+
|
127
|
+
## Encode API
|
128
|
+
|
129
|
+
`Encode` is an API for converting content.
|
130
|
+
|
131
|
+
```ts
|
132
|
+
interface Encode {
|
133
|
+
(stream: ReadableStream<Uint8Array>): BodyInit | Promise<BodyInit>;
|
134
|
+
}
|
135
|
+
```
|
136
|
+
|
137
|
+
## Compressible
|
138
|
+
|
139
|
+
Filter media types to be compressed according to the following
|
140
|
+
[specifications](https://www.rfc-editor.org/rfc/rfc9110.html#section-8.4-8):
|
141
|
+
|
142
|
+
> If the media type includes an inherent encoding, such as a data format that is
|
143
|
+
> always compressed, then that encoding would not be restated in
|
144
|
+
> Content-Encoding even if it happens to be the same algorithm as one of the
|
145
|
+
> content codings.
|
146
|
+
|
147
|
+
Determine from the `Content-Type` response header whether the representation is
|
148
|
+
compressible.
|
149
|
+
|
150
|
+
For example, image media such as `image/jpag` is not compressible because it is
|
151
|
+
already compressed.
|
152
|
+
|
153
|
+
## Content-Length
|
154
|
+
|
155
|
+
If the Response has a `Content-Length` header, compression may cause the actual
|
156
|
+
content length to deviate.
|
157
|
+
|
158
|
+
In that case, the `Content-Length` is recalculated.
|
159
|
+
|
160
|
+
```ts
|
161
|
+
import { compression } from "https://deno.land/x/compression_middleware@$VERSION/mod.ts";
|
162
|
+
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
|
163
|
+
|
164
|
+
declare const request: Request;
|
165
|
+
const middleware = compression();
|
166
|
+
|
167
|
+
const response = await middleware(
|
168
|
+
request,
|
169
|
+
() =>
|
170
|
+
new Response("<body>", { headers: { "content-length": "<body:length>" } }),
|
171
|
+
);
|
172
|
+
|
173
|
+
assertEquals(await response.text(), "<gzip:body>");
|
174
|
+
assertEquals(response.headers.get("content-length"), "<gzip:body:length>");
|
175
|
+
```
|
176
|
+
|
177
|
+
## Effects
|
178
|
+
|
179
|
+
Middleware may make changes to the following elements of the HTTP message.
|
180
|
+
|
181
|
+
- HTTP Content
|
182
|
+
- HTTP Headers
|
183
|
+
- Content-Encoding
|
184
|
+
- Vary
|
185
|
+
- [Content-Length](#content-length)
|
186
|
+
|
187
|
+
## Conditions
|
188
|
+
|
189
|
+
Middleware is executed if all of the following conditions are met:
|
190
|
+
|
191
|
+
- Encoding matches `Accept-Encoding` header in request
|
192
|
+
- `Content-Type` header exists in response
|
193
|
+
- `Content-Encoding` header does not exists in response
|
194
|
+
- `Cache-Control` header does not have `no-transform` directive in response
|
195
|
+
- Response body exists
|
196
|
+
- Response body is readable
|
197
|
+
- Response body is [compressible](#compressible)
|
198
|
+
|
199
|
+
## API
|
200
|
+
|
201
|
+
All APIs can be found in the
|
202
|
+
[deno doc](https://doc.deno.land/https/deno.land/x/compression_middleware/mod.ts).
|
203
|
+
|
204
|
+
## License
|
205
|
+
|
206
|
+
Copyright © 2023-present [httpland](https://github.com/httpland).
|
207
|
+
|
208
|
+
Released under the [MIT](./LICENSE) license
|
@@ -0,0 +1,40 @@
|
|
1
|
+
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
2
|
+
/*!
|
3
|
+
* Adapted directly from negotiator at https://github.com/jshttp/negotiator/
|
4
|
+
* which is licensed as follows:
|
5
|
+
*
|
6
|
+
* (The MIT License)
|
7
|
+
*
|
8
|
+
* Copyright (c) 2012-2014 Federico Romero
|
9
|
+
* Copyright (c) 2012-2014 Isaac Z. Schlueter
|
10
|
+
* Copyright (c) 2014-2015 Douglas Christopher Wilson
|
11
|
+
*
|
12
|
+
* Permission is hereby granted, free of charge, to any person obtaining
|
13
|
+
* a copy of this software and associated documentation files (the
|
14
|
+
* 'Software'), to deal in the Software without restriction, including
|
15
|
+
* without limitation the rights to use, copy, modify, merge, publish,
|
16
|
+
* distribute, sublicense, and/or sell copies of the Software, and to
|
17
|
+
* permit persons to whom the Software is furnished to do so, subject to
|
18
|
+
* the following conditions:
|
19
|
+
*
|
20
|
+
* The above copyright notice and this permission notice shall be
|
21
|
+
* included in all copies or substantial portions of the Software.
|
22
|
+
*
|
23
|
+
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
24
|
+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
25
|
+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
26
|
+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
27
|
+
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
28
|
+
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
29
|
+
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
30
|
+
*/
|
31
|
+
export function compareSpecs(a, b) {
|
32
|
+
return (b.q - a.q ||
|
33
|
+
(b.s ?? 0) - (a.s ?? 0) ||
|
34
|
+
(a.o ?? 0) - (b.o ?? 0) ||
|
35
|
+
a.i - b.i ||
|
36
|
+
0);
|
37
|
+
}
|
38
|
+
export function isQuality(spec) {
|
39
|
+
return spec.q > 0;
|
40
|
+
}
|
@@ -0,0 +1,121 @@
|
|
1
|
+
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
2
|
+
/*!
|
3
|
+
* Adapted directly from negotiator at https://github.com/jshttp/negotiator/
|
4
|
+
* which is licensed as follows:
|
5
|
+
*
|
6
|
+
* (The MIT License)
|
7
|
+
*
|
8
|
+
* Copyright (c) 2012-2014 Federico Romero
|
9
|
+
* Copyright (c) 2012-2014 Isaac Z. Schlueter
|
10
|
+
* Copyright (c) 2014-2015 Douglas Christopher Wilson
|
11
|
+
*
|
12
|
+
* Permission is hereby granted, free of charge, to any person obtaining
|
13
|
+
* a copy of this software and associated documentation files (the
|
14
|
+
* 'Software'), to deal in the Software without restriction, including
|
15
|
+
* without limitation the rights to use, copy, modify, merge, publish,
|
16
|
+
* distribute, sublicense, and/or sell copies of the Software, and to
|
17
|
+
* permit persons to whom the Software is furnished to do so, subject to
|
18
|
+
* the following conditions:
|
19
|
+
*
|
20
|
+
* The above copyright notice and this permission notice shall be
|
21
|
+
* included in all copies or substantial portions of the Software.
|
22
|
+
*
|
23
|
+
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
24
|
+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
25
|
+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
26
|
+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
27
|
+
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
28
|
+
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
29
|
+
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
30
|
+
*/
|
31
|
+
import { compareSpecs, isQuality } from "./common.js";
|
32
|
+
const simpleEncodingRegExp = /^\s*([^\s;]+)\s*(?:;(.*))?$/;
|
33
|
+
function parseEncoding(str, i) {
|
34
|
+
const match = simpleEncodingRegExp.exec(str);
|
35
|
+
if (!match) {
|
36
|
+
return undefined;
|
37
|
+
}
|
38
|
+
const encoding = match[1];
|
39
|
+
let q = 1;
|
40
|
+
if (match[2]) {
|
41
|
+
const params = match[2].split(";");
|
42
|
+
for (const param of params) {
|
43
|
+
const p = param.trim().split("=");
|
44
|
+
if (p[0] === "q") {
|
45
|
+
q = parseFloat(p[1]);
|
46
|
+
break;
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
return { encoding, q, i };
|
51
|
+
}
|
52
|
+
function specify(encoding, spec, i = -1) {
|
53
|
+
if (!spec.encoding) {
|
54
|
+
return;
|
55
|
+
}
|
56
|
+
let s = 0;
|
57
|
+
if (spec.encoding.toLocaleLowerCase() === encoding.toLocaleLowerCase()) {
|
58
|
+
s = 1;
|
59
|
+
}
|
60
|
+
else if (spec.encoding !== "*") {
|
61
|
+
return;
|
62
|
+
}
|
63
|
+
return {
|
64
|
+
i,
|
65
|
+
o: spec.i,
|
66
|
+
q: spec.q,
|
67
|
+
s,
|
68
|
+
};
|
69
|
+
}
|
70
|
+
function parseAcceptEncoding(accept) {
|
71
|
+
const accepts = accept.split(",");
|
72
|
+
const parsedAccepts = [];
|
73
|
+
let hasIdentity = false;
|
74
|
+
let minQuality = 1;
|
75
|
+
for (let i = 0; i < accepts.length; i++) {
|
76
|
+
const encoding = parseEncoding(accepts[i].trim(), i);
|
77
|
+
if (encoding) {
|
78
|
+
parsedAccepts.push(encoding);
|
79
|
+
hasIdentity = hasIdentity || !!specify("identity", encoding);
|
80
|
+
minQuality = Math.min(minQuality, encoding.q || 1);
|
81
|
+
}
|
82
|
+
}
|
83
|
+
if (!hasIdentity) {
|
84
|
+
parsedAccepts.push({
|
85
|
+
encoding: "identity",
|
86
|
+
q: minQuality,
|
87
|
+
i: accepts.length - 1,
|
88
|
+
});
|
89
|
+
}
|
90
|
+
return parsedAccepts;
|
91
|
+
}
|
92
|
+
function getEncodingPriority(encoding, accepted, index) {
|
93
|
+
let priority = { o: -1, q: 0, s: 0, i: 0 };
|
94
|
+
for (const s of accepted) {
|
95
|
+
const spec = specify(encoding, s, index);
|
96
|
+
if (spec &&
|
97
|
+
(priority.s - spec.s || priority.q - spec.q ||
|
98
|
+
priority.o - spec.o) <
|
99
|
+
0) {
|
100
|
+
priority = spec;
|
101
|
+
}
|
102
|
+
}
|
103
|
+
return priority;
|
104
|
+
}
|
105
|
+
/** Given an `Accept-Encoding` string, parse out the encoding returning a
|
106
|
+
* negotiated encoding based on the `provided` encodings otherwise just a
|
107
|
+
* prioritized array of encodings. */
|
108
|
+
export function preferredEncodings(accept, provided) {
|
109
|
+
const accepts = parseAcceptEncoding(accept);
|
110
|
+
if (!provided) {
|
111
|
+
return accepts
|
112
|
+
.filter(isQuality)
|
113
|
+
.sort(compareSpecs)
|
114
|
+
.map((spec) => spec.encoding);
|
115
|
+
}
|
116
|
+
const priorities = provided.map((type, index) => getEncodingPriority(type, accepts, index));
|
117
|
+
return priorities
|
118
|
+
.filter(isQuality)
|
119
|
+
.sort(compareSpecs)
|
120
|
+
.map((priority) => provided[priorities.indexOf(priority)]);
|
121
|
+
}
|
@@ -0,0 +1,110 @@
|
|
1
|
+
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
2
|
+
/*!
|
3
|
+
* Adapted directly from negotiator at https://github.com/jshttp/negotiator/
|
4
|
+
* which is licensed as follows:
|
5
|
+
*
|
6
|
+
* (The MIT License)
|
7
|
+
*
|
8
|
+
* Copyright (c) 2012-2014 Federico Romero
|
9
|
+
* Copyright (c) 2012-2014 Isaac Z. Schlueter
|
10
|
+
* Copyright (c) 2014-2015 Douglas Christopher Wilson
|
11
|
+
*
|
12
|
+
* Permission is hereby granted, free of charge, to any person obtaining
|
13
|
+
* a copy of this software and associated documentation files (the
|
14
|
+
* 'Software'), to deal in the Software without restriction, including
|
15
|
+
* without limitation the rights to use, copy, modify, merge, publish,
|
16
|
+
* distribute, sublicense, and/or sell copies of the Software, and to
|
17
|
+
* permit persons to whom the Software is furnished to do so, subject to
|
18
|
+
* the following conditions:
|
19
|
+
*
|
20
|
+
* The above copyright notice and this permission notice shall be
|
21
|
+
* included in all copies or substantial portions of the Software.
|
22
|
+
*
|
23
|
+
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
24
|
+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
25
|
+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
26
|
+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
27
|
+
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
28
|
+
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
29
|
+
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
30
|
+
*/
|
31
|
+
import { compareSpecs, isQuality } from "./common.js";
|
32
|
+
const SIMPLE_LANGUAGE_REGEXP = /^\s*([^\s\-;]+)(?:-([^\s;]+))?\s*(?:;(.*))?$/;
|
33
|
+
function parseLanguage(str, i) {
|
34
|
+
const match = SIMPLE_LANGUAGE_REGEXP.exec(str);
|
35
|
+
if (!match) {
|
36
|
+
return undefined;
|
37
|
+
}
|
38
|
+
const [, prefix, suffix] = match;
|
39
|
+
const full = suffix ? `${prefix}-${suffix}` : prefix;
|
40
|
+
let q = 1;
|
41
|
+
if (match[3]) {
|
42
|
+
const params = match[3].split(";");
|
43
|
+
for (const param of params) {
|
44
|
+
const [key, value] = param.trim().split("=");
|
45
|
+
if (key === "q") {
|
46
|
+
q = parseFloat(value);
|
47
|
+
break;
|
48
|
+
}
|
49
|
+
}
|
50
|
+
}
|
51
|
+
return { prefix, suffix, full, q, i };
|
52
|
+
}
|
53
|
+
function parseAcceptLanguage(accept) {
|
54
|
+
const accepts = accept.split(",");
|
55
|
+
const result = [];
|
56
|
+
for (let i = 0; i < accepts.length; i++) {
|
57
|
+
const language = parseLanguage(accepts[i].trim(), i);
|
58
|
+
if (language) {
|
59
|
+
result.push(language);
|
60
|
+
}
|
61
|
+
}
|
62
|
+
return result;
|
63
|
+
}
|
64
|
+
function specify(language, spec, i) {
|
65
|
+
const p = parseLanguage(language, i);
|
66
|
+
if (!p) {
|
67
|
+
return undefined;
|
68
|
+
}
|
69
|
+
let s = 0;
|
70
|
+
if (spec.full.toLowerCase() === p.full.toLowerCase()) {
|
71
|
+
s |= 4;
|
72
|
+
}
|
73
|
+
else if (spec.prefix.toLowerCase() === p.prefix.toLowerCase()) {
|
74
|
+
s |= 2;
|
75
|
+
}
|
76
|
+
else if (spec.full.toLowerCase() === p.prefix.toLowerCase()) {
|
77
|
+
s |= 1;
|
78
|
+
}
|
79
|
+
else if (spec.full !== "*") {
|
80
|
+
return;
|
81
|
+
}
|
82
|
+
return { i, o: spec.i, q: spec.q, s };
|
83
|
+
}
|
84
|
+
function getLanguagePriority(language, accepted, index) {
|
85
|
+
let priority = { i: -1, o: -1, q: 0, s: 0 };
|
86
|
+
for (const accepts of accepted) {
|
87
|
+
const spec = specify(language, accepts, index);
|
88
|
+
if (spec &&
|
89
|
+
((priority.s ?? 0) - (spec.s ?? 0) || priority.q - spec.q ||
|
90
|
+
(priority.o ?? 0) - (spec.o ?? 0)) < 0) {
|
91
|
+
priority = spec;
|
92
|
+
}
|
93
|
+
}
|
94
|
+
return priority;
|
95
|
+
}
|
96
|
+
export function preferredLanguages(accept = "*", provided) {
|
97
|
+
const accepts = parseAcceptLanguage(accept);
|
98
|
+
if (!provided) {
|
99
|
+
return accepts
|
100
|
+
.filter(isQuality)
|
101
|
+
.sort(compareSpecs)
|
102
|
+
.map((spec) => spec.full);
|
103
|
+
}
|
104
|
+
const priorities = provided
|
105
|
+
.map((type, index) => getLanguagePriority(type, accepts, index));
|
106
|
+
return priorities
|
107
|
+
.filter(isQuality)
|
108
|
+
.sort(compareSpecs)
|
109
|
+
.map((priority) => provided[priorities.indexOf(priority)]);
|
110
|
+
}
|
@@ -0,0 +1,174 @@
|
|
1
|
+
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
2
|
+
/*!
|
3
|
+
* Adapted directly from negotiator at https://github.com/jshttp/negotiator/
|
4
|
+
* which is licensed as follows:
|
5
|
+
*
|
6
|
+
* (The MIT License)
|
7
|
+
*
|
8
|
+
* Copyright (c) 2012-2014 Federico Romero
|
9
|
+
* Copyright (c) 2012-2014 Isaac Z. Schlueter
|
10
|
+
* Copyright (c) 2014-2015 Douglas Christopher Wilson
|
11
|
+
*
|
12
|
+
* Permission is hereby granted, free of charge, to any person obtaining
|
13
|
+
* a copy of this software and associated documentation files (the
|
14
|
+
* 'Software'), to deal in the Software without restriction, including
|
15
|
+
* without limitation the rights to use, copy, modify, merge, publish,
|
16
|
+
* distribute, sublicense, and/or sell copies of the Software, and to
|
17
|
+
* permit persons to whom the Software is furnished to do so, subject to
|
18
|
+
* the following conditions:
|
19
|
+
*
|
20
|
+
* The above copyright notice and this permission notice shall be
|
21
|
+
* included in all copies or substantial portions of the Software.
|
22
|
+
*
|
23
|
+
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
24
|
+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
25
|
+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
26
|
+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
27
|
+
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
28
|
+
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
29
|
+
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
30
|
+
*/
|
31
|
+
import { compareSpecs, isQuality } from "./common.js";
|
32
|
+
const simpleMediaTypeRegExp = /^\s*([^\s\/;]+)\/([^;\s]+)\s*(?:;(.*))?$/;
|
33
|
+
function quoteCount(str) {
|
34
|
+
let count = 0;
|
35
|
+
let index = 0;
|
36
|
+
while ((index = str.indexOf(`"`, index)) !== -1) {
|
37
|
+
count++;
|
38
|
+
index++;
|
39
|
+
}
|
40
|
+
return count;
|
41
|
+
}
|
42
|
+
function splitMediaTypes(accept) {
|
43
|
+
const accepts = accept.split(",");
|
44
|
+
let j = 0;
|
45
|
+
for (let i = 1; i < accepts.length; i++) {
|
46
|
+
if (quoteCount(accepts[j]) % 2 === 0) {
|
47
|
+
accepts[++j] = accepts[i];
|
48
|
+
}
|
49
|
+
else {
|
50
|
+
accepts[j] += `,${accepts[i]}`;
|
51
|
+
}
|
52
|
+
}
|
53
|
+
accepts.length = j + 1;
|
54
|
+
return accepts;
|
55
|
+
}
|
56
|
+
function splitParameters(str) {
|
57
|
+
const parameters = str.split(";");
|
58
|
+
let j = 0;
|
59
|
+
for (let i = 1; i < parameters.length; i++) {
|
60
|
+
if (quoteCount(parameters[j]) % 2 === 0) {
|
61
|
+
parameters[++j] = parameters[i];
|
62
|
+
}
|
63
|
+
else {
|
64
|
+
parameters[j] += `;${parameters[i]}`;
|
65
|
+
}
|
66
|
+
}
|
67
|
+
parameters.length = j + 1;
|
68
|
+
return parameters.map((p) => p.trim());
|
69
|
+
}
|
70
|
+
function splitKeyValuePair(str) {
|
71
|
+
const [key, value] = str.split("=");
|
72
|
+
return [key.toLowerCase(), value];
|
73
|
+
}
|
74
|
+
function parseMediaType(str, i) {
|
75
|
+
const match = simpleMediaTypeRegExp.exec(str);
|
76
|
+
if (!match) {
|
77
|
+
return;
|
78
|
+
}
|
79
|
+
const params = Object.create(null);
|
80
|
+
let q = 1;
|
81
|
+
const [, type, subtype, parameters] = match;
|
82
|
+
if (parameters) {
|
83
|
+
const kvps = splitParameters(parameters).map(splitKeyValuePair);
|
84
|
+
for (const [key, val] of kvps) {
|
85
|
+
const value = val && val[0] === `"` && val[val.length - 1] === `"`
|
86
|
+
? val.slice(1, val.length - 1)
|
87
|
+
: val;
|
88
|
+
if (key === "q" && value) {
|
89
|
+
q = parseFloat(value);
|
90
|
+
break;
|
91
|
+
}
|
92
|
+
params[key] = value;
|
93
|
+
}
|
94
|
+
}
|
95
|
+
return { type, subtype, params, q, i };
|
96
|
+
}
|
97
|
+
function parseAccept(accept) {
|
98
|
+
const accepts = splitMediaTypes(accept);
|
99
|
+
const mediaTypes = [];
|
100
|
+
for (let i = 0; i < accepts.length; i++) {
|
101
|
+
const mediaType = parseMediaType(accepts[i].trim(), i);
|
102
|
+
if (mediaType) {
|
103
|
+
mediaTypes.push(mediaType);
|
104
|
+
}
|
105
|
+
}
|
106
|
+
return mediaTypes;
|
107
|
+
}
|
108
|
+
function getFullType(spec) {
|
109
|
+
return `${spec.type}/${spec.subtype}`;
|
110
|
+
}
|
111
|
+
function specify(type, spec, index) {
|
112
|
+
const p = parseMediaType(type, index);
|
113
|
+
if (!p) {
|
114
|
+
return;
|
115
|
+
}
|
116
|
+
let s = 0;
|
117
|
+
if (spec.type.toLowerCase() === p.type.toLowerCase()) {
|
118
|
+
s |= 4;
|
119
|
+
}
|
120
|
+
else if (spec.type !== "*") {
|
121
|
+
return;
|
122
|
+
}
|
123
|
+
if (spec.subtype.toLowerCase() === p.subtype.toLowerCase()) {
|
124
|
+
s |= 2;
|
125
|
+
}
|
126
|
+
else if (spec.subtype !== "*") {
|
127
|
+
return;
|
128
|
+
}
|
129
|
+
const keys = Object.keys(spec.params);
|
130
|
+
if (keys.length) {
|
131
|
+
if (keys.every((key) => (spec.params[key] || "").toLowerCase() ===
|
132
|
+
(p.params[key] || "").toLowerCase())) {
|
133
|
+
s |= 1;
|
134
|
+
}
|
135
|
+
else {
|
136
|
+
return;
|
137
|
+
}
|
138
|
+
}
|
139
|
+
return {
|
140
|
+
i: index,
|
141
|
+
o: spec.o,
|
142
|
+
q: spec.q,
|
143
|
+
s,
|
144
|
+
};
|
145
|
+
}
|
146
|
+
function getMediaTypePriority(type, accepted, index) {
|
147
|
+
let priority = { o: -1, q: 0, s: 0, i: index };
|
148
|
+
for (const accepts of accepted) {
|
149
|
+
const spec = specify(type, accepts, index);
|
150
|
+
if (spec &&
|
151
|
+
((priority.s || 0) - (spec.s || 0) ||
|
152
|
+
(priority.q || 0) - (spec.q || 0) ||
|
153
|
+
(priority.o || 0) - (spec.o || 0)) < 0) {
|
154
|
+
priority = spec;
|
155
|
+
}
|
156
|
+
}
|
157
|
+
return priority;
|
158
|
+
}
|
159
|
+
export function preferredMediaTypes(accept, provided) {
|
160
|
+
const accepts = parseAccept(accept === undefined ? "*/*" : accept || "");
|
161
|
+
if (!provided) {
|
162
|
+
return accepts
|
163
|
+
.filter(isQuality)
|
164
|
+
.sort(compareSpecs)
|
165
|
+
.map(getFullType);
|
166
|
+
}
|
167
|
+
const priorities = provided.map((type, index) => {
|
168
|
+
return getMediaTypePriority(type, accepts, index);
|
169
|
+
});
|
170
|
+
return priorities
|
171
|
+
.filter(isQuality)
|
172
|
+
.sort(compareSpecs)
|
173
|
+
.map((priority) => provided[priorities.indexOf(priority)]);
|
174
|
+
}
|