Comparing version 0.0.14 to 0.1.0
@@ -5,4 +5,5 @@ import { Body, JsonBody, StreamBody, DataBody } from './lib/body'; | ||
import { Response } from './lib/response'; | ||
import { AbortError, TimeoutError, FetchInit, OnTrailers } from './lib/core'; | ||
import { AbortError, TimeoutError, FetchInit, OnTrailers, DecodeFunction, Decoder } from './lib/core'; | ||
import { ContextOptions, PushHandler } from './lib/context'; | ||
declare const setup: (opts: ContextOptions) => void; | ||
declare const fetch: (input: string | Request, init?: Partial<FetchInit>) => Promise<Response>; | ||
@@ -13,2 +14,3 @@ declare const disconnect: (url: string) => Promise<void>; | ||
declare function context(opts?: Partial<ContextOptions>): { | ||
setup: (opts: ContextOptions) => void; | ||
fetch: (input: string | Request, init?: Partial<FetchInit>) => Promise<Response>; | ||
@@ -19,2 +21,2 @@ disconnect: (url: string) => Promise<void>; | ||
}; | ||
export { context, fetch, disconnect, disconnectAll, onPush, Body, JsonBody, StreamBody, DataBody, Headers, Request, Response, AbortError, TimeoutError, OnTrailers }; | ||
export { setup, context, fetch, disconnect, disconnectAll, onPush, Body, JsonBody, StreamBody, DataBody, Headers, Request, Response, AbortError, TimeoutError, OnTrailers, ContextOptions, DecodeFunction, Decoder }; |
@@ -19,2 +19,4 @@ 'use strict'; | ||
const defaultContext = new context_1.Context(); | ||
const setup = (opts) => defaultContext.setup(opts); | ||
exports.setup = setup; | ||
const fetch = (input, init) => defaultContext.fetch(input, init); | ||
@@ -31,2 +33,3 @@ exports.fetch = fetch; | ||
return { | ||
setup: ctx.setup.bind(ctx), | ||
fetch: ctx.fetch.bind(ctx), | ||
@@ -33,0 +36,0 @@ disconnect: ctx.disconnect.bind(ctx), |
@@ -1,2 +0,2 @@ | ||
import { FetchInit } from './core'; | ||
import { FetchInit, Decoder } from './core'; | ||
import { Request } from './request'; | ||
@@ -10,2 +10,3 @@ import { Response } from './response'; | ||
cookieJar: CookieJar; | ||
decoders: ReadonlyArray<Decoder>; | ||
} | ||
@@ -19,3 +20,5 @@ export declare type PushHandler = (origin: string, request: Request, getResponse: () => Promise<Response>) => void; | ||
private _pushHandler; | ||
private _decoders; | ||
constructor(opts?: Partial<ContextOptions>); | ||
setup(opts?: Partial<ContextOptions>): void; | ||
onPush(pushHandler: PushHandler): void; | ||
@@ -25,3 +28,3 @@ private handlePush(origin, pushedStream, requestHeaders); | ||
private getOrCreate(origin, options, created?); | ||
private get(url, options?); | ||
private get(url); | ||
private handleDisconnect(sessionItem); | ||
@@ -28,0 +31,0 @@ fetch(input: string | Request, init?: Partial<FetchInit>): Promise<Response>; |
@@ -33,17 +33,23 @@ 'use strict'; | ||
this._h2sessions = new Map(); | ||
this.setup(opts); | ||
} | ||
setup(opts) { | ||
opts = opts || {}; | ||
this._userAgent = | ||
(opts && | ||
'userAgent' in opts && | ||
('userAgent' in opts && | ||
'overwriteUserAgent' in opts && | ||
opts.overwriteUserAgent) | ||
? opts.userAgent | ||
: opts && 'userAgent' in opts | ||
: 'userAgent' in opts | ||
? opts.userAgent + " " + defaultUserAgent | ||
: defaultUserAgent; | ||
this._accept = opts && 'accept' in opts | ||
this._accept = 'accept' in opts | ||
? opts.accept | ||
: defaultAccept; | ||
this._cookieJar = opts && 'cookieJar' in opts | ||
this._cookieJar = 'cookieJar' in opts | ||
? opts.cookieJar | ||
: new cookie_jar_1.CookieJar(); | ||
this._decoders = 'decoders' in opts | ||
? opts.decoders || [] | ||
: []; | ||
} | ||
@@ -68,3 +74,3 @@ onPush(pushHandler) { | ||
pushedStream.once('push', guard(responseHeaders => { | ||
const response = new response_1.H2StreamResponse(path, pushedStream, responseHeaders, false); | ||
const response = new response_1.H2StreamResponse(this._decoders, path, pushedStream, responseHeaders, false); | ||
resolve(response); | ||
@@ -123,4 +129,5 @@ })); | ||
} | ||
get(url, options) { | ||
get(url) { | ||
const { origin } = new url_1.URL(url); | ||
const options = null; | ||
return this.getOrCreate(origin, options); | ||
@@ -140,6 +147,7 @@ } | ||
const sessionGetter = { | ||
get: (url, options) => this.get(url, options), | ||
get: (url) => this.get(url), | ||
userAgent: () => this._userAgent, | ||
accept: () => this._accept, | ||
cookieJar: this._cookieJar, | ||
contentDecoders: () => this._decoders, | ||
}; | ||
@@ -146,0 +154,0 @@ return fetch_1.fetch(sessionGetter, input, init); |
@@ -61,2 +61,7 @@ /// <reference types="node" /> | ||
} | ||
export declare type DecodeFunction = (stream: NodeJS.ReadableStream) => NodeJS.ReadableStream; | ||
export interface Decoder { | ||
name: string; | ||
decode: DecodeFunction; | ||
} | ||
export interface SimpleSession { | ||
@@ -67,2 +72,3 @@ get(url: string | URL, options?: SessionOptions | SecureClientSessionOptions): Promise<ClientHttp2Session>; | ||
cookieJar: CookieJar; | ||
contentDecoders(): ReadonlyArray<Decoder>; | ||
} |
@@ -55,2 +55,8 @@ 'use strict'; | ||
.map(cookie => cookie.cookieString()); | ||
const contentDecoders = session.contentDecoders(); | ||
const acceptEncoding = contentDecoders.length === 0 | ||
? 'gzip;q=1.0, deflate;q=0.5' | ||
: contentDecoders | ||
.map(decoder => `${decoder.name};q=1.0`) | ||
.join(', ') + ', gzip;q=0.8, deflate;q=0.5'; | ||
if (headers.has(HTTP2_HEADER_COOKIE)) | ||
@@ -66,3 +72,3 @@ cookies.push(...utils_1.arrayify(headers.get(HTTP2_HEADER_COOKIE))); | ||
[HTTP2_HEADER_USER_AGENT]: session.userAgent(), | ||
[HTTP2_HEADER_ACCEPT_ENCODING]: 'gzip;q=1.0, deflate;q=0.5', | ||
[HTTP2_HEADER_ACCEPT_ENCODING]: acceptEncoding, | ||
}; | ||
@@ -210,3 +216,3 @@ if (cookies.length > 0) | ||
if (!isRedirected || redirect === 'manual') | ||
return resolve(new response_1.H2StreamResponse(url, stream, headers, redirect === 'manual' | ||
return resolve(new response_1.H2StreamResponse(contentDecoders, url, stream, headers, redirect === 'manual' | ||
? false | ||
@@ -213,0 +219,0 @@ : extra.redirected.length > 0)); |
@@ -1,1 +0,1 @@ | ||
export declare const version = "0.0.14"; | ||
export declare const version = "0.1.0"; |
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.version = '0.0.14'; | ||
exports.version = '0.1.0'; | ||
//# sourceMappingURL=version.js.map |
/// <reference types="node" /> | ||
import { ClientHttp2Stream, IncomingHttpHeaders } from 'http2'; | ||
import { BodyTypes, ResponseInit, ResponseTypes } from './core'; | ||
import { BodyTypes, ResponseInit, ResponseTypes, Decoder } from './core'; | ||
import { Headers } from './headers'; | ||
@@ -21,3 +21,3 @@ import { Body } from './body'; | ||
export declare class H2StreamResponse extends Response { | ||
constructor(url: string, stream: ClientHttp2Stream, headers: IncomingHttpHeaders, redirected: boolean); | ||
constructor(contentDecoders: ReadonlyArray<Decoder>, url: string, stream: ClientHttp2Stream, headers: IncomingHttpHeaders, redirected: boolean); | ||
} |
@@ -103,3 +103,3 @@ 'use strict'; | ||
} | ||
function handleEncoding(stream, headers) { | ||
function handleEncoding(contentDecoders, stream, headers) { | ||
const contentEncoding = headers[HTTP2_HEADER_CONTENT_ENCODING]; | ||
@@ -112,2 +112,5 @@ if (!contentEncoding) | ||
}; | ||
contentDecoders.forEach(decoder => { | ||
decoders[decoder.name] = decoder.decode; | ||
}); | ||
const decoder = decoders[contentEncoding]; | ||
@@ -121,4 +124,4 @@ if (!decoder) | ||
class H2StreamResponse extends Response { | ||
constructor(url, stream, headers, redirected) { | ||
super(handleEncoding(stream, headers), makeInit(headers), makeExtra(url, headers, redirected)); | ||
constructor(contentDecoders, url, stream, headers, redirected) { | ||
super(handleEncoding(contentDecoders, stream, headers), makeInit(headers), makeExtra(url, headers, redirected)); | ||
} | ||
@@ -125,0 +128,0 @@ } |
10
index.ts
@@ -12,2 +12,4 @@ 'use strict' | ||
OnTrailers, | ||
DecodeFunction, | ||
Decoder, | ||
} from './lib/core' | ||
@@ -19,2 +21,5 @@ import { Context, ContextOptions, PushHandler } from './lib/context' | ||
const setup = | ||
( opts: ContextOptions ) => | ||
defaultContext.setup( opts ); | ||
const fetch = | ||
@@ -37,2 +42,3 @@ ( input: string | Request, init?: Partial< FetchInit > ) => | ||
return { | ||
setup: ctx.setup.bind( ctx ) as typeof setup, | ||
fetch: ctx.fetch.bind( ctx ) as typeof fetch, | ||
@@ -46,2 +52,3 @@ disconnect: ctx.disconnect.bind( ctx ) as typeof disconnect, | ||
export { | ||
setup, | ||
context, | ||
@@ -64,2 +71,5 @@ fetch, | ||
OnTrailers, | ||
ContextOptions, | ||
DecodeFunction, | ||
Decoder, | ||
} |
@@ -20,2 +20,3 @@ 'use strict' | ||
SimpleSession, | ||
Decoder, | ||
TimeoutError, | ||
@@ -54,2 +55,3 @@ AbortError, | ||
cookieJar: CookieJar; | ||
decoders: ReadonlyArray< Decoder >; | ||
} | ||
@@ -89,2 +91,3 @@ | ||
private _pushHandler: PushHandler; | ||
private _decoders: ReadonlyArray< Decoder >; | ||
@@ -95,5 +98,11 @@ constructor( opts?: Partial< ContextOptions > ) | ||
this.setup( opts ); | ||
} | ||
public setup( opts?: Partial< ContextOptions > ) | ||
{ | ||
opts = opts || { }; | ||
this._userAgent = | ||
( | ||
opts && | ||
'userAgent' in opts && | ||
@@ -104,13 +113,17 @@ 'overwriteUserAgent' in opts && | ||
? opts.userAgent | ||
: opts && 'userAgent' in opts | ||
: 'userAgent' in opts | ||
? opts.userAgent + " " + defaultUserAgent | ||
: defaultUserAgent; | ||
this._accept = opts && 'accept' in opts | ||
this._accept = 'accept' in opts | ||
? opts.accept | ||
: defaultAccept; | ||
this._cookieJar = opts && 'cookieJar' in opts | ||
this._cookieJar = 'cookieJar' in opts | ||
? opts.cookieJar | ||
: new CookieJar( ); | ||
this._decoders = 'decoders' in opts | ||
? opts.decoders || [ ] | ||
: [ ]; | ||
} | ||
@@ -156,3 +169,8 @@ | ||
const response = new H2StreamResponse( | ||
path, pushedStream, responseHeaders, false ); | ||
this._decoders, | ||
path, | ||
pushedStream, | ||
responseHeaders, | ||
false | ||
); | ||
@@ -258,6 +276,3 @@ resolve( response ); | ||
private get( | ||
url: string, | ||
options?: SessionOptions | SecureClientSessionOptions | ||
) | ||
private get( url: string ) | ||
: Promise< ClientHttp2Session > | ||
@@ -267,2 +282,4 @@ { | ||
const options: SessionOptions | SecureClientSessionOptions = null; | ||
return this.getOrCreate( origin, options ); | ||
@@ -290,9 +307,7 @@ } | ||
const sessionGetter: SimpleSession = { | ||
get: ( | ||
url: string, | ||
options?: SessionOptions | SecureClientSessionOptions | ||
) => this.get( url, options ), | ||
get: ( url: string ) => this.get( url ), | ||
userAgent: ( ) => this._userAgent, | ||
accept: ( ) => this._accept, | ||
cookieJar: this._cookieJar, | ||
contentDecoders: ( ) => this._decoders, | ||
}; | ||
@@ -299,0 +314,0 @@ return fetch( sessionGetter, input, init ); |
@@ -173,2 +173,11 @@ 'use strict' | ||
export type DecodeFunction = | ||
( stream: NodeJS.ReadableStream ) => NodeJS.ReadableStream; | ||
export interface Decoder | ||
{ | ||
name: string; | ||
decode: DecodeFunction; | ||
} | ||
export interface SimpleSession | ||
@@ -185,2 +194,4 @@ { | ||
cookieJar: CookieJar; | ||
contentDecoders( ): ReadonlyArray< Decoder >; | ||
} |
@@ -113,2 +113,11 @@ 'use strict' | ||
const contentDecoders = session.contentDecoders( ); | ||
const acceptEncoding = | ||
contentDecoders.length === 0 | ||
? 'gzip;q=1.0, deflate;q=0.5' | ||
: contentDecoders | ||
.map( decoder => `${decoder.name};q=1.0` ) | ||
.join( ', ' ) + ', gzip;q=0.8, deflate;q=0.5'; | ||
if ( headers.has( HTTP2_HEADER_COOKIE ) ) | ||
@@ -126,3 +135,3 @@ cookies.push( ...arrayify( headers.get( HTTP2_HEADER_COOKIE ) ) ); | ||
[ HTTP2_HEADER_USER_AGENT ]: session.userAgent( ), | ||
[ HTTP2_HEADER_ACCEPT_ENCODING ]: 'gzip;q=1.0, deflate;q=0.5', | ||
[ HTTP2_HEADER_ACCEPT_ENCODING ]: acceptEncoding, | ||
}; | ||
@@ -355,2 +364,3 @@ | ||
new H2StreamResponse( | ||
contentDecoders, | ||
url, | ||
@@ -357,0 +367,0 @@ stream, |
'use strict' | ||
export const version = '0.0.14'; | ||
export const version = '0.1.0'; |
@@ -26,2 +26,4 @@ 'use strict' | ||
ResponseTypes, | ||
Decoder, | ||
DecodeFunction, | ||
} from './core' | ||
@@ -188,2 +190,3 @@ | ||
function handleEncoding( | ||
contentDecoders: ReadonlyArray< Decoder >, | ||
stream: NodeJS.ReadableStream, | ||
@@ -199,3 +202,3 @@ headers: IncomingHttpHeaders | ||
const decoders = { | ||
const decoders: { [ name: string ]: DecodeFunction; } = { | ||
gzip: ( stream: NodeJS.ReadableStream ) => | ||
@@ -207,2 +210,7 @@ stream.pipe( createGunzip( ) ), | ||
contentDecoders.forEach( decoder => | ||
{ | ||
decoders[ decoder.name ] = decoder.decode; | ||
} ); | ||
const decoder = decoders[ contentEncoding ]; | ||
@@ -221,2 +229,3 @@ | ||
constructor( | ||
contentDecoders: ReadonlyArray< Decoder >, | ||
url: string, | ||
@@ -229,3 +238,7 @@ stream: ClientHttp2Stream, | ||
super( | ||
handleEncoding( < NodeJS.ReadableStream >stream, headers ), | ||
handleEncoding( | ||
contentDecoders, | ||
< NodeJS.ReadableStream >stream, | ||
headers | ||
), | ||
makeInit( headers ), | ||
@@ -232,0 +245,0 @@ makeExtra( url, headers, redirected ) |
{ | ||
"name": "fetch-h2", | ||
"version": "0.0.14", | ||
"version": "0.1.0", | ||
"description": "HTTP/2-only Fetch API client for Node.js", | ||
@@ -55,3 +55,3 @@ "author": "Gustaf Räntilä", | ||
"ts-node": "3.x", | ||
"typescript": "2.5.x" | ||
"typescript": "2.6.x" | ||
}, | ||
@@ -58,0 +58,0 @@ "dependencies": { |
@@ -15,7 +15,7 @@ [![npm version][npm-image]][npm-url] | ||
By default, `fetch-h2` will accept `gzip` and `deflate` encodings, and decode transparently. If you also want to allow Brotli (`br`), use the [`fetch-h2-br`](https://www.npmjs.com/package/fetch-h2-br) package. | ||
**NOTE;** HTTP/2 support was recently introduced in Node.js (version 8.4), and required `node` to be started with a flag `--expose-http2` up to version 8.7 (this module won't work without it). From Node.js 8.8, the `http2` module is available without any flag. | ||
**DISCLAIMER: This is an early project, don't expect everything to "just work".** | ||
## Imports | ||
@@ -33,2 +33,3 @@ | ||
import { | ||
setup, | ||
context, | ||
@@ -46,2 +47,6 @@ fetch, | ||
ContextOptions, | ||
DecodeFunction, | ||
Decoder, | ||
// TypeScript types: | ||
@@ -52,4 +57,6 @@ OnTrailers, | ||
Apart from the obvious `fetch`, the functions `context`, `disconnect`, `disconnectAll` and `onPush` are described below, and the classes [`Body`](https://developer.mozilla.org/docs/Web/API/Body), [`Headers`](https://developer.mozilla.org/docs/Web/API/Headers), [`Request`](https://developer.mozilla.org/docs/Web/API/Request) and [`Response`](https://developer.mozilla.org/docs/Web/API/Response) are part of the [Fetch API](https://developer.mozilla.org/docs/Web/API/Fetch_API). `AbortError` is the error thrown in case of an [abort signal](https://developer.mozilla.org/docs/Web/API/AbortSignal) (this is also the error thrown in case of a *timeout*, which in `fetch-h2` is internally implemented as an abort signal), `TimeoutError` is thrown if the request times out. | ||
Apart from the obvious `fetch`, the functions `setup`, `context`, `disconnect`, `disconnectAll` and `onPush` are described below, and the classes [`Body`](https://developer.mozilla.org/docs/Web/API/Body), [`Headers`](https://developer.mozilla.org/docs/Web/API/Headers), [`Request`](https://developer.mozilla.org/docs/Web/API/Request) and [`Response`](https://developer.mozilla.org/docs/Web/API/Response) are part of the [Fetch API](https://developer.mozilla.org/docs/Web/API/Fetch_API). `AbortError` is the error thrown in case of an [abort signal](https://developer.mozilla.org/docs/Web/API/AbortSignal) (this is also the error thrown in case of a *timeout*, which in `fetch-h2` is internally implemented as an abort signal), `TimeoutError` is thrown if the request times out. | ||
The `ContextOptions`, `DecodeFunction` and `Decoder` types are described below. | ||
The `OnTrailers` is the type for the `onTrailers` callback. | ||
@@ -156,3 +163,33 @@ | ||
Contexts can be configured with options when constructed. The default context can be configured using the `setup( )` function, but if this function is used, call it only once, and before any usage of `fetch-h2`, or the result is undefined. | ||
### Context configuration | ||
The options to `setup( )` are the same as those to `context( )` and is available as a TypeScript type `ContextOptions`. | ||
```ts | ||
// The options object | ||
interface ContextOptions | ||
{ | ||
userAgent: string; | ||
overwriteUserAgent: boolean; | ||
accept: string; | ||
//cookieJar: CookieJar; | ||
decoders: ReadonlyArray< Decoder >; | ||
} | ||
``` | ||
By specifying a `userAgent` string, this will be added to the built-in `user-agent` header. If defined, and `overwriteUserAgent` is true, the built-in user agent string will not be sent. | ||
`accept` can be specified, which is the `accept` header. The default is: | ||
``` | ||
application/json, text/*;0.9, */*;q=0.8 | ||
``` | ||
`cookieJar` can be set to a custom cookie jar. This logic is currently not decided upon, don't use this. | ||
`decoders` can be an array of custom decoders, such as [`fetch-h2-br`](https://www.npmjs.com/package/fetch-h2-br) which adds Brotli content decoding support. | ||
## Errors | ||
@@ -159,0 +196,0 @@ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
551335
73
3439
257
7