Skip to content

Commit

Permalink
Add Accept-Encoding support
Browse files Browse the repository at this point in the history
  • Loading branch information
mjackson committed Dec 20, 2024
1 parent 0735a75 commit dfc13a7
Show file tree
Hide file tree
Showing 10 changed files with 460 additions and 21 deletions.
20 changes: 19 additions & 1 deletion packages/headers/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,27 @@ header.getQuality('text/plain'); // 0.9
header.getPreferred(['text/html', 'text/plain']); // 'text/html'
```

- Added property support (getters and setters) for
- Added `Accept-Encoding` support

```ts
import { AcceptEncoding } from '@mjackson/headers';

let header = new AcceptEncoding({ gzip: 1, deflate: 0.9 });

header.accepts('gzip'); // true
header.accepts('deflate'); // true
header.accepts('identity'); // true
header.accepts('br'); // false

header.getQuality('gzip'); // 1
header.getQuality('deflate'); // 0.9

header.getPreferred(['gzip', 'deflate']); // 'gzip'
```

- Added `SuperHeaders.prototype` (getters and setters) for:
- `headers.accept`
- `headers.acceptEncoding`
- `headers.connection`
- `headers.host`
- `headers.referer`
Expand Down
42 changes: 38 additions & 4 deletions packages/headers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ import Headers from '@mjackson/headers';
let headers = new Headers();

// Accept
headers.accept = 'text/html,text/*;q=0.9';
headers.accept = { 'text/html': 1, 'text/*': 0.9 };
// or headers.accept = 'text/html,text/*;q=0.9';

console.log(headers.accept.mediaTypes); // [ 'text/html', 'text/*' ]
console.log(Object.fromEntries(headers.accept.entries())); // { 'text/html': 1, 'text/*': 0.9 }
Expand All @@ -41,8 +42,18 @@ headers.accept.set('text/*', 0.8);

console.log(headers.get('Accept')); // "text/html,text/plain;q=0.9,text/*;q=0.8"

// Accept-Encoding
headers.acceptEncoding = { gzip: 1, deflate: 0.8 };
// or headers.acceptEncoding = 'gzip, deflate;q=0.8';

headers.acceptEncoding.accepts('gzip'); // true
headers.acceptEncoding.accepts('br'); // false

headers.acceptEncoding.getPreferred(['gzip', 'deflate']); // 'gzip'

// Accept-Language
headers.acceptLanguage = 'en-US,en;q=0.9';
headers.acceptLanguage = { 'en-US': 1, en: 0.9 };
// or headers.acceptLanguage = 'en-US,en;q=0.9';

console.log(headers.acceptLanguage.languages); // [ 'en-us', 'en' ]
console.log(Object.fromEntries(headers.acceptLanguage.entries())); // { 'en-us': 1, en: 0.9 }
Expand Down Expand Up @@ -190,6 +201,7 @@ The following headers are currently supported:
- [Overview](#overview)
- [Low-level API](#low-level-api)
- [Accept](#accept)
- [Accept-Encoding](#accept-encoding)
- [Accept-Language](#accept-language)
- [Cache-Control](#cache-control)
- [Content-Disposition](#content-disposition)
Expand All @@ -215,7 +227,6 @@ header.accepts('image/jpeg'); // false

header.getPreferred(['text/html', 'text/plain']); // 'text/html'

// Iterate over media type/quality pairs
for (let [mediaType, quality] of header) {
// ...
}
Expand All @@ -225,6 +236,30 @@ let header = new Accept({ 'text/html': 1, 'text/*': 0.9 });
let header = new Accept(['text/html', ['text/*', 0.9]]);
```

### Accept-Encoding

```ts
let header = new AcceptEncoding('gzip,deflate;q=0.9');

header.has('gzip'); // true
header.has('br'); // false

header.accepts('gzip'); // true
header.accepts('deflate'); // true
header.accepts('identity'); // true
header.accepts('br'); // true

header.getPreferred(['gzip', 'deflate']); // 'gzip'

for (let [encoding, weight] of header) {
// ...
}

// Alternative init styles
let header = new AcceptEncoding({ gzip: 1, deflate: 0.9 });
let header = new AcceptEncoding(['gzip', ['deflate', 0.9]]);
```

### Accept-Language

