diff options
author | James M Snell <jasnell@gmail.com> | 2018-09-16 19:13:11 -0700 |
---|---|---|
committer | James M Snell <jasnell@gmail.com> | 2018-09-21 13:23:08 -0700 |
commit | b92ce5165f91eec1f312bb9f762357e673f83501 (patch) | |
tree | 26b2416d55c157aba43e76953f3abebb8e65343f /lib | |
parent | c55ebd8502352c6405bfd8c3b356285ccdd242fd (diff) | |
download | android-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.js | 4 | ||||
-rw-r--r-- | lib/internal/http2/core.js | 103 |
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) |