net#AddressInfo TypeScript Examples
The following examples show how to use
net#AddressInfo.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: portFinder.ts From Assistive-Webdriver with MIT License | 7 votes |
/**
* Returns a free TCP port on the local IP address specified by the host parameter.
*
* @param host - Specifies the local host name or IP address to use when looking for a free TCP port.
* @returns An unused port number chosen by the operating system.
*
* @remarks
*
* This function internally creates a TCP server with port 0 so that,
* as mentioned in the {@link https://nodejs.org/dist/latest-v15.x/docs/api/net.html#net_server_listen_port_host_backlog_callback | node.js documentation},
* the operating system can assign an arbitrary unused port.
* The function then closes the server and returns the port number.
* @public
*/
export async function getFreePort({ host }: { host: string }): Promise<number> {
const server = createServer();
await new Promise<void>((resolve, reject) =>
server.listen(0, host, resolve).on("error", reject)
);
const address = server.address() as AddressInfo;
const port = address.port;
await new Promise(resolve => server.close(resolve));
return port;
}
Example #2
Source File: utils.ts From opentelemetry-ext-js with Apache License 2.0 | 6 votes |
createServer = (callback: (server: Server, port: number) => void) => {
const server = http.createServer();
const sio = createServerInstance(server);
server.listen(0, () => {
const port = (server.address() as AddressInfo).port;
callback(sio, port);
});
}
Example #3
Source File: index.ts From type-graphql-dataloader with MIT License | 6 votes |
export async function listen(
port: number,
resolvers: NonEmptyArray<Function>
): Promise<ListenResult> {
const app = express();
const schema = await buildSchema({
resolvers,
});
const apollo = new ApolloServer({
schema,
plugins: [
ApolloServerLoaderPlugin({
typeormGetConnection: getConnection,
}),
],
});
await apollo.start();
apollo.applyMiddleware({ app, cors: false });
const server = http.createServer(app);
await promisify(server.listen.bind(server, port))();
return {
port: (server.address() as AddressInfo).port,
close: promisify(server.close).bind(server),
};
}
Example #4
Source File: hooks.ts From electron-browser-shell with GNU General Public License v3.0 | 6 votes |
useServer = () => {
const emptyPage = '<script>console.log("loaded")</script>'
// NB. extensions are only allowed on http://, https:// and ftp:// (!) urls by default.
let server: http.Server
let url: string
before(async () => {
server = http.createServer((req, res) => {
res.end(emptyPage)
})
await new Promise<void>((resolve) =>
server.listen(0, '127.0.0.1', () => {
url = `http://127.0.0.1:${(server.address() as AddressInfo).port}`
resolve()
})
)
})
after(() => {
server.close()
})
return {
getUrl: () => url,
}
}
Example #5
Source File: next-fixture.ts From dendron with GNU Affero General Public License v3.0 | 6 votes |
test = base.extend<{
port: string;
}>({
port: [
// eslint-disable-next-line no-empty-pattern
async ({}, use) => {
const app = next({
dev: false,
dir: path.resolve(__dirname, ".."),
});
await app.prepare();
const handle = app.getRequestHandler();
// start next server on arbitrary port
const server: Server = await new Promise((resolve) => {
const server = createServer((req, res) => {
if (req.url) {
const parsedUrl = parse(req.url, true);
handle(req, res, parsedUrl);
}
});
server.listen((error: any) => {
if (error) throw error;
resolve(server);
});
});
// get the randomly assigned port from the server
const port = String((server.address() as AddressInfo).port);
// provide port to tests
await use(port);
},
{
//@ts-ignore
scope: "worker",
},
],
})
Example #6
Source File: utils.ts From fastify-sse-v2 with MIT License | 6 votes |
export function getBaseUrl(fastifyInstance: FastifyInstance): string {
const address = fastifyInstance.server.address() as AddressInfo;
return `http://${address.address}:${address.port}`;
}
Example #7
Source File: index.ts From eth2stats-dashboard with MIT License | 5 votes |
server = app.listen(Number(process.env.PORT) || 3000, process.env.HOST || "127.0.0.1", () => {
let address = server.address() as AddressInfo;
process.stdout.write(`Listening on ${address.address + ":" + address.port}\n`);
opn(`http://localhost:${address.port}`);
})
Example #8
Source File: server.ts From node-boilerplate with Apache License 2.0 | 5 votes |
function serverListening(): void {
const addressInfo: AddressInfo = <AddressInfo>server.address();
logger.info(`Listening on ${addressInfo.address}:${env.port}`);
}
Example #9
Source File: mmdb.ts From posthog-foss with MIT License | 5 votes |
export async function createMmdbServer(serverInstance: MMDBPrepServerInstance): Promise<net.Server> {
status.info('?', 'Starting internal MMDB server...')
const mmdbServer = net.createServer((socket) => {
socket.setEncoding('utf8')
let status: MMDBRequestStatus = MMDBRequestStatus.OK
socket.on('data', (partialData) => {
// partialData SHOULD be an IP address string
let responseData: any
if (status === MMDBRequestStatus.OK) {
if (serverInstance.mmdb) {
try {
responseData = serverInstance.mmdb.city(partialData.toString().trim())
} catch (e) {
responseData = null
}
} else {
captureException(new Error(status))
status = MMDBRequestStatus.ServiceUnavailable
}
}
if (status !== MMDBRequestStatus.OK) {
responseData = status
}
socket.write(serialize(responseData ?? null))
})
socket.setTimeout(MMDB_INTERNAL_SERVER_TIMEOUT_SECONDS * 1000).on('timeout', () => {
captureException(new Error(status))
status = MMDBRequestStatus.TimedOut
socket.emit('end')
})
socket.once('end', () => {
if (status !== MMDBRequestStatus.OK) {
socket.write(serialize(status))
}
socket.destroy()
})
})
mmdbServer.on('error', (error) => {
captureException(error)
})
return new Promise((resolve, reject) => {
const rejectTimeout = setTimeout(
() => reject(new Error('Internal MMDB server could not start listening!')),
3000
)
mmdbServer.listen(serverInstance.hub.INTERNAL_MMDB_SERVER_PORT, 'localhost', () => {
const port = (mmdbServer.address() as AddressInfo).port
status.info('?', `Internal MMDB server listening on port ${port}`)
clearTimeout(rejectTimeout)
resolve(mmdbServer)
})
})
}
Example #10
Source File: screenReaderMock.ts From Assistive-Webdriver with MIT License | 5 votes |
useScreenReaderMock = () => {
const host = "127.0.0.1";
let server: Server;
let port: number;
let app: WebSocket.Server;
const urlMap = new WeakMap<WebSocket, string>();
beforeAll(async () => {
server = createServer();
app = new WebSocket.Server({ server });
app.on("connection", (ws, req) => urlMap.set(ws, req.url!));
server.listen(0, host);
await new Promise((resolve, reject) =>
server.on("listening", resolve).on("error", reject)
);
port = (server.address() as AddressInfo).port;
});
afterAll(async () => {
await new Promise(resolve => server.close(resolve));
await new Promise(resolve => app.close(resolve));
});
return {
sendScreenReaderMessage(message: string) {
app.clients.forEach(client => client.send(message));
},
getScreenReaderClients() {
const clients: string[] = [];
app.clients.forEach(client => clients.push(urlMap.get(client)!));
return clients;
},
getScreenReaderTCPRedirection(
vmPort = DEFAULT_VM_PORT_SCREENREADER
): PortRedirection {
return {
hostAddress: host,
hostPort: port,
vmPort
};
}
};
}
Example #11
Source File: server.ts From amman with Apache License 2.0 | 5 votes |
static async startServer(
ammanState: AmmanState,
accountProviders: Record<string, AmmanAccountProvider>,
accountRenderers: AmmanAccountRendererMap,
programs: Program[],
accounts: Account[],
loadedAccountInfos: Map<string, AccountInfo<Buffer>>,
loadedKeypairs: Map<string, Keypair>,
accountsFolder: string,
snapshotRoot: string,
killRunning: boolean = true
): Promise<{
app: HttpServer
io: Server
relayServer: RelayServer
}> {
if (killRunning) {
await killRunningServer(AMMAN_RELAY_PORT)
}
const accountProvider = AccountProvider.fromRecord(
accountProviders,
accountRenderers
)
AccountStates.createInstance(
accountProvider.connection,
accountProvider,
loadedAccountInfos,
loadedKeypairs
)
const accountPersister = new AccountPersister(
accountsFolder,
accountProvider.connection
)
const snapshotPersister = new AccountPersister(
snapshotRoot,
accountProvider.connection
)
const programLabels = programs
.filter((x) => x.label != null)
.reduce((acc: Record<string, string>, x) => {
acc[x.programId] = x.label!
return acc
}, {})
const accountLabels = accounts
.filter((x) => x.label != null)
.reduce((acc: Record<string, string>, x) => {
acc[x.accountId] = x.label!
return acc
}, {})
const knownLabels = { ...programLabels, ...accountLabels }
const { app, io, relayServer } = Relay.createApp(
ammanState,
accountProvider,
accountPersister,
snapshotPersister,
AccountStates.instance,
knownLabels
)
return new Promise((resolve, reject) => {
app.on('error', reject).listen(AMMAN_RELAY_PORT, () => {
const addr = app.address() as AddressInfo
const msg = `Amman Relay listening on ${addr.address}:${addr.port}`
logDebug(msg)
resolve({ app, io, relayServer })
})
})
}
Example #12
Source File: inoreader_collection.ts From vscode-rss with MIT License | 5 votes |
private async authorize(): Promise<Token> {
const server = http.createServer().listen(0, '127.0.0.1');
const addr = await new Promise<AddressInfo>(resolve => {
server.on('listening', () => {
resolve(server.address() as AddressInfo);
});
});
const client_id = this.cfg.appid;
const redirect_uri = encodeURIComponent(`http://127.0.0.1:${addr.port}`);
const url = `https://${this.domain}/oauth2/auth?client_id=${client_id}&redirect_uri=${redirect_uri}&response_type=code&scope=read+write&state=1`;
await vscode.env.openExternal(vscode.Uri.parse(url));
const auth_code = await vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: 'Authorizing...',
cancellable: true
}, async (_, token) => new Promise<string>((resolve, reject) => {
const timer = setTimeout(() => {
reject('Authorization Timeout');
server.close();
}, 300000);
token.onCancellationRequested(() => {
reject('Cancelled');
server.close();
clearInterval(timer);
});
server.on('request', (req: IncomingMessage, res: ServerResponse) => {
const query = url_parse(req.url!, true).query;
if (query.code) {
resolve(query.code as string);
res.end('<h1>Authorization Succeeded</h1>');
server.close();
clearInterval(timer);
}
});
}));
const res = await got({
url: `https://${this.domain}/oauth2/token`,
method: 'POST',
form: {
code: auth_code,
redirect_uri: redirect_uri,
client_id: this.cfg.appid,
client_secret: this.cfg.appkey,
grant_type: 'authorization_code',
},
throwHttpErrors: false,
});
const response = JSON.parse(res.body);
if (!response.refresh_token || !response.access_token || !response.expires_in) {
throw Error('Get Token Fail: ' + response.error_description);
}
return {
auth_code: auth_code,
refresh_token: response.refresh_token,
access_token: response.access_token,
expire: new Date().getTime() + response.expires_in * 1000,
};
}
Example #13
Source File: seleniumMock.ts From Assistive-Webdriver with MIT License | 5 votes |
useSeleniumMock = () => {
const host = "127.0.0.1";
let server: Server;
let port: number;
let app;
let routingFn: AsyncFnMock<void, [Koa.Context, () => Promise<any>]>;
beforeAll(async () => {
app = new Koa();
app.use(async (ctx, next) => {
if (routingFn) {
await routingFn(ctx, next);
} else {
await next();
}
});
server = app.listen(0, host);
await new Promise((resolve, reject) =>
server.on("listening", resolve).on("error", reject)
);
port = (server.address() as AddressInfo).port;
});
beforeEach(() => {
routingFn = asyncFnMock("seleniumMock");
});
afterEach(async () => {
const getConnections = promisify(server.getConnections.bind(server));
await waitFor(async () => 0 == (await getConnections()));
});
afterAll(async () => {
await new Promise(resolve => server.close(resolve));
});
return {
async seleniumAnswerRequest(
method: string,
url: string,
responseBody: ((ctx: Koa.Context) => Promise<any>) | Record<string, any>
) {
const call = await routingFn.waitForCall();
const ctx = call.args[0];
expect(ctx.method).toBe(method);
expect(ctx.url).toBe(url);
if (typeof responseBody === "function") {
ctx.body = await responseBody(ctx);
} else {
ctx.body = responseBody;
}
call.result.value.mockResolve();
},
getSeleniumTCPRedirection(
vmPort = DEFAULT_VM_PORT_WEBDRIVER
): PortRedirection {
return {
hostAddress: host,
hostPort: port,
vmPort
};
}
};
}
Example #14
Source File: dev-server.ts From markmap with MIT License | 5 votes |
function setUpServer(
transformer: Transformer,
provider: IContentProvider,
options: IDevelopOptions
) {
let assets = transformer.getAssets();
if (options.toolbar) assets = addToolbar(assets);
const html = `${fillTemplate(
null,
assets
)}<script>(${startServer.toString()})(${options.toolbar ? 60 : 0})</script>`;
const app = new Koa();
app.use(async (ctx, next) => {
if (ctx.path === '/') {
ctx.body = html;
} else if (ctx.path === '/data') {
const update = await provider.getUpdate(ctx.query.ts);
const result =
update.content == null
? null
: transformer.transform(update.content || '');
ctx.body = { ts: update.ts, result, line: update.line };
} else {
await next();
}
});
const handle = app.callback() as http.RequestListener;
const server = http.createServer(handle);
server.listen(() => {
const { port } = server.address() as AddressInfo;
console.info(`Listening at http://localhost:${port}`);
if (options.open) open(`http://localhost:${port}`);
});
let closing: Promise<void>;
return {
provider,
close() {
if (!closing) {
closing = new Promise((resolve, reject) =>
server.close((err?: Error) => {
if (err) reject(err);
else resolve();
})
);
}
return closing;
},
};
}
Example #15
Source File: index.ts From hoprnet with GNU General Public License v3.0 | 5 votes |
/**
* Converts a Node.js address instance to a format that is
* understood by Multiaddr
* @param addr a Node.js address instance
* @returns
*/
export function nodeToMultiaddr(addr: AddressInfo, peerId: PeerId | undefined): Multiaddr {
let address: string
let family: 4 | 6
switch (addr.family) {
case 'IPv4':
family = 4
// Node.js tends answer `socket.address()` calls on `udp4`
// sockets with `::` instead of `0.0.0.0`
if (isAnyAddress(addr.address, 'IPv6')) {
address = '0.0.0.0'
} else {
address = addr.address
}
break
case 'IPv6':
family = 6
// Make sure that we use the right any address,
// even if this is IPv4 any address
if (isAnyAddress(addr.address, 'IPv4')) {
address = '::'
} else {
address = addr.address
}
break
default:
throw Error(`Invalid family. Got ${addr.family}`)
}
let ma = Multiaddr.fromNodeAddress(
{
family,
address,
port: addr.port
},
'tcp'
)
if (peerId != undefined) {
ma = ma.encapsulate(`/p2p/${peerId.toB58String()}`)
}
return ma
}
Example #16
Source File: bin.ts From Assistive-Webdriver with MIT License | 4 votes |
(async function () {
const argv = await yargs.options({
server: {
type: "string",
alias: "s",
default: "http://localhost:3000"
},
browser: {
type: "string",
alias: "b",
default: Browser.CHROME
},
capabilities: {
type: "string",
default: "{}"
},
"log-level": {
type: "string",
alias: "l",
default: "info"
},
"public-host": {
type: "string",
alias: "h",
default: `${hostname()}.`
},
"listen-host": {
type: "string",
default: "0.0.0.0"
},
"public-port": {
type: "number",
alias: "p",
default: 0
},
"listen-port": {
type: "number",
default: 0
},
"vm-config": {
type: "string",
alias: "m"
},
"skip-keys": {
type: "array",
alias: "k"
}
}).argv;
configure({
level: argv["log-level"] as any,
format: format.combine(format.colorize(), format.simple()),
transports: [new transports.Console()]
});
const capabilities = JSON.parse(argv.capabilities);
if (argv["vm-config"]) {
capabilities["awd:vm-config"] = argv["vm-config"];
}
info(`Connecting to webdriver server at ${argv.server}`, {
capabilities,
browser: argv.browser
});
const driver = await new Builder()
.withCapabilities(capabilities)
.forBrowser(argv.browser)
.usingServer(argv.server)
.build();
process.on("SIGINT", async () => {
warn("Closing the session...");
await driver.quit();
error("Tests were interrupted.");
process.exit(2);
});
sigintWin32();
const testerSession = new TesterSession(driver);
try {
const eventsServerAddress = await new Promise<AddressInfo>(
(resolve, reject) =>
testerSession.eventsServer
.listen(
argv["listen-port"] || argv["public-port"],
argv["listen-host"] || argv["public-host"],
() => {
resolve(testerSession.eventsServer.address() as AddressInfo);
}
)
.once("error", reject)
);
const eventsServerListenAddress = `http://${eventsServerAddress.address}:${eventsServerAddress.port}`;
const publicPort = argv["public-port"] || eventsServerAddress.port;
const publicHost = argv["public-host"] || eventsServerAddress.address;
const eventsServerPublicAddress = `http://${publicHost}:${publicPort}`;
info(
`Test page server started at ${eventsServerListenAddress}, VM will connect to ${eventsServerPublicAddress}`
);
try {
await refreshScreenReaderText(testerSession.driver);
info("Screen reader testing is ENABLED.");
addScreenReaderTextListener(testerSession.driver, message =>
info(`Screen reader said: ${message}`)
);
testerSession.screenReader = true;
} catch (error) {
info("Screen reader testing is DISABLED.");
}
info(`Loading test page from ${eventsServerPublicAddress}`);
await driver.get(eventsServerPublicAddress);
const body = await driver.findElement(By.css("body"));
await driver.wait(until.elementTextContains(body, "ready"));
info(`Test page was successfully loaded`);
const testInput = await driver.findElement(By.css("#testInput"));
await driver.actions().click(testInput).perform();
await driver.wait(untilElementHasFocus(testInput), 10000);
if (testerSession.screenReader) {
await clearCachedScreenReaderText(testerSession.driver);
}
await driver.actions().keyDown(Key.TAB).keyUp(Key.TAB).perform();
if (testerSession.screenReader) {
try {
await testerSession.driver.wait(
forScreenReaderToSay("mysupertestlabeltocheck"),
5000
);
} catch (e) {
testerSession.reportError(`Screen reader test failed: ${e}`);
}
}
const testDiv = await driver.findElement(By.css("#testDiv"));
await driver.wait(untilElementHasFocus(testDiv), 10000);
await testerSession.eventsQueue.getAllWaitingValues();
await testAllKeys(testerSession, argv["skip-keys"] as any);
await testMouseButtons(testerSession);
if (testerSession.errorsNumber === 0) {
info("All tests were successful!");
} else {
throw new Error("Some tests failed!");
}
} finally {
testerSession.reportMeasures();
await driver.quit();
testerSession.eventsServer.close();
}
})().catch(e => {
error(`${e}`);
process.exit(1);
});
Example #17
Source File: index.ts From livepeer-com with MIT License | 4 votes |
export default async function makeApp(params: CliArgs) {
const {
storage,
dbPath,
httpPrefix,
port,
postgresUrl,
cloudflareNamespace,
cloudflareAccount,
cloudflareAuth,
listen = true,
clientId,
trustedDomain,
kubeNamespace,
kubeBroadcasterService,
kubeBroadcasterTemplate,
kubeOrchestratorService,
kubeOrchestratorTemplate,
fallbackProxy,
orchestrators,
broadcasters,
prices,
s3Url,
s3UrlExternal,
s3Access,
s3Secret,
upstreamBroadcaster,
insecureTestToken,
amqpUrl,
} = params;
// Storage init
let listener: Server;
let listenPort: number;
const close = async () => {
process.off("SIGTERM", sigterm);
process.off("unhandledRejection", unhandledRejection);
listener.close();
await store.close();
};
// Handle SIGTERM gracefully. It's polite, and Kubernetes likes it.
const sigterm = handleSigterm(close);
process.on("SIGTERM", sigterm);
const unhandledRejection = (err) => {
logger.error("fatal, unhandled promise rejection: ", err);
err.stack && logger.error(err.stack);
sigterm();
};
process.on("unhandledRejection", unhandledRejection);
const appRoute = await appRouter(params).catch((e) => {
console.error("Error on startup");
console.error(e);
throw e;
// process.exit(1)
});
const {
db,
queue,
router,
store,
webhookCannon: webhook,
taskScheduler,
} = appRoute;
const app = express();
const isSilentTest =
process.env.NODE_ENV === "test" && process.argv.indexOf("--silent") > 0;
app.use(
morgan("dev", {
skip: (req, res) => {
if (isSilentTest) {
return true;
}
if (req.path.startsWith("/_next")) {
return true;
}
if (insecureTestToken) {
if (req.originalUrl.includes(insecureTestToken)) {
return true;
}
}
return false;
},
})
);
app.use(router);
if (listen) {
await new Promise<void>((resolve, reject) => {
listener = app.listen(port, () => {
const address = listener.address() as AddressInfo;
listenPort = address.port;
logger.info(
`API server listening on http://0.0.0.0:${listenPort}${httpPrefix}`
);
resolve();
});
listener.on("error", (err) => {
logger.error("Error starting server", err);
reject(err);
});
});
}
return {
...params,
app,
listener,
port: listenPort,
close,
store,
db,
webhook,
taskScheduler,
queue,
};
}
Example #18
Source File: MacUpdater.ts From electron-differential-updater with MIT License | 4 votes |
protected doDownloadUpdate(
downloadUpdateOptions: DownloadUpdateOptions
): Promise<Array<string>> {
this.updateInfoForPendingUpdateDownloadedEvent = null;
const provider = downloadUpdateOptions.updateInfoAndProvider.provider;
const files = downloadUpdateOptions.updateInfoAndProvider.provider.resolveFiles(
downloadUpdateOptions.updateInfoAndProvider.info
);
const zipFileInfo = findFile(files, "zip", ["pkg", "dmg"]);
if (zipFileInfo == null) {
throw newError(
`ZIP file not provided: ${safeStringifyJson(files)}`,
"ERR_UPDATER_ZIP_FILE_NOT_FOUND"
);
}
const server = createServer();
server.on("close", () => {
this._logger.info(
`Proxy server for native Squirrel.Mac is closed (was started to download ${zipFileInfo.url.href})`
);
});
function getServerUrl(): string {
const address = server.address() as AddressInfo;
return `http://127.0.0.1:${address.port}`;
}
return this.executeDownload({
fileExtension: "zip",
fileInfo: zipFileInfo,
downloadUpdateOptions,
task: async (destinationFile, downloadOptions) => {
try {
if (
await this.differentialDownloadInstaller(
zipFileInfo,
downloadUpdateOptions,
destinationFile,
provider
)
) {
await this.httpExecutor.download(
zipFileInfo.url,
destinationFile,
downloadOptions
);
}
} catch (e) {
console.log(e);
}
},
done: async event => {
const downloadedFile = event.downloadedFile;
this.updateInfoForPendingUpdateDownloadedEvent = event;
let updateFileSize = zipFileInfo.info.size;
if (updateFileSize == null) {
updateFileSize = (await stat(downloadedFile)).size;
}
return await new Promise<Array<string>>((resolve, reject) => {
// insecure random is ok
const fileUrl =
"/" + Date.now() + "-" + Math.floor(Math.random() * 9999) + ".zip";
server.on(
"request",
(request: IncomingMessage, response: ServerResponse) => {
const requestUrl = request.url!!;
this._logger.info(`${requestUrl} requested`);
if (requestUrl === "/") {
const data = Buffer.from(
`{ "url": "${getServerUrl()}${fileUrl}" }`
);
response.writeHead(200, {
"Content-Type": "application/json",
"Content-Length": data.length
});
response.end(data);
return;
}
if (!requestUrl.startsWith(fileUrl)) {
this._logger.warn(`${requestUrl} requested, but not supported`);
response.writeHead(404);
response.end();
return;
}
this._logger.info(
`${fileUrl} requested by Squirrel.Mac, pipe ${downloadedFile}`
);
let errorOccurred = false;
response.on("finish", () => {
try {
setImmediate(() => server.close());
} finally {
if (!errorOccurred) {
this.nativeUpdater.removeListener("error", reject);
resolve([]);
}
}
});
const readStream = createReadStream(downloadedFile);
readStream.on("error", error => {
try {
response.end();
} catch (e) {
this._logger.warn(`cannot end response: ${e}`);
}
errorOccurred = true;
this.nativeUpdater.removeListener("error", reject);
reject(new Error(`Cannot pipe "${downloadedFile}": ${error}`));
});
response.writeHead(200, {
"Content-Type": "application/zip",
"Content-Length": updateFileSize
});
readStream.pipe(response);
}
);
server.listen(0, "127.0.0.1", () => {
this.nativeUpdater.setFeedURL({
url: getServerUrl(),
headers: { "Cache-Control": "no-cache" }
});
this.nativeUpdater.once("error", reject);
this.nativeUpdater.checkForUpdates();
});
});
}
});
}
Example #19
Source File: browser.test.ts From blake3 with MIT License | 4 votes |
// Much of the browser code is also used in Node's wasm. We test things more
// thoroughly there because tests are easier to write and debug, these tests
// are primarily for sanity and checking browser-specific behavior.
describe('browser', () => {
const addInputs = `window.inputs = ${JSON.stringify(inputs)}`;
describe('webpack', () => {
const testDir = resolve(tmpdir(), 'blake3-browser-test');
let server: Server;
let browser: Browser;
let page: Page;
/**
* Builds the browser lib into the testDir.
*/
async function buildWebpack() {
try {
mkdirSync(testDir);
} catch {
// already exists, probably
}
writeFileSync(
resolve(testDir, 'entry-src.js'),
`import("blake3/browser").then(b3 => window.blake3 = b3);`,
);
const stats = await new Promise<webpack.Stats>((res, rej) =>
webpack(
{
mode: 'production',
devtool: 'source-map',
entry: resolve(testDir, 'entry-src.js'),
output: {
path: testDir,
filename: 'main.js',
},
resolve: {
alias: {
'blake3/browser': resolve(__dirname, '../', 'browser.js'),
},
},
},
(err, stats) => (err ? rej(err) : res(stats)),
),
);
if (stats.hasErrors()) {
throw stats.toString('errors-only');
}
writeFileSync(resolve(testDir, 'index.html'), `<script src="/main.js"></script>`);
}
async function serve() {
server = createServer((req, res) => handler(req, res, { public: testDir }));
await new Promise<void>(resolve => server.listen(0, resolve));
}
before(async function() {
await buildWebpack();
await serve();
this.timeout(20 * 1000);
const { port } = server.address() as AddressInfo;
browser = await chromium.launch();
page = await browser.newPage();
await page.goto(`http://localhost:${port}`);
await page.waitForFunction('!!window.blake3');
await page.evaluate(addInputs);
});
runTests({
get page() {
return page;
},
});
after(async () => {
await browser?.close();
server?.close();
});
});
describe('native browser', () => {
let server: Server;
let page: Page;
let browser: Browser;
async function serve() {
server = createServer((req, res) => handler(req, res, { public: resolve(__dirname, '..') }));
await new Promise<void>(resolve => server.listen(0, resolve));
}
before(async function() {
await serve();
this.timeout(20 * 1000);
const { port } = server.address() as AddressInfo;
browser = await chromium.launch();
page = await browser.newPage();
page.on('console', console.log);
page.on('pageerror', console.log);
page.on('pageerror', console.log);
await page.goto(`http://localhost:${port}/browser-async.test.html`);
await page.waitForFunction('!!window.blake3');
await page.evaluate(addInputs);
});
runTests({
get page() {
return page;
},
});
after(async () => {
await browser?.close();
server.close();
});
});
});
Example #20
Source File: PermissionIntegrationClient.test.ts From backstage with Apache License 2.0 | 4 votes |
describe('PermissionIntegrationClient', () => {
describe('applyConditions', () => {
let server: SetupServerApi;
const mockConditions: PermissionCriteria<PermissionCondition> = {
not: {
allOf: [
{ rule: 'RULE_1', resourceType: 'test-resource', params: [] },
{ rule: 'RULE_2', resourceType: 'test-resource', params: ['abc'] },
],
},
};
const mockApplyConditionsHandler = jest.fn(
(_req, res, { json }: RestContext) => {
return res(
json({ items: [{ id: '123', result: AuthorizeResult.ALLOW }] }),
);
},
);
const mockBaseUrl = 'http://backstage:9191';
const discovery: PluginEndpointDiscovery = {
async getBaseUrl(pluginId) {
return `${mockBaseUrl}/${pluginId}`;
},
async getExternalBaseUrl() {
throw new Error('Not implemented.');
},
};
const client: PermissionIntegrationClient = new PermissionIntegrationClient(
{
discovery,
},
);
beforeAll(() => {
server = setupServer();
server.listen({ onUnhandledRequest: 'error' });
server.use(
rest.post(
`${mockBaseUrl}/plugin-1/.well-known/backstage/permissions/apply-conditions`,
mockApplyConditionsHandler,
),
);
});
afterAll(() => server.close());
afterEach(() => {
jest.clearAllMocks();
});
it('should make a POST request to the correct endpoint', async () => {
await client.applyConditions('plugin-1', [
{
id: '123',
resourceRef: 'testResource1',
resourceType: 'test-resource',
conditions: mockConditions,
},
]);
expect(mockApplyConditionsHandler).toHaveBeenCalled();
});
it('should include a request body', async () => {
await client.applyConditions('plugin-1', [
{
id: '123',
resourceRef: 'testResource1',
resourceType: 'test-resource',
conditions: mockConditions,
},
]);
expect(mockApplyConditionsHandler).toHaveBeenCalledWith(
expect.objectContaining({
body: {
items: [
{
id: '123',
resourceRef: 'testResource1',
resourceType: 'test-resource',
conditions: mockConditions,
},
],
},
}),
expect.anything(),
expect.anything(),
);
});
it('should return the response from the fetch request', async () => {
const response = await client.applyConditions('plugin-1', [
{
id: '123',
resourceRef: 'testResource1',
resourceType: 'test-resource',
conditions: mockConditions,
},
]);
expect(response).toEqual(
expect.objectContaining([{ id: '123', result: AuthorizeResult.ALLOW }]),
);
});
it('should not include authorization headers if no token is supplied', async () => {
await client.applyConditions('plugin-1', [
{
id: '123',
resourceRef: 'testResource1',
resourceType: 'test-resource',
conditions: mockConditions,
},
]);
const request = mockApplyConditionsHandler.mock.calls[0][0];
expect(request.headers.has('authorization')).toEqual(false);
});
it('should include correctly-constructed authorization header if token is supplied', async () => {
await client.applyConditions(
'plugin-1',
[
{
id: '123',
resourceRef: 'testResource1',
resourceType: 'test-resource',
conditions: mockConditions,
},
],
'Bearer fake-token',
);
const request = mockApplyConditionsHandler.mock.calls[0][0];
expect(request.headers.get('authorization')).toEqual('Bearer fake-token');
});
it('should forward response errors', async () => {
mockApplyConditionsHandler.mockImplementationOnce(
(_req, res, { status }: RestContext) => {
return res(status(401));
},
);
await expect(
client.applyConditions('plugin-1', [
{
id: '123',
resourceRef: 'testResource1',
resourceType: 'test-resource',
conditions: mockConditions,
},
]),
).rejects.toThrowError(/401/i);
});
it('should reject invalid responses', async () => {
mockApplyConditionsHandler.mockImplementationOnce(
(_req, res, { json }: RestContext) => {
return res(
json({ items: [{ id: '123', outcome: AuthorizeResult.ALLOW }] }),
);
},
);
await expect(
client.applyConditions('plugin-1', [
{
id: '123',
resourceRef: 'testResource1',
resourceType: 'test-resource',
conditions: mockConditions,
},
]),
).rejects.toThrowError(/invalid input/i);
});
it('should batch requests to plugin backends', async () => {
mockApplyConditionsHandler.mockImplementationOnce(
(_req, res, { json }: RestContext) => {
return res(
json({
items: [
{ id: '123', result: AuthorizeResult.ALLOW },
{ id: '456', result: AuthorizeResult.DENY },
{ id: '789', result: AuthorizeResult.ALLOW },
],
}),
);
},
);
await expect(
client.applyConditions('plugin-1', [
{
id: '123',
resourceRef: 'testResource1',
resourceType: 'test-resource',
conditions: mockConditions,
},
{
id: '456',
resourceRef: 'testResource1',
resourceType: 'test-resource',
conditions: mockConditions,
},
{
id: '789',
resourceRef: 'testResource1',
resourceType: 'test-resource',
conditions: mockConditions,
},
]),
).resolves.toEqual([
{ id: '123', result: AuthorizeResult.ALLOW },
{ id: '456', result: AuthorizeResult.DENY },
{ id: '789', result: AuthorizeResult.ALLOW },
]);
expect(mockApplyConditionsHandler).toHaveBeenCalledTimes(1);
});
});
describe('integration with @backstage/plugin-permission-node', () => {
let server: Server;
let client: PermissionIntegrationClient;
let routerSpy: RequestHandler;
beforeAll(async () => {
const router = Router();
router.use(
createPermissionIntegrationRouter({
resourceType: 'test-resource',
getResources: async resourceRefs =>
resourceRefs.map(resourceRef => ({
id: resourceRef,
})),
rules: [
createPermissionRule({
name: 'RULE_1',
description: 'Test rule 1',
resourceType: 'test-resource',
apply: (_resource: any, input: 'yes' | 'no') => input === 'yes',
toQuery: () => {
throw new Error('Not implemented');
},
}),
createPermissionRule({
name: 'RULE_2',
description: 'Test rule 2',
resourceType: 'test-resource',
apply: (_resource: any, input: 'yes' | 'no') => input === 'yes',
toQuery: () => {
throw new Error('Not implemented');
},
}),
],
}),
);
const app = express();
routerSpy = jest.fn(router);
app.use('/plugin-1', routerSpy);
await new Promise<void>(resolve => {
server = app.listen(resolve);
});
const discovery: PluginEndpointDiscovery = {
async getBaseUrl(pluginId: string) {
const listenPort = (server.address()! as AddressInfo).port;
return `http://0.0.0.0:${listenPort}/${pluginId}`;
},
async getExternalBaseUrl() {
throw new Error('Not implemented.');
},
};
client = new PermissionIntegrationClient({
discovery,
});
});
afterAll(
async () =>
new Promise<void>((resolve, reject) =>
server.close(err => (err ? reject(err) : resolve())),
),
);
afterEach(() => {
jest.clearAllMocks();
});
it('works for simple conditions', async () => {
await expect(
client.applyConditions('plugin-1', [
{
id: '123',
resourceRef: 'testResource1',
resourceType: 'test-resource',
conditions: {
rule: 'RULE_1',
resourceType: 'test-resource',
params: ['no'],
},
},
]),
).resolves.toEqual([{ id: '123', result: AuthorizeResult.DENY }]);
});
it('works for complex criteria', async () => {
await expect(
client.applyConditions('plugin-1', [
{
id: '123',
resourceRef: 'testResource1',
resourceType: 'test-resource',
conditions: {
allOf: [
{
allOf: [
{
rule: 'RULE_1',
resourceType: 'test-resource',
params: ['yes'],
},
{
not: {
rule: 'RULE_2',
resourceType: 'test-resource',
params: ['no'],
},
},
],
},
{
not: {
allOf: [
{
rule: 'RULE_1',
resourceType: 'test-resource',
params: ['no'],
},
{
rule: 'RULE_2',
resourceType: 'test-resource',
params: ['yes'],
},
],
},
},
],
},
},
]),
).resolves.toEqual([{ id: '123', result: AuthorizeResult.ALLOW }]);
});
});
});
Example #21
Source File: cli.ts From Assistive-Webdriver with MIT License | 4 votes |
(async function () {
const argv = await yargs.options({
browser: {
array: true,
type: "string",
alias: "b",
default: ["chromium"]
},
"log-level": {
type: "string",
alias: "l",
default: "info"
},
"public-host": {
type: "string",
alias: "h",
default: `${hostname()}.`
},
"listen-host": {
type: "string",
default: "0.0.0.0"
},
"public-port": {
type: "number",
alias: "p",
default: 0
},
"listen-port": {
type: "number",
default: 0
},
"vm-settings": {
type: "string",
alias: "m",
demandOption: true
},
"server-port": {
type: "number",
default: 7779
},
"skip-keys": {
type: "array",
alias: "k"
},
"screen-reader": {
type: "boolean",
default: true
}
}).argv;
configure({
level: argv["log-level"] as any,
format: format.combine(format.colorize(), format.simple()),
transports: [new transports.Console()]
});
const vmSettings = JSON.parse(argv["vm-settings"]);
info(`Starting the VM...`);
const driver = await createVM({
log: (entry: any) => log(entry),
vmSettings,
playwrightPort: argv["server-port"]
});
const availableBrowsers: {
[name: string]:
| BrowserType<ChromiumBrowser>
| BrowserType<FirefoxBrowser>
| BrowserType<WebKitBrowser>;
} = {
chromium: driver.chromium,
firefox: driver.firefox,
webkit: driver.webkit
};
const screenReader = argv["screen-reader"];
const testerSession = new TesterSession(driver, screenReader);
let vmDestroyed = false;
process.on("SIGINT", async () => {
if (!vmDestroyed) {
warn("Stopping the VM...");
vmDestroyed = true;
await driver.vm.destroy();
error("Tests were interrupted.");
process.exit(2);
}
});
sigintWin32();
try {
const eventsServerAddress = await new Promise<AddressInfo>(
(resolve, reject) =>
testerSession.eventsServer
.listen(
argv["listen-port"] || argv["public-port"],
argv["listen-host"] || argv["public-host"],
() => {
resolve(testerSession.eventsServer.address() as AddressInfo);
}
)
.once("error", reject)
);
const eventsServerListenAddress = `http://${eventsServerAddress.address}:${eventsServerAddress.port}`;
const publicPort = argv["public-port"] || eventsServerAddress.port;
const publicHost = argv["public-host"] || eventsServerAddress.address;
const eventsServerPublicAddress = `http://${publicHost}:${publicPort}`;
info(
`Test page server started at ${eventsServerListenAddress}, VM will connect to ${eventsServerPublicAddress}`
);
if (screenReader) {
info("Screen reader testing is ENABLED.");
driver.screenReader.on("message", message =>
info(`Screen reader said: ${message}`)
);
} else {
info("Screen reader testing is DISABLED.");
}
for (const browserName of argv.browser) {
const browser = availableBrowsers[browserName];
if (!browser) {
testerSession.reportError(`Unknown browser: ${browserName}`);
continue;
}
info(`Testing on browser ${browserName}`);
const browserInstance = await browser.launch({ headless: false });
try {
const page = await browserInstance.newPage({
viewport: null
});
testerSession.page = page;
const mouse = await driver.calibrateMouse(page);
testerSession.mouse = mouse;
info(`Loading test page from ${eventsServerPublicAddress}`);
const response = await page.goto(eventsServerPublicAddress);
if (!response?.ok) {
testerSession.reportError(
`Could not successfully load page ${eventsServerPublicAddress}`
);
continue;
}
info(`Test page was successfully loaded`);
const testInput = await page.$("#testInput");
await mouse.click(0, 0, { origin: testInput });
await page.waitForFunction(hasFocus, testInput);
if (screenReader) {
await driver.screenReader.clearMessages();
}
await driver.keyboard.press(Key.Tab);
if (screenReader) {
try {
await driver.screenReader.waitForMessage("mysupertestlabeltocheck");
} catch (e) {
testerSession.reportError(`Screen reader test failed: ${e}`);
}
}
const testDiv = await page.$("#testDiv");
await page.waitForFunction(hasFocus, testDiv);
await testerSession.eventsQueue.getAllWaitingValues();
await testAllKeys(testerSession, argv["skip-keys"] as any);
await testMouseButtons(testerSession);
} catch (error: any) {
testerSession.reportError(`${error.stack || error.message || error}`);
continue;
} finally {
try {
info(`Closing browser ${browserName}...`);
await browserInstance.close();
} catch (e) {
console.log(`Error in browserInstance.close()`);
}
info(`Tests finished for browser ${browserName}`);
}
}
if (testerSession.errorsNumber === 0) {
info("All tests were successful!");
} else {
throw "Some tests failed!";
}
} finally {
await collectCoverage(driver.url);
testerSession.reportMeasures();
vmDestroyed = true;
await driver.vm.destroy();
testerSession.eventsServer.close();
}
})().catch(e => {
error(`${e.stack || e.message || e}`);
process.exit(1);
});
Example #22
Source File: opentelemetry-express.spec.ts From opentelemetry-ext-js with Apache License 2.0 | 4 votes |
describe('opentelemetry-express', () => {
let app: express.Application;
before(() => {
// registerInstrumentationTesting currently support only 1 instrumentation
// test memory exporter initialized at beforeAll hook
httpInstrumentation.setTracerProvider(trace.getTracerProvider());
instrumentation.enable();
httpInstrumentation.enable();
app = express();
app.use(bodyParser.json());
});
after(() => {
instrumentation.disable();
httpInstrumentation.disable();
});
it('express attributes', (done) => {
const router = express.Router();
app.use('/toto', router);
router.post('/:id', (req, res, next) => {
res.set('res-custom-header-key', 'res-custom-header-val');
return res.json({ hello: 'world' });
});
const server = http.createServer(app);
server.listen(0, async () => {
const port = (server.address() as AddressInfo).port;
const requestData = { 'req-data-key': 'req-data-val' };
try {
await axios.post(
`http://localhost:${port}/toto/tata?req-query-param-key=req-query-param-val`,
requestData,
{
headers: {
'req-custom-header-key': 'req-custom-header-val',
},
}
);
} catch (err) {}
try {
const expressSpans: ReadableSpan[] = getExpressSpans();
expect(expressSpans.length).toBe(1);
const span: ReadableSpan = expressSpans[0];
// Span name
expect(span.name).toBe('POST /toto/:id');
// HTTP Attributes
expect(span.attributes[SemanticAttributes.HTTP_METHOD]).toBeUndefined();
expect(span.attributes[SemanticAttributes.HTTP_TARGET]).toBeUndefined();
expect(span.attributes[SemanticAttributes.HTTP_SCHEME]).toBeUndefined();
expect(span.attributes[SemanticAttributes.HTTP_STATUS_CODE]).toBeUndefined();
expect(span.attributes[SemanticAttributes.HTTP_HOST]).toBeUndefined();
expect(span.attributes[SemanticAttributes.HTTP_FLAVOR]).toBeUndefined();
expect(span.attributes[SemanticAttributes.NET_PEER_IP]).toBeUndefined();
// http span route
const [incomingHttpSpan] = getTestSpans().filter(
(s) => s.kind === SpanKind.SERVER && s.instrumentationLibrary.name.includes('http')
);
expect(incomingHttpSpan.attributes[SemanticAttributes.HTTP_ROUTE]).toMatch('/toto/:id');
done();
} catch (error) {
done(error);
} finally {
server.close();
}
});
});
it('express with http attributes', (done) => {
instrumentation.disable();
instrumentation.setConfig({
includeHttpAttributes: true,
});
instrumentation.enable();
const router = express.Router();
app.use('/toto', router);
router.post('/:id', (req, res, next) => {
res.set('res-custom-header-key', 'res-custom-header-val');
return res.json({ hello: 'world' });
});
const server = http.createServer(app);
server.listen(0, async () => {
const port = (server.address() as AddressInfo).port;
const requestData = { 'req-data-key': 'req-data-val' };
try {
await axios.post(
`http://localhost:${port}/toto/tata?req-query-param-key=req-query-param-val`,
requestData,
{
headers: {
'req-custom-header-key': 'req-custom-header-val',
},
}
);
} catch (err) {}
const expressSpans: ReadableSpan[] = getExpressSpans();
expect(expressSpans.length).toBe(1);
const span: ReadableSpan = expressSpans[0];
// HTTP Attributes
expect(span.attributes[SemanticAttributes.HTTP_METHOD]).toBe('POST');
expect(span.attributes[SemanticAttributes.HTTP_TARGET]).toBe(
'/toto/tata?req-query-param-key=req-query-param-val'
);
expect(span.attributes[SemanticAttributes.HTTP_SCHEME]).toBe('http');
expect(span.attributes[SemanticAttributes.HTTP_STATUS_CODE]).toBe(200);
expect(span.attributes[SemanticAttributes.HTTP_HOST]).toBe(`localhost:${port}`);
expect(span.attributes[SemanticAttributes.HTTP_FLAVOR]).toBe('1.1');
expect(span.attributes[SemanticAttributes.NET_PEER_IP]).toBe('::ffff:127.0.0.1');
server.close();
done();
});
});
it('use empty res.end() to terminate response', (done) => {
app.get('/toto', (req, res, next) => {
res.end();
});
const server = http.createServer(app);
server.listen(0, async () => {
const port = (server.address() as AddressInfo).port;
try {
await axios.get(`http://localhost:${port}/toto`);
} catch (err) {}
const expressSpans: ReadableSpan[] = getExpressSpans();
expect(expressSpans.length).toBe(1);
server.close();
done();
});
});
it('mount app', (done) => {
const subApp = express();
subApp.get('/sub-app', (req, res) => res.end());
app.use('/top-level-app', subApp);
const server = http.createServer(app);
server.listen(0, async () => {
const port = (server.address() as AddressInfo).port;
try {
await axios.get(`http://localhost:${port}/top-level-app/sub-app`);
} catch (err) {}
const expressSpans: ReadableSpan[] = getExpressSpans();
expect(expressSpans.length).toBe(1);
server.close();
done();
});
});
it('should record exceptions as span events', async () => {
app.get('/throws-exception', (req, res, next) => {
next(new Error('internal exception'));
});
const server = http.createServer(app);
await new Promise<void>((resolve) => server.listen(0, () => resolve()));
const port = (server.address() as AddressInfo).port;
try {
await axios.get(`http://localhost:${port}/throws-exception`);
} catch (err) {
// we expect 500
}
const expressSpans: ReadableSpan[] = getExpressSpans();
expect(expressSpans.length).toBe(1);
const span: ReadableSpan = expressSpans[0];
expect(span.events?.length).toEqual(1);
const [event] = span.events;
expect(event).toMatchObject({
name: 'exception',
attributes: {
'exception.type': 'Error',
'exception.message': 'internal exception',
},
});
server.close();
});
it('should record multiple exceptions', async () => {
app.get('/throws-exception', (req, res, next) => {
next(new Error('internal exception'));
});
app.use((err, req, res, next) => {
next(new Error('error-handling middleware exception'));
});
const server = http.createServer(app);
await new Promise<void>((resolve) => server.listen(0, () => resolve()));
const port = (server.address() as AddressInfo).port;
try {
await axios.get(`http://localhost:${port}/throws-exception`);
} catch (err) {
// we expect 500
}
const expressSpans: ReadableSpan[] = getExpressSpans();
expect(expressSpans.length).toBe(1);
const span: ReadableSpan = expressSpans[0];
expect(span.events?.length).toEqual(2);
const [event1, event2] = span.events;
expect(event1).toMatchObject({
name: 'exception',
attributes: {
'exception.type': 'Error',
'exception.message': 'internal exception',
},
});
expect(event2).toMatchObject({
name: 'exception',
attributes: {
'exception.type': 'Error',
'exception.message': 'error-handling middleware exception',
},
});
server.close();
});
it('requestHook', async () => {
instrumentation.disable();
instrumentation.setConfig({
requestHook: (span, requestInfo: ExpressRequestHookInformation) => {
span.setAttribute('content_type', requestInfo.req.headers['content-type']);
},
});
instrumentation.enable();
app.post('/request-hook', (_req, res) => {
res.sendStatus(200);
});
const server = http.createServer(app);
await new Promise<void>((resolve) => server.listen(0, () => resolve()));
const port = (server.address() as AddressInfo).port;
await axios.post(`http://localhost:${port}/request-hook`, { rick: 'morty' });
const expressSpans: ReadableSpan[] = getExpressSpans();
expect(expressSpans.length).toBe(1);
const span: ReadableSpan = expressSpans[0];
expect(span.attributes['content_type']).toBe('application/json;charset=utf-8');
server.close();
});
});
Example #23
Source File: opentelemetry-express-layers.spec.ts From opentelemetry-ext-js with Apache License 2.0 | 4 votes |
describe('opentelemetry-express-layers', () => {
let app: express.Application;
const sendRequest = async (urlPath: string): Promise<ReadableSpan> => {
return new Promise((resolve) => {
const server = http.createServer(app);
server.listen(0, async () => {
try {
const port = (server.address() as AddressInfo).port;
try {
await axios.get(`http://localhost:${port}${urlPath}`, {
params: { 'test-param-1': 'test-param-1-value' },
});
} catch (err) {
// console.log(err);
}
server.close();
resolve(getExpressSpans()[0]);
} catch (err) {
console.log(err);
}
});
});
};
beforeEach(() => {
instrumentation.enable();
});
beforeEach(() => {
app = express();
});
it('no routes registered', async () => {
await sendRequest('/no-routes');
});
it('app use without path', async () => {
app.use((req, res, next) => {
res.sendStatus(200);
});
const s = await sendRequest('/foo');
// '/foo' was not consumed by any router or route. thus it's not part of the route.
expectRouteAttributes(s, '', '/foo');
});
it('app use', async () => {
app.use('/foo', (req, res, next) => {
res.sendStatus(200);
});
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('app use middleware with name', async () => {
const middlewareName = (req, res, next) => {
res.sendStatus(200);
};
app.use('/foo', middlewareName);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('app use multiple middlewares', async () => {
const middlewareName1 = (req, res, next) => {
next();
};
const middlewareName2 = (req, res, next) => {
next();
};
const middlewareName3 = (req, res, next) => {
res.sendStatus(200);
};
app.use('/foo', middlewareName1, middlewareName2, middlewareName3);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('multiple middlewares set as array', async () => {
const middlewareName1 = (req, res, next) => {
next();
};
const middlewareName2 = (req, res, next) => {
next();
};
const middlewareName3 = (req, res, next) => {
res.sendStatus(200);
};
app.use('/foo', [middlewareName1, middlewareName2, middlewareName3]);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('router without path', async () => {
const router = express.Router();
router.use((req, res, next) => {
res.sendStatus(200);
});
app.use('/foo', router);
const s = await await sendRequest('/foo/bar');
expectRouteAttributes(s, '/foo', '/foo/bar');
});
it('router use multiple middlewares', async () => {
const middlewareName1 = (req, res, next) => {
next();
};
const middlewareName2 = (req, res, next) => {
next();
};
const middlewareName3 = (req, res, next) => {
res.sendStatus(200);
};
const router = express.Router();
router.use('/bar', middlewareName1, middlewareName2, middlewareName3);
app.use('/foo', router);
const s = await sendRequest('/foo/bar');
expectRouteAttributes(s, '/foo/bar', '/foo/bar');
});
it('middleware chain break, expect only executed', async () => {
const middlewareName1 = (req, res, next) => {
next();
};
const middlewareName2 = (req, res, next) => {
res.sendStatus(200);
};
const middlewareName3 = (req, res, next) => {
throw new Error('middleware should not run as previous middleware did not call next');
};
app.use('/foo', middlewareName1, middlewareName2, middlewareName3);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('path parameter', async () => {
const middlewareName1 = (req, res, next) => {
next();
};
const middlewareName2 = (req, res, next) => {
res.sendStatus(200);
};
app.use('/foo/:id', middlewareName1, middlewareName2);
const s = await sendRequest('/foo/19');
expectRouteAttributes(s, '/foo/:id', '/foo/:id', { expectedParams: { id: '19' } });
});
it('router with use', async () => {
const middlewareName1 = (req, res, next) => {
res.sendStatus(200);
};
const router = express.Router().use(middlewareName1);
app.use('/foo', router);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('router with path parameters', async () => {
const router = express.Router().all('/:id2', (req, res, next) => {
res.sendStatus(200);
});
app.use('/foo/:id1', router);
const s = await sendRequest('/foo/1/2');
expectRouteAttributes(s, '/foo/:id1/:id2', '/foo/:id1/:id2', { expectedParams: { id1: '1', id2: '2' } });
});
it('route created in router with path', async () => {
const middlewareName1 = (req, res, next) => {
res.sendStatus(200);
};
app.route('/foo').get(middlewareName1);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('multiple middlewares in route', async () => {
const middlewareName1 = (req, res, next) => {
next();
};
const middlewareName2 = (req, res, next) => {
res.sendStatus(200);
};
app.route('/foo').get(middlewareName1, middlewareName2);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('midlleware in route with http method `all`', async () => {
const middlewareName1 = (req, res, next) => {
res.sendStatus(200);
};
app.route('/foo').all(middlewareName1);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('app with call to method', async () => {
const middlewareName1 = (req, res, next) => {
res.sendStatus(200);
};
app.get('/foo', middlewareName1);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('middleware with two parameters', async () => {
const middlewareName1 = (req, res) => {
res.sendStatus(200);
};
app.get('/foo', middlewareName1);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('two routers', async () => {
const middlewareName1 = (req, res, next) => {
res.sendStatus(200);
};
app.use('/foo', (req, res, next) => {
next();
});
app.use('/foo', middlewareName1);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('multi nested routers create branched tree, and finishing with finalhandler', async () => {
const router1 = express.Router();
router1.use('/bar', function mw1(req, res, next) {
next();
});
const router2 = express.Router();
router2.use('/bar', function mw2(req, res, next) {
next();
});
app.use('/foo', router1, router2);
const s = await sendRequest('/foo/bar');
expectRouteFromFinalHandler(s, '/foo/bar');
});
it('multiple paths in app', async () => {
app.use(['/p1', '/p2'], (req, res, next) => {
res.sendStatus(200);
});
const s = await sendRequest('/p2');
expectRouteAttributes(s, '/p2', '/p2', { configuredRoute: '["/p1","/p2"]' });
});
it('multiple path in router', async () => {
const router = express.Router();
router.use(['/p1', '/p2'], (req, res, next) => {
res.sendStatus(200);
});
app.use(router);
const s = await sendRequest('/p2');
expectRouteAttributes(s, '/p2', '/p2', {
configuredRoute: '["/p1","/p2"]',
});
});
it('path is regex', async () => {
app.use(new RegExp('/ab*c'), (req, res, next) => {
res.sendStatus(200);
});
const s = await sendRequest('/abbbbbc');
expectRouteAttributes(s, '/\\/ab*c/', '/\\/ab*c/');
});
it('error indication break middleware chain, and captured by finalhandler', async () => {
const middlewareName1 = (req, res, next) => {
next();
};
const middlewareName2 = (req, res, next) => {
next('error on middleware 2'); // handled by finalhandler
};
const middlewareName3 = (req, res, next) => {
throw new Error('middleware should not run as previous middleware did not call next');
};
app.use('/foo', middlewareName1, middlewareName2, middlewareName3);
const s = await sendRequest('/foo');
expectRouteFromFinalHandler(s, '/foo');
});
it('basic err capture from middleware next(err) call', async () => {
app.use('/foo', (req, res, next) => {
next('error message from middleware'); // handled by finalhandler
});
const s = await sendRequest('/foo');
expectRouteFromFinalHandler(s, '/foo');
});
it('next("route") should not be marked as error', async () => {
const middlewareStopRoute = (req, res, next) => {
next('route'); // handled by finalhandler
};
const middleware2 = (req, res, next) => {
res.sendStatus(200);
};
app.route('/foo').all(middlewareStopRoute, middleware2); // request fallback to finalhandler
const s = await sendRequest('/foo');
expectRouteFromFinalHandler(s, '/foo');
});
it('basic err capture from middleware throw string', async () => {
app.use('/foo', (req, res, next) => {
throw 'error message from middleware';
});
const s = await sendRequest('/foo');
expectRouteFromFinalHandler(s, '/foo');
});
it('basic err capture from middleware throw exception', async () => {
app.use('/foo', (req, res, next) => {
throw Error('error message from middleware');
});
const s = await sendRequest('/foo');
expectRouteFromFinalHandler(s, '/foo');
});
it('error middleware should not create a layer for non error req', async () => {
const middlewareName1 = (req, res, next) => {
next();
};
const middlewareName2 = (err, req, res, next) => {
throw new Error('middleware should not run since its an error handling middleware');
};
const middlewareName3 = (req, res, next) => {
res.sendStatus(200);
};
app.use('/foo', middlewareName1, middlewareName2, middlewareName3);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('error in one route should not forward to other route', async () => {
app.route('/foo').all(function firstRoute(req, res, next) {
next('error in first route'); // handled by finalhandler
});
// second route should NOT be called and collected as we have an error
app.route('/foo').all(function secondRoute(req, res, next) {
res.sendStatus(200);
});
const s = await sendRequest('/foo');
expectRouteFromFinalHandler(s, '/foo');
});
it('error middleware in layers if executed', async () => {
const middlewareThrow = (req, res, next) => {
next('error msg from first middleware');
};
const errorHandlingMiddleware = (err, req, res, next) => {
next('some other err from error middleware');
};
app.use('/foo', middlewareThrow, errorHandlingMiddleware); // handled by finalhandler
const s = await sendRequest('/foo');
expectRouteFromFinalHandler(s, '/foo');
});
// express default error handler is not a middleware (implemented as a callback function),
// so it does not create a layer
it('error in middleware without custom handler', async () => {
const middlewareThrow = (req, res, next) => {
next('error msg from first middleware');
};
app.use('/foo', middlewareThrow); // handled by finalhandler
const s = await sendRequest('/foo');
expectRouteFromFinalHandler(s, '/foo');
});
it('multiple error middleware handlers in different levels', async () => {
const throwingMiddleware = (req, res, next) => {
next('error msg from first middleware');
};
const routerInternalErrorMiddleware = (err, req, res, next) => {
next(err);
};
const generalErrorMiddleware = (err, req, res, next) => {
next(err);
};
const router = express.Router().use(throwingMiddleware, routerInternalErrorMiddleware);
app.use('/foo', router, generalErrorMiddleware);
const s = await sendRequest('/foo');
expectRouteFromFinalHandler(s, '/foo');
});
describe('express router', () => {
describe('path trimming', () => {
it('router without path', async () => {
const router = express.Router();
router.use(resEndMiddleware);
app.use(router);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '', '/foo');
});
it('router with path till end', async () => {
const router = express.Router();
router.use('/foo', resEndMiddleware);
app.use(router);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('router with strict false remove trailing slash in path end', async () => {
const router = express.Router({ strict: false });
router.use('/foo/', resEndMiddleware);
app.use(router);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('router with partial path', async () => {
const router = express.Router();
router.use('/foo', resEndMiddleware);
app.use(router);
const s = await sendRequest('/foo/bar');
expectRouteAttributes(s, '/foo', '/foo/bar');
});
it('router without path registered under a path', async () => {
const router = express.Router();
router.use(resEndMiddleware);
app.use('/foo', router);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('router without path registered under a path with path leftovers', async () => {
const router = express.Router();
router.use(resEndMiddleware);
app.use('/foo', router);
const s = await sendRequest('/foo/bar');
expectRouteAttributes(s, '/foo', '/foo/bar');
});
it('router with path registered under a path without leftovers', async () => {
const router = express.Router();
router.use('/bar', resEndMiddleware);
app.use('/foo', router);
const s = await sendRequest('/foo/bar');
expectRouteAttributes(s, '/foo/bar', '/foo/bar');
});
it('router with path registered under a path with leftovers', async () => {
const router = express.Router();
router.use('/bar', resEndMiddleware);
app.use('/foo', router);
const s = await sendRequest('/foo/bar/baz');
expectRouteAttributes(s, '/foo/bar', '/foo/bar/baz');
});
it('router with slash', async () => {
const router = express.Router();
router.use('/foo', resEndMiddleware);
app.use('/', router);
const s = await sendRequest('/foo/bar/baz');
expectRouteAttributes(s, '/foo', '/foo/bar/baz');
});
it('router use with slash', async () => {
const router = express.Router();
router.use('/', resEndMiddleware);
app.use('/foo', router);
const s = await sendRequest('/foo/bar/baz');
expectRouteAttributes(s, '/foo', '/foo/bar/baz');
});
});
describe('multiple middlewares', () => {
it('two middlewares under same router', async () => {
app.use(express.Router().use('/foo', noopMiddleware, resEndMiddleware));
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('two middlewares under different router, second is invoked', async () => {
const router = express.Router();
router.use('/:firstRouteParam', noopMiddleware);
router.use('/:secondRouteParam', resEndMiddleware);
app.use(router);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/:secondRouteParam', '/:secondRouteParam', {
expectedParams: { secondRouteParam: 'foo' },
});
});
it('two middlewares under different router, first is invoked', async () => {
const router = express.Router();
router.use('/:firstRouteParam', resEndMiddleware);
router.use('/:secondRouteParam', shouldNotInvokeMiddleware);
app.use(router);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/:firstRouteParam', '/:firstRouteParam', {
expectedParams: { firstRouteParam: 'foo' },
});
});
});
describe('multiple routers', () => {
describe('sibling routers', () => {
it('second router terminate req', async () => {
app.use(express.Router().use('/:firstRouteParam', noopMiddleware));
app.use(express.Router().use('/:secondRouteParam', resEndMiddleware));
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/:secondRouteParam', '/:secondRouteParam', {
expectedParams: { secondRouteParam: 'foo' },
});
});
it('first router not matching path', async () => {
app.use(express.Router().use('/non-executing-path', resEndMiddleware));
app.use(express.Router().use('/foo', resEndMiddleware));
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('first router not matching path', async () => {
app.use(express.Router().use('/non-executing-path', resEndMiddleware));
app.use(express.Router().use('/foo', resEndMiddleware));
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
});
describe('descendant', () => {
it('routers without path', async () => {
const childRouter = express.Router().use(resEndMiddleware);
const parentRouter = express.Router().use(childRouter);
app.use(parentRouter);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '', '/foo');
});
it('routers with path', async () => {
const childRouter = express.Router().use('/baz', resEndMiddleware);
const parentRouter = express.Router().use('/bar', childRouter);
app.use('/foo', parentRouter);
const s = await sendRequest('/foo/bar/baz');
expectRouteAttributes(s, '/foo/bar/baz', '/foo/bar/baz');
});
});
});
});
describe('express route', () => {
describe('registration types', () => {
it('route.all', async () => {
app.all('/foo', resEndMiddleware);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('route.get', async () => {
app.get('/foo', resEndMiddleware);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('verb other than the one being invoked', async () => {
// req is sent with GET but we registered POST
// thus it should not be executed and fallback toi finalhandler
app.post('/foo', resEndMiddleware);
const s = await sendRequest('/foo');
expectRouteFromFinalHandler(s, '/foo');
});
it('registered to route directly', async () => {
const route = app.route('/foo');
route.get(resEndMiddleware);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('route with two verbs both executed', async () => {
const route = app.route('/foo');
route.all(noopMiddleware);
route.get(resEndMiddleware);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
it('route with slash', async () => {
// fast_slash does not work with routes in express, thus this request will reach finalhandler
app.all('/', resEndMiddleware);
const s = await sendRequest('/foo');
expectRouteFromFinalHandler(s, '/foo');
});
});
describe('path manipulations', () => {
it('route path with multiple parts', async () => {
app.all('/foo/bar', resEndMiddleware);
const s = await sendRequest('/foo/bar');
expectRouteAttributes(s, '/foo/bar', '/foo/bar');
});
it('route path only match prefix', async () => {
// route should match path to the end,
// '/foo' does not match '/foo/bar'
// thus, request is handled by finalhandler which does not set HTTP_ROUTE
app.all('/foo', resEndMiddleware);
const s = await sendRequest('/foo/bar');
expectRouteFromFinalHandler(s, '/foo/bar');
});
it('route path slash should not be catched', async () => {
// route should match path to the end, with not exception to slash (all paths)
// thus, request is handled by finalhandler which does not set HTTP_ROUTE
app.all('/', resEndMiddleware);
const s = await sendRequest('/foo');
expectRouteFromFinalHandler(s, '/foo');
});
});
describe('multiple routes', () => {
it('two sibling routes, first is calling res.end()', async () => {
app.get('/:firstRouteParam', resEndMiddleware);
app.get('/:secondRouteParam', (_req: express.Request, _res: express.Response) => {
throw new Error('should not be invoked');
});
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/:firstRouteParam', '/:firstRouteParam', {
expectedParams: { firstRouteParam: 'foo' },
});
});
it('two sibling routes, second is calling res.end()', async () => {
app.get('/:firstRouteParam', noopMiddleware);
app.get('/:secondRouteParam', resEndMiddleware);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/:secondRouteParam', '/:secondRouteParam', {
expectedParams: { secondRouteParam: 'foo' },
});
});
});
});
describe('layer multiple paths', () => {
it('path array with single value', async () => {
app.use(['/foo'], resEndMiddleware);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo', { configuredRoute: '["/foo"]' });
});
it('two string paths, match first one', async () => {
app.use(['/foo', '/bar'], resEndMiddleware);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo', {
configuredRoute: '["/foo","/bar"]',
});
});
it('two string paths, match second', async () => {
app.use(['/foo', '/bar'], resEndMiddleware);
const s = await sendRequest('/bar');
expectRouteAttributes(s, '/bar', '/bar', {
configuredRoute: '["/foo","/bar"]',
});
});
it('two string paths, match both', async () => {
app.use(['/:pathParam', '/foo'], resEndMiddleware);
const s = await sendRequest('/foo');
// /:pathParam is first in the list, so it should match
expectRouteAttributes(s, '/:pathParam', '/:pathParam', {
expectedParams: { pathParam: 'foo' },
configuredRoute: '["/:pathParam","/foo"]',
});
});
it('strict mode true should capture the right path', async () => {
const router = express.Router({ strict: true });
router.use(['/foo/', '/foo'], resEndMiddleware);
app.use(router);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo', {
configuredRoute: '["/foo","/foo"]',
});
});
it('two regexp paths', async () => {
app.use([/^\/foo$/, '/foo'], resEndMiddleware);
const s = await sendRequest('/foo');
// should match the regexp as it is first in the list
expectRouteAttributes(s, '/^\\/foo$/', '/^\\/foo$/', {
configuredRoute: '["/^\\\\/foo$/","/foo"]',
});
});
it('multiple paths, non match', async () => {
// path not matched, so it will invoke finalhandler
app.use(['/bar', '/baz'], shouldNotInvokeMiddleware);
const s = await sendRequest('/foo');
expectRouteFromFinalHandler(s, '/foo');
});
it('multiple paths in multiple hierarchies', async () => {
const router1 = express.Router();
router1.all('/baz', resEndMiddleware);
const router2 = express.Router();
router2.use(['/foo2', '/bar2'], router1);
app.use(['/foo1', '/bar1'], router2);
const s = await sendRequest('/foo1/bar2/baz');
expectRouteAttributes(s, '/foo1/bar2/baz', '/foo1/bar2/baz', {
configuredRoute: '["/foo1","/bar1"]["/foo2","/bar2"]/baz',
});
});
});
describe('mounted app', () => {
it('app mounted under another app', async () => {
const mountedApp = express();
mountedApp.use('/route-in-internal-app', resEndMiddleware);
app.use('/mounted-app', mountedApp);
const s = await sendRequest('/mounted-app/route-in-internal-app/foo');
expectRouteAttributes(s, '/mounted-app/route-in-internal-app', '/mounted-app/route-in-internal-app/foo');
});
it('mounted app not invoked', async () => {
const mountedApp = express();
mountedApp.use('/route-in-internal-app', resEndMiddleware);
app.use('/mounted-app', mountedApp);
app.use('/foo', resEndMiddleware);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '/foo', '/foo');
});
});
describe('error middleware', () => {
it('error middleware under app', async () => {
app.use('/foo', errorMiddleware);
app.use(resEndErrorMiddleware);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '', '/foo');
});
it('error middleware under same use call', async () => {
app.use('/foo', errorMiddleware, resEndErrorMiddleware);
const s = await sendRequest('/foo');
// in this case '/foo' path has been consumed by the error middleware in use.
expectRouteAttributes(s, '/foo', '/foo');
});
it('error middleware under router', async () => {
const router = express.Router();
router.use('/bar', errorMiddleware);
router.use(resEndErrorMiddleware);
app.use('/foo', router);
const s = await sendRequest('/foo/bar/baz');
// in this case '/foo' path has been consumed by the error middleware in use.
expectRouteAttributes(s, '/foo', '/foo/bar/baz');
});
it('error in router, handled by app', async () => {
const router = express.Router();
router.use('/bar', errorMiddleware);
app.use('/foo', router);
app.use(resEndErrorMiddleware);
const s = await sendRequest('/foo/bar');
expectRouteAttributes(s, '', '/foo/bar');
});
it('error middleware that just forward the error', async () => {
app.use('/foo', errorMiddleware, noopErrorMiddleware);
app.use(resEndErrorMiddleware);
const s = await sendRequest('/foo');
expectRouteAttributes(s, '', '/foo');
});
});
describe('path params', () => {
it('single parameter in app', async () => {
app.use('/:id', resEndMiddleware);
const s = await sendRequest('/1234');
expectRouteAttributes(s, '/:id', '/:id', { expectedParams: { id: '1234' } });
});
it('multiple parameter in app', async () => {
app.use('/:id1/:id2', resEndMiddleware);
const s = await sendRequest('/1234/5678');
expectRouteAttributes(s, '/:id1/:id2', '/:id1/:id2', { expectedParams: { id1: '1234', id2: '5678' } });
});
it('params from router', async () => {
const router = express.Router();
router.use('/:id2', resEndMiddleware);
app.use('/:id1', router);
const s = await sendRequest('/1234/5678');
expectRouteAttributes(s, '/:id1/:id2', '/:id1/:id2', { expectedParams: { id1: '1234', id2: '5678' } });
});
describe('advanced path param', () => {
it('two params on same path part', async () => {
app.use('/:from-:to', resEndMiddleware);
const s = await sendRequest('/1234-5678');
expectRouteAttributes(s, '/:from-:to', '/:from-:to', { expectedParams: { from: '1234', to: '5678' } });
});
it('params with regexp', async () => {
app.use('/:idnumeric(\\d+)', resEndMiddleware);
const s = await sendRequest('/1234');
expectRouteAttributes(s, '/:idnumeric(\\d+)', '/:idnumeric(\\d+)', {
expectedParams: { idnumeric: '1234' },
});
});
});
describe('multiple path options', () => {
it('multiple matching path alternative should use first', async () => {
app.use(['/:idnumeric(\\d+)', '/:idnonnumeric'], resEndMiddleware);
const s = await sendRequest('/1234');
expectRouteAttributes(s, '/:idnumeric(\\d+)', '/:idnumeric(\\d+)', {
expectedParams: { idnumeric: '1234' },
configuredRoute: '["/:idnumeric(\\\\d+)","/:idnonnumeric"]',
});
});
it('multiple path alternative second matches', async () => {
app.use(['/:idnumeric(\\d+)', '/:idnonnumeric'], resEndMiddleware);
const s = await sendRequest('/text');
expectRouteAttributes(s, '/:idnonnumeric', '/:idnonnumeric', {
expectedParams: { idnonnumeric: 'text' },
configuredRoute: '["/:idnumeric(\\\\d+)","/:idnonnumeric"]',
});
});
});
describe('param hiding', () => {
it('same param name multiple times should capture last value', async () => {
const router = express.Router();
router.use('/:id', resEndMiddleware);
app.use('/:id', router);
const s = await sendRequest('/1234/5678');
expectRouteAttributes(s, '/:id/:id', '/:id/:id', { expectedParams: { id: '5678' } });
});
});
});
describe('async middlewares', () => {
it('res.end called from async context should create correct route', async () => {
app.use('/foo', async (req, res) => {
await new Promise((resolve) => setTimeout(resolve, 1));
res.sendStatus(200);
});
const s = await sendRequest('/foo/bar');
expectRouteAttributes(s, '/foo', '/foo/bar');
});
it('res.end from callback', async () => {
app.use('/foo', (req, res) => {
setTimeout(() => res.sendStatus(200), 2);
});
const s = await sendRequest('/foo/bar');
expectRouteAttributes(s, '/foo', '/foo/bar');
});
it('res.end from promise then', async () => {
app.use('/foo', (req, res) => {
new Promise((resolve) => setTimeout(resolve, 2)).then(() => res.sendStatus(200));
});
const s = await sendRequest('/foo/bar');
expectRouteAttributes(s, '/foo', '/foo/bar');
});
it('multiple async calling each other', async () => {
const router = express.Router();
router.all('/bar', async (req, res, next) => {
await new Promise((resolve) => setTimeout(resolve, 1));
res.sendStatus(200);
});
app.use(
'/foo',
async (req, res, next) => {
new Promise((resolve) => setTimeout(resolve, 2));
next();
},
router
);
const s = await sendRequest('/foo/bar');
expectRouteAttributes(s, '/foo/bar', '/foo/bar');
});
it('async middleware runs in parallel to downstream middlewares', async () => {
app.use('/foo', (req, res, next) => {
next();
new Promise((resolve) => setTimeout(resolve, 2)).then(() => res.sendStatus(200));
res.sendStatus(200);
});
app.use((req, res, next) => {
/* terminate middleware chain */
});
const s = await sendRequest('/foo/bar');
expectRouteAttributes(s, '/foo', '/foo/bar');
});
it('thenable should not break the context', async () => {
app.use('/foo', async (req, res, next) => {
const thenable = {
then: function (onFulfilled) {
setTimeout(onFulfilled, 2);
},
};
// @ts-ignore
await Promise.resolve(thenable);
res.sendStatus(200);
});
const s = await sendRequest('/foo/bar');
expectRouteAttributes(s, '/foo', '/foo/bar');
});
});
});
Example #24
Source File: pluginsServer.ts From posthog-foss with MIT License | 4 votes |
export async function startPluginsServer(
config: Partial<PluginsServerConfig>,
makePiscina: (config: PluginsServerConfig) => Piscina
): Promise<ServerInstance> {
const serverConfig: PluginsServerConfig = {
...defaultConfig,
...config,
}
status.info('ℹ️', `${serverConfig.WORKER_CONCURRENCY} workers, ${serverConfig.TASKS_PER_WORKER} tasks per worker`)
let pubSub: PubSub | undefined
let hub: Hub | undefined
let actionsReloadJob: schedule.Job | undefined
let pingJob: schedule.Job | undefined
let piscinaStatsJob: schedule.Job | undefined
let internalMetricsStatsJob: schedule.Job | undefined
let pluginMetricsJob: schedule.Job | undefined
let piscina: Piscina | undefined
let queue: Queue | undefined // ingestion queue
let redisQueueForPluginJobs: Queue | undefined | null
let jobQueueConsumer: JobQueueConsumerControl | undefined
let closeHub: () => Promise<void> | undefined
let scheduleControl: ScheduleControl | undefined
let mmdbServer: net.Server | undefined
let lastActivityCheck: NodeJS.Timeout | undefined
let shutdownStatus = 0
async function closeJobs(): Promise<void> {
shutdownStatus += 1
if (shutdownStatus === 2) {
status.info('?', 'Try again to shut down forcibly')
return
}
if (shutdownStatus >= 3) {
status.info('❗️', 'Shutting down forcibly!')
void piscina?.destroy()
process.exit()
}
status.info('?', ' Shutting down gracefully...')
lastActivityCheck && clearInterval(lastActivityCheck)
await queue?.stop()
await redisQueueForPluginJobs?.stop()
await pubSub?.stop()
actionsReloadJob && schedule.cancelJob(actionsReloadJob)
pingJob && schedule.cancelJob(pingJob)
pluginMetricsJob && schedule.cancelJob(pluginMetricsJob)
statusReport.stopStatusReportSchedule()
piscinaStatsJob && schedule.cancelJob(piscinaStatsJob)
internalMetricsStatsJob && schedule.cancelJob(internalMetricsStatsJob)
await jobQueueConsumer?.stop()
await scheduleControl?.stopSchedule()
await new Promise<void>((resolve, reject) =>
!mmdbServer
? resolve()
: mmdbServer.close((error) => {
if (error) {
reject(error)
} else {
status.info('?', 'Closed internal MMDB server!')
resolve()
}
})
)
if (piscina) {
await stopPiscina(piscina)
}
await closeHub?.()
status.info('?', 'Over and out!')
// wait an extra second for any misc async task to finish
await delay(1000)
}
for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
process.on(signal, () => process.emit('beforeExit', 0))
}
process.on('beforeExit', async () => {
// This makes async exit possible with the process waiting until jobs are closed
await closeJobs()
process.exit(0)
})
try {
;[hub, closeHub] = await createHub(serverConfig, null)
const serverInstance: Partial<ServerInstance> & Pick<ServerInstance, 'hub'> = {
hub,
}
if (!serverConfig.DISABLE_MMDB) {
serverInstance.mmdb = (await prepareMmdb(serverInstance)) ?? undefined
serverInstance.mmdbUpdateJob = schedule.scheduleJob(
'0 */4 * * *',
async () => await performMmdbStalenessCheck(serverInstance)
)
mmdbServer = await createMmdbServer(serverInstance)
serverConfig.INTERNAL_MMDB_SERVER_PORT = (mmdbServer.address() as AddressInfo).port
hub.INTERNAL_MMDB_SERVER_PORT = serverConfig.INTERNAL_MMDB_SERVER_PORT
}
piscina = makePiscina(serverConfig)
scheduleControl = await startSchedule(hub, piscina)
jobQueueConsumer = await startJobQueueConsumer(hub, piscina)
const queues = await startQueues(hub, piscina)
// `queue` refers to the ingestion queue. With Celery ingestion, we only
// have one queue for plugin jobs and ingestion. With Kafka ingestion, we
// use Kafka for events but still start Redis for plugin jobs.
// Thus, if Kafka is disabled, we don't need to call anything on
// redisQueueForPluginJobs, as that will also be the ingestion queue.
queue = queues.ingestion
redisQueueForPluginJobs = config.KAFKA_ENABLED ? queues.auxiliary : null
piscina.on('drain', () => {
void queue?.resume()
void redisQueueForPluginJobs?.resume()
void jobQueueConsumer?.resume()
})
// use one extra Redis connection for pub-sub
pubSub = new PubSub(hub, {
[hub.PLUGINS_RELOAD_PUBSUB_CHANNEL]: async () => {
status.info('⚡', 'Reloading plugins!')
await piscina?.broadcastTask({ task: 'reloadPlugins' })
await scheduleControl?.reloadSchedule()
},
'reload-action': async (message) =>
await piscina?.broadcastTask({ task: 'reloadAction', args: JSON.parse(message) }),
'drop-action': async (message) =>
await piscina?.broadcastTask({ task: 'dropAction', args: JSON.parse(message) }),
'plugins-alert': async (message) =>
await piscina?.run({ task: 'handleAlert', args: { alert: JSON.parse(message) } }),
})
await pubSub.start()
if (hub.jobQueueManager) {
const queueString = hub.jobQueueManager.getJobQueueTypesAsString()
await hub!.db!.redisSet('@posthog-plugin-server/enabled-job-queues', queueString)
}
// every 5 minutes all ActionManager caches are reloaded for eventual consistency
actionsReloadJob = schedule.scheduleJob('*/5 * * * *', async () => {
await piscina?.broadcastTask({ task: 'reloadAllActions' })
})
// every 5 seconds set Redis keys @posthog-plugin-server/ping and @posthog-plugin-server/version
pingJob = schedule.scheduleJob('*/5 * * * * *', async () => {
await hub!.db!.redisSet('@posthog-plugin-server/ping', new Date().toISOString(), 60, {
jsonSerialize: false,
})
await hub!.db!.redisSet('@posthog-plugin-server/version', version, undefined, { jsonSerialize: false })
})
// every 10 seconds sends stuff to StatsD
piscinaStatsJob = schedule.scheduleJob('*/10 * * * * *', () => {
if (piscina) {
for (const [key, value] of Object.entries(getPiscinaStats(piscina))) {
hub!.statsd?.gauge(`piscina.${key}`, value)
}
}
})
// every minute flush internal metrics
if (hub.internalMetrics) {
internalMetricsStatsJob = schedule.scheduleJob('0 * * * * *', async () => {
await hub!.internalMetrics?.flush(piscina!)
})
}
pluginMetricsJob = schedule.scheduleJob('*/30 * * * *', async () => {
await piscina!.broadcastTask({ task: 'sendPluginMetrics' })
})
if (serverConfig.STALENESS_RESTART_SECONDS > 0) {
// check every 10 sec how long it has been since the last activity
let lastFoundActivity: number
lastActivityCheck = setInterval(() => {
if (
hub?.lastActivity &&
new Date().valueOf() - hub?.lastActivity > serverConfig.STALENESS_RESTART_SECONDS * 1000 &&
lastFoundActivity !== hub?.lastActivity
) {
lastFoundActivity = hub?.lastActivity
const extra = {
instanceId: hub.instanceId.toString(),
lastActivity: hub.lastActivity ? new Date(hub.lastActivity).toISOString() : null,
lastActivityType: hub.lastActivityType,
piscina: piscina ? JSON.stringify(getPiscinaStats(piscina)) : null,
}
Sentry.captureMessage(
`Plugin Server has not ingested events for over ${serverConfig.STALENESS_RESTART_SECONDS} seconds! Rebooting.`,
{
extra,
}
)
console.log(
`Plugin Server has not ingested events for over ${serverConfig.STALENESS_RESTART_SECONDS} seconds! Rebooting.`,
extra
)
hub.statsd?.increment(`alerts.stale_plugin_server_restarted`)
killProcess()
}
}, Math.min(serverConfig.STALENESS_RESTART_SECONDS, 10000))
}
serverInstance.piscina = piscina
serverInstance.queue = queue
serverInstance.stop = closeJobs
status.info('?', 'All systems go')
hub.lastActivity = new Date().valueOf()
hub.lastActivityType = 'serverStart'
return serverInstance as ServerInstance
} catch (error) {
Sentry.captureException(error)
status.error('?', 'Launchpad failure!', error)
void Sentry.flush().catch(() => null) // Flush Sentry in the background
await closeJobs()
process.exit(1)
}
}
Example #25
Source File: impl.ts From kassette with MIT License | 4 votes |
////////////////////////////////////////////////////////////////////////////////
// Server
// FIXME 2018-09-25T14:00:24+02:00
// Multiple requests will be received in parallel, leading to a messed up output in the console...
// they should be queued and processed one by one to avoid that
////////////////////////////////////////////////////////////////////////////////
/** Spawns the server */
export async function spawnServer({ configuration, root }: ApplicationData): Promise<Server> {
const server = createServer(async (request, response) => {
const mock = new Mock({
options: { root, userConfiguration: configuration },
request: new Request(request, await readAll(request)),
response: new Response(response),
});
logInfo({
timestamp: true,
message: CONF.messages.handlingRequest,
data: `${request.method} ${request.url}`,
});
await configuration.hook.value({ mock, console: getConsole() });
await mock.process();
});
const tlsManager = new TLSManager({
tlsCAKeyPath: configuration.tlsCAKeyPath.value,
tlsKeySize: configuration.tlsKeySize.value,
root,
});
await tlsManager.init();
const handleSocket = (socket: Socket) => {
socket.once('data', async (data) => {
socket.pause();
socket.unshift(data);
// cf https://github.com/mscdex/httpolyglot/issues/3#issuecomment-173680155
// HTTPS:
if (data.readUInt8(0) === 22) {
socket = await tlsManager.process(socket);
setConnectionProtocol(socket, 'https');
} else {
await Promise.resolve();
setConnectionProtocol(socket, 'http');
}
server.emit('connection', socket);
socket.resume();
});
socket.on('error', (exception) => logError({ message: CONF.messages.socketError, exception }));
};
server.on('connect', async (request: IncomingMessage, socket: Socket, data: Buffer) => {
if (data.length > 0) {
socket.unshift(data);
}
const api = new ProxyConnectAPI(request, configuration.proxyConnectMode.value, handleSocket);
logInfo({
timestamp: true,
message: CONF.messages.handlingRequest,
data: `${request.method} ${request.url}`,
});
await configuration.onProxyConnect.value(api);
api.process();
});
// server that can receive both http and https connections
const netServer = createNetServer(handleSocket);
netServer.listen(configuration.port.value, configuration.hostname.value, function (this: Socket) {
const port = (this.address() as AddressInfo).port;
configuration.onListen.value({ port });
logInfo({ message: CONF.messages.listening, data: port });
logSeparator();
});
netServer.on('close', () => configuration.onExit.value());
return netServer;
}