-
-
Notifications
You must be signed in to change notification settings - Fork 31.8k
http2: add origin frame support #22956
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,13 +43,15 @@ 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, | ||
ERR_HTTP2_INVALID_STREAM, | ||
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') { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a quick question. And an error is thrown below even though this assignment goes through. I just came here by chance and was wondering about it :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the object does not have an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks 👍 |
||
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) | ||
|
Uh oh!
There was an error while loading. Please reload this page.