http2-wrapper
Advanced tools
Comparing version 0.1.1 to 0.2.0
{ | ||
"name": "http2-wrapper", | ||
"version": "0.1.1", | ||
"version": "0.2.0", | ||
"description": "Use HTTP2 the same way like HTTP1", | ||
@@ -10,3 +10,4 @@ "main": "source", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
"test": "xo && nyc ava", | ||
"coveralls": "nyc report --reporter=text-lcov | coveralls" | ||
}, | ||
@@ -33,3 +34,14 @@ "files": [ | ||
} | ||
}, | ||
"devDependencies": { | ||
"ava": "^0.25.0", | ||
"coveralls": "^3.0.2", | ||
"delay": "^3.0.0", | ||
"get-stream": "^4.0.0", | ||
"nyc": "^12.0.2", | ||
"p-event": "^2.1.0", | ||
"pem": "^1.12.5", | ||
"to-readable-stream": "^1.0.0", | ||
"xo": "^0.22.0" | ||
} | ||
} |
@@ -1,4 +0,8 @@ | ||
# http2-wrapper | ||
# http2-wrapper (under heavy development) | ||
> Use HTTP2 the same way like HTTP1 | ||
[![Build Status](https://travis-ci.org/szmarczak/http2-wrapper.svg?branch=master)](https://travis-ci.org/szmarczak/http2-wrapper) [![Coverage Status](https://coveralls.io/repos/github/szmarczak/http2-wrapper/badge.svg?branch=master)](https://coveralls.io/github/szmarczak/http2-wrapper?branch=master) | ||
![npm](https://img.shields.io/npm/dm/http2-wrapper.svg) | ||
[![install size](https://packagephobia.now.sh/badge?p=http2-wrapper@0.1.1)](https://packagephobia.now.sh/result?p=http2-wrapper@0.1.1) | ||
## Usage | ||
@@ -88,3 +92,3 @@ ```js | ||
* [x] Event: 'continue' | ||
* [ ] Event: 'information' | ||
* [ ] ~~Event: 'information'~~ | ||
* [x] Event: 'response' | ||
@@ -100,8 +104,9 @@ * [x] Event: 'socket' | ||
* [x] request.getHeader(name) | ||
* [ ] ~~request.maxHeadersCount~~ | ||
* [x] request.removeHeader(name) | ||
* [x] request.setHeader(name, value) | ||
* [x] request.setNoDelay([noDelay]) | ||
* [x] request.setSocketKeepAlive([enable][, initialDelay]) | ||
* [ ] ~~request.setSocketKeepAlive([enable][, initialDelay])~~ | ||
* [x] request.setTimeout(timeout[, callback]) | ||
* [x] request.socket | ||
* [x] request.write(chunk[, encoding][, callback]) |
@@ -5,2 +5,9 @@ 'use strict'; | ||
const HTTP2IncomingMessage = require('./HTTP2IncomingMessage'); | ||
const { | ||
ERR_HTTP2_HEADERS_SENT, | ||
ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED, | ||
ERR_INVALID_HTTP_TOKEN, | ||
ERR_HTTP2_INVALID_HEADER_VALUE, | ||
createHangUpError | ||
} = require('./errors'); | ||
@@ -12,3 +19,4 @@ const { | ||
HTTP2_HEADER_SCHEME, | ||
HTTP2_HEADER_STATUS | ||
HTTP2_HEADER_STATUS, | ||
NGHTTP2_CANCEL | ||
} = http2.constants; | ||
@@ -29,60 +37,3 @@ | ||
const makeError = (Base, key, getMessage) => { | ||
return class NodeError extends Base { | ||
constructor(...args) { | ||
super(typeof getMessage === 'string' ? getMessage : getMessage(args)); | ||
} | ||
get name() { | ||
return `${super.name} [${key}]`; | ||
} | ||
set name(value) { | ||
Object.defineProperty(this, 'name', { | ||
configurable: true, | ||
enumerable: true, | ||
value, | ||
writable: true | ||
}); | ||
} | ||
get code() { | ||
return key; | ||
} | ||
set code(value) { | ||
Object.defineProperty(this, 'code', { | ||
configurable: true, | ||
enumerable: true, | ||
value, | ||
writable: true | ||
}); | ||
} | ||
}; | ||
}; | ||
const ERR_HTTP2_HEADERS_SENT = makeError(Error, 'ERR_HTTP2_HEADERS_SENT', args => { | ||
if (args && args[0]) { | ||
return `Cannot ${args[0]} headers after they are sent to the client`; | ||
} | ||
return 'Response has already been initated.'; | ||
}); | ||
const ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED = makeError(TypeError, 'ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED', 'Cannot set HTTP/2 pseudo-headers'); | ||
const ERR_INVALID_HTTP_TOKEN = makeError(TypeError, 'ERR_INVALID_HTTP_TOKEN', args => { | ||
return `${args[0]} must be a valid HTTP token ["${args[1]}"]`; | ||
}); | ||
const ERR_HTTP2_INVALID_HEADER_VALUE = makeError(TypeError, 'ERR_HTTP2_INVALID_HEADER_VALUE', args => { | ||
return `{Invalid value "${args[0]}" for header "${args[1]}"}`; | ||
}); | ||
const createHangUpError = () => { | ||
const err = new Error('socket hang up'); | ||
err.code = 'ECONNRESET'; | ||
return err; | ||
}; | ||
/* istanbul ignore next */ | ||
const deferToConnect = (socket, method, ...args) => { | ||
@@ -105,2 +56,3 @@ let call; | ||
const kHeaders = Symbol('headers'); | ||
const kKeepAlive = Symbol('keepAlive'); | ||
@@ -117,13 +69,19 @@ class HTTP2ClientRequest extends Writable { | ||
let authority; | ||
if (options.headers && options.headers.authority) { | ||
authority = options.headers.authority; // eslint-disable-line prefer-destructuring | ||
delete options.headers.authority; | ||
if (options.session) { | ||
this.session = options.session; | ||
} else { | ||
authority = `${options.protocol}//${options.hostname || options.host}:${port}`; | ||
let authority; | ||
if (options.headers && options.headers.authority) { | ||
authority = options.headers.authority; // eslint-disable-line prefer-destructuring | ||
delete options.headers.authority; | ||
} else { | ||
authority = `${options.protocol}//${options.hostname || options.host}:${port}`; | ||
} | ||
this.session = http2.connect(authority); | ||
} | ||
this.session = http2.connect(authority); | ||
this.socket = this.session.socket; | ||
this.connection = this.session.connection; | ||
// Haha, this function isn't proxied, so we can get raw socket here! Workaround for #2 | ||
this.socket = this.session.socket.ref(); | ||
this.connection = this.socket; | ||
@@ -174,2 +132,3 @@ this.session.on('error', err => self.emit('error', err)); | ||
this.aborted = false; | ||
this[kKeepAlive] = false; | ||
@@ -206,5 +165,5 @@ if (options.headers) { | ||
if (this._request) { | ||
this._request.destroy(); | ||
this._request.close(NGHTTP2_CANCEL); | ||
} else { | ||
this.session.destroy(); | ||
this.session.close(); | ||
} | ||
@@ -222,2 +181,6 @@ } | ||
const proxyEvent = e => this._request.on(e, (...args) => { | ||
self.emit(e, ...args); | ||
}); | ||
[ | ||
@@ -227,9 +190,9 @@ 'timeout', | ||
'error' | ||
].forEach(e => this._request.on(e, (...args) => { | ||
self.emit(e, ...args); | ||
})); | ||
].forEach(proxyEvent); | ||
this._request.on('response', (headers, flags, rawHeaders) => { | ||
this.res = new HTTP2IncomingMessage(self, {headers, flags, rawHeaders}); | ||
self.emit('response', this.res); | ||
if (!self.emit('response', this.res)) { | ||
this.res._dump(); | ||
} | ||
}); | ||
@@ -284,3 +247,3 @@ | ||
if (value === undefined || value === null) { | ||
throw new ERR_HTTP2_INVALID_HEADER_VALUE(); | ||
throw new ERR_HTTP2_INVALID_HEADER_VALUE(value, name); | ||
} | ||
@@ -292,9 +255,5 @@ | ||
setNoDelay(noDelay) { | ||
deferToConnect(this.session.socket, 'setNoDelay', noDelay); | ||
deferToConnect(this.socket, 'setNoDelay', noDelay); | ||
} | ||
setSocketKeepAlive(keepAlive) { | ||
deferToConnect(this.session.socket, 'setKeepAlive', keepAlive); | ||
} | ||
setTimeout(ms, cb) { | ||
@@ -301,0 +260,0 @@ if (this._request) { |
'use strict'; | ||
const {Readable} = require('stream'); | ||
const kDidRead = Symbol('didRead'); | ||
class HTTP2IncomingMessage extends Readable { | ||
@@ -23,9 +25,9 @@ constructor(req, response) { | ||
this.socket = req.socket; | ||
this.connection = req.socket; | ||
const self = this; | ||
this.socket = req.session.socket; | ||
this.connection = req.session.socket; | ||
this.req._request.on('close', () => { | ||
if (!self.aborted) { | ||
this.on('end', () => { | ||
if (!self.req.aborted) { | ||
self.complete = true; | ||
@@ -35,2 +37,5 @@ } | ||
this[kDidRead] = false; | ||
req._request.on('end', () => self.push(null)); | ||
this._dumped = false; | ||
@@ -40,19 +45,18 @@ } | ||
_read() { | ||
if (this.req.session.destroyed) { | ||
this.push(null); | ||
return; | ||
if (this[kDidRead]) { | ||
process.nextTick(stream => stream.resume(), this.req._request); | ||
} else { | ||
this[kDidRead] = true; | ||
const self = this; | ||
const listener = chunk => { | ||
if (!self.push(chunk)) { | ||
self.req._request.pause(); | ||
} | ||
}; | ||
this.req._request.on('data', listener); | ||
} | ||
const self = this; | ||
const listener = chunk => { | ||
if (!self.push(chunk)) { | ||
self.req._request.removeListener('data', listener); | ||
} | ||
}; | ||
this.req._request.on('data', listener); | ||
} | ||
destroy(error) { | ||
this.req.session.destroy(error); | ||
this.req._request.destroy(error); | ||
} | ||
@@ -67,5 +71,8 @@ | ||
if (!this._dumped) { | ||
this.dumped = true; | ||
this._dumped = true; | ||
this.req._request.removeAllListeners('data'); | ||
this.req._request.resume(); | ||
this.removeAllListeners('data'); | ||
this.resume(); | ||
} | ||
@@ -72,0 +79,0 @@ } |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
13743
7
340
1
111
9