Skip to content

Checking diff here #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 29 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
5e5dc6f
Initial working experiment with http2 over ws
Feb 17, 2017
a785493
Working in browser
Feb 17, 2017
8db7ca2
polyfil hack
Feb 17, 2017
f22f327
Fixed stupid polyfil with real browserify polyfil
Feb 17, 2017
98324db
Remove / undid pointless work
Feb 17, 2017
c788b27
Fixed logData call to work in non-node environments
Feb 17, 2017
fd845a7
Allowed call to Bunyan (node) not to break non node envs, add url to …
Feb 17, 2017
b5cd8c8
moved _.extend to Object.assign
Feb 17, 2017
d413a93
Appears to be working ok
Feb 18, 2017
645a047
Working http over ws server
Feb 22, 2017
456ad16
test clean up
Feb 22, 2017
5e160ad
Clean up temp files
Feb 22, 2017
89e0947
removed test library, minor code clean up
Feb 22, 2017
d248656
Celaned up unneeded dependencies
Feb 22, 2017
f5ff190
Removed console.log
Feb 22, 2017
5c7c0d1
Changed API to be fully pluggable for any transport or server
Feb 24, 2017
b9a5350
Added status code and worked around mid stream close with hack
Mar 6, 2017
25b44ed
Renamed transport to create connection to be consistent with node API
Mar 8, 2017
9b4e5a5
Clean up formatting
Mar 30, 2017
39f3392
Update http.js
Mar 30, 2017
9c77188
Update framer.js
Mar 30, 2017
ac1c1c9
removed unneeded class
Mar 30, 2017
34ce5ba
replace options.createConnection by options.transport and Re-use tran…
hthetiot Jun 13, 2017
8eeb9ec
Merge pull request #3 from hthetiot/http2ws
Jun 13, 2017
9146e1d
fix test/http 'get over plain generic transport'
hthetiot Jul 1, 2017
77176f1
make that by default DeprecatedHeaders trigger only warning.
hthetiot Jul 1, 2017
4175929
make that by default DeprecatedHeaders trigger only warning on Incomi…
hthetiot Jul 1, 2017
7f8f879
revert allowDeprecatedHeader feature on IncomingMessage and OutgoingM…
hthetiot Jul 1, 2017
07e57f8
Added port to :authority header
Jul 7, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ coverage
doc
.vscode/.browse*
npm-debug.log
typings
typings
builds
153 changes: 131 additions & 22 deletions lib/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,64 @@ var Readable = require('stream').Readable;
var Writable = require('stream').Writable;
var protocol = require('./protocol');
var Endpoint = protocol.Endpoint;
var http = require('http');
var https = require('https');

exports.STATUS_CODES = http.STATUS_CODES;

