Skip to content

Commit

Permalink
Add Content-Encoding, Content-Language, and Location support
Browse files Browse the repository at this point in the history
  • Loading branch information
mjackson committed Dec 20, 2024
1 parent 3d49e31 commit de7b61e
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 39 deletions.
3 changes: 3 additions & 0 deletions packages/headers/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,10 @@ header.getPreferred(['gzip', 'deflate']); // 'gzip'
- `acceptEncoding`
- `acceptRanges`
- `connection`
- `contentEncoding`
- `contentLanguage`
- `host`
- `location`
- `referer`

## v0.8.0 (2024-11-14)
Expand Down
49 changes: 28 additions & 21 deletions packages/headers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ let headers = new Headers();
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 }
headers.accept.mediaTypes; // [ 'text/html', 'text/*' ]
Object.fromEntries(headers.accept.entries()); // { 'text/html': 1, 'text/*': 0.9 }

headers.accept.accepts('text/html'); // true
headers.accept.accepts('text/plain'); // true
Expand All @@ -40,7 +40,7 @@ headers.accept.getPreferred(['text/plain', 'text/html']); // 'text/html'
headers.accept.set('text/plain', 0.9);
headers.accept.set('text/*', 0.8);

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

// Accept-Encoding
headers.acceptEncoding = { gzip: 1, deflate: 0.8 };
Expand All @@ -55,8 +55,8 @@ headers.acceptEncoding.getPreferred(['gzip', 'deflate']); // 'gzip'
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 }
headers.acceptLanguage.languages; // [ 'en-us', 'en' ]
Object.fromEntries(headers.acceptLanguage.entries()); // { 'en-us': 1, en: 0.9 }

headers.acceptLanguage.accepts('en'); // true
headers.acceptLanguage.accepts('ja'); // false
Expand All @@ -73,51 +73,58 @@ headers.connection = 'close';
// Content-Type
headers.contentType = 'application/json; charset=utf-8';

console.log(headers.contentType.mediaType); // "application/json"
console.log(headers.contentType.charset); // "utf-8"
headers.contentType.mediaType; // "application/json"
headers.contentType.charset; // "utf-8"

headers.contentType.charset = 'iso-8859-1';

console.log(headers.get('Content-Type')); // "application/json; charset=iso-8859-1"
headers.get('Content-Type'); // "application/json; charset=iso-8859-1"

// Content-Disposition
headers.contentDisposition =
'attachment; filename="example.pdf"; filename*=UTF-8\'\'%E4%BE%8B%E5%AD%90.pdf';

console.log(headers.contentDisposition.type); // 'attachment'
console.log(headers.contentDisposition.filename); // 'example.pdf'
console.log(headers.contentDisposition.filenameSplat); // 'UTF-8\'\'%E4%BE%8B%E5%AD%90.pdf'
console.log(headers.contentDisposition.preferredFilename); // '例子.pdf'
headers.contentDisposition.type; // 'attachment'
headers.contentDisposition.filename; // 'example.pdf'
headers.contentDisposition.filenameSplat; // 'UTF-8\'\'%E4%BE%8B%E5%AD%90.pdf'
headers.contentDisposition.preferredFilename; // '例子.pdf'

// Cookie
headers.cookie = 'session_id=abc123; user_id=12345';

console.log(headers.cookie.get('session_id')); // 'abc123'
console.log(headers.cookie.get('user_id')); // '12345'
headers.cookie.get('session_id'); // 'abc123'
headers.cookie.get('user_id'); // '12345'

headers.cookie.set('theme', 'dark');
console.log(headers.get('Cookie')); // 'session_id=abc123; user_id=12345; theme=dark'
headers.get('Cookie'); // 'session_id=abc123; user_id=12345; theme=dark'

// Host
headers.host = 'example.com';

// Last-Modified
headers.lastModified = new Date();
// or headers.lastModified = new Date().getTime();
headers.get('Last-Modified'); // 'Fri, 20 Dec 2024 08:08:05 GMT'

// Location
headers.location = 'https://example.com';

// Referer
headers.referer = 'https://example.com/';

// Set-Cookie
headers.setCookie = ['session_id=abc123; Path=/; HttpOnly'];

