summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJames M Snell <jasnell@gmail.com>2018-09-16 19:13:11 -0700
committerJames M Snell <jasnell@gmail.com>2018-09-21 13:23:08 -0700
commitb92ce5165f91eec1f312bb9f762357e673f83501 (patch)
tree26b2416d55c157aba43e76953f3abebb8e65343f /lib
parentc55ebd8502352c6405bfd8c3b356285ccdd242fd (diff)
downloadandroid-node-v8-b92ce5165f91eec1f312bb9f762357e673f83501.tar.gz
android-node-v8-b92ce5165f91eec1f312bb9f762357e673f83501.tar.bz2
android-node-v8-b92ce5165f91eec1f312bb9f762357e673f83501.zip
http2: add origin frame support
PR-URL: https://github.com/nodejs/node/pull/22956 Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/internal/errors.js4
-rw-r--r--lib/internal/http2/core.js103
2 files changed, 90 insertions, 17 deletions
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index f6aee89dca..e3c5a2b0b6 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -567,6 +567,8 @@ E('ERR_HTTP2_INVALID_HEADER_VALUE',
'Invalid value "%s" for header "%s"', TypeError);
E('ERR_HTTP2_INVALID_INFO_STATUS',
'Invalid informational status code: %s', RangeError);
+E('ERR_HTTP2_INVALID_ORIGIN',
+ 'HTTP/2 ORIGIN frames require a valid origin', TypeError);
E('ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH',
'Packed settings length must be a multiple of six', RangeError);
E('ERR_HTTP2_INVALID_PSEUDOHEADER',
@@ -582,6 +584,8 @@ E('ERR_HTTP2_NESTED_PUSH',
E('ERR_HTTP2_NO_SOCKET_MANIPULATION',
'HTTP/2 sockets should not be directly manipulated (e.g. read and written)',
Error);
+E('ERR_HTTP2_ORIGIN_LENGTH',
+ 'HTTP/2 ORIGIN frames are limited to 16382 bytes', TypeError);
E('ERR_HTTP2_OUT_OF_STREAMS',
'No stream ID is available because maximum stream ID has been reached',
Error);
diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js
index 27fe4f26e3..96ca97f893 100644
--- a/lib/internal/http2/core.js
+++ b/lib/internal/http2/core.js
@@ -43,6 +43,7 @@ const {
ERR_HTTP2_HEADERS_AFTER_RESPOND,
ERR_HTTP2_HEADERS_SENT,
ERR_HTTP2_INVALID_INFO_STATUS,
+ ERR_HTTP2_INVALID_ORIGIN,
ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH,
ERR_HTTP2_INVALID_SESSION,
ERR_HTTP2_INVALID_SETTING_VALUE,
@@ -50,6 +51,7 @@ const {
ERR_HTTP2_MAX_PENDING_SETTINGS_ACK,
ERR_HTTP2_NESTED_PUSH,
ERR_HTTP2_NO_SOCKET_MANIPULATION,
+ ERR_HTTP2_ORIGIN_LENGTH,
ERR_HTTP2_OUT_OF_STREAMS,
ERR_HTTP2_PAYLOAD_FORBIDDEN,
ERR_HTTP2_PING_CANCEL,
@@ -148,6 +150,7 @@ const kInfoHeaders = Symbol('sent-info-headers');
const kLocalSettings = Symbol('local-settings');
const kOptions = Symbol('options');
const kOwner = owner_symbol;
+const kOrigin = Symbol('origin');
const kProceed = Symbol('proceed');
const kProtocol = Symbol('protocol');
const kProxySocket = Symbol('proxy-socket');
@@ -209,6 +212,7 @@ const {
HTTP_STATUS_NO_CONTENT,
HTTP_STATUS_NOT_MODIFIED,
HTTP_STATUS_SWITCHING_PROTOCOLS,
+ HTTP_STATUS_MISDIRECTED_REQUEST,
STREAM_OPTION_EMPTY_PAYLOAD,
STREAM_OPTION_GET_TRAILERS
@@ -299,6 +303,11 @@ function onSessionHeaders(handle, id, cat, flags, headers) {
} else {
event = endOfStream ? 'trailers' : 'headers';
}
+ const session = stream.session;
+ if (status === HTTP_STATUS_MISDIRECTED_REQUEST) {
+ const originSet = session[kState].originSet = initOriginSet(session);
+ originSet.delete(stream[kOrigin]);
+ }
debug(`Http2Stream ${id} [Http2Session ` +
`${sessionName(type)}]: emitting stream '${event}' event`);
process.nextTick(emit, stream, event, obj, flags, headers);
@@ -429,6 +438,39 @@ function onAltSvc(stream, origin, alt) {
session.emit('altsvc', alt, origin, stream);
}
+function initOriginSet(session) {
+ let originSet = session[kState].originSet;
+ if (originSet === undefined) {
+ const socket = session[kSocket];
+ session[kState].originSet = originSet = new Set();
+ if (socket.servername != null) {
+ let originString = `https://${socket.servername}`;
+ if (socket.remotePort != null)
+ originString += `:${socket.remotePort}`;
+ // We have to ensure that it is a properly serialized
+ // ASCII origin string. The socket.servername might not
+ // be properly ASCII encoded.
+ originSet.add((new URL(originString)).origin);
+ }
+ }
+ return originSet;
+}
+
+function onOrigin(origins) {
+ const session = this[kOwner];
+ if (session.destroyed)
+ return;
+ debug(`Http2Session ${sessionName(session[kType])}: origin received: ` +
+ `${origins.join(', ')}`);
+ session[kUpdateTimer]();
+ if (!session.encrypted || session.destroyed)
+ return undefined;
+ const originSet = initOriginSet(session);
+ for (var n = 0; n < origins.length; n++)
+ originSet.add(origins[n]);
+ session.emit('origin', origins);
+}
+
// Receiving a GOAWAY frame from the connected peer is a signal that no
// new streams should be created. If the code === NGHTTP2_NO_ERROR, we
// are going to send our close, but allow existing frames to close
@@ -782,6 +824,7 @@ function setupHandle(socket, type, options) {
handle.onframeerror = onFrameError;
handle.ongoawaydata = onGoawayData;
handle.onaltsvc = onAltSvc;
+ handle.onorigin = onOrigin;
if (typeof options.selectPadding === 'function')
handle.ongetpadding = onSelectPadding(options.selectPadding);
@@ -808,6 +851,12 @@ function setupHandle(socket, type, options) {
options.settings : {};
this.settings(settings);
+
+ if (type === NGHTTP2_SESSION_SERVER &&
+ Array.isArray(options.origins)) {
+ this.origin(...options.origins);
+ }
+
process.nextTick(emit, this, 'connect', this, socket);
}
@@ -947,23 +996,7 @@ class Http2Session extends EventEmitter {
get originSet() {
if (!this.encrypted || this.destroyed)
return undefined;
-
- let originSet = this[kState].originSet;
- if (originSet === undefined) {
- const socket = this[kSocket];
- this[kState].originSet = originSet = new Set();
- if (socket.servername != null) {
- let originString = `https://${socket.servername}`;
- if (socket.remotePort != null)
- originString += `:${socket.remotePort}`;
- // We have to ensure that it is a properly serialized
- // ASCII origin string. The socket.servername might not
- // be properly ASCII encoded.
- originSet.add((new URL(originString)).origin);
- }
- }
-
- return Array.from(originSet);
+ return Array.from(initOriginSet(this));
}
// True if the Http2Session is still waiting for the socket to connect
@@ -1338,6 +1371,40 @@ class ServerHttp2Session extends Http2Session {
this[kHandle].altsvc(stream, origin || '', alt);
}
+
+ // Submits an origin frame to be sent.
+ origin(...origins) {
+ if (this.destroyed)
+ throw new ERR_HTTP2_INVALID_SESSION();
+
+ if (origins.length === 0)
+ return;
+
+ let arr = '';
+ let len = 0;
+ const count = origins.length;
+ for (var i = 0; i < count; i++) {
+ let origin = origins[i];
+ if (typeof origin === 'string') {
+ origin = (new URL(origin)).origin;
+ } else if (origin != null && typeof origin === 'object') {
+ origin = origin.origin;
+ }
+ if (typeof origin !== 'string')
+ throw new ERR_INVALID_ARG_TYPE('origin', 'string', origin);
+ if (origin === 'null')
+ throw new ERR_HTTP2_INVALID_ORIGIN();
+
+ arr += `${origin}\0`;
+ len += origin.length;
+ }
+
+ if (len > 16382)
+ throw new ERR_HTTP2_ORIGIN_LENGTH();
+
+ this[kHandle].origin(arr, count);
+ }
+
}
// ClientHttp2Session instances have to wait for the socket to connect after
@@ -1406,6 +1473,8 @@ class ClientHttp2Session extends Http2Session {
const stream = new ClientHttp2Stream(this, undefined, undefined, {});
stream[kSentHeaders] = headers;
+ stream[kOrigin] = `${headers[HTTP2_HEADER_SCHEME]}://` +
+ `${headers[HTTP2_HEADER_AUTHORITY]}`;
// Close the writable side of the stream if options.endStream is set.
if (options.endStream)