Testing / .deno /gen /https /deno.land /8d6b5cd21d4064cfdc92ce7f09807d551dcbce46a7ecc6af2532ffe73b7aba43.js
Chunte's picture
Chunte HF staff
Upload 1904 files
5de1c56 verified
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
import { BufReader, BufWriter } from "../io/bufio.ts";
import { assert } from "../_util/assert.ts";
import { deferred, MuxAsyncIterator } from "../async/mod.ts";
import { bodyReader, chunkedBodyReader, emptyReader, readRequest, writeResponse } from "./_io.ts";
export class ServerRequest {
url;
method;
proto;
protoMinor;
protoMajor;
headers;
conn;
r;
w;
#done = deferred();
#contentLength = undefined;
#body = undefined;
#finalized = false;
get done() {
return this.#done.then((e)=>e);
}
/**
* Value of Content-Length header.
* If null, then content length is invalid or not given (e.g. chunked encoding).
*/ get contentLength() {
// undefined means not cached.
// null means invalid or not provided.
if (this.#contentLength === undefined) {
const cl = this.headers.get("content-length");
if (cl) {
this.#contentLength = parseInt(cl);
// Convert NaN to null (as NaN harder to test)
if (Number.isNaN(this.#contentLength)) {
this.#contentLength = null;
}
} else {
this.#contentLength = null;
}
}
return this.#contentLength;
}
/**
* Body of the request. The easiest way to consume the body is:
*
* const buf: Uint8Array = await readAll(req.body);
*/ get body() {
if (!this.#body) {
if (this.contentLength != null) {
this.#body = bodyReader(this.contentLength, this.r);
} else {
const transferEncoding = this.headers.get("transfer-encoding");
if (transferEncoding != null) {
const parts = transferEncoding.split(",").map((e)=>e.trim().toLowerCase());
assert(parts.includes("chunked"), 'transfer-encoding must include "chunked" if content-length is not set');
this.#body = chunkedBodyReader(this.headers, this.r);
} else {
// Neither content-length nor transfer-encoding: chunked
this.#body = emptyReader();
}
}
}
return this.#body;
}
async respond(r) {
let err;
try {
// Write our response!
await writeResponse(this.w, r);
} catch (e) {
try {
// Eagerly close on error.
this.conn.close();
} catch {
// Pass
}
err = e;
}
// Signal that this request has been processed and the next pipelined
// request on the same connection can be accepted.
this.#done.resolve(err);
if (err) {
// Error during responding, rethrow.
throw err;
}
}
async finalize() {
if (this.#finalized) return;
// Consume unread body
const body = this.body;
const buf = new Uint8Array(1024);
while(await body.read(buf) !== null){
// Pass
}
this.#finalized = true;
}
}
export class Server {
listener;
#closing;
#connections;
constructor(listener){
this.listener = listener;
this.#closing = false;
this.#connections = [];
}
close() {
this.#closing = true;
this.listener.close();
for (const conn of this.#connections){
try {
conn.close();
} catch (e) {
// Connection might have been already closed
if (!(e instanceof Deno.errors.BadResource)) {
throw e;
}
}
}
}
// Yields all HTTP requests on a single TCP connection.
async *iterateHttpRequests(conn) {
const reader = new BufReader(conn);
const writer = new BufWriter(conn);
while(!this.#closing){
let request;
try {
request = await readRequest(conn, reader);
} catch (error) {
if (error instanceof Deno.errors.InvalidData || error instanceof Deno.errors.UnexpectedEof) {
// An error was thrown while parsing request headers.
// Try to send the "400 Bad Request" before closing the connection.
try {
await writeResponse(writer, {
status: 400,
body: new TextEncoder().encode(`${error.message}\r\n\r\n`)
});
} catch {
// The connection is broken.
}
}
break;
}
if (request === null) {
break;
}
request.w = writer;
yield request;
// Wait for the request to be processed before we accept a new request on
// this connection.
const responseError = await request.done;
if (responseError) {
// Something bad happened during response.
// (likely other side closed during pipelined req)
// req.done implies this connection already closed, so we can just return.
this.untrackConnection(request.conn);
return;
}
try {
// Consume unread body and trailers if receiver didn't consume those data
await request.finalize();
} catch {
break;
}
}
this.untrackConnection(conn);
try {
conn.close();
} catch {
// might have been already closed
}
}
trackConnection(conn) {
this.#connections.push(conn);
}
untrackConnection(conn) {
const index = this.#connections.indexOf(conn);
if (index !== -1) {
this.#connections.splice(index, 1);
}
}
// Accepts a new TCP connection and yields all HTTP requests that arrive on
// it. When a connection is accepted, it also creates a new iterator of the
// same kind and adds it to the request multiplexer so that another TCP
// connection can be accepted.
async *acceptConnAndIterateHttpRequests(mux) {
if (this.#closing) return;
// Wait for a new connection.
let conn;
try {
conn = await this.listener.accept();
} catch (error) {
if (// The listener is closed:
error instanceof Deno.errors.BadResource || // TLS handshake errors:
error instanceof Deno.errors.InvalidData || error instanceof Deno.errors.UnexpectedEof || error instanceof Deno.errors.ConnectionReset) {
return mux.add(this.acceptConnAndIterateHttpRequests(mux));
}
throw error;
}
this.trackConnection(conn);
// Try to accept another connection and add it to the multiplexer.
mux.add(this.acceptConnAndIterateHttpRequests(mux));
// Yield the requests that arrive on the just-accepted connection.
yield* this.iterateHttpRequests(conn);
}
[Symbol.asyncIterator]() {
const mux = new MuxAsyncIterator();
mux.add(this.acceptConnAndIterateHttpRequests(mux));
return mux.iterate();
}
}
/**
* Parse addr from string
*
* const addr = "::1:8000";
* parseAddrFromString(addr);
*
* @param addr Address string
*/ export function _parseAddrFromStr(addr) {
let url;
try {
const host = addr.startsWith(":") ? `0.0.0.0${addr}` : addr;
url = new URL(`http://${host}`);
} catch {
throw new TypeError("Invalid address.");
}
if (url.username || url.password || url.pathname != "/" || url.search || url.hash) {
throw new TypeError("Invalid address.");
}
return {
hostname: url.hostname,
port: url.port === "" ? 80 : Number(url.port)
};
}
/**
* Create a HTTP server
*
* import { serve } from "https://deno.land/std/http/server.ts";
* const body = "Hello World\n";
* const server = serve({ port: 8000 });
* for await (const req of server) {
* req.respond({ body });
* }
*/ export function serve(addr) {
if (typeof addr === "string") {
addr = _parseAddrFromStr(addr);
}
const listener = Deno.listen(addr);
return new Server(listener);
}
/**
* Start an HTTP server with given options and request handler
*
* const body = "Hello World\n";
* const options = { port: 8000 };
* listenAndServe(options, (req) => {
* req.respond({ body });
* });
*
* @param options Server configuration
* @param handler Request handler
*/ export async function listenAndServe(addr, handler) {
const server = serve(addr);
for await (const request of server){
handler(request);
}
}
/**
* Create an HTTPS server with given options
*
* const body = "Hello HTTPS";
* const options = {
* hostname: "localhost",
* port: 443,
* certFile: "./path/to/localhost.crt",
* keyFile: "./path/to/localhost.key",
* };
* for await (const req of serveTLS(options)) {
* req.respond({ body });
* }
*
* @param options Server configuration
* @return Async iterable server instance for incoming requests
*/ export function serveTLS(options) {
const tlsOptions = {
...options,
transport: "tcp"
};
const listener = Deno.listenTls(tlsOptions);
return new Server(listener);
}
/**
* Start an HTTPS server with given options and request handler
*
* const body = "Hello HTTPS";
* const options = {
* hostname: "localhost",
* port: 443,
* certFile: "./path/to/localhost.crt",
* keyFile: "./path/to/localhost.key",
* };
* listenAndServeTLS(options, (req) => {
* req.respond({ body });
* });
*
* @param options Server configuration
* @param handler Request handler
*/ export async function listenAndServeTLS(options, handler) {
const server = serveTLS(options);
for await (const request of server){
handler(request);
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImh0dHBzOi8vZGVuby5sYW5kL3N0ZEAwLjkyLjAvaHR0cC9zZXJ2ZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMTgtMjAyMSB0aGUgRGVubyBhdXRob3JzLiBBbGwgcmlnaHRzIHJlc2VydmVkLiBNSVQgbGljZW5zZS5cbmltcG9ydCB7IEJ1ZlJlYWRlciwgQnVmV3JpdGVyIH0gZnJvbSBcIi4uL2lvL2J1ZmlvLnRzXCI7XG5pbXBvcnQgeyBhc3NlcnQgfSBmcm9tIFwiLi4vX3V0aWwvYXNzZXJ0LnRzXCI7XG5pbXBvcnQgeyBEZWZlcnJlZCwgZGVmZXJyZWQsIE11eEFzeW5jSXRlcmF0b3IgfSBmcm9tIFwiLi4vYXN5bmMvbW9kLnRzXCI7XG5pbXBvcnQge1xuICBib2R5UmVhZGVyLFxuICBjaHVua2VkQm9keVJlYWRlcixcbiAgZW1wdHlSZWFkZXIsXG4gIHJlYWRSZXF1ZXN0LFxuICB3cml0ZVJlc3BvbnNlLFxufSBmcm9tIFwiLi9faW8udHNcIjtcbmV4cG9ydCBjbGFzcyBTZXJ2ZXJSZXF1ZXN0IHtcbiAgdXJsITogc3RyaW5nO1xuICBtZXRob2QhOiBzdHJpbmc7XG4gIHByb3RvITogc3RyaW5nO1xuICBwcm90b01pbm9yITogbnVtYmVyO1xuICBwcm90b01ham9yITogbnVtYmVyO1xuICBoZWFkZXJzITogSGVhZGVycztcbiAgY29ubiE6IERlbm8uQ29ubjtcbiAgciE6IEJ1ZlJlYWRlcjtcbiAgdyE6IEJ1ZldyaXRlcjtcblxuICAjZG9uZTogRGVmZXJyZWQ8RXJyb3IgfCB1bmRlZmluZWQ+ID0gZGVmZXJyZWQoKTtcbiAgI2NvbnRlbnRMZW5ndGg/OiBudW1iZXIgfCBudWxsID0gdW5kZWZpbmVkO1xuICAjYm9keT86IERlbm8uUmVhZGVyID0gdW5kZWZpbmVkO1xuICAjZmluYWxpemVkID0gZmFsc2U7XG5cbiAgZ2V0IGRvbmUoKTogUHJvbWlzZTxFcnJvciB8IHVuZGVmaW5lZD4ge1xuICAgIHJldHVybiB0aGlzLiNkb25lLnRoZW4oKGUpID0+IGUpO1xuICB9XG5cbiAgLyoqXG4gICAqIFZhbHVlIG9mIENvbnRlbnQtTGVuZ3RoIGhlYWRlci5cbiAgICogSWYgbnVsbCwgdGhlbiBjb250ZW50IGxlbmd0aCBpcyBpbnZhbGlkIG9yIG5vdCBnaXZlbiAoZS5nLiBjaHVua2VkIGVuY29kaW5nKS5cbiAgICovXG4gIGdldCBjb250ZW50TGVuZ3RoKCk6IG51bWJlciB8IG51bGwge1xuICAgIC8vIHVuZGVmaW5lZCBtZWFucyBub3QgY2FjaGVkLlxuICAgIC8vIG51bGwgbWVhbnMgaW52YWxpZCBvciBub3QgcHJvdmlkZWQuXG4gICAgaWYgKHRoaXMuI2NvbnRlbnRMZW5ndGggPT09IHVuZGVmaW5lZCkge1xuICAgICAgY29uc3QgY2wgPSB0aGlzLmhlYWRlcnMuZ2V0KFwiY29udGVudC1sZW5ndGhcIik7XG4gICAgICBpZiAoY2wpIHtcbiAgICAgICAgdGhpcy4jY29udGVudExlbmd0aCA9IHBhcnNlSW50KGNsKTtcbiAgICAgICAgLy8gQ29udmVydCBOYU4gdG8gbnVsbCAoYXMgTmFOIGhhcmRlciB0byB0ZXN0KVxuICAgICAgICBpZiAoTnVtYmVyLmlzTmFOKHRoaXMuI2NvbnRlbnRMZW5ndGgpKSB7XG4gICAgICAgICAgdGhpcy4jY29udGVudExlbmd0aCA9IG51bGw7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuI2NvbnRlbnRMZW5ndGggPSBudWxsO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gdGhpcy4jY29udGVudExlbmd0aDtcbiAgfVxuXG4gIC8qKlxuICAgKiBCb2R5IG9mIHRoZSByZXF1ZXN0LiAgVGhlIGVhc2llc3Qgd2F5IHRvIGNvbnN1bWUgdGhlIGJvZHkgaXM6XG4gICAqXG4gICAqICAgICBjb25zdCBidWY6IFVpbnQ4QXJyYXkgPSBhd2FpdCByZWFkQWxsKHJlcS5ib2R5KTtcbiAgICovXG4gIGdldCBib2R5KCk6IERlbm8uUmVhZGVyIHtcbiAgICBpZiAoIXRoaXMuI2JvZHkpIHtcbiAgICAgIGlmICh0aGlzLmNvbnRlbnRMZW5ndGggIT0gbnVsbCkge1xuICAgICAgICB0aGlzLiNib2R5ID0gYm9keVJlYWRlcih0aGlzLmNvbnRlbnRMZW5ndGgsIHRoaXMucik7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zdCB0cmFuc2ZlckVuY29kaW5nID0gdGhpcy5oZWFkZXJzLmdldChcInRyYW5zZmVyLWVuY29kaW5nXCIpO1xuICAgICAgICBpZiAodHJhbnNmZXJFbmNvZGluZyAhPSBudWxsKSB7XG4gICAgICAgICAgY29uc3QgcGFydHMgPSB0cmFuc2ZlckVuY29kaW5nXG4gICAgICAgICAgICAuc3BsaXQoXCIsXCIpXG4gICAgICAgICAgICAubWFwKChlKTogc3RyaW5nID0+IGUudHJpbSgpLnRvTG93ZXJDYXNlKCkpO1xuICAgICAgICAgIGFzc2VydChcbiAgICAgICAgICAgIHBhcnRzLmluY2x1ZGVzKFwiY2h1bmtlZFwiKSxcbiAgICAgICAgICAgICd0cmFuc2Zlci1lbmNvZGluZyBtdXN0IGluY2x1ZGUgXCJjaHVua2VkXCIgaWYgY29udGVudC1sZW5ndGggaXMgbm90IHNldCcsXG4gICAgICAgICAgKTtcbiAgICAgICAgICB0aGlzLiNib2R5ID0gY2h1bmtlZEJvZHlSZWFkZXIodGhpcy5oZWFkZXJzLCB0aGlzLnIpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIE5laXRoZXIgY29udGVudC1sZW5ndGggbm9yIHRyYW5zZmVyLWVuY29kaW5nOiBjaHVua2VkXG4gICAgICAgICAgdGhpcy4jYm9keSA9IGVtcHR5UmVhZGVyKCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuI2JvZHk7XG4gIH1cblxuICBhc3luYyByZXNwb25kKHI6IFJlc3BvbnNlKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgbGV0IGVycjogRXJyb3IgfCB1bmRlZmluZWQ7XG4gICAgdHJ5IHtcbiAgICAgIC8vIFdyaXRlIG91ciByZXNwb25zZSFcbiAgICAgIGF3YWl0IHdyaXRlUmVzcG9uc2UodGhpcy53LCByKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICB0cnkge1xuICAgICAgICAvLyBFYWdlcmx5IGNsb3NlIG9uIGVycm9yLlxuICAgICAgICB0aGlzLmNvbm4uY2xvc2UoKTtcbiAgICAgIH0gY2F0Y2gge1xuICAgICAgICAvLyBQYXNzXG4gICAgICB9XG4gICAgICBlcnIgPSBlO1xuICAgIH1cbiAgICAvLyBTaWduYWwgdGhhdCB0aGlzIHJlcXVlc3QgaGFzIGJlZW4gcHJvY2Vzc2VkIGFuZCB0aGUgbmV4dCBwaXBlbGluZWRcbiAgICAvLyByZXF1ZXN0IG9uIHRoZSBzYW1lIGNvbm5lY3Rpb24gY2FuIGJlIGFjY2VwdGVkLlxuICAgIHRoaXMuI2RvbmUucmVzb2x2ZShlcnIpO1xuICAgIGlmIChlcnIpIHtcbiAgICAgIC8vIEVycm9yIGR1cmluZyByZXNwb25kaW5nLCByZXRocm93LlxuICAgICAgdGhyb3cgZXJyO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGZpbmFsaXplKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICh0aGlzLiNmaW5hbGl6ZWQpIHJldHVybjtcbiAgICAvLyBDb25zdW1lIHVucmVhZCBib2R5XG4gICAgY29uc3QgYm9keSA9IHRoaXMuYm9keTtcbiAgICBjb25zdCBidWYgPSBuZXcgVWludDhBcnJheSgxMDI0KTtcbiAgICB3aGlsZSAoKGF3YWl0IGJvZHkucmVhZChidWYpKSAhPT0gbnVsbCkge1xuICAgICAgLy8gUGFzc1xuICAgIH1cbiAgICB0aGlzLiNmaW5hbGl6ZWQgPSB0cnVlO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBTZXJ2ZXIgaW1wbGVtZW50cyBBc3luY0l0ZXJhYmxlPFNlcnZlclJlcXVlc3Q+IHtcbiAgI2Nsb3NpbmcgPSBmYWxzZTtcbiAgI2Nvbm5lY3Rpb25zOiBEZW5vLkNvbm5bXSA9IFtdO1xuXG4gIGNvbnN0cnVjdG9yKHB1YmxpYyBsaXN0ZW5lcjogRGVuby5MaXN0ZW5lcikge31cblxuICBjbG9zZSgpOiB2b2lkIHtcbiAgICB0aGlzLiNjbG9zaW5nID0gdHJ1ZTtcbiAgICB0aGlzLmxpc3RlbmVyLmNsb3NlKCk7XG4gICAgZm9yIChjb25zdCBjb25uIG9mIHRoaXMuI2Nvbm5lY3Rpb25zKSB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25uLmNsb3NlKCk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIC8vIENvbm5lY3Rpb24gbWlnaHQgaGF2ZSBiZWVuIGFscmVhZHkgY2xvc2VkXG4gICAgICAgIGlmICghKGUgaW5zdGFuY2VvZiBEZW5vLmVycm9ycy5CYWRSZXNvdXJjZSkpIHtcbiAgICAgICAgICB0aHJvdyBlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLy8gWWllbGRzIGFsbCBIVFRQIHJlcXVlc3RzIG9uIGEgc2luZ2xlIFRDUCBjb25uZWN0aW9uLlxuICBwcml2YXRlIGFzeW5jICppdGVyYXRlSHR0cFJlcXVlc3RzKFxuICAgIGNvbm46IERlbm8uQ29ubixcbiAgKTogQXN5bmNJdGVyYWJsZUl0ZXJhdG9yPFNlcnZlclJlcXVlc3Q+IHtcbiAgICBjb25zdCByZWFkZXIgPSBuZXcgQnVmUmVhZGVyKGNvbm4pO1xuICAgIGNvbnN0IHdyaXRlciA9IG5ldyBCdWZXcml0ZXIoY29ubik7XG5cbiAgICB3aGlsZSAoIXRoaXMuI2Nsb3NpbmcpIHtcbiAgICAgIGxldCByZXF1ZXN0OiBTZXJ2ZXJSZXF1ZXN0IHwgbnVsbDtcbiAgICAgIHRyeSB7XG4gICAgICAgIHJlcXVlc3QgPSBhd2FpdCByZWFkUmVxdWVzdChjb25uLCByZWFkZXIpO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgaWYgKFxuICAgICAgICAgIGVycm9yIGluc3RhbmNlb2YgRGVuby5lcnJvcnMuSW52YWxpZERhdGEgfHxcbiAgICAgICAgICBlcnJvciBpbnN0YW5jZW9mIERlbm8uZXJyb3JzLlVuZXhwZWN0ZWRFb2ZcbiAgICAgICAgKSB7XG4gICAgICAgICAgLy8gQW4gZXJyb3Igd2FzIHRocm93biB3aGlsZSBwYXJzaW5nIHJlcXVlc3QgaGVhZGVycy5cbiAgICAgICAgICAvLyBUcnkgdG8gc2VuZCB0aGUgXCI0MDAgQmFkIFJlcXVlc3RcIiBiZWZvcmUgY2xvc2luZyB0aGUgY29ubmVjdGlvbi5cbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXdhaXQgd3JpdGVSZXNwb25zZSh3cml0ZXIsIHtcbiAgICAgICAgICAgICAgc3RhdHVzOiA0MDAsXG4gICAgICAgICAgICAgIGJvZHk6IG5ldyBUZXh0RW5jb2RlcigpLmVuY29kZShgJHtlcnJvci5tZXNzYWdlfVxcclxcblxcclxcbmApLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgICAvLyBUaGUgY29ubmVjdGlvbiBpcyBicm9rZW4uXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgaWYgKHJlcXVlc3QgPT09IG51bGwpIHtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG5cbiAgICAgIHJlcXVlc3QudyA9IHdyaXRlcjtcbiAgICAgIHlpZWxkIHJlcXVlc3Q7XG5cbiAgICAgIC8vIFdhaXQgZm9yIHRoZSByZXF1ZXN0IHRvIGJlIHByb2Nlc3NlZCBiZWZvcmUgd2UgYWNjZXB0IGEgbmV3IHJlcXVlc3Qgb25cbiAgICAgIC8vIHRoaXMgY29ubmVjdGlvbi5cbiAgICAgIGNvbnN0IHJlc3BvbnNlRXJyb3IgPSBhd2FpdCByZXF1ZXN0LmRvbmU7XG4gICAgICBpZiAocmVzcG9uc2VFcnJvcikge1xuICAgICAgICAvLyBTb21ldGhpbmcgYmFkIGhhcHBlbmVkIGR1cmluZyByZXNwb25zZS5cbiAgICAgICAgLy8gKGxpa2VseSBvdGhlciBzaWRlIGNsb3NlZCBkdXJpbmcgcGlwZWxpbmVkIHJlcSlcbiAgICAgICAgLy8gcmVxLmRvbmUgaW1wbGllcyB0aGlzIGNvbm5lY3Rpb24gYWxyZWFkeSBjbG9zZWQsIHNvIHdlIGNhbiBqdXN0IHJldHVybi5cbiAgICAgICAgdGhpcy51bnRyYWNrQ29ubmVjdGlvbihyZXF1ZXN0LmNvbm4pO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIHRyeSB7XG4gICAgICAgIC8vIENvbnN1bWUgdW5yZWFkIGJvZHkgYW5kIHRyYWlsZXJzIGlmIHJlY2VpdmVyIGRpZG4ndCBjb25zdW1lIHRob3NlIGRhdGFcbiAgICAgICAgYXdhaXQgcmVxdWVzdC5maW5hbGl6ZSgpO1xuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIC8vIEludmFsaWQgZGF0YSB3YXMgcmVjZWl2ZWQgb3IgdGhlIGNvbm5lY3Rpb24gd2FzIGNsb3NlZC5cbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuXG4gICAgdGhpcy51bnRyYWNrQ29ubmVjdGlvbihjb25uKTtcbiAgICB0cnkge1xuICAgICAgY29ubi5jbG9zZSgpO1xuICAgIH0gY2F0Y2gge1xuICAgICAgLy8gbWlnaHQgaGF2ZSBiZWVuIGFscmVhZHkgY2xvc2VkXG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSB0cmFja0Nvbm5lY3Rpb24oY29ubjogRGVuby5Db25uKTogdm9pZCB7XG4gICAgdGhpcy4jY29ubmVjdGlvbnMucHVzaChjb25uKTtcbiAgfVxuXG4gIHByaXZhdGUgdW50cmFja0Nvbm5lY3Rpb24oY29ubjogRGVuby5Db25uKTogdm9pZCB7XG4gICAgY29uc3QgaW5kZXggPSB0aGlzLiNjb25uZWN0aW9ucy5pbmRleE9mKGNvbm4pO1xuICAgIGlmIChpbmRleCAhPT0gLTEpIHtcbiAgICAgIHRoaXMuI2Nvbm5lY3Rpb25zLnNwbGljZShpbmRleCwgMSk7XG4gICAgfVxuICB9XG5cbiAgLy8gQWNjZXB0cyBhIG5ldyBUQ1AgY29ubmVjdGlvbiBhbmQgeWllbGRzIGFsbCBIVFRQIHJlcXVlc3RzIHRoYXQgYXJyaXZlIG9uXG4gIC8vIGl0LiBXaGVuIGEgY29ubmVjdGlvbiBpcyBhY2NlcHRlZCwgaXQgYWxzbyBjcmVhdGVzIGEgbmV3IGl0ZXJhdG9yIG9mIHRoZVxuICAvLyBzYW1lIGtpbmQgYW5kIGFkZHMgaXQgdG8gdGhlIHJlcXVlc3QgbXVsdGlwbGV4ZXIgc28gdGhhdCBhbm90aGVyIFRDUFxuICAvLyBjb25uZWN0aW9uIGNhbiBiZSBhY2NlcHRlZC5cbiAgcHJpdmF0ZSBhc3luYyAqYWNjZXB0Q29ubkFuZEl0ZXJhdGVIdHRwUmVxdWVzdHMoXG4gICAgbXV4OiBNdXhBc3luY0l0ZXJhdG9yPFNlcnZlclJlcXVlc3Q+LFxuICApOiBBc3luY0l0ZXJhYmxlSXRlcmF0b3I8U2VydmVyUmVxdWVzdD4ge1xuICAgIGlmICh0aGlzLiNjbG9zaW5nKSByZXR1cm47XG4gICAgLy8gV2FpdCBmb3IgYSBuZXcgY29ubmVjdGlvbi5cbiAgICBsZXQgY29ubjogRGVuby5Db25uO1xuICAgIHRyeSB7XG4gICAgICBjb25uID0gYXdhaXQgdGhpcy5saXN0ZW5lci5hY2NlcHQoKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgaWYgKFxuICAgICAgICAvLyBUaGUgbGlzdGVuZXIgaXMgY2xvc2VkOlxuICAgICAgICBlcnJvciBpbnN0YW5jZW9mIERlbm8uZXJyb3JzLkJhZFJlc291cmNlIHx8XG4gICAgICAgIC8vIFRMUyBoYW5kc2hha2UgZXJyb3JzOlxuICAgICAgICBlcnJvciBpbnN0YW5jZW9mIERlbm8uZXJyb3JzLkludmFsaWREYXRhIHx8XG4gICAgICAgIGVycm9yIGluc3RhbmNlb2YgRGVuby5lcnJvcnMuVW5leHBlY3RlZEVvZiB8fFxuICAgICAgICBlcnJvciBpbnN0YW5jZW9mIERlbm8uZXJyb3JzLkNvbm5lY3Rpb25SZXNldFxuICAgICAgKSB7XG4gICAgICAgIHJldHVybiBtdXguYWRkKHRoaXMuYWNjZXB0Q29ubkFuZEl0ZXJhdGVIdHRwUmVxdWVzdHMobXV4KSk7XG4gICAgICB9XG4gICAgICB0aHJvdyBlcnJvcjtcbiAgICB9XG4gICAgdGhpcy50cmFja0Nvbm5lY3Rpb24oY29ubik7XG4gICAgLy8gVHJ5IHRvIGFjY2VwdCBhbm90aGVyIGNvbm5lY3Rpb24gYW5kIGFkZCBpdCB0byB0aGUgbXVsdGlwbGV4ZXIuXG4gICAgbXV4LmFkZCh0aGlzLmFjY2VwdENvbm5BbmRJdGVyYXRlSHR0cFJlcXVlc3RzKG11eCkpO1xuICAgIC8vIFlpZWxkIHRoZSByZXF1ZXN0cyB0aGF0IGFycml2ZSBvbiB0aGUganVzdC1hY2NlcHRlZCBjb25uZWN0aW9uLlxuICAgIHlpZWxkKiB0aGlzLml0ZXJhdGVIdHRwUmVxdWVzdHMoY29ubik7XG4gIH1cblxuICBbU3ltYm9sLmFzeW5jSXRlcmF0b3JdKCk6IEFzeW5jSXRlcmFibGVJdGVyYXRvcjxTZXJ2ZXJSZXF1ZXN0PiB7XG4gICAgY29uc3QgbXV4OiBNdXhBc3luY0l0ZXJhdG9yPFNlcnZlclJlcXVlc3Q+ID0gbmV3IE11eEFzeW5jSXRlcmF0b3IoKTtcbiAgICBtdXguYWRkKHRoaXMuYWNjZXB0Q29ubkFuZEl0ZXJhdGVIdHRwUmVxdWVzdHMobXV4KSk7XG4gICAgcmV0dXJuIG11eC5pdGVyYXRlKCk7XG4gIH1cbn1cblxuLyoqIE9wdGlvbnMgZm9yIGNyZWF0aW5nIGFuIEhUVFAgc2VydmVyLiAqL1xuZXhwb3J0IHR5cGUgSFRUUE9wdGlvbnMgPSBPbWl0PERlbm8uTGlzdGVuT3B0aW9ucywgXCJ0cmFuc3BvcnRcIj47XG5cbi8qKlxuICogUGFyc2UgYWRkciBmcm9tIHN0cmluZ1xuICpcbiAqICAgICBjb25zdCBhZGRyID0gXCI6OjE6ODAwMFwiO1xuICogICAgIHBhcnNlQWRkckZyb21TdHJpbmcoYWRkcik7XG4gKlxuICogQHBhcmFtIGFkZHIgQWRkcmVzcyBzdHJpbmdcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIF9wYXJzZUFkZHJGcm9tU3RyKGFkZHI6IHN0cmluZyk6IEhUVFBPcHRpb25zIHtcbiAgbGV0IHVybDogVVJMO1xuICB0cnkge1xuICAgIGNvbnN0IGhvc3QgPSBhZGRyLnN0YXJ0c1dpdGgoXCI6XCIpID8gYDAuMC4wLjAke2FkZHJ9YCA6IGFkZHI7XG4gICAgdXJsID0gbmV3IFVSTChgaHR0cDovLyR7aG9zdH1gKTtcbiAgfSBjYXRjaCB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcihcIkludmFsaWQgYWRkcmVzcy5cIik7XG4gIH1cbiAgaWYgKFxuICAgIHVybC51c2VybmFtZSB8fFxuICAgIHVybC5wYXNzd29yZCB8fFxuICAgIHVybC5wYXRobmFtZSAhPSBcIi9cIiB8fFxuICAgIHVybC5zZWFyY2ggfHxcbiAgICB1cmwuaGFzaFxuICApIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKFwiSW52YWxpZCBhZGRyZXNzLlwiKTtcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgaG9zdG5hbWU6IHVybC5ob3N0bmFtZSxcbiAgICBwb3J0OiB1cmwucG9ydCA9PT0gXCJcIiA/IDgwIDogTnVtYmVyKHVybC5wb3J0KSxcbiAgfTtcbn1cblxuLyoqXG4gKiBDcmVhdGUgYSBIVFRQIHNlcnZlclxuICpcbiAqICAgICBpbXBvcnQgeyBzZXJ2ZSB9IGZyb20gXCJodHRwczovL2Rlbm8ubGFuZC9zdGQvaHR0cC9zZXJ2ZXIudHNcIjtcbiAqICAgICBjb25zdCBib2R5ID0gXCJIZWxsbyBXb3JsZFxcblwiO1xuICogICAgIGNvbnN0IHNlcnZlciA9IHNlcnZlKHsgcG9ydDogODAwMCB9KTtcbiAqICAgICBmb3IgYXdhaXQgKGNvbnN0IHJlcSBvZiBzZXJ2ZXIpIHtcbiAqICAgICAgIHJlcS5yZXNwb25kKHsgYm9keSB9KTtcbiAqICAgICB9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzZXJ2ZShhZGRyOiBzdHJpbmcgfCBIVFRQT3B0aW9ucyk6IFNlcnZlciB7XG4gIGlmICh0eXBlb2YgYWRkciA9PT0gXCJzdHJpbmdcIikge1xuICAgIGFkZHIgPSBfcGFyc2VBZGRyRnJvbVN0cihhZGRyKTtcbiAgfVxuXG4gIGNvbnN0IGxpc3RlbmVyID0gRGVuby5saXN0ZW4oYWRkcik7XG4gIHJldHVybiBuZXcgU2VydmVyKGxpc3RlbmVyKTtcbn1cblxuLyoqXG4gKiBTdGFydCBhbiBIVFRQIHNlcnZlciB3aXRoIGdpdmVuIG9wdGlvbnMgYW5kIHJlcXVlc3QgaGFuZGxlclxuICpcbiAqICAgICBjb25zdCBib2R5ID0gXCJIZWxsbyBXb3JsZFxcblwiO1xuICogICAgIGNvbnN0IG9wdGlvbnMgPSB7IHBvcnQ6IDgwMDAgfTtcbiAqICAgICBsaXN0ZW5BbmRTZXJ2ZShvcHRpb25zLCAocmVxKSA9PiB7XG4gKiAgICAgICByZXEucmVzcG9uZCh7IGJvZHkgfSk7XG4gKiAgICAgfSk7XG4gKlxuICogQHBhcmFtIG9wdGlvbnMgU2VydmVyIGNvbmZpZ3VyYXRpb25cbiAqIEBwYXJhbSBoYW5kbGVyIFJlcXVlc3QgaGFuZGxlclxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbGlzdGVuQW5kU2VydmUoXG4gIGFkZHI6IHN0cmluZyB8IEhUVFBPcHRpb25zLFxuICBoYW5kbGVyOiAocmVxOiBTZXJ2ZXJSZXF1ZXN0KSA9PiB2b2lkLFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IHNlcnZlciA9IHNlcnZlKGFkZHIpO1xuXG4gIGZvciBhd2FpdCAoY29uc3QgcmVxdWVzdCBvZiBzZXJ2ZXIpIHtcbiAgICBoYW5kbGVyKHJlcXVlc3QpO1xuICB9XG59XG5cbi8qKiBPcHRpb25zIGZvciBjcmVhdGluZyBhbiBIVFRQUyBzZXJ2ZXIuICovXG5leHBvcnQgdHlwZSBIVFRQU09wdGlvbnMgPSBPbWl0PERlbm8uTGlzdGVuVGxzT3B0aW9ucywgXCJ0cmFuc3BvcnRcIj47XG5cbi8qKlxuICogQ3JlYXRlIGFuIEhUVFBTIHNlcnZlciB3aXRoIGdpdmVuIG9wdGlvbnNcbiAqXG4gKiAgICAgY29uc3QgYm9keSA9IFwiSGVsbG8gSFRUUFNcIjtcbiAqICAgICBjb25zdCBvcHRpb25zID0ge1xuICogICAgICAgaG9zdG5hbWU6IFwibG9jYWxob3N0XCIsXG4gKiAgICAgICBwb3J0OiA0NDMsXG4gKiAgICAgICBjZXJ0RmlsZTogXCIuL3BhdGgvdG8vbG9jYWxob3N0LmNydFwiLFxuICogICAgICAga2V5RmlsZTogXCIuL3BhdGgvdG8vbG9jYWxob3N0LmtleVwiLFxuICogICAgIH07XG4gKiAgICAgZm9yIGF3YWl0IChjb25zdCByZXEgb2Ygc2VydmVUTFMob3B0aW9ucykpIHtcbiAqICAgICAgIHJlcS5yZXNwb25kKHsgYm9keSB9KTtcbiAqICAgICB9XG4gKlxuICogQHBhcmFtIG9wdGlvbnMgU2VydmVyIGNvbmZpZ3VyYXRpb25cbiAqIEByZXR1cm4gQXN5bmMgaXRlcmFibGUgc2VydmVyIGluc3RhbmNlIGZvciBpbmNvbWluZyByZXF1ZXN0c1xuICovXG5leHBvcnQgZnVuY3Rpb24gc2VydmVUTFMob3B0aW9uczogSFRUUFNPcHRpb25zKTogU2VydmVyIHtcbiAgY29uc3QgdGxzT3B0aW9uczogRGVuby5MaXN0ZW5UbHNPcHRpb25zID0ge1xuICAgIC4uLm9wdGlvbnMsXG4gICAgdHJhbnNwb3J0OiBcInRjcFwiLFxuICB9O1xuICBjb25zdCBsaXN0ZW5lciA9IERlbm8ubGlzdGVuVGxzKHRsc09wdGlvbnMpO1xuICByZXR1cm4gbmV3IFNlcnZlcihsaXN0ZW5lcik7XG59XG5cbi8qKlxuICogU3RhcnQgYW4gSFRUUFMgc2VydmVyIHdpdGggZ2l2ZW4gb3B0aW9ucyBhbmQgcmVxdWVzdCBoYW5kbGVyXG4gKlxuICogICAgIGNvbnN0IGJvZHkgPSBcIkhlbGxvIEhUVFBTXCI7XG4gKiAgICAgY29uc3Qgb3B0aW9ucyA9IHtcbiAqICAgICAgIGhvc3RuYW1lOiBcImxvY2FsaG9zdFwiLFxuICogICAgICAgcG9ydDogNDQzLFxuICogICAgICAgY2VydEZpbGU6IFwiLi9wYXRoL3RvL2xvY2FsaG9zdC5jcnRcIixcbiAqICAgICAgIGtleUZpbGU6IFwiLi9wYXRoL3RvL2xvY2FsaG9zdC5rZXlcIixcbiAqICAgICB9O1xuICogICAgIGxpc3RlbkFuZFNlcnZlVExTKG9wdGlvbnMsIChyZXEpID0+IHtcbiAqICAgICAgIHJlcS5yZXNwb25kKHsgYm9keSB9KTtcbiAqICAgICB9KTtcbiAqXG4gKiBAcGFyYW0gb3B0aW9ucyBTZXJ2ZXIgY29uZmlndXJhdGlvblxuICogQHBhcmFtIGhhbmRsZXIgUmVxdWVzdCBoYW5kbGVyXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBsaXN0ZW5BbmRTZXJ2ZVRMUyhcbiAgb3B0aW9uczogSFRUUFNPcHRpb25zLFxuICBoYW5kbGVyOiAocmVxOiBTZXJ2ZXJSZXF1ZXN0KSA9PiB2b2lkLFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IHNlcnZlciA9IHNlcnZlVExTKG9wdGlvbnMpO1xuXG4gIGZvciBhd2FpdCAoY29uc3QgcmVxdWVzdCBvZiBzZXJ2ZXIpIHtcbiAgICBoYW5kbGVyKHJlcXVlc3QpO1xuICB9XG59XG5cbi8qKlxuICogSW50ZXJmYWNlIG9mIEhUVFAgc2VydmVyIHJlc3BvbnNlLlxuICogSWYgYm9keSBpcyBhIFJlYWRlciwgcmVzcG9uc2Ugd291bGQgYmUgY2h1bmtlZC5cbiAqIElmIGJvZHkgaXMgYSBzdHJpbmcsIGl0IHdvdWxkIGJlIFVURi04IGVuY29kZWQgYnkgZGVmYXVsdC5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBSZXNwb25zZSB7XG4gIHN0YXR1cz86IG51bWJlcjtcbiAgaGVhZGVycz86IEhlYWRlcnM7XG4gIGJvZHk/OiBVaW50OEFycmF5IHwgRGVuby5SZWFkZXIgfCBzdHJpbmc7XG4gIHRyYWlsZXJzPzogKCkgPT4gUHJvbWlzZTxIZWFkZXJzPiB8IEhlYWRlcnM7XG59XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsMEVBQTBFO0FBQzFFLFNBQVMsU0FBUyxFQUFFLFNBQVMsUUFBUSxpQkFBaUI7QUFDdEQsU0FBUyxNQUFNLFFBQVEscUJBQXFCO0FBQzVDLFNBQW1CLFFBQVEsRUFBRSxnQkFBZ0IsUUFBUSxrQkFBa0I7QUFDdkUsU0FDRSxVQUFVLEVBQ1YsaUJBQWlCLEVBQ2pCLFdBQVcsRUFDWCxXQUFXLEVBQ1gsYUFBYSxRQUNSLFdBQVc7QUFDbEIsT0FBTyxNQUFNO0lBQ1gsSUFB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