mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
1869 lines
85 KiB
HTML
1869 lines
85 KiB
HTML
<!DOCTYPE html>
|
|
|
|
<html>
|
|
<head>
|
|
<title>Public API</title>
|
|
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
|
<meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">
|
|
<link rel="stylesheet" media="all" href="docco.css" />
|
|
</head>
|
|
<body>
|
|
<div id="container">
|
|
<div id="background"></div>
|
|
|
|
<ul id="jump_to">
|
|
<li>
|
|
<a class="large" href="javascript:void(0);">Jump To …</a>
|
|
<a class="small" href="javascript:void(0);">+</a>
|
|
<div id="jump_wrapper">
|
|
<div id="jump_page">
|
|
|
|
|
|
<a class="source" href="http.html">
|
|
http.js
|
|
</a>
|
|
|
|
|
|
<a class="source" href="index.html">
|
|
index.js
|
|
</a>
|
|
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
|
|
<ul class="sections">
|
|
|
|
|
|
|
|
<li id="section-1">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h1">
|
|
<a class="pilcrow" href="#section-1">¶</a>
|
|
</div>
|
|
<h1>Public API</h1>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-2">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-2">¶</a>
|
|
</div>
|
|
<p>The main governing power behind the http2 API design is that it should look very similar to the
|
|
existing node.js [HTTPS API][1] (which is, in turn, almost identical to the [HTTP API][2]). The
|
|
additional features of HTTP/2 are exposed as extensions to this API. Furthermore, node-http2
|
|
should fall back to using HTTP/1.1 if needed. Compatibility with undocumented or deprecated
|
|
elements of the node.js HTTP/HTTPS API is a non-goal.</p>
|
|
<h2>Additional and modified API elements</h2>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-3">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-3">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li><p><strong>Class: http2.Endpoint</strong>: an API for using the raw HTTP/2 framing layer. For documentation
|
|
see the <a href="endpoint.html">lib/endpoint.js</a> file.</p>
|
|
</li>
|
|
<li><p><strong>Class: http2.Server</strong></p>
|
|
<ul>
|
|
<li><strong>Event: 'connection' (socket, [endpoint])</strong>: there's a second argument if the negotiation of
|
|
HTTP/2 was successful: the reference to the <a href="endpoint.html">Endpoint</a> object tied to the
|
|
socket.</li>
|
|
</ul>
|
|
</li>
|
|
<li><p><strong>http2.createServer(options, [requestListener])</strong>: additional option:</p>
|
|
<ul>
|
|
<li><strong>log</strong>: an optional <a href="https://github.com/trentm/node-bunyan">bunyan</a> logger object</li>
|
|
<li><strong>plain</strong>: if <code>true</code>, the server will accept HTTP/2 connections over plain TCP instead of
|
|
TLS</li>
|
|
</ul>
|
|
</li>
|
|
<li><p><strong>Class: http2.ServerResponse</strong></p>
|
|
<ul>
|
|
<li><strong>response.push(options)</strong>: initiates a server push. <code>options</code> describes the 'imaginary'
|
|
request to which the push stream is a response; the possible options are identical to the
|
|
ones accepted by <code>http2.request</code>. Returns a ServerResponse object that can be used to send
|
|
the response headers and content.</li>
|
|
</ul>
|
|
</li>
|
|
<li><p><strong>Class: http2.Agent</strong></p>
|
|
<ul>
|
|
<li><strong>new Agent(options)</strong>: additional option:<ul>
|
|
<li><strong>log</strong>: an optional <a href="https://github.com/trentm/node-bunyan">bunyan</a> logger object</li>
|
|
</ul>
|
|
</li>
|
|
<li><strong>agent.sockets</strong>: only contains TCP sockets that corresponds to HTTP/1 requests.</li>
|
|
<li><strong>agent.endpoints</strong>: contains <a href="endpoint.html">Endpoint</a> objects for HTTP/2 connections.</li>
|
|
</ul>
|
|
</li>
|
|
<li><p><strong>http2.request(options, [callback])</strong>: additional option:</p>
|
|
<ul>
|
|
<li><strong>plain</strong>: if <code>true</code>, the client will not try to build a TLS tunnel, instead it will use
|
|
the raw TCP stream for HTTP/2</li>
|
|
</ul>
|
|
</li>
|
|
<li><p><strong>Class: http2.ClientRequest</strong></p>
|
|
<ul>
|
|
<li><strong>Event: 'socket' (socket)</strong>: in case of an HTTP/2 incoming message, <code>socket</code> is a reference
|
|
to the associated <a href="stream.html">HTTP/2 Stream</a> object (and not to the TCP socket).</li>
|
|
<li><strong>Event: 'push' (promise)</strong>: signals the intention of a server push associated to this
|
|
request. <code>promise</code> is an IncomingPromise. If there's no listener for this event, the server
|
|
push is cancelled.</li>
|
|
<li><strong>request.setPriority(priority)</strong>: assign a priority to this request. <code>priority</code> is a number
|
|
between 0 (highest priority) and 2^31-1 (lowest priority). Default value is 2^30.</li>
|
|
</ul>
|
|
</li>
|
|
<li><p><strong>Class: http2.IncomingMessage</strong></p>
|
|
<ul>
|
|
<li>has two subclasses for easier interface description: <strong>IncomingRequest</strong> and
|
|
<strong>IncomingResponse</strong></li>
|
|
<li><strong>message.socket</strong>: in case of an HTTP/2 incoming message, it's a reference to the associated
|
|
<a href="stream.html">HTTP/2 Stream</a> object (and not to the TCP socket).</li>
|
|
</ul>
|
|
</li>
|
|
<li><p><strong>Class: http2.IncomingRequest (IncomingMessage)</strong></p>
|
|
<ul>
|
|
<li><strong>message.url</strong>: in case of an HTTP/2 incoming request, the <code>url</code> field always contains the
|
|
path, and never a full url (it contains the path in most cases in the HTTPS api as well).</li>
|
|
<li><strong>message.scheme</strong>: additional field. Mandatory HTTP/2 request metadata.</li>
|
|
<li><strong>message.host</strong>: additional field. Mandatory HTTP/2 request metadata. Note that this
|
|
replaces the old Host header field, but node-http2 will add Host to the <code>message.headers</code> for
|
|
backwards compatibility.</li>
|
|
</ul>
|
|
</li>
|
|
<li><p><strong>Class: http2.IncomingPromise (IncomingRequest)</strong></p>
|
|
<ul>
|
|
<li>contains the metadata of the 'imaginary' request to which the server push is an answer.</li>
|
|
<li><strong>Event: 'response' (response)</strong>: signals the arrival of the actual push stream. <code>response</code>
|
|
is an IncomingResponse.</li>
|
|
<li><strong>Event: 'push' (promise)</strong>: signals the intention of a server push associated to this
|
|
request. <code>promise</code> is an IncomingPromise. If there's no listener for this event, the server
|
|
push is cancelled.</li>
|
|
<li><strong>promise.cancel()</strong>: cancels the promised server push.</li>
|
|
<li><strong>promise.setPriority(priority)</strong>: assign a priority to this push stream. <code>priority</code> is a
|
|
number between 0 (highest priority) and 2^31-1 (lowest priority). Default value is 2^30.</li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
<h2>API elements not yet implemented</h2>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-4">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-4">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li><strong>Class: http2.Server</strong><ul>
|
|
<li><strong>server.maxHeadersCount</strong></li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
<h2>API elements that are not applicable to HTTP/2</h2>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-5">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-5">¶</a>
|
|
</div>
|
|
<p>The reason may be deprecation of certain HTTP/1.1 features, or that some API elements simply
|
|
don't make sense when using HTTP/2. These will not be present when a request is done with HTTP/2,
|
|
but will function normally when falling back to using HTTP/1.1.</p>
|
|
<ul>
|
|
<li><p><strong>Class: http2.Server</strong></p>
|
|
<ul>
|
|
<li><strong>Event: 'checkContinue'</strong>: not in the spec, yet (see <a href="https://github.com/http2/http2-spec/issues/18">http-spec#18</a>)</li>
|
|
<li><strong>Event: 'upgrade'</strong>: upgrade is deprecated in HTTP/2</li>
|
|
<li><strong>Event: 'timeout'</strong>: HTTP/2 sockets won't timeout because of application level keepalive
|
|
(PING frames)</li>
|
|
<li><strong>Event: 'connect'</strong>: not in the spec, yet (see <a href="https://github.com/http2/http2-spec/issues/230">http-spec#230</a>)</li>
|
|
<li><strong>server.setTimeout(msecs, [callback])</strong></li>
|
|
<li><strong>server.timeout</strong></li>
|
|
</ul>
|
|
</li>
|
|
<li><p><strong>Class: http2.ServerResponse</strong></p>
|
|
<ul>
|
|
<li><strong>Event: 'close'</strong></li>
|
|
<li><strong>Event: 'timeout'</strong></li>
|
|
<li><strong>response.writeContinue()</strong></li>
|
|
<li><strong>response.writeHead(statusCode, [reasonPhrase], [headers])</strong>: reasonPhrase will always be
|
|
ignored since <a href="http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-8.1.3.2">it's not supported in HTTP/2</a></li>
|
|
<li><strong>response.setTimeout(timeout, [callback])</strong></li>
|
|
</ul>
|
|
</li>
|
|
<li><p><strong>Class: http2.Agent</strong></p>
|
|
<ul>
|
|
<li><strong>agent.maxSockets</strong>: only affects HTTP/1 connection pool. When using HTTP/2, there's always
|
|
one connection per host.</li>
|
|
</ul>
|
|
</li>
|
|
<li><p><strong>Class: http2.ClientRequest</strong></p>
|
|
<ul>
|
|
<li><strong>Event: 'upgrade'</strong></li>
|
|
<li><strong>Event: 'connect'</strong></li>
|
|
<li><strong>Event: 'continue'</strong></li>
|
|
<li><strong>request.setTimeout(timeout, [callback])</strong></li>
|
|
<li><strong>request.setNoDelay([noDelay])</strong></li>
|
|
<li><strong>request.setSocketKeepAlive([enable], [initialDelay])</strong></li>
|
|
</ul>
|
|
</li>
|
|
<li><p><strong>Class: http2.IncomingMessage</strong></p>
|
|
<ul>
|
|
<li><strong>Event: 'close'</strong></li>
|
|
<li><strong>message.setTimeout(timeout, [callback])</strong></li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
<h1>Common server and client side code</h1>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-6">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-6">¶</a>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre><span class="keyword">var</span> net = require(<span class="string">'net'</span>);
|
|
<span class="keyword">var</span> url = require(<span class="string">'url'</span>);
|
|
<span class="keyword">var</span> util = require(<span class="string">'util'</span>);
|
|
<span class="keyword">var</span> EventEmitter = require(<span class="string">'events'</span>).EventEmitter;
|
|
<span class="keyword">var</span> PassThrough = require(<span class="string">'stream'</span>).PassThrough;
|
|
<span class="keyword">var</span> Readable = require(<span class="string">'stream'</span>).Readable;
|
|
<span class="keyword">var</span> Writable = require(<span class="string">'stream'</span>).Writable;
|
|
<span class="keyword">var</span> Endpoint = require(<span class="string">'http2-protocol'</span>).Endpoint;
|
|
<span class="keyword">var</span> http = require(<span class="string">'http'</span>);
|
|
<span class="keyword">var</span> https = require(<span class="string">'https'</span>);
|
|
|
|
exports.STATUS_CODES = http.STATUS_CODES;
|
|
exports.IncomingMessage = IncomingMessage;
|
|
exports.OutgoingMessage = OutgoingMessage;
|
|
|
|
<span class="keyword">var</span> deprecatedHeaders = [
|
|
<span class="string">'connection'</span>,
|
|
<span class="string">'host'</span>,
|
|
<span class="string">'keep-alive'</span>,
|
|
<span class="string">'proxy-connection'</span>,
|
|
<span class="string">'te'</span>,
|
|
<span class="string">'transfer-encoding'</span>,
|
|
<span class="string">'upgrade'</span>
|
|
];</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-7">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-7">¶</a>
|
|
</div>
|
|
<p>The implemented version of the HTTP/2 specification is <a href="http://tools.ietf.org/html/draft-ietf-httpbis-http2-09">draft 09</a>.</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre><span class="keyword">var</span> implementedVersion = <span class="string">'HTTP-draft-09/2.0'</span>;</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-8">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-8">¶</a>
|
|
</div>
|
|
<p>When doing NPN/ALPN negotiation, HTTP/1.1 is used as fallback</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre><span class="keyword">var</span> supportedProtocols = [implementedVersion, <span class="string">'http/1.1'</span>, <span class="string">'http/1.0'</span>];</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-9">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h2">
|
|
<a class="pilcrow" href="#section-9">¶</a>
|
|
</div>
|
|
<h2>Logging</h2>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-10">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-10">¶</a>
|
|
</div>
|
|
<p>Logger shim, used when no logger is provided by the user.</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre><span class="function"><span class="keyword">function</span> <span class="title">noop</span><span class="params">()</span> {</span>}
|
|
<span class="keyword">var</span> defaultLogger = {
|
|
fatal: noop,
|
|
error: noop,
|
|
warn : noop,
|
|
info : noop,
|
|
debug: noop,
|
|
trace: noop,
|
|
|
|
child: <span class="keyword">function</span>() { <span class="keyword">return</span> <span class="keyword">this</span>; }
|
|
};</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-11">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-11">¶</a>
|
|
</div>
|
|
<p>Bunyan serializers exported by submodules that are worth adding when creating a logger.</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>exports.serializers = require(<span class="string">'http2-protocol'</span>).serializers;</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-12">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h2">
|
|
<a class="pilcrow" href="#section-12">¶</a>
|
|
</div>
|
|
<h2>IncomingMessage class</h2>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-13">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-13">¶</a>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre><span class="function"><span class="keyword">function</span> <span class="title">IncomingMessage</span><span class="params">(stream)</span> {</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-14">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-14">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>This is basically a read-only wrapper for the <a href="stream.html">Stream</a> class.</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> PassThrough.call(<span class="keyword">this</span>);
|
|
stream.pipe(<span class="keyword">this</span>);
|
|
<span class="keyword">this</span>.socket = <span class="keyword">this</span>.stream = stream;
|
|
|
|
<span class="keyword">this</span>._log = stream._log.child({ component: <span class="string">'http'</span> });</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-15">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-15">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>HTTP/2.0 does not define a way to carry the version identifier that is included in the
|
|
HTTP/1.1 request/status line. Version is always 2.0.</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">this</span>.httpVersion = <span class="string">'2.0'</span>;
|
|
<span class="keyword">this</span>.httpVersionMajor = <span class="number">2</span>;
|
|
<span class="keyword">this</span>.httpVersionMinor = <span class="number">0</span>;</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-16">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-16">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li><code>this.headers</code> will store the regular headers (and none of the special colon headers)</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">this</span>.headers = {};
|
|
<span class="keyword">this</span>.trailers = <span class="literal">undefined</span>;
|
|
<span class="keyword">this</span>._lastHeadersSeen = <span class="literal">undefined</span>;</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-17">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-17">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>Other metadata is filled in when the headers arrive.</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> stream.once(<span class="string">'headers'</span>, <span class="keyword">this</span>._onHeaders.bind(<span class="keyword">this</span>));
|
|
stream.once(<span class="string">'end'</span>, <span class="keyword">this</span>._onEnd.bind(<span class="keyword">this</span>));
|
|
}
|
|
IncomingMessage.prototype = Object.create(PassThrough.prototype, { constructor: { value: IncomingMessage } });</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-18">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-18">¶</a>
|
|
</div>
|
|
<p><a href="http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-8.1.3.1">Request Header Fields</a>
|
|
* <code>headers</code> argument: HTTP/2.0 request and response header fields carry information as a series
|
|
of key-value pairs. This includes the target URI for the request, the status code for the
|
|
response, as well as HTTP header fields.</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>IncomingMessage.prototype._onHeaders = <span class="function"><span class="keyword">function</span> <span class="title">_onHeaders</span><span class="params">(headers)</span> {</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-19">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-19">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>An HTTP/2.0 request or response MUST NOT include any of the following header fields:
|
|
Connection, Host, Keep-Alive, Proxy-Connection, TE, Transfer-Encoding, and Upgrade. A server
|
|
MUST treat the presence of any of these header fields as a stream error of type
|
|
PROTOCOL_ERROR.</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < deprecatedHeaders.length; i++) {
|
|
<span class="keyword">var</span> key = deprecatedHeaders[i];
|
|
<span class="keyword">if</span> (key <span class="keyword">in</span> headers) {
|
|
<span class="keyword">this</span>._log.error({ key: key, value: headers[key] }, <span class="string">'Deprecated header found'</span>);
|
|
<span class="keyword">this</span>.stream.emit(<span class="string">'error'</span>, <span class="string">'PROTOCOL_ERROR'</span>);
|
|
<span class="keyword">return</span>;
|
|
}
|
|
}</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-20">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-20">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>Store the <em>regular</em> headers in <code>this.headers</code></li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">for</span> (<span class="keyword">var</span> name <span class="keyword">in</span> headers) {
|
|
<span class="keyword">if</span> (name[<span class="number">0</span>] !== <span class="string">':'</span>) {
|
|
<span class="keyword">this</span>.headers[name] = headers[name];
|
|
}
|
|
}</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-21">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-21">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>The last header block, if it's not the first, will represent the trailers</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">var</span> self = <span class="keyword">this</span>;
|
|
<span class="keyword">this</span>.stream.on(<span class="string">'headers'</span>, <span class="keyword">function</span>(headers) {
|
|
self._lastHeadersSeen = headers;
|
|
});
|
|
};
|
|
|
|
IncomingMessage.prototype._onEnd = <span class="function"><span class="keyword">function</span> <span class="title">_onEnd</span><span class="params">()</span> {</span>
|
|
<span class="keyword">this</span>.trailers = <span class="keyword">this</span>._lastHeadersSeen;
|
|
};
|
|
|
|
IncomingMessage.prototype.setTimeout = noop;
|
|
|
|
IncomingMessage.prototype._checkSpecialHeader = <span class="function"><span class="keyword">function</span> <span class="title">_checkSpecialHeader</span><span class="params">(key, value)</span> {</span>
|
|
<span class="keyword">if</span> ((<span class="keyword">typeof</span> value !== <span class="string">'string'</span>) || (value.length === <span class="number">0</span>)) {
|
|
<span class="keyword">this</span>._log.error({ key: key, value: value }, <span class="string">'Invalid or missing special header field'</span>);
|
|
<span class="keyword">this</span>.stream.emit(<span class="string">'error'</span>, <span class="string">'PROTOCOL_ERROR'</span>);
|
|
}
|
|
|
|
<span class="keyword">return</span> value;
|
|
}
|
|
;</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-22">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h2">
|
|
<a class="pilcrow" href="#section-22">¶</a>
|
|
</div>
|
|
<h2>OutgoingMessage class</h2>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-23">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-23">¶</a>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre><span class="function"><span class="keyword">function</span> <span class="title">OutgoingMessage</span><span class="params">()</span> {</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-24">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-24">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>This is basically a read-only wrapper for the <a href="stream.html">Stream</a> class.</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> Writable.call(<span class="keyword">this</span>);
|
|
|
|
<span class="keyword">this</span>._headers = {};
|
|
<span class="keyword">this</span>._trailers = <span class="literal">undefined</span>;
|
|
<span class="keyword">this</span>.headersSent = <span class="literal">false</span>;
|
|
|
|
<span class="keyword">this</span>.on(<span class="string">'finish'</span>, <span class="keyword">this</span>._finish);
|
|
}
|
|
OutgoingMessage.prototype = Object.create(Writable.prototype, { constructor: { value: OutgoingMessage } });
|
|
|
|
OutgoingMessage.prototype._write = <span class="function"><span class="keyword">function</span> <span class="title">_write</span><span class="params">(chunk, encoding, callback)</span> {</span>
|
|
<span class="keyword">if</span> (<span class="keyword">this</span>.stream) {
|
|
<span class="keyword">this</span>.stream.write(chunk, encoding, callback);
|
|
} <span class="keyword">else</span> {
|
|
<span class="keyword">this</span>.once(<span class="string">'socket'</span>, <span class="keyword">this</span>._write.bind(<span class="keyword">this</span>, chunk, encoding, callback));
|
|
}
|
|
};
|
|
|
|
OutgoingMessage.prototype._finish = <span class="function"><span class="keyword">function</span> <span class="title">_finish</span><span class="params">()</span> {</span>
|
|
<span class="keyword">if</span> (<span class="keyword">this</span>.stream) {
|
|
<span class="keyword">if</span> (<span class="keyword">this</span>._trailers) {
|
|
<span class="keyword">if</span> (<span class="keyword">this</span>.request) {
|
|
<span class="keyword">this</span>.request.addTrailers(<span class="keyword">this</span>._trailers);
|
|
} <span class="keyword">else</span> {
|
|
<span class="keyword">this</span>.stream.headers(<span class="keyword">this</span>._trailers);
|
|
}
|
|
}
|
|
<span class="keyword">this</span>.stream.end();
|
|
} <span class="keyword">else</span> {
|
|
<span class="keyword">this</span>.once(<span class="string">'socket'</span>, <span class="keyword">this</span>._finish.bind(<span class="keyword">this</span>));
|
|
}
|
|
};
|
|
|
|
OutgoingMessage.prototype.setHeader = <span class="function"><span class="keyword">function</span> <span class="title">setHeader</span><span class="params">(name, value)</span> {</span>
|
|
<span class="keyword">if</span> (<span class="keyword">this</span>.headersSent) {
|
|
<span class="keyword">throw</span> <span class="keyword">new</span> Error(<span class="string">'Can\'t set headers after they are sent.'</span>);
|
|
} <span class="keyword">else</span> {
|
|
name = name.toLowerCase();
|
|
<span class="keyword">if</span> (deprecatedHeaders.indexOf(name) !== -<span class="number">1</span>) {
|
|
<span class="keyword">throw</span> <span class="keyword">new</span> Error(<span class="string">'Cannot set deprecated header: '</span> + name);
|
|
}
|
|
<span class="keyword">this</span>._headers[name] = value;
|
|
}
|
|
};
|
|
|
|
OutgoingMessage.prototype.removeHeader = <span class="function"><span class="keyword">function</span> <span class="title">removeHeader</span><span class="params">(name)</span> {</span>
|
|
<span class="keyword">if</span> (<span class="keyword">this</span>.headersSent) {
|
|
<span class="keyword">throw</span> <span class="keyword">new</span> Error(<span class="string">'Can\'t remove headers after they are sent.'</span>);
|
|
} <span class="keyword">else</span> {
|
|
<span class="keyword">delete</span> <span class="keyword">this</span>._headers[name.toLowerCase()];
|
|
}
|
|
};
|
|
|
|
OutgoingMessage.prototype.getHeader = <span class="function"><span class="keyword">function</span> <span class="title">getHeader</span><span class="params">(name)</span> {</span>
|
|
<span class="keyword">return</span> <span class="keyword">this</span>._headers[name.toLowerCase()];
|
|
};
|
|
|
|
OutgoingMessage.prototype.addTrailers = <span class="function"><span class="keyword">function</span> <span class="title">addTrailers</span><span class="params">(trailers)</span> {</span>
|
|
<span class="keyword">this</span>._trailers = trailers;
|
|
};
|
|
|
|
OutgoingMessage.prototype.setTimeout = noop;
|
|
|
|
OutgoingMessage.prototype._checkSpecialHeader = IncomingMessage.prototype._checkSpecialHeader;</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-25">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h1">
|
|
<a class="pilcrow" href="#section-25">¶</a>
|
|
</div>
|
|
<h1>Server side</h1>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-26">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-26">¶</a>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>exports.createServer = createServer;
|
|
exports.Server = Server;
|
|
exports.IncomingRequest = IncomingRequest;
|
|
exports.OutgoingResponse = OutgoingResponse;
|
|
exports.ServerResponse = OutgoingResponse; <span class="comment">// for API compatibility</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-27">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h2">
|
|
<a class="pilcrow" href="#section-27">¶</a>
|
|
</div>
|
|
<h2>Server class</h2>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-28">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-28">¶</a>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre><span class="function"><span class="keyword">function</span> <span class="title">Server</span><span class="params">(options)</span> {</span>
|
|
options = util._extend({}, options);
|
|
|
|
<span class="keyword">this</span>._log = (options.log || defaultLogger).child({ component: <span class="string">'http'</span> });
|
|
<span class="keyword">this</span>._settings = options.settings;
|
|
|
|
<span class="keyword">var</span> start = <span class="keyword">this</span>._start.bind(<span class="keyword">this</span>);
|
|
<span class="keyword">var</span> fallback = <span class="keyword">this</span>._fallback.bind(<span class="keyword">this</span>);</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-29">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-29">¶</a>
|
|
</div>
|
|
<p>HTTP2 over TLS (using NPN or ALPN)</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">if</span> ((options.key && options.cert) || options.pfx) {
|
|
<span class="keyword">this</span>._log.info(<span class="string">'Creating HTTP/2 server over TLS'</span>);
|
|
<span class="keyword">this</span>._mode = <span class="string">'tls'</span>;
|
|
options.ALPNProtocols = supportedProtocols;
|
|
options.NPNProtocols = supportedProtocols;
|
|
<span class="keyword">this</span>._server = https.createServer(options);
|
|
<span class="keyword">this</span>._originalSocketListeners = <span class="keyword">this</span>._server.listeners(<span class="string">'secureConnection'</span>);
|
|
<span class="keyword">this</span>._server.removeAllListeners(<span class="string">'secureConnection'</span>);
|
|
<span class="keyword">this</span>._server.on(<span class="string">'secureConnection'</span>, <span class="keyword">function</span>(socket) {
|
|
<span class="keyword">var</span> negotiatedProtocol = socket.alpnProtocol || socket.npnProtocol;
|
|
<span class="keyword">if</span> ((negotiatedProtocol === implementedVersion) && socket.servername) {
|
|
start(socket);
|
|
} <span class="keyword">else</span> {
|
|
fallback(socket);
|
|
}
|
|
});
|
|
<span class="keyword">this</span>._server.on(<span class="string">'request'</span>, <span class="keyword">this</span>.emit.bind(<span class="keyword">this</span>, <span class="string">'request'</span>));
|
|
}</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-30">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-30">¶</a>
|
|
</div>
|
|
<p>HTTP2 over plain TCP</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">else</span> <span class="keyword">if</span> (options.plain) {
|
|
<span class="keyword">this</span>._log.info(<span class="string">'Creating HTTP/2 server over plain TCP'</span>);
|
|
<span class="keyword">this</span>._mode = <span class="string">'plain'</span>;
|
|
<span class="keyword">this</span>._server = net.createServer(start);
|
|
}</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-31">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-31">¶</a>
|
|
</div>
|
|
<p>HTTP/2 with HTTP/1.1 upgrade</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">else</span> {
|
|
<span class="keyword">this</span>._log.error(<span class="string">'Trying to create HTTP/2 server with Upgrade from HTTP/1.1'</span>);
|
|
<span class="keyword">throw</span> <span class="keyword">new</span> Error(<span class="string">'HTTP1.1 -> HTTP2 upgrade is not yet supported. Please provide TLS keys.'</span>);
|
|
}
|
|
|
|
<span class="keyword">this</span>._server.on(<span class="string">'close'</span>, <span class="keyword">this</span>.emit.bind(<span class="keyword">this</span>, <span class="string">'close'</span>));
|
|
}
|
|
Server.prototype = Object.create(EventEmitter.prototype, { constructor: { value: Server } });</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-32">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-32">¶</a>
|
|
</div>
|
|
<p>Starting HTTP/2</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>Server.prototype._start = <span class="function"><span class="keyword">function</span> <span class="title">_start</span><span class="params">(socket)</span> {</span>
|
|
<span class="keyword">var</span> endpoint = <span class="keyword">new</span> Endpoint(<span class="keyword">this</span>._log, <span class="string">'SERVER'</span>, <span class="keyword">this</span>._settings);
|
|
|
|
<span class="keyword">this</span>._log.info({ e: endpoint,
|
|
client: socket.remoteAddress + <span class="string">':'</span> + socket.remotePort,
|
|
SNI: socket.servername
|
|
}, <span class="string">'New incoming HTTP/2 connection'</span>);
|
|
|
|
endpoint.pipe(socket).pipe(endpoint);
|
|
|
|
<span class="keyword">var</span> self = <span class="keyword">this</span>;
|
|
endpoint.on(<span class="string">'stream'</span>, <span class="function"><span class="keyword">function</span> <span class="title">_onStream</span><span class="params">(stream)</span> {</span>
|
|
<span class="keyword">var</span> response = <span class="keyword">new</span> OutgoingResponse(stream);
|
|
<span class="keyword">var</span> request = <span class="keyword">new</span> IncomingRequest(stream);
|
|
|
|
request.once(<span class="string">'ready'</span>, self.emit.bind(self, <span class="string">'request'</span>, request, response));
|
|
});
|
|
|
|
endpoint.on(<span class="string">'error'</span>, <span class="keyword">this</span>.emit.bind(<span class="keyword">this</span>, <span class="string">'clientError'</span>));
|
|
socket.on(<span class="string">'error'</span>, <span class="keyword">this</span>.emit.bind(<span class="keyword">this</span>, <span class="string">'clientError'</span>));
|
|
|
|
<span class="keyword">this</span>.emit(<span class="string">'connection'</span>, socket, endpoint);
|
|
};
|
|
|
|
Server.prototype._fallback = <span class="function"><span class="keyword">function</span> <span class="title">_fallback</span><span class="params">(socket)</span> {</span>
|
|
<span class="keyword">var</span> negotiatedProtocol = socket.alpnProtocol || socket.npnProtocol;
|
|
|
|
<span class="keyword">this</span>._log.info({ client: socket.remoteAddress + <span class="string">':'</span> + socket.remotePort,
|
|
protocol: negotiatedProtocol,
|
|
SNI: socket.servername
|
|
}, <span class="string">'Falling back to simple HTTPS'</span>);
|
|
|
|
<span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < <span class="keyword">this</span>._originalSocketListeners.length; i++) {
|
|
<span class="keyword">this</span>._originalSocketListeners[i].call(<span class="keyword">this</span>._server, socket);
|
|
}
|
|
|
|
<span class="keyword">this</span>.emit(<span class="string">'connection'</span>, socket);
|
|
};</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-33">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-33">¶</a>
|
|
</div>
|
|
<p>There are <a href="http://nodejs.org/api/http.html#http_server_listen_port_hostname_backlog_callback">3 possible signatures</a> of the <code>listen</code> function. Every arguments is forwarded to
|
|
the backing TCP or HTTPS server.</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>Server.prototype.listen = <span class="function"><span class="keyword">function</span> <span class="title">listen</span><span class="params">(port, hostname)</span> {</span>
|
|
<span class="keyword">this</span>._log.info({ on: ((<span class="keyword">typeof</span> hostname === <span class="string">'string'</span>) ? (hostname + <span class="string">':'</span> + port) : port) },
|
|
<span class="string">'Listening for incoming connections'</span>);
|
|
<span class="keyword">this</span>._server.listen.apply(<span class="keyword">this</span>._server, arguments);
|
|
};
|
|
|
|
Server.prototype.close = <span class="function"><span class="keyword">function</span> <span class="title">close</span><span class="params">(callback)</span> {</span>
|
|
<span class="keyword">this</span>._log.info(<span class="string">'Closing server'</span>);
|
|
<span class="keyword">this</span>._server.close(callback);
|
|
};
|
|
|
|
Server.prototype.setTimeout = <span class="function"><span class="keyword">function</span> <span class="title">setTimeout</span><span class="params">(timeout, callback)</span> {</span>
|
|
<span class="keyword">if</span> (<span class="keyword">this</span>._mode === <span class="string">'tls'</span>) {
|
|
<span class="keyword">this</span>._server.setTimeout(timeout, callback);
|
|
}
|
|
};
|
|
|
|
Object.defineProperty(Server.prototype, <span class="string">'timeout'</span>, {
|
|
get: <span class="function"><span class="keyword">function</span> <span class="title">getTimeout</span><span class="params">()</span> {</span>
|
|
<span class="keyword">if</span> (<span class="keyword">this</span>._mode === <span class="string">'tls'</span>) {
|
|
<span class="keyword">return</span> <span class="keyword">this</span>._server.timeout;
|
|
} <span class="keyword">else</span> {
|
|
<span class="keyword">return</span> <span class="literal">undefined</span>;
|
|
}
|
|
},
|
|
set: <span class="function"><span class="keyword">function</span> <span class="title">setTimeout</span><span class="params">(timeout)</span> {</span>
|
|
<span class="keyword">if</span> (<span class="keyword">this</span>._mode === <span class="string">'tls'</span>) {
|
|
<span class="keyword">this</span>._server.timeout = timeout;
|
|
}
|
|
}
|
|
});</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-34">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-34">¶</a>
|
|
</div>
|
|
<p>Overriding <code>EventEmitter</code>'s <code>on(event, listener)</code> method to forward certain subscriptions to
|
|
<code>server</code>.There are events on the <code>http.Server</code> class where it makes difference whether someone is
|
|
listening on the event or not. In these cases, we can not simply forward the events from the
|
|
<code>server</code> to <code>this</code> since that means a listener. Instead, we forward the subscriptions.</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>Server.prototype.on = <span class="function"><span class="keyword">function</span> <span class="title">on</span><span class="params">(event, listener)</span> {</span>
|
|
<span class="keyword">if</span> ((event === <span class="string">'upgrade'</span>) || (event === <span class="string">'timeout'</span>)) {
|
|
<span class="keyword">this</span>._server.on(event, listener && listener.bind(<span class="keyword">this</span>));
|
|
} <span class="keyword">else</span> {
|
|
EventEmitter.prototype.on.call(<span class="keyword">this</span>, event, listener);
|
|
}
|
|
};</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-35">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-35">¶</a>
|
|
</div>
|
|
<p><code>addContext</code> is used to add Server Name Indication contexts</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>Server.prototype.addContext = <span class="function"><span class="keyword">function</span> <span class="title">addContext</span><span class="params">(hostname, credentials)</span> {</span>
|
|
<span class="keyword">if</span> (<span class="keyword">this</span>._mode === <span class="string">'tls'</span>) {
|
|
<span class="keyword">this</span>._server.addContext(hostname, credentials);
|
|
}
|
|
};
|
|
|
|
<span class="function"><span class="keyword">function</span> <span class="title">createServer</span><span class="params">(options, requestListener)</span> {</span>
|
|
<span class="keyword">if</span> (<span class="keyword">typeof</span> options === <span class="string">'function'</span>) {
|
|
requestListener = options;
|
|
options = <span class="literal">undefined</span>;
|
|
}
|
|
|
|
<span class="keyword">var</span> server = <span class="keyword">new</span> Server(options);
|
|
|
|
<span class="keyword">if</span> (requestListener) {
|
|
server.on(<span class="string">'request'</span>, requestListener);
|
|
}
|
|
|
|
<span class="keyword">return</span> server;
|
|
}</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-36">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h2">
|
|
<a class="pilcrow" href="#section-36">¶</a>
|
|
</div>
|
|
<h2>IncomingRequest class</h2>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-37">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-37">¶</a>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre><span class="function"><span class="keyword">function</span> <span class="title">IncomingRequest</span><span class="params">(stream)</span> {</span>
|
|
IncomingMessage.call(<span class="keyword">this</span>, stream);
|
|
}
|
|
IncomingRequest.prototype = Object.create(IncomingMessage.prototype, { constructor: { value: IncomingRequest } });</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-38">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-38">¶</a>
|
|
</div>
|
|
<p><a href="http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-8.1.3.1">Request Header Fields</a>
|
|
* <code>headers</code> argument: HTTP/2.0 request and response header fields carry information as a series
|
|
of key-value pairs. This includes the target URI for the request, the status code for the
|
|
response, as well as HTTP header fields.</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>IncomingRequest.prototype._onHeaders = <span class="function"><span class="keyword">function</span> <span class="title">_onHeaders</span><span class="params">(headers)</span> {</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-39">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-39">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>The ":method" header field includes the HTTP method</li>
|
|
<li>The ":scheme" header field includes the scheme portion of the target URI</li>
|
|
<li>The ":authority" header field includes the authority portion of the target URI</li>
|
|
<li>The ":path" header field includes the path and query parts of the target URI.
|
|
This field MUST NOT be empty; URIs that do not contain a path component MUST include a value
|
|
of '/', unless the request is an OPTIONS request for '<em>', in which case the ":path" header
|
|
field MUST include '</em>'.</li>
|
|
<li>All HTTP/2.0 requests MUST include exactly one valid value for all of these header fields. A
|
|
server MUST treat the absence of any of these header fields, presence of multiple values, or
|
|
an invalid value as a stream error of type PROTOCOL_ERROR.</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">this</span>.method = <span class="keyword">this</span>._checkSpecialHeader(<span class="string">':method'</span> , headers[<span class="string">':method'</span>]);
|
|
<span class="keyword">this</span>.scheme = <span class="keyword">this</span>._checkSpecialHeader(<span class="string">':scheme'</span> , headers[<span class="string">':scheme'</span>]);
|
|
<span class="keyword">this</span>.host = <span class="keyword">this</span>._checkSpecialHeader(<span class="string">':authority'</span>, headers[<span class="string">':authority'</span>] );
|
|
<span class="keyword">this</span>.url = <span class="keyword">this</span>._checkSpecialHeader(<span class="string">':path'</span> , headers[<span class="string">':path'</span>] );</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-40">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-40">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>Host header is included in the headers object for backwards compatibility.</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">this</span>.headers.host = <span class="keyword">this</span>.host;</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-41">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-41">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>Handling regular headers.</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> IncomingMessage.prototype._onHeaders.call(<span class="keyword">this</span>, headers);</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-42">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-42">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>Signaling that the headers arrived.</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">this</span>._log.info({ method: <span class="keyword">this</span>.method, scheme: <span class="keyword">this</span>.scheme, host: <span class="keyword">this</span>.host,
|
|
path: <span class="keyword">this</span>.url, headers: <span class="keyword">this</span>.headers }, <span class="string">'Incoming request'</span>);
|
|
<span class="keyword">this</span>.emit(<span class="string">'ready'</span>);
|
|
};</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-43">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h2">
|
|
<a class="pilcrow" href="#section-43">¶</a>
|
|
</div>
|
|
<h2>OutgoingResponse class</h2>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-44">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-44">¶</a>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre><span class="function"><span class="keyword">function</span> <span class="title">OutgoingResponse</span><span class="params">(stream)</span> {</span>
|
|
OutgoingMessage.call(<span class="keyword">this</span>);
|
|
|
|
<span class="keyword">this</span>._log = stream._log.child({ component: <span class="string">'http'</span> });
|
|
|
|
<span class="keyword">this</span>.stream = stream;
|
|
<span class="keyword">this</span>.statusCode = <span class="number">200</span>;
|
|
<span class="keyword">this</span>.sendDate = <span class="literal">true</span>;
|
|
|
|
<span class="keyword">this</span>.stream.once(<span class="string">'headers'</span>, <span class="keyword">this</span>._onRequestHeaders.bind(<span class="keyword">this</span>));
|
|
}
|
|
OutgoingResponse.prototype = Object.create(OutgoingMessage.prototype, { constructor: { value: OutgoingResponse } });
|
|
|
|
OutgoingResponse.prototype.writeHead = <span class="function"><span class="keyword">function</span> <span class="title">writeHead</span><span class="params">(statusCode, reasonPhrase, headers)</span> {</span>
|
|
<span class="keyword">if</span> (<span class="keyword">typeof</span> reasonPhrase === <span class="string">'string'</span>) {
|
|
<span class="keyword">this</span>._log.warn(<span class="string">'Reason phrase argument was present but ignored by the writeHead method'</span>);
|
|
} <span class="keyword">else</span> {
|
|
headers = reasonPhrase;
|
|
}
|
|
|
|
<span class="keyword">for</span> (<span class="keyword">var</span> name <span class="keyword">in</span> headers) {
|
|
<span class="keyword">this</span>.setHeader(name, headers[name]);
|
|
}
|
|
headers = <span class="keyword">this</span>._headers;
|
|
|
|
<span class="keyword">if</span> (<span class="keyword">this</span>.sendDate && !(<span class="string">'date'</span> <span class="keyword">in</span> <span class="keyword">this</span>._headers)) {
|
|
headers.date = (<span class="keyword">new</span> Date()).toUTCString();
|
|
}
|
|
|
|
<span class="keyword">this</span>._log.info({ status: statusCode, headers: <span class="keyword">this</span>._headers }, <span class="string">'Sending server response'</span>);
|
|
|
|
headers[<span class="string">':status'</span>] = <span class="keyword">this</span>.statusCode = statusCode;
|
|
|
|
<span class="keyword">this</span>.stream.headers(headers);
|
|
<span class="keyword">this</span>.headersSent = <span class="literal">true</span>;
|
|
};
|
|
|
|
OutgoingResponse.prototype._implicitHeaders = <span class="function"><span class="keyword">function</span> <span class="title">_implicitHeaders</span><span class="params">()</span> {</span>
|
|
<span class="keyword">if</span> (!<span class="keyword">this</span>.headersSent) {
|
|
<span class="keyword">this</span>.writeHead(<span class="keyword">this</span>.statusCode);
|
|
}
|
|
};
|
|
|
|
OutgoingResponse.prototype.write = <span class="function"><span class="keyword">function</span> <span class="title">write</span><span class="params">()</span> {</span>
|
|
<span class="keyword">this</span>._implicitHeaders();
|
|
<span class="keyword">return</span> OutgoingMessage.prototype.write.apply(<span class="keyword">this</span>, arguments);
|
|
};
|
|
|
|
OutgoingResponse.prototype.end = <span class="function"><span class="keyword">function</span> <span class="title">end</span><span class="params">()</span> {</span>
|
|
<span class="keyword">this</span>._implicitHeaders();
|
|
<span class="keyword">return</span> OutgoingMessage.prototype.end.apply(<span class="keyword">this</span>, arguments);
|
|
};
|
|
|
|
OutgoingResponse.prototype._onRequestHeaders = <span class="function"><span class="keyword">function</span> <span class="title">_onRequestHeaders</span><span class="params">(headers)</span> {</span>
|
|
<span class="keyword">this</span>._requestHeaders = headers;
|
|
};
|
|
|
|
OutgoingResponse.prototype.push = <span class="function"><span class="keyword">function</span> <span class="title">push</span><span class="params">(options)</span> {</span>
|
|
<span class="keyword">if</span> (<span class="keyword">typeof</span> options === <span class="string">'string'</span>) {
|
|
options = url.parse(options);
|
|
}
|
|
|
|
<span class="keyword">if</span> (!options.path) {
|
|
<span class="keyword">throw</span> <span class="keyword">new</span> Error(<span class="string">'`path` option is mandatory.'</span>);
|
|
}
|
|
|
|
<span class="keyword">var</span> promise = util._extend({
|
|
<span class="string">':method'</span>: (options.method || <span class="string">'GET'</span>).toUpperCase(),
|
|
<span class="string">':scheme'</span>: (options.protocol && options.protocol.slice(<span class="number">0</span>, -<span class="number">1</span>)) || <span class="keyword">this</span>._requestHeaders[<span class="string">':scheme'</span>],
|
|
<span class="string">':authority'</span>: options.hostname || options.host || <span class="keyword">this</span>._requestHeaders[<span class="string">':authority'</span>],
|
|
<span class="string">':path'</span>: options.path
|
|
}, options.headers);
|
|
|
|
<span class="keyword">this</span>._log.info({ method: promise[<span class="string">':method'</span>], scheme: promise[<span class="string">':scheme'</span>],
|
|
authority: promise[<span class="string">':authority'</span>], path: promise[<span class="string">':path'</span>],
|
|
headers: options.headers }, <span class="string">'Promising push stream'</span>);
|
|
|
|
<span class="keyword">var</span> pushStream = <span class="keyword">this</span>.stream.promise(promise);
|
|
|
|
<span class="keyword">return</span> <span class="keyword">new</span> OutgoingResponse(pushStream);
|
|
};</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-45">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-45">¶</a>
|
|
</div>
|
|
<p>Overriding <code>EventEmitter</code>'s <code>on(event, listener)</code> method to forward certain subscriptions to
|
|
<code>request</code>. See <code>Server.prototype.on</code> for explanation.</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>OutgoingResponse.prototype.on = <span class="function"><span class="keyword">function</span> <span class="title">on</span><span class="params">(event, listener)</span> {</span>
|
|
<span class="keyword">if</span> (<span class="keyword">this</span>.request && (event === <span class="string">'timeout'</span>)) {
|
|
<span class="keyword">this</span>.request.on(event, listener && listener.bind(<span class="keyword">this</span>));
|
|
} <span class="keyword">else</span> {
|
|
OutgoingMessage.prototype.on.call(<span class="keyword">this</span>, event, listener);
|
|
}
|
|
};</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-46">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h1">
|
|
<a class="pilcrow" href="#section-46">¶</a>
|
|
</div>
|
|
<h1>Client side</h1>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-47">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-47">¶</a>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>exports.ClientRequest = OutgoingRequest; <span class="comment">// for API compatibility</span>
|
|
exports.OutgoingRequest = OutgoingRequest;
|
|
exports.IncomingResponse = IncomingResponse;
|
|
exports.Agent = Agent;
|
|
exports.globalAgent = <span class="literal">undefined</span>;
|
|
exports.request = <span class="function"><span class="keyword">function</span> <span class="title">request</span><span class="params">(options, callback)</span> {</span>
|
|
<span class="keyword">return</span> (options.agent || exports.globalAgent).request(options, callback);
|
|
};
|
|
exports.get = <span class="function"><span class="keyword">function</span> <span class="title">get</span><span class="params">(options, callback)</span> {</span>
|
|
<span class="keyword">return</span> (options.agent || exports.globalAgent).get(options, callback);
|
|
};</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-48">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h2">
|
|
<a class="pilcrow" href="#section-48">¶</a>
|
|
</div>
|
|
<h2>Agent class</h2>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-49">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-49">¶</a>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre><span class="function"><span class="keyword">function</span> <span class="title">Agent</span><span class="params">(options)</span> {</span>
|
|
EventEmitter.call(<span class="keyword">this</span>);
|
|
|
|
options = util._extend({}, options);
|
|
|
|
<span class="keyword">this</span>._settings = options.settings;
|
|
<span class="keyword">this</span>._log = (options.log || defaultLogger).child({ component: <span class="string">'http'</span> });
|
|
<span class="keyword">this</span>.endpoints = {};</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-50">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-50">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>Using an own HTTPS agent, because the global agent does not look at <code>NPN/ALPNProtocols</code> when
|
|
generating the key identifying the connection, so we may get useless non-negotiated TLS
|
|
channels even if we ask for a negotiated one. This agent will contain only negotiated
|
|
channels.</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">var</span> agentOptions = {};
|
|
agentOptions.ALPNProtocols = supportedProtocols;
|
|
agentOptions.NPNProtocols = supportedProtocols;
|
|
<span class="keyword">this</span>._httpsAgent = <span class="keyword">new</span> https.Agent(agentOptions);
|
|
|
|
<span class="keyword">this</span>.sockets = <span class="keyword">this</span>._httpsAgent.sockets;
|
|
<span class="keyword">this</span>.requests = <span class="keyword">this</span>._httpsAgent.requests;
|
|
}
|
|
Agent.prototype = Object.create(EventEmitter.prototype, { constructor: { value: Agent } });
|
|
|
|
Agent.prototype.request = <span class="function"><span class="keyword">function</span> <span class="title">request</span><span class="params">(options, callback)</span> {</span>
|
|
<span class="keyword">if</span> (<span class="keyword">typeof</span> options === <span class="string">'string'</span>) {
|
|
options = url.parse(options);
|
|
} <span class="keyword">else</span> {
|
|
options = util._extend({}, options);
|
|
}
|
|
|
|
options.method = (options.method || <span class="string">'GET'</span>).toUpperCase();
|
|
options.protocol = options.protocol || <span class="string">'https:'</span>;
|
|
options.host = options.hostname || options.host || <span class="string">'localhost'</span>;
|
|
options.port = options.port || <span class="number">443</span>;
|
|
options.path = options.path || <span class="string">'/'</span>;
|
|
|
|
<span class="keyword">if</span> (!options.plain && options.protocol === <span class="string">'http:'</span>) {
|
|
<span class="keyword">this</span>._log.error(<span class="string">'Trying to negotiate client request with Upgrade from HTTP/1.1'</span>);
|
|
<span class="keyword">throw</span> <span class="keyword">new</span> Error(<span class="string">'HTTP1.1 -> HTTP2 upgrade is not yet supported.'</span>);
|
|
}
|
|
|
|
<span class="keyword">var</span> request = <span class="keyword">new</span> OutgoingRequest(<span class="keyword">this</span>._log);
|
|
|
|
<span class="keyword">if</span> (callback) {
|
|
request.on(<span class="string">'response'</span>, callback);
|
|
}
|
|
|
|
<span class="keyword">var</span> key = [
|
|
!!options.plain,
|
|
options.host,
|
|
options.port
|
|
].join(<span class="string">':'</span>);</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-51">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-51">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>There's an existing HTTP/2 connection to this host</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">if</span> (key <span class="keyword">in</span> <span class="keyword">this</span>.endpoints) {
|
|
<span class="keyword">var</span> endpoint = <span class="keyword">this</span>.endpoints[key];
|
|
request._start(endpoint.createStream(), options);
|
|
}</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-52">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-52">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>HTTP/2 over plain TCP</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">else</span> <span class="keyword">if</span> (options.plain) {
|
|
endpoint = <span class="keyword">new</span> Endpoint(<span class="keyword">this</span>._log, <span class="string">'CLIENT'</span>, <span class="keyword">this</span>._settings);
|
|
endpoint.socket = net.connect({
|
|
host: options.host,
|
|
port: options.port,
|
|
localAddress: options.localAddress
|
|
});
|
|
endpoint.pipe(endpoint.socket).pipe(endpoint);
|
|
request._start(endpoint.createStream(), options);
|
|
}</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-53">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-53">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>HTTP/2 over TLS negotiated using NPN or ALPN</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">else</span> {
|
|
<span class="keyword">var</span> started = <span class="literal">false</span>;
|
|
options.ALPNProtocols = supportedProtocols;
|
|
options.NPNProtocols = supportedProtocols;
|
|
options.servername = options.host; <span class="comment">// Server Name Indication</span>
|
|
options.agent = <span class="keyword">this</span>._httpsAgent;
|
|
<span class="keyword">var</span> httpsRequest = https.request(options);
|
|
|
|
httpsRequest.on(<span class="string">'socket'</span>, <span class="keyword">function</span>(socket) {
|
|
<span class="keyword">var</span> negotiatedProtocol = socket.alpnProtocol || socket.npnProtocol;
|
|
<span class="keyword">if</span> (negotiatedProtocol !== <span class="literal">undefined</span>) {
|
|
negotiated();
|
|
} <span class="keyword">else</span> {
|
|
socket.on(<span class="string">'secureConnect'</span>, negotiated);
|
|
}
|
|
});
|
|
|
|
<span class="keyword">var</span> self = <span class="keyword">this</span>;
|
|
<span class="function"><span class="keyword">function</span> <span class="title">negotiated</span><span class="params">()</span> {</span>
|
|
<span class="keyword">var</span> endpoint;
|
|
<span class="keyword">var</span> negotiatedProtocol = httpsRequest.socket.alpnProtocol || httpsRequest.socket.npnProtocol;
|
|
<span class="keyword">if</span> (negotiatedProtocol === implementedVersion) {
|
|
httpsRequest.socket.emit(<span class="string">'agentRemove'</span>);
|
|
unbundleSocket(httpsRequest.socket);
|
|
endpoint = <span class="keyword">new</span> Endpoint(self._log, <span class="string">'CLIENT'</span>, self._settings);
|
|
endpoint.socket = httpsRequest.socket;
|
|
endpoint.pipe(endpoint.socket).pipe(endpoint);
|
|
}
|
|
<span class="keyword">if</span> (started) {
|
|
<span class="keyword">if</span> (endpoint) {
|
|
endpoint.close();
|
|
} <span class="keyword">else</span> {
|
|
httpsRequest.abort();
|
|
}
|
|
} <span class="keyword">else</span> {
|
|
<span class="keyword">if</span> (endpoint) {
|
|
self._log.info({ e: endpoint, server: options.host + <span class="string">':'</span> + options.port },
|
|
<span class="string">'New outgoing HTTP/2 connection'</span>);
|
|
self.endpoints[key] = endpoint;
|
|
self.emit(key, endpoint);
|
|
} <span class="keyword">else</span> {
|
|
self.emit(key, <span class="literal">undefined</span>);
|
|
}
|
|
}
|
|
}
|
|
|
|
<span class="keyword">this</span>.once(key, <span class="keyword">function</span>(endpoint) {
|
|
started = <span class="literal">true</span>;
|
|
<span class="keyword">if</span> (endpoint) {
|
|
request._start(endpoint.createStream(), options);
|
|
} <span class="keyword">else</span> {
|
|
request._fallback(httpsRequest);
|
|
}
|
|
});
|
|
}
|
|
|
|
<span class="keyword">return</span> request;
|
|
};
|
|
|
|
Agent.prototype.get = <span class="function"><span class="keyword">function</span> <span class="title">get</span><span class="params">(options, callback)</span> {</span>
|
|
<span class="keyword">var</span> request = <span class="keyword">this</span>.request(options, callback);
|
|
request.end();
|
|
<span class="keyword">return</span> request;
|
|
};
|
|
|
|
<span class="function"><span class="keyword">function</span> <span class="title">unbundleSocket</span><span class="params">(socket)</span> {</span>
|
|
socket.removeAllListeners(<span class="string">'data'</span>);
|
|
socket.removeAllListeners(<span class="string">'end'</span>);
|
|
socket.removeAllListeners(<span class="string">'readable'</span>);
|
|
socket.removeAllListeners(<span class="string">'close'</span>);
|
|
socket.removeAllListeners(<span class="string">'error'</span>);
|
|
socket.unpipe();
|
|
<span class="keyword">delete</span> socket.ondata;
|
|
<span class="keyword">delete</span> socket.onend;
|
|
}
|
|
|
|
Object.defineProperty(Agent.prototype, <span class="string">'maxSockets'</span>, {
|
|
get: <span class="function"><span class="keyword">function</span> <span class="title">getMaxSockets</span><span class="params">()</span> {</span>
|
|
<span class="keyword">return</span> <span class="keyword">this</span>._httpsAgent.maxSockets;
|
|
},
|
|
set: <span class="function"><span class="keyword">function</span> <span class="title">setMaxSockets</span><span class="params">(value)</span> {</span>
|
|
<span class="keyword">this</span>._httpsAgent.maxSockets = value;
|
|
}
|
|
});
|
|
|
|
exports.globalAgent = <span class="keyword">new</span> Agent();</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-54">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h2">
|
|
<a class="pilcrow" href="#section-54">¶</a>
|
|
</div>
|
|
<h2>OutgoingRequest class</h2>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-55">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-55">¶</a>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre><span class="function"><span class="keyword">function</span> <span class="title">OutgoingRequest</span><span class="params">()</span> {</span>
|
|
OutgoingMessage.call(<span class="keyword">this</span>);
|
|
|
|
<span class="keyword">this</span>._log = <span class="literal">undefined</span>;
|
|
|
|
<span class="keyword">this</span>.stream = <span class="literal">undefined</span>;
|
|
}
|
|
OutgoingRequest.prototype = Object.create(OutgoingMessage.prototype, { constructor: { value: OutgoingRequest } });
|
|
|
|
OutgoingRequest.prototype._start = <span class="function"><span class="keyword">function</span> <span class="title">_start</span><span class="params">(stream, options)</span> {</span>
|
|
<span class="keyword">this</span>.stream = stream;
|
|
|
|
<span class="keyword">this</span>._log = stream._log.child({ component: <span class="string">'http'</span> });
|
|
|
|
<span class="keyword">for</span> (<span class="keyword">var</span> key <span class="keyword">in</span> options.headers) {
|
|
<span class="keyword">this</span>.setHeader(key, options.headers[key]);
|
|
}
|
|
<span class="keyword">var</span> headers = <span class="keyword">this</span>._headers;
|
|
<span class="keyword">delete</span> headers.host;
|
|
|
|
<span class="keyword">if</span> (options.auth) {
|
|
headers.authorization = <span class="string">'Basic '</span> + <span class="keyword">new</span> Buffer(options.auth).toString(<span class="string">'base64'</span>);
|
|
}
|
|
|
|
headers[<span class="string">':scheme'</span>] = options.protocol.slice(<span class="number">0</span>, -<span class="number">1</span>);
|
|
headers[<span class="string">':method'</span>] = options.method;
|
|
headers[<span class="string">':authority'</span>] = options.host;
|
|
headers[<span class="string">':path'</span>] = options.path;
|
|
|
|
<span class="keyword">this</span>._log.info({ scheme: headers[<span class="string">':scheme'</span>], method: headers[<span class="string">':method'</span>],
|
|
authority: headers[<span class="string">':authority'</span>], path: headers[<span class="string">':path'</span>],
|
|
headers: (options.headers || {}) }, <span class="string">'Sending request'</span>);
|
|
<span class="keyword">this</span>.stream.headers(headers);
|
|
<span class="keyword">this</span>.headersSent = <span class="literal">true</span>;
|
|
|
|
<span class="keyword">this</span>.emit(<span class="string">'socket'</span>, <span class="keyword">this</span>.stream);
|
|
|
|
<span class="keyword">var</span> response = <span class="keyword">new</span> IncomingResponse(<span class="keyword">this</span>.stream);
|
|
response.once(<span class="string">'ready'</span>, <span class="keyword">this</span>.emit.bind(<span class="keyword">this</span>, <span class="string">'response'</span>, response));
|
|
|
|
<span class="keyword">this</span>.stream.on(<span class="string">'promise'</span>, <span class="keyword">this</span>._onPromise.bind(<span class="keyword">this</span>));
|
|
};
|
|
|
|
OutgoingRequest.prototype._fallback = <span class="function"><span class="keyword">function</span> <span class="title">_fallback</span><span class="params">(request)</span> {</span>
|
|
request.on(<span class="string">'response'</span>, <span class="keyword">this</span>.emit.bind(<span class="keyword">this</span>, <span class="string">'response'</span>));
|
|
<span class="keyword">this</span>.stream = <span class="keyword">this</span>.request = request;
|
|
<span class="keyword">this</span>.emit(<span class="string">'socket'</span>, <span class="keyword">this</span>.socket);
|
|
};
|
|
|
|
OutgoingRequest.prototype.setPriority = <span class="function"><span class="keyword">function</span> <span class="title">setPriority</span><span class="params">(priority)</span> {</span>
|
|
<span class="keyword">if</span> (<span class="keyword">this</span>.stream) {
|
|
<span class="keyword">this</span>.stream.priority(priority);
|
|
} <span class="keyword">else</span> {
|
|
<span class="keyword">this</span>.once(<span class="string">'socket'</span>, <span class="keyword">this</span>.setPriority.bind(<span class="keyword">this</span>, priority));
|
|
}
|
|
};</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-56">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-56">¶</a>
|
|
</div>
|
|
<p>Overriding <code>EventEmitter</code>'s <code>on(event, listener)</code> method to forward certain subscriptions to
|
|
<code>request</code>. See <code>Server.prototype.on</code> for explanation.</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>OutgoingRequest.prototype.on = <span class="function"><span class="keyword">function</span> <span class="title">on</span><span class="params">(event, listener)</span> {</span>
|
|
<span class="keyword">if</span> (<span class="keyword">this</span>.request && (event === <span class="string">'upgrade'</span>)) {
|
|
<span class="keyword">this</span>.request.on(event, listener && listener.bind(<span class="keyword">this</span>));
|
|
} <span class="keyword">else</span> {
|
|
OutgoingMessage.prototype.on.call(<span class="keyword">this</span>, event, listener);
|
|
}
|
|
};</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-57">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-57">¶</a>
|
|
</div>
|
|
<p>Methods only in fallback mode</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>OutgoingRequest.prototype.setNoDelay = <span class="function"><span class="keyword">function</span> <span class="title">setNoDelay</span><span class="params">(noDelay)</span> {</span>
|
|
<span class="keyword">if</span> (<span class="keyword">this</span>.request) {
|
|
<span class="keyword">this</span>.request.setNoDelay(noDelay);
|
|
} <span class="keyword">else</span> <span class="keyword">if</span> (!<span class="keyword">this</span>.stream) {
|
|
<span class="keyword">this</span>.on(<span class="string">'socket'</span>, <span class="keyword">this</span>.setNoDelay.bind(<span class="keyword">this</span>, noDelay));
|
|
}
|
|
};
|
|
|
|
OutgoingRequest.prototype.setSocketKeepAlive = <span class="function"><span class="keyword">function</span> <span class="title">setSocketKeepAlive</span><span class="params">(enable, initialDelay)</span> {</span>
|
|
<span class="keyword">if</span> (<span class="keyword">this</span>.request) {
|
|
<span class="keyword">this</span>.request.setSocketKeepAlive(enable, initialDelay);
|
|
} <span class="keyword">else</span> <span class="keyword">if</span> (!<span class="keyword">this</span>.stream) {
|
|
<span class="keyword">this</span>.on(<span class="string">'socket'</span>, <span class="keyword">this</span>.setSocketKeepAlive.bind(<span class="keyword">this</span>, enable, initialDelay));
|
|
}
|
|
};
|
|
|
|
OutgoingRequest.prototype.setTimeout = <span class="function"><span class="keyword">function</span> <span class="title">setTimeout</span><span class="params">(timeout, callback)</span> {</span>
|
|
<span class="keyword">if</span> (<span class="keyword">this</span>.request) {
|
|
<span class="keyword">this</span>.request.setTimeout(timeout, callback);
|
|
} <span class="keyword">else</span> <span class="keyword">if</span> (!<span class="keyword">this</span>.stream) {
|
|
<span class="keyword">this</span>.on(<span class="string">'socket'</span>, <span class="keyword">this</span>.setTimeout.bind(<span class="keyword">this</span>, timeout, callback));
|
|
}
|
|
};</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-58">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-58">¶</a>
|
|
</div>
|
|
<p>Aborting the request</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>OutgoingRequest.prototype.abort = <span class="function"><span class="keyword">function</span> <span class="title">abort</span><span class="params">()</span> {</span>
|
|
<span class="keyword">if</span> (<span class="keyword">this</span>.request) {
|
|
<span class="keyword">this</span>.request.abort();
|
|
} <span class="keyword">else</span> <span class="keyword">if</span> (<span class="keyword">this</span>.stream) {
|
|
<span class="keyword">this</span>.stream.reset(<span class="string">'CANCEL'</span>);
|
|
} <span class="keyword">else</span> {
|
|
<span class="keyword">this</span>.on(<span class="string">'socket'</span>, <span class="keyword">this</span>.abort.bind(<span class="keyword">this</span>));
|
|
}
|
|
};</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-59">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-59">¶</a>
|
|
</div>
|
|
<p>Receiving push promises</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>OutgoingRequest.prototype._onPromise = <span class="function"><span class="keyword">function</span> <span class="title">_onPromise</span><span class="params">(stream, headers)</span> {</span>
|
|
<span class="keyword">this</span>._log.info({ push_stream: stream.id }, <span class="string">'Receiving push promise'</span>);
|
|
|
|
<span class="keyword">var</span> promise = <span class="keyword">new</span> IncomingPromise(stream, headers);
|
|
|
|
<span class="keyword">if</span> (<span class="keyword">this</span>.listeners(<span class="string">'push'</span>).length > <span class="number">0</span>) {
|
|
<span class="keyword">this</span>.emit(<span class="string">'push'</span>, promise);
|
|
} <span class="keyword">else</span> {
|
|
promise.cancel();
|
|
}
|
|
};</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-60">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h2">
|
|
<a class="pilcrow" href="#section-60">¶</a>
|
|
</div>
|
|
<h2>IncomingResponse class</h2>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-61">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-61">¶</a>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre><span class="function"><span class="keyword">function</span> <span class="title">IncomingResponse</span><span class="params">(stream)</span> {</span>
|
|
IncomingMessage.call(<span class="keyword">this</span>, stream);
|
|
}
|
|
IncomingResponse.prototype = Object.create(IncomingMessage.prototype, { constructor: { value: IncomingResponse } });</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-62">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-62">¶</a>
|
|
</div>
|
|
<p><a href="http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-8.1.3.2">Response Header Fields</a>
|
|
* <code>headers</code> argument: HTTP/2.0 request and response header fields carry information as a series
|
|
of key-value pairs. This includes the target URI for the request, the status code for the
|
|
response, as well as HTTP header fields.</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>IncomingResponse.prototype._onHeaders = <span class="function"><span class="keyword">function</span> <span class="title">_onHeaders</span><span class="params">(headers)</span> {</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-63">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-63">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>A single ":status" header field is defined that carries the HTTP status code field. This
|
|
header field MUST be included in all responses.</li>
|
|
<li>A client MUST treat the absence of the ":status" header field, the presence of multiple
|
|
values, or an invalid value as a stream error of type PROTOCOL_ERROR.
|
|
Note: currently, we do not enforce it strictly: we accept any format, and parse it as int</li>
|
|
<li>HTTP/2.0 does not define a way to carry the reason phrase that is included in an HTTP/1.1
|
|
status line.</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">this</span>.statusCode = parseInt(<span class="keyword">this</span>._checkSpecialHeader(<span class="string">':status'</span>, headers[<span class="string">':status'</span>]));</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-64">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-64">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>Handling regular headers.</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> IncomingMessage.prototype._onHeaders.call(<span class="keyword">this</span>, headers);</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-65">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-65">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>Signaling that the headers arrived.</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">this</span>._log.info({ status: <span class="keyword">this</span>.statusCode, headers: <span class="keyword">this</span>.headers}, <span class="string">'Incoming response'</span>);
|
|
<span class="keyword">this</span>.emit(<span class="string">'ready'</span>);
|
|
};</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-66">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h2">
|
|
<a class="pilcrow" href="#section-66">¶</a>
|
|
</div>
|
|
<h2>IncomingPromise class</h2>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-67">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-67">¶</a>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre><span class="function"><span class="keyword">function</span> <span class="title">IncomingPromise</span><span class="params">(responseStream, promiseHeaders)</span> {</span>
|
|
<span class="keyword">var</span> stream = <span class="keyword">new</span> Readable();
|
|
stream._read = noop;
|
|
stream.push(<span class="literal">null</span>);
|
|
stream._log = responseStream._log;
|
|
|
|
IncomingRequest.call(<span class="keyword">this</span>, stream);
|
|
|
|
<span class="keyword">this</span>._onHeaders(promiseHeaders);
|
|
|
|
<span class="keyword">this</span>._responseStream = responseStream;
|
|
|
|
<span class="keyword">var</span> response = <span class="keyword">new</span> IncomingResponse(<span class="keyword">this</span>._responseStream);
|
|
response.once(<span class="string">'ready'</span>, <span class="keyword">this</span>.emit.bind(<span class="keyword">this</span>, <span class="string">'response'</span>, response));
|
|
|
|
<span class="keyword">this</span>.stream.on(<span class="string">'promise'</span>, <span class="keyword">this</span>._onPromise.bind(<span class="keyword">this</span>));
|
|
}
|
|
IncomingPromise.prototype = Object.create(IncomingRequest.prototype, { constructor: { value: IncomingPromise } });
|
|
|
|
IncomingPromise.prototype.cancel = <span class="function"><span class="keyword">function</span> <span class="title">cancel</span><span class="params">()</span> {</span>
|
|
<span class="keyword">this</span>._responseStream.reset(<span class="string">'CANCEL'</span>);
|
|
};
|
|
|
|
IncomingPromise.prototype.setPriority = <span class="function"><span class="keyword">function</span> <span class="title">setPriority</span><span class="params">(priority)</span> {</span>
|
|
<span class="keyword">this</span>._responseStream.priority(priority);
|
|
};
|
|
|
|
IncomingPromise.prototype._onPromise = OutgoingRequest.prototype._onPromise;</pre></div></div>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</div>
|
|
</body>
|
|
</html>
|