exports.STATUS_CODES = {
'202': 'Accepted',
'502': 'Bad Gateway',
'400': 'Bad Request',
'409': 'Conflict',
'100': 'Continue',
'201': 'Created',
'417': 'Expectation Failed',
'424': 'Failed Dependency',
'403': 'Forbidden',
'504': 'Gateway Timeout',
'410': 'Gone',
'505': 'HTTP Version Not Supported',
'419': 'Insufficient Space on Resource',
'507': 'Insufficient Storage',
'500': 'Server Error',
'411': 'Length Required',
'423': 'Locked',
'420': 'Method Failure',
'405': 'Method Not Allowed',
'301': 'Moved Permanently',
'302': 'Moved Temporarily',
'207': 'Multi-Status',
'300': 'Multiple Choices',
'511': 'Network Authentication Required',
'204': 'No Content',
'203': 'Non Authoritative Information',
'406': 'Not Acceptable',
'404': 'Not Found',
'501': 'Not Implemented',
'304': 'Not Modified',
'200': 'OK',
'206': 'Partial Content',
'402': 'Payment Required',
'308': 'Permanent Redirect',
'412': 'Precondition Failed',
'428': 'Precondition Required',
'102': 'Processing',
'407': 'Proxy Authentication Required',
'431': 'Request Header Fields Too Large',
'408': 'Request Timeout',
'413': 'Request Entity Too Large',
'414': 'Request-URI Too Long',
'416': 'Requested Range Not Satisfiable',
'205': 'Reset Content',
'303': 'See Other',
'503': 'Service Unavailable',
'101': 'Switching Protocols',
'307': 'Temporary Redirect',
'429': 'Too Many Requests',
'401': 'Unauthorized',
'422': 'Unprocessable Entity',
'415': 'Unsupported Media Type',
'305': 'Use Proxy'
};
exports.IncomingMessage = IncomingMessage;
exports.OutgoingMessage = OutgoingMessage;
exports.protocol = protocol;
Expand Down Expand Up @@ -293,8 +347,8 @@ IncomingMessage.prototype._validateHeaders = function _validateHeaders(headers)
for (var i = 0; i < deprecatedHeaders.length; i++) {
var key = deprecatedHeaders[i];
if (key in headers || (key === 'te' && headers[key] !== 'trailers')) {
this._log.error({ key: key, value: headers[key] }, 'Deprecated header found');
this.stream.reset('PROTOCOL_ERROR');
this._log.error({ key: key, value: headers[key] }, 'Deprecated header found');
this.stream.reset('PROTOCOL_ERROR');
return;
}
}
Expand Down Expand Up @@ -354,14 +408,13 @@ OutgoingMessage.prototype._finish = function _finish() {
this.once('socket', this._finish.bind(this));
}
};

