Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | 1x 1x 1x 1x 1564x 2044x 1564x 1564x 1564x 540x 1024x 1024x 166x 104x 104x 104x 124x 124x 124x 124x 124x 124x 20x 104x 166x 166x 124x 124x 293819x 293819x 293819x 293819x 293819x 4x 293815x 293815x 293743x 4x 293743x 544x 4x 540x 540x 540x 540x 540x 540x 540x 8x 556x 532x 64x 64x 468x 468x 500x 468x 468x 468x 468x 468x 468x 540x 166x 166x 1x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 124x 62x 62x 62x 124x 1x | /*eslint-env node */
/**
* HTTP/2 transport for browsers. This allows messages to be sent over HTTP/2
* streams to browsers.
*
* If your client isn't a browser, you should be able to use the
* {@link centro-js/lib/server_transports/http2|http2} transport.
*
* This transport uses the {@link https://github.com/davedoesdev/browser-http2-duplex|http2-duplex} module to emulate a full-duplex connection with browsers.
* Browser-to-server streaming isn't implemented by any browser, nor are there
* any plans to do so. `http2-duplex` emulates it by using POST requests.
*
* @module centro-js/lib/server_transports/http2-duplex
* @param {Object} config - Configuration options. This supports all the options supported by {@link https://nodejs.org/api/http2.html#http2_http2_createserver_options_onrequesthandler|http2.createServer}, {@link https://nodejs.org/api/http2.html#http2_http2_createsecureserver_options_onrequesthandler|http2.createSecureServer}, {@link https://nodejs.org/api/net.html#net_server_listen_options_callback|net.Server#listen} and {@link https://github.com/davedoesdev/browser-http2-duplex|http2-duplex} as well as the following:
* @param {centro-js/lib/server_transports/http2-duplex.CentroHttp2DuplexServer} [config.server] - If you want to supply your own HTTP/2 full-duplex emulation server. This class inherits from `Http2DuplexServer` in {@link https://github.com/davedoesdev/browser-http2-duplex|http2-duplex}. If you don't supply an instance (the default) then one will be created for you, along with an instance of {@link https://nodejs.org/api/http2.html#http2_class_http2server|Http2Server} or {@link https://nodejs.org/api/http2.html#http2_class_http2secureserver|Http2SecureServer}.
* @param {string} [config.pathname=/centro/v2/http2-duplex] - Pathname prefix on which to listen for requests.
* @param {Object} [config.http2_duplex] - If present then this is used in preference to `config`.
* @param {Object} [config.access] - Passed to {@link https://github.com/primus/access-control|access-control}.
*/
"use strict";
const { promisify } = require('util');
const http2 = require('http2');
const centro = require('../..');
const access = require('access-control');
function on_close(stream, cb) {
let called = false;
function cb2() {
if (!called) {
called = true;
cb();
}
}
if (stream.closed) {
return cb2();
}
stream.on('close', cb2);
stream.on('aborted', cb2);
}
async function init()
{
const { Http2DuplexServer } = await import('http2-duplex');
class CentroHttp2DuplexServer extends Http2DuplexServer {
constructor(http2_server, options) {
super(http2_server,
options.pathname || `/centro/v${centro.version}/http2-duplex`,
options);
this.cors = access({
methods: ['GET', 'POST', 'OPTIONS'],
...options.access
});
this.num_streams = 0;
}
init(authorize, connected, warning, cb) {
super.attach();
this.authorize = authorize;
this.connected = connected;
this.warning = warning;
this.on('warning', warning);
if (this.http2_server.listening) {
return cb();
}
this.http2_server.listen(this.options, cb);
}
detach() {
super.detach();
this.removeListener('warning', this.warning);
}
async process_session(session) {
session.on('error', this.warning);
await super.process_session(session);
}
async process_stream(stream, headers, flags, raw_headers, duplexes, response_headers) {
stream.on('error', this.warning);
this.own_stream(stream);
const req = new http2.Http2ServerRequest(stream, headers, undefined, raw_headers);
const res = new http2.Http2ServerResponse(stream);
if (this.cors(req, res)) {
return true;
}
Object.assign(response_headers, res.getHeaders());
const handled = await super.process_stream(
stream, headers, flags, raw_headers, duplexes, response_headers);
if (!handled) {
stream.respond({
':status': 404,
...response_headers
}, {
endStream: true
});
}
return handled;
}
async new_stream(stream, headers, flags, raw_headers, duplexes, response_headers) {
if (this.maxConnections && (this.num_streams >= this.maxConnections)) {
return stream.respond({
':status': 503,
...response_headers
}, {
endStream: true
});
}
++this.num_streams;
on_close(stream, () => {
--this.num_streams;
});
stream.headers = headers;
stream.url = headers[':path'];
const authorize = done => {
this.authorize(stream, function () {
stream.respond({
':status': 503,
...response_headers
}, {
endStream: true
});
}, cb => {
on_close(stream, cb);
}, async (err, handshakes, unused_tokens) => {
if (err) {
stream.respond({
':status': err.statusCode,
'Content-Type': 'application/json',
'WWW-Authenticate': err.authenticate,
...response_headers
});
return stream.end(JSON.stringify({ error: err.message }));
}
const duplex = await super.new_stream(
stream, headers, flags, raw_headers,
duplexes, response_headers);
const destroy = () => {
this.destroy(duplex);
};
duplex.on('end', destroy);
duplex.on('finish', destroy);
duplex.on('error', destroy);
this.connected(handshakes,
duplex,
destroy,
function(cb) {
on_close(duplex, cb)
});
done(null, duplex);
});
};
return await promisify(authorize)();
}
}
module.exports.CentroHttp2DuplexServer = CentroHttp2DuplexServer;
return CentroHttp2DuplexServer;
}
module.exports = async function (config, authorize, connected, ready, error, warning)
{
const CentroHttp2DuplexServer = await init();
config = config.http2_duplex || config;
const certs = config.key && config.cert;
const port = config.port || /* istanbul ignore next */ 443;
const secure = certs || port === 443;
const server = config.server ||
new CentroHttp2DuplexServer(
secure ? http2.createSecureServer(config) :
http2.createServer(config),
config);
server.http2_server.on('error', error);
function listening() {
ready(null, {
close: function (cb) {
server.http2_server.removeListener('error', error);
server.detach();
if (config.server) {
return cb();
}
server.http2_server.on('session', server.destroy.bind(server));
server.http2_server.close(cb);
},
server: server
});
}
server.init(authorize, connected, warning, listening);
};
module.exports.init = init;
|