```ts
Expand All @@ -241,7 +276,6 @@ header.accepts('fr'); // true
header.getPreferred(['en-US', 'en-GB']); // 'en-US'
header.getPreferred(['en', 'fr']); // 'en'

// Iterate over language/quality pairs
for (let [language, quality] of header) {
// ...
}
Expand Down
1 change: 1 addition & 0 deletions packages/headers/src/headers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { type AcceptInit, Accept } from './lib/accept.ts';
export { type AcceptEncodingInit, AcceptEncoding } from './lib/accept-encoding.ts';
export { type AcceptLanguageInit, AcceptLanguage } from './lib/accept-language.ts';
export { type CacheControlInit, CacheControl } from './lib/cache-control.ts';
export { type ContentDispositionInit, ContentDisposition } from './lib/content-disposition.ts';
Expand Down
151 changes: 151 additions & 0 deletions packages/headers/src/lib/accept-encoding.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import * as assert from 'node:assert/strict';
import { describe, it } from 'node:test';

import { AcceptEncoding } from './accept-encoding.ts';

describe('Accept-Encoding', () => {
it('initializes with an empty string', () => {
let header = new AcceptEncoding('');
assert.equal(header.size, 0);
});

it('initializes with a string', () => {
let header = new AcceptEncoding('gzip, deflate;q=0.9');
assert.equal(header.size, 2);
});

it('initializes with an array', () => {
let header = new AcceptEncoding(['gzip', ['deflate', 0.9]]);
assert.equal(header.size, 2);
});

it('initializes with an object', () => {
let header = new AcceptEncoding({ gzip: 1, deflate: 0.9 });
assert.equal(header.size, 2);
});

it('initializes with another AcceptEncoding', () => {
let header = new AcceptEncoding(new AcceptEncoding('gzip, deflate;q=0.9'));
assert.equal(header.size, 2);
});

it('handles whitespace in initial value', () => {
let header = new AcceptEncoding(' gzip , deflate;q= 0.9 ');
assert.equal(header.size, 2);
});

it('gets all encodings', () => {
let header = new AcceptEncoding('gzip, deflate;q=0.9');
assert.deepEqual(header.encodings, ['gzip', 'deflate']);
});

it('gets all weights', () => {
let header = new AcceptEncoding('gzip, deflate;q=0.9');
assert.deepEqual(header.weights, [1, 0.9]);
});

it('checks if an encoding is acceptable', () => {
let header = new AcceptEncoding('gzip, deflate;q=0.9,br;q=0.8');
assert.equal(header.accepts('gzip'), true);
assert.equal(header.accepts('deflate'), true);
assert.equal(header.accepts('br'), true);
assert.equal(header.accepts('compress'), false);
assert.equal(header.accepts('identity'), true); // special case
});

it('gets the correct weights', () => {
let header = new AcceptEncoding('gzip, deflate;q=0.9,*;q=0.8');
assert.equal(header.getWeight('gzip'), 1);
assert.equal(header.getWeight('deflate'), 0.9);
assert.equal(header.getWeight('br'), 0.8);
});

it('gets the preferred encoding', () => {
let header = new AcceptEncoding('gzip, deflate;q=0.9,*;q=0.8');
assert.equal(header.getPreferred(['gzip', 'deflate']), 'gzip');
assert.equal(header.getPreferred(['deflate', 'br']), 'deflate');
});

it('sets and gets encodings', () => {
let header = new AcceptEncoding();
header.set('gzip', 1);
assert.equal(header.get('gzip'), 1);
});

it('deletes encodings', () => {
let header = new AcceptEncoding('gzip, deflate;q=0.9');
assert.equal(header.has('gzip'), true);
header.delete('gzip');
assert.equal(header.has('gzip'), false);
});

it('clears all encodings', () => {
let header = new AcceptEncoding('gzip, deflate;q=0.9');
assert.equal(header.size, 2);
header.clear();
assert.equal(header.size, 0);
});

it('iterates over entries', () => {
let header = new AcceptEncoding('gzip, deflate;q=0.9');
assert.deepEqual(Array.from(header.entries()), [
['gzip', 1],
['deflate', 0.9],
]);
});

it('is directly iterable', () => {
let header = new AcceptEncoding('gzip, deflate;q=0.9');
assert.deepEqual(Array.from(header), [
['gzip', 1],
['deflate', 0.9],
]);
});

it('uses forEach correctly', () => {
let header = new AcceptEncoding('gzip, deflate;q=0.9');
let result: [string, number][] = [];
header.forEach((encoding, weight) => {
result.push([encoding, weight]);
});
assert.deepEqual(result, [
['gzip', 1],
['deflate', 0.9],
]);
});

it('returns correct size', () => {
let header = new AcceptEncoding('gzip, deflate;q=0.9');
assert.equal(header.size, 2);
});

it('converts to a string', () => {
let header = new AcceptEncoding('gzip, deflate;q=0.9');
assert.equal(header.toString(), 'gzip,deflate;q=0.9');
});

it('handles setting empty weights', () => {
let header = new AcceptEncoding();
header.set('deflate');
assert.equal(header.get('deflate'), 1);
});

it('handles setting wildcard value', () => {
let header = new AcceptEncoding();
header.set('*', 0.8);
assert.equal(header.get('*'), 0.8);
});

it('sorts initial value', () => {
let header = new AcceptEncoding('deflate;q=0.9,gzip');
assert.equal(header.toString(), 'gzip,deflate;q=0.9');
});

it('sorts updated value', () => {
let header = new AcceptEncoding('gzip;q=0.8,deflate');
header.set('br');
assert.equal(header.toString(), 'deflate,br,gzip;q=0.8');
header.set('deflate', 0.9);
assert.equal(header.toString(), 'br,deflate;q=0.9,gzip;q=0.8');
});
});
Loading

0 comments on commit dfc13a7

Please sign in to comment.