console.log(headers.setCookie[0].name); // 'session_id'
console.log(headers.setCookie[0].value); // 'abc123'
console.log(headers.setCookie[0].path); // '/'
console.log(headers.setCookie[0].httpOnly); // true
headers.setCookie[0].name; // 'session_id'
headers.setCookie[0].value; // 'abc123'
headers.setCookie[0].path; // '/'
headers.setCookie[0].httpOnly; // true

// Modifying Set-Cookie attributes
headers.setCookie[0].maxAge = 3600;
headers.setCookie[0].secure = true;

console.log(headers.get('Set-Cookie'));
// session_id=abc123; Path=/; HttpOnly; Max-Age=3600; Secure
headers.get('Set-Cookie'); // 'session_id=abc123; Path=/; HttpOnly; Max-Age=3600; Secure'

// Setting multiple cookies is easy, it's just an array
headers.setCookie.push('user_id=12345; Path=/api; Secure');
Expand Down
93 changes: 75 additions & 18 deletions packages/headers/src/lib/super-headers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,16 @@ describe('SuperHeaders', () => {
assert.equal(headers.get('Content-Disposition'), 'attachment; filename=example.txt');
});

it('handles the contentEncoding property', () => {
let headers = new SuperHeaders({ contentEncoding: 'gzip' });
assert.equal(headers.get('Content-Encoding'), 'gzip');
});

it('handles the contentLanguage property', () => {
let headers = new SuperHeaders({ contentLanguage: 'en-US' });
assert.equal(headers.get('Content-Language'), 'en-US');
});

