gecko/testing/xpcshell/node-http2/doc/http.html
2014-01-13 17:16:28 -08:00

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 &hellip;</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">&#182;</a>
</div>
<h1>Public API</h1>
</div>
</li>
<li id="section-2">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-2">&#182;</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">&#182;</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: &#39;connection&#39; (socket, [endpoint])</strong>: there&#39;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 &#39;imaginary&#39;
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: &#39;socket&#39; (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: &#39;push&#39; (promise)</strong>: signals the intention of a server push associated to this
request. <code>promise</code> is an IncomingPromise. If there&#39;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&#39;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 &#39;imaginary&#39; request to which the server push is an answer.</li>
<li><strong>Event: &#39;response&#39; (response)</strong>: signals the arrival of the actual push stream. <code>response</code>
is an IncomingResponse.</li>
<li><strong>Event: &#39;push&#39; (promise)</strong>: signals the intention of a server push associated to this
request. <code>promise</code> is an IncomingPromise. If there&#39;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">&#182;</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">&#182;</a>
</div>
<p>The reason may be deprecation of certain HTTP/1.1 features, or that some API elements simply
don&#39;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: &#39;checkContinue&#39;</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: &#39;upgrade&#39;</strong>: upgrade is deprecated in HTTP/2</li>
<li><strong>Event: &#39;timeout&#39;</strong>: HTTP/2 sockets won&#39;t timeout because of application level keepalive
(PING frames)</li>
<li><strong>Event: &#39;connect&#39;</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: &#39;close&#39;</strong></li>
<li><strong>Event: &#39;timeout&#39;</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&#39;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&#39;s always
one connection per host.</li>
</ul>
</li>
<li><p><strong>Class: http2.ClientRequest</strong></p>
<ul>
<li><strong>Event: &#39;upgrade&#39;</strong></li>
<li><strong>Event: &#39;connect&#39;</strong></li>
<li><strong>Event: &#39;continue&#39;</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: &#39;close&#39;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</a>
</div>
<h2>Logging</h2>
</div>
</li>
<li id="section-10">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-10">&#182;</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">&#182;</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">&#182;</a>
</div>
<h2>IncomingMessage class</h2>
</div>
</li>
<li id="section-13">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-13">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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 &lt; 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">&#182;</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">&#182;</a>
</div>
<ul>
<li>The last header block, if it&#39;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">&#182;</a>
</div>
<h2>OutgoingMessage class</h2>
</div>
</li>
<li id="section-23">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-23">&#182;</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">&#182;</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">&#182;</a>
</div>
<h1>Server side</h1>
</div>
</li>
<li id="section-26">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-26">&#182;</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">&#182;</a>
</div>
<h2>Server class</h2>
</div>
</li>
<li id="section-28">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-28">&#182;</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">&#182;</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 &amp;&amp; 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) &amp;&amp; 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">&#182;</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">&#182;</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 -&gt; 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">&#182;</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 &lt; <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">&#182;</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">&#182;</a>
</div>
<p>Overriding <code>EventEmitter</code>&#39;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 &amp;&amp; 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">&#182;</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">&#182;</a>
</div>
<h2>IncomingRequest class</h2>
</div>
</li>
<li id="section-37">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-37">&#182;</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">&#182;</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">&#182;</a>
</div>
<ul>
<li>The &quot;:method&quot; header field includes the HTTP method</li>
<li>The &quot;:scheme&quot; header field includes the scheme portion of the target URI</li>
<li>The &quot;:authority&quot; header field includes the authority portion of the target URI</li>
<li>The &quot;:path&quot; 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 &#39;/&#39;, unless the request is an OPTIONS request for &#39;<em>&#39;, in which case the &quot;:path&quot; header
field MUST include &#39;</em>&#39;.</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">&#182;</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">&#182;</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">&#182;</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">&#182;</a>
</div>
<h2>OutgoingResponse class</h2>
</div>
</li>
<li id="section-44">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-44">&#182;</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 &amp;&amp; !(<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 &amp;&amp; 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">&#182;</a>
</div>
<p>Overriding <code>EventEmitter</code>&#39;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 &amp;&amp; (event === <span class="string">'timeout'</span>)) {
<span class="keyword">this</span>.request.on(event, listener &amp;&amp; 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">&#182;</a>
</div>
<h1>Client side</h1>
</div>
</li>
<li id="section-47">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-47">&#182;</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">&#182;</a>
</div>
<h2>Agent class</h2>
</div>
</li>
<li id="section-49">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-49">&#182;</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">&#182;</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 &amp;&amp; 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 -&gt; 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">&#182;</a>
</div>
<ul>
<li>There&#39;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">&#182;</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">&#182;</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">&#182;</a>
</div>
<h2>OutgoingRequest class</h2>
</div>
</li>
<li id="section-55">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-55">&#182;</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">&#182;</a>
</div>
<p>Overriding <code>EventEmitter</code>&#39;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 &amp;&amp; (event === <span class="string">'upgrade'</span>)) {
<span class="keyword">this</span>.request.on(event, listener &amp;&amp; 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">&#182;</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">&#182;</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">&#182;</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 &gt; <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">&#182;</a>
</div>
<h2>IncomingResponse class</h2>
</div>
</li>
<li id="section-61">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-61">&#182;</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">&#182;</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">&#182;</a>
</div>
<ul>
<li>A single &quot;:status&quot; 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 &quot;:status&quot; 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">&#182;</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">&#182;</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">&#182;</a>
</div>
<h2>IncomingPromise class</h2>
</div>
</li>
<li id="section-67">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-67">&#182;</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>