OutgoingMessage.prototype.setHeader = function setHeader(name, value) {
if (this.headersSent) {
return this.emit('error', new Error('Can\'t set headers after they are sent.'));
} else {
name = name.toLowerCase();
if (deprecatedHeaders.indexOf(name) !== -1) {
return this.emit('error', new Error('Cannot set deprecated header: ' + name));
return this.emit('error', new Error('Cannot set deprecated header: ' + name));
}
this._headers[name] = value;
}
Expand Down Expand Up @@ -429,7 +482,7 @@ function forwardEvent(event, source, target) {
// ------------

function Server(options) {
options = util._extend({}, options);
options = Object.assign({}, options);

this._log = (options.log || defaultLogger).child({ component: 'http' });
this._settings = options.settings;
Expand Down Expand Up @@ -464,7 +517,13 @@ function Server(options) {
forwardEvent('listening', this._server, this);
}

// HTTP2 over plain TCP
// HTTP2 over any generic transport
else if (options.transport){
this._mode = 'plain';
this._server = options.transport(options, start);
}

// HTTP2 over plain TCP (Perhaps this should be deprecated??)
else if (options.plain) {
this._log.info('Creating HTTP/2 server over plain TCP');
this._mode = 'plain';
Expand Down Expand Up @@ -766,7 +825,7 @@ OutgoingResponse.prototype.push = function push(options) {
throw new Error('`path` option is mandatory.');
}

var promise = util._extend({
var promise = Object.assign({
':method': (options.method || 'GET').toUpperCase(),
':scheme': (options.protocol && options.protocol.slice(0, -1)) || this._requestHeaders[':scheme'],
':authority': options.hostname || options.host || this._requestHeaders[':authority'],
Expand Down Expand Up @@ -817,7 +876,7 @@ function requestRaw(options, callback) {
throw new Error('This interface only supports http-schemed URLs');
}
if (options.agent && typeof(options.agent.request) === 'function') {
var agentOptions = util._extend({}, options);
var agentOptions = Object.assign({}, options);
delete agentOptions.agent;
return options.agent.request(agentOptions, callback);
}
Expand All @@ -833,7 +892,7 @@ function requestTLS(options, callback) {
throw new Error('This interface only supports https-schemed URLs');
}
if (options.agent && typeof(options.agent.request) === 'function') {
var agentOptions = util._extend({}, options);
var agentOptions = Object.assign({}, options);
delete agentOptions.agent;
return options.agent.request(agentOptions, callback);
}
Expand All @@ -849,7 +908,7 @@ function getRaw(options, callback) {
throw new Error('This interface only supports http-schemed URLs');
}
if (options.agent && typeof(options.agent.get) === 'function') {
var agentOptions = util._extend({}, options);
var agentOptions = Object.assign({}, options);
delete agentOptions.agent;
return options.agent.get(agentOptions, callback);
}
Expand All @@ -865,7 +924,7 @@ function getTLS(options, callback) {
throw new Error('This interface only supports https-schemed URLs');
}
if (options.agent && typeof(options.agent.get) === 'function') {
var agentOptions = util._extend({}, options);
var agentOptions = Object.assign({}, options);
delete agentOptions.agent;
return options.agent.get(agentOptions, callback);
}
Expand All @@ -879,7 +938,7 @@ function Agent(options) {
EventEmitter.call(this);
this.setMaxListeners(0);

options = util._extend({}, options);
options = Object.assign({}, options);

this._settings = options.settings;
this._log = (options.log || defaultLogger).child({ component: 'http' });
Expand All @@ -902,7 +961,7 @@ Agent.prototype.request = function request(options, callback) {
if (typeof options === 'string') {
options = url.parse(options);
} else {
options = util._extend({}, options);
options = Object.assign({}, options);
}

options.method = (options.method || 'GET').toUpperCase();
Expand All @@ -922,20 +981,63 @@ Agent.prototype.request = function request(options, callback) {
request.on('response', callback);
}

var key = [
!!options.plain,
options.host,
options.port
].join(':');
// Re-use transportUrl endPoint if specified
if (options.transportUrl && options.transport) {
key = ([
options.transportUrl
]).join(':');

// Re-use host:port endPoint
} else {
key = ([
!!options.plain,
options.host,
options.port
]).join(':');
}

var self = this;

// * There's an existing HTTP/2 connection to this host
if (key in this.endpoints) {
if (key in this.endpoints && this.endpoints[key]) {
var endpoint = this.endpoints[key];
request._start(endpoint.createStream(), options);
}

// * HTTP/2 over generic stream transport
else if(options.transport) {
endpoint = new Endpoint(this._log, 'CLIENT', this._settings);
endpoint.socket = options.transport;

endpoint.socket.on('error', function (error) {
self._log.error('Socket error: ' + error.toString());
request.emit('error', error);
});

endpoint.on('error', function(error){
self._log.error('Connection error: ' + error.toString());
request.emit('error', error);
});

var self = this;
endpoint.socket.on('close', function (error) {
// DPW This is sort of a hack to protect against
// the reuse of a endpoint that has the underlying
// connection closed. It would probably be better
// to implement this near lin 933 (if (key in this.endpoints))
// by checking the endpoint state (requires new API to expose)

// Alternatively, this could be a bug with my WS connection
// not emitting an error when it is unexpectedly closed ??
delete self.endpoints[key];
});

this.endpoints[key] = endpoint;
endpoint.pipe(endpoint.socket).pipe(endpoint);
request._start(endpoint.createStream(), options);
}
// * HTTP/2 over plain TCP
// TODO deprecate?
else if (options.plain) {
endpoint = new Endpoint(this._log, 'CLIENT', this._settings);
endpoint.socket = net.connect({
Expand Down Expand Up @@ -1109,7 +1211,14 @@ OutgoingRequest.prototype._start = function _start(stream, options) {

headers[':scheme'] = options.protocol.slice(0, -1);
headers[':method'] = options.method;
headers[':authority'] = options.host;
if(options.port)
{
headers[':authority'] = options.host + ':' + options.port;
}
else
{
headers[':authority'] = options.host;
}
headers[':path'] = options.path;

this._log.info({ scheme: headers[':scheme'], method: headers[':method'],
Expand Down
3 changes: 2 additions & 1 deletion lib/protocol/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ var assert = require('assert');
// [Flow](flow.html) subclass.

var Flow = require('./flow').Flow;
var timers = require('timers');

exports.Connection = Connection;

Expand Down Expand Up @@ -272,7 +273,7 @@ Connection.prototype._send = function _send(immediate) {
} else {
if (!this._sendScheduled) {
this._sendScheduled = true;
setImmediate(this._send.bind(this, true));
timers.setImmediate(this._send.bind(this, true));
}
return;
}
Expand Down
3 changes: 2 additions & 1 deletion lib/protocol/endpoint.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ var Decompressor = require('./compressor').Decompressor;
var Connection = require('./connection').Connection;
var Duplex = require('stream').Duplex;
var Transform = require('stream').Transform;
var timers = require('timers');

exports.Endpoint = Endpoint;

Expand Down Expand Up @@ -240,7 +241,7 @@ Endpoint.prototype._initializeErrorHandling = function _initializeErrorHandling(
Endpoint.prototype._error = function _error(component, error) {
this._log.fatal({ source: component, message: error }, 'Fatal error, closing connection');
this.close(error);
setImmediate(this.emit.bind(this, 'error', error));
timers.setImmediate(this.emit.bind(this, 'error', error));
};

Endpoint.prototype.close = function close(error) {
Expand Down
3 changes: 2 additions & 1 deletion lib/protocol/flow.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
var assert = require('assert');
var timers = require('timers');

// The Flow class
// ==============
Expand Down Expand Up @@ -91,7 +92,7 @@ Flow.prototype._write = function _write(frame, encoding, callback) {
this._receive(frame, function() {
this._received += frame.data.length;
if (!this._restoreWindowTimer) {
this._restoreWindowTimer = setImmediate(this._restoreWindow.bind(this));
this._restoreWindowTimer = timers.setImmediate(this._restoreWindow.bind(this));
}
callback();
}.bind(this));
Expand Down
2 changes: 1 addition & 1 deletion lib/protocol/framer.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ var Transform = require('stream').Transform;
exports.Serializer = Serializer;
exports.Deserializer = Deserializer;

var logData = Boolean(process.env.HTTP2_LOG_DATA);
var logData = (process !== 'undefined' && process.env !== 'undefined' && process.env.HTTP2_LOG_DATA);

var MAX_PAYLOAD_SIZE = 16384;
var WINDOW_UPDATE_PAYLOAD_SIZE = 4;
Expand Down
16 changes: 11 additions & 5 deletions lib/protocol/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,19 @@ exports.VERSION = 'h2';
exports.Endpoint = require('./endpoint').Endpoint;

/* Bunyan serializers exported by submodules that are worth adding when creating a logger. */

exports.serializers = {};
var modules = ['./framer', './compressor', './flow', './connection', './stream', './endpoint'];
modules.map(require).forEach(function(module) {
for (var name in module.serializers) {
exports.serializers[name] = module.serializers[name];
}
});
try {
modules.map(require).forEach(function (module) {
for (var name in module.serializers) {
exports.serializers[name] = module.serializers[name];
}
});
} catch (e) {
// NOOP, probably in browser
}


/*
Stream API Endpoint API
Expand Down
15 changes: 13 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,25 @@
"version": "3.3.6",
"description": "An HTTP/2 client and server implementation",
"main": "lib/index.js",
"engines" : {
"node" : ">=0.12.0"
"engines": {
"node": ">=0.12.0"
},
"dependencies": {
"assert": "1.4.1",
"stream-browserify": "2.0.1",
"websocket-stream": "3.3.3",
"url": "0.11.0",
"setimmediate": "1.0.5",
"https-browserify": "0.0.1",
"timers-browserify": "2.0.2",
"events": "1.1.1"
},
"devDependencies": {
"istanbul": "*",
"chai": "*",
"mocha": "*",
"docco": "*",
"browserify": "14.0.0 ",
"bunyan": "*"
},
"scripts": {
Expand Down
Loading