it('handles the contentLength property', () => {
let headers = new SuperHeaders({ contentLength: 42 });
assert.equal(headers.get('Content-Length'), '42');
Expand Down Expand Up @@ -232,11 +242,6 @@ describe('SuperHeaders', () => {
assert.equal(headers.get('Host'), 'example.com');
});

it('handles the lastModified property', () => {
let headers = new SuperHeaders({ lastModified: new Date('2021-01-01T00:00:00Z') });
assert.equal(headers.get('Last-Modified'), 'Fri, 01 Jan 2021 00:00:00 GMT');
});

it('handles the ifModifiedSince property', () => {
let headers = new SuperHeaders({ ifModifiedSince: new Date('2021-01-01T00:00:00Z') });
assert.equal(headers.get('If-Modified-Since'), 'Fri, 01 Jan 2021 00:00:00 GMT');
Expand All @@ -247,6 +252,16 @@ describe('SuperHeaders', () => {
assert.equal(headers.get('If-Unmodified-Since'), 'Fri, 01 Jan 2021 00:00:00 GMT');
});

it('handles the lastModified property', () => {
let headers = new SuperHeaders({ lastModified: new Date('2021-01-01T00:00:00Z') });
assert.equal(headers.get('Last-Modified'), 'Fri, 01 Jan 2021 00:00:00 GMT');
});

it('handles the location property', () => {
let headers = new SuperHeaders({ location: 'https://example.com' });
assert.equal(headers.get('Location'), 'https://example.com');
});

it('handles the referer property', () => {
let headers = new SuperHeaders({ referer: 'https://example.com' });
assert.equal(headers.get('Referer'), 'https://example.com');
Expand Down Expand Up @@ -413,6 +428,36 @@ describe('SuperHeaders', () => {
assert.equal(headers.contentDisposition.toString(), '');
});

it('supports the contentEncoding property', () => {
let headers = new SuperHeaders();

assert.equal(headers.contentEncoding, null);

headers.contentEncoding = 'gzip';
assert.equal(headers.contentEncoding, 'gzip');

headers.contentEncoding = ['deflate', 'gzip'];
assert.equal(headers.contentEncoding, 'deflate, gzip');

headers.contentEncoding = null;
assert.equal(headers.contentEncoding, null);
});

it('supports the contentLanguage property', () => {
let headers = new SuperHeaders();

assert.equal(headers.contentLanguage, null);

headers.contentLanguage = 'en-US';
assert.equal(headers.contentLanguage, 'en-US');

headers.contentLanguage = ['en', 'fr'];
assert.equal(headers.contentLanguage, 'en, fr');

headers.contentLanguage = null;
assert.equal(headers.contentLanguage, null);
});

it('supports the contentLength property', () => {
let headers = new SuperHeaders();

Expand Down Expand Up @@ -506,19 +551,6 @@ describe('SuperHeaders', () => {
assert.equal(headers.host, null);
});

it('supports the lastModified property', () => {
let headers = new SuperHeaders();

assert.equal(headers.lastModified, null);

headers.lastModified = new Date('2021-01-01T00:00:00Z');
assert.ok(headers.lastModified instanceof Date);
assert.equal(headers.lastModified.toUTCString(), 'Fri, 01 Jan 2021 00:00:00 GMT');

headers.lastModified = null;
assert.equal(headers.lastModified, null);
});

it('supports the ifModifiedSince property', () => {
let headers = new SuperHeaders();

Expand All @@ -545,6 +577,31 @@ describe('SuperHeaders', () => {
assert.equal(headers.ifUnmodifiedSince, null);
});

it('supports the lastModified property', () => {
let headers = new SuperHeaders();

assert.equal(headers.lastModified, null);

headers.lastModified = new Date('2021-01-01T00:00:00Z');
assert.ok(headers.lastModified instanceof Date);
assert.equal(headers.lastModified.toUTCString(), 'Fri, 01 Jan 2021 00:00:00 GMT');

headers.lastModified = null;
assert.equal(headers.lastModified, null);
});

it('supports the location property', () => {
let headers = new SuperHeaders();

assert.equal(headers.location, null);

headers.location = 'https://example.com';
assert.equal(headers.location, 'https://example.com');

headers.location = null;
assert.equal(headers.location, null);
});

it('supports the referer property', () => {
let headers = new SuperHeaders();

Expand Down
63 changes: 63 additions & 0 deletions packages/headers/src/lib/super-headers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ interface SuperHeadersPropertyInit {
* The [`Content-Disposition`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) header value.
*/
contentDisposition?: string | ContentDispositionInit;
/**
* The [`Content-Encoding`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding) header value.
*/
contentEncoding?: string | string[];
/**
* The [`Content-Language`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Language) header value.
*/
contentLanguage?: string | string[];
/**
* The [`Content-Length`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Length) header value.
*/
Expand Down Expand Up @@ -84,6 +92,10 @@ interface SuperHeadersPropertyInit {
* The [`Last-Modified`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified) header value.
*/
lastModified?: string | DateInit;
/**
* The [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location) header value.
*/
location?: string;
/**
* The [`Referer`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer) header value.
*/
Expand Down Expand Up @@ -434,6 +446,42 @@ export class SuperHeaders extends Headers {
this.#setHeaderValue('content-disposition', ContentDisposition, value);
}

/**
* The `Content-Encoding` header specifies the encoding of the resource.
*
* Note: If multiple encodings have been used, this value may be a comma-separated list. However, most often this
* header will only contain a single value.
*
* [MDN `Content-Encoding` Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding)
*
* [HTTP/1.1 Specification](https://httpwg.org/specs/rfc9110.html#field.content-encoding)
*/
get contentEncoding(): string | null {
return this.get('content-encoding');
}

set contentEncoding(value: string | string[] | undefined | null) {
this.#setValue('content-encoding', Array.isArray(value) ? value.join(', ') : value);
}

/**
* The `Content-Language` header describes the natural language(s) of the intended audience for the response content.
*
* Note: If the response content is intended for multiple audiences, this value may be a comma-separated list. However,
* most often this header will only contain a single value.
*
* [MDN `Content-Language` Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Language)
*
* [HTTP/1.1 Specification](https://httpwg.org/specs/rfc9110.html#field.content-language)
*/
get contentLanguage(): string | null {
return this.get('content-language');
}

set contentLanguage(value: string | string[] | undefined | null) {
this.#setValue('content-language', Array.isArray(value) ? value.join(', ') : value);
}

/**
* The `Content-Length` header indicates the size of the entity-body in bytes.
*
Expand Down Expand Up @@ -572,6 +620,21 @@ export class SuperHeaders extends Headers {
this.#setDateValue('last-modified', value);
}

/**
* The `Location` header indicates the URL to redirect to.
*
* [MDN `Location` Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
*
* [HTTP/1.1 Specification](https://datatracker.ietf.org/doc/html/rfc7231#section-7.1.2)
*/
get location(): string | null {
return this.get('location');
}

set location(value: string | undefined | null) {
this.#setValue('location', value);
}

/**
* The `Referer` header contains the address of the previous web page from which a link to the
* currently requested page was followed.
Expand Down

0 comments on commit de7b61e

Please sign in to comment.