chore: migrate http server to ts (#17677)
This commit is contained in:
parent
6fc7d20e35
commit
cadd4d1dd0
19
package-lock.json
generated
19
package-lock.json
generated
|
|
@ -26,6 +26,7 @@
|
||||||
"@types/react": "^18.0.12",
|
"@types/react": "^18.0.12",
|
||||||
"@types/react-dom": "^18.0.5",
|
"@types/react-dom": "^18.0.5",
|
||||||
"@types/resize-observer-browser": "^0.1.7",
|
"@types/resize-observer-browser": "^0.1.7",
|
||||||
|
"@types/ws": "^8.5.3",
|
||||||
"@types/xml2js": "^0.4.9",
|
"@types/xml2js": "^0.4.9",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.10.2",
|
"@typescript-eslint/eslint-plugin": "^5.10.2",
|
||||||
"@typescript-eslint/parser": "^5.10.2",
|
"@typescript-eslint/parser": "^5.10.2",
|
||||||
|
|
@ -1294,6 +1295,15 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/ws": {
|
||||||
|
"version": "8.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
|
||||||
|
"integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/xml2js": {
|
"node_modules/@types/xml2js": {
|
||||||
"version": "0.4.9",
|
"version": "0.4.9",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
|
@ -9205,6 +9215,15 @@
|
||||||
"version": "0.16.1",
|
"version": "0.16.1",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/ws": {
|
||||||
|
"version": "8.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
|
||||||
|
"integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/xml2js": {
|
"@types/xml2js": {
|
||||||
"version": "0.4.9",
|
"version": "0.4.9",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@
|
||||||
"@types/react": "^18.0.12",
|
"@types/react": "^18.0.12",
|
||||||
"@types/react-dom": "^18.0.5",
|
"@types/react-dom": "^18.0.5",
|
||||||
"@types/resize-observer-browser": "^0.1.7",
|
"@types/resize-observer-browser": "^0.1.7",
|
||||||
|
"@types/ws": "^8.5.3",
|
||||||
"@types/xml2js": "^0.4.9",
|
"@types/xml2js": "^0.4.9",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.10.2",
|
"@typescript-eslint/eslint-plugin": "^5.10.2",
|
||||||
"@typescript-eslint/parser": "^5.10.2",
|
"@typescript-eslint/parser": "^5.10.2",
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ServerResponse } from '../../utils/testserver';
|
import type { ServerResponse } from 'http';
|
||||||
import { test as it, expect } from './pageTest';
|
import { test as it, expect } from './pageTest';
|
||||||
|
|
||||||
it('Page.Events.Request @smoke', async ({ page, server }) => {
|
it('Page.Events.Request @smoke', async ({ page, server }) => {
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,26 @@
|
||||||
const { TestServer } = require('../../../utils/testserver/');
|
const http = require('http');
|
||||||
TestServer.create(__dirname, process.argv[2] || 3000).then(server => {
|
|
||||||
console.log('listening on port', server.PORT);
|
const port = process.argv[2] || 3000;
|
||||||
let ready = false;
|
|
||||||
setTimeout(() => ready = true, 750);
|
let ready = false;
|
||||||
server.setRoute('/ready', (message, response) => {
|
setTimeout(() => ready = true, 750);
|
||||||
|
|
||||||
|
const requestListener = function (req, res) {
|
||||||
|
if (req.url === '/ready') {
|
||||||
if (ready) {
|
if (ready) {
|
||||||
response.statusCode = 200;
|
res.writeHead(200);
|
||||||
response.end('hello');
|
res.end('hello');
|
||||||
} else {
|
} else {
|
||||||
response.statusCode = 404;
|
res.writeHead(404);
|
||||||
response.end('not-ready');
|
res.end('not-ready');
|
||||||
}
|
}
|
||||||
});
|
} else {
|
||||||
|
res.writeHead(404);
|
||||||
|
res.end();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const server = http.createServer(requestListener);
|
||||||
|
server.listen(port, () => {
|
||||||
|
console.log('listening on port', port);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,36 @@
|
||||||
const { TestServer } = require('../../../utils/testserver/');
|
const http = require('http');
|
||||||
|
|
||||||
console.error('error from server');
|
console.error('error from server');
|
||||||
|
|
||||||
|
const port = process.argv[2] || 3000;
|
||||||
|
|
||||||
|
const requestListener = function (req, res) {
|
||||||
|
if (req.url === '/hello') {
|
||||||
|
res.end('hello');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (req.url === '/env-FOO') {
|
||||||
|
res.end(process.env.FOO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (req.url === '/port') {
|
||||||
|
res.end('' + port);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (req.url === '/redirect') {
|
||||||
|
res.writeHead(301, 'Moved');
|
||||||
|
res.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.writeHead(404);
|
||||||
|
res.end();
|
||||||
|
};
|
||||||
|
|
||||||
|
const server = http.createServer(requestListener);
|
||||||
|
|
||||||
// delay creating the server to test waiting for it
|
// delay creating the server to test waiting for it
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
TestServer.create(__dirname, process.argv[2] || 3000).then(server => {
|
server.listen(port, () => {
|
||||||
console.log('listening on port', server.PORT);
|
console.log('listening on port', port);
|
||||||
server.setRoute('/hello', (message, response) => {
|
|
||||||
response.end('hello');
|
|
||||||
});
|
|
||||||
server.setRoute('/env-FOO', (message, response) => {
|
|
||||||
response.end(process.env.FOO);
|
|
||||||
});
|
|
||||||
server.setRoute('/port', (_, response) => {
|
|
||||||
response.end('' + server.PORT);
|
|
||||||
});
|
|
||||||
server.setRoute('/redirect', (_, response) => {
|
|
||||||
response.writeHead(301, 'Moved');
|
|
||||||
response.end();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}, process.argv[3] ? +process.argv[3] : 0);
|
}, process.argv[3] ? +process.argv[3] : 0);
|
||||||
|
|
|
||||||
44
utils/testserver/index.d.ts
vendored
44
utils/testserver/index.d.ts
vendored
|
|
@ -1,44 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) Microsoft Corporation.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
type ServerResponse = import('http').ServerResponse;
|
|
||||||
type IncomingMessage = import('http').IncomingMessage;
|
|
||||||
import WebSocket from 'ws';
|
|
||||||
|
|
||||||
export class TestServer {
|
|
||||||
static create(dirPath: string, port: number, loopback?: string): Promise<TestServer>;
|
|
||||||
static createHTTPS(dirPath: string, port: number, loopback?: string): Promise<TestServer>;
|
|
||||||
enableHTTPCache(pathPrefix: string);
|
|
||||||
setAuth(path: string, username: string, password: string);
|
|
||||||
enableGzip(path: string);
|
|
||||||
setCSP(path: string, csp: string);
|
|
||||||
setExtraHeaders(path: string, headers: { [key: string]: string });
|
|
||||||
stop(): Promise<void>;
|
|
||||||
setRoute(path: string, handler: (message: IncomingMessage & { postBody: Promise<Buffer> }, response: ServerResponse) => void);
|
|
||||||
setRedirect(from: string, to: string);
|
|
||||||
waitForRequest(path: string): Promise<IncomingMessage & { postBody: Promise<Buffer> }>;
|
|
||||||
waitForWebSocketConnectionRequest(): Promise<IncomingMessage>;
|
|
||||||
onceWebSocketConnection(handler: (ws: WebSocket, request: IncomingMessage) => void);
|
|
||||||
sendOnWebSocketConnection(data: string);
|
|
||||||
reset();
|
|
||||||
serveFile(request: IncomingMessage, response: ServerResponse);
|
|
||||||
serveFile(request: IncomingMessage, response: ServerResponse, filePath: string);
|
|
||||||
|
|
||||||
PORT: number;
|
|
||||||
PREFIX: string;
|
|
||||||
CROSS_PROCESS_PREFIX: string;
|
|
||||||
EMPTY_PAGE: string;
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* Copyright 2017 Google Inc. All rights reserved.
|
* Copyright 2017 Google Inc. All rights reserved.
|
||||||
|
* Modifications copyright (c) Microsoft Corporation.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -14,41 +15,48 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const http = require('http');
|
import fs from 'fs';
|
||||||
const https = require('https');
|
import http from 'http';
|
||||||
const url = require('url');
|
import https from 'https';
|
||||||
const fs = require('fs');
|
import mime from 'mime';
|
||||||
const path = require('path');
|
import type net from 'net';
|
||||||
const zlib = require('zlib');
|
import path from 'path';
|
||||||
const util = require('util');
|
import url from 'url';
|
||||||
const mime = require('mime');
|
import util from 'util';
|
||||||
const WebSocketServer = require('ws').Server;
|
import ws from 'ws';
|
||||||
|
import zlib, { gzip } from 'zlib';
|
||||||
|
|
||||||
const fulfillSymbol = Symbol('fullfil callback');
|
const fulfillSymbol = Symbol('fullfil callback');
|
||||||
const rejectSymbol = Symbol('reject callback');
|
const rejectSymbol = Symbol('reject callback');
|
||||||
|
|
||||||
const gzipAsync = util.promisify(zlib.gzip.bind(zlib));
|
const gzipAsync = util.promisify(gzip.bind(zlib));
|
||||||
|
|
||||||
class TestServer {
|
export class TestServer {
|
||||||
/**
|
private _server: http.Server;
|
||||||
* @param {string} dirPath
|
private _wsServer: ws.WebSocketServer;
|
||||||
* @param {number} port
|
private _dirPath: string;
|
||||||
* @param {string=} loopback
|
readonly debugServer: any;
|
||||||
* @return {!Promise<TestServer>}
|
private _startTime: Date;
|
||||||
*/
|
private _cachedPathPrefix: string | null;
|
||||||
static async create(dirPath, port, loopback) {
|
private _sockets = new Set<net.Socket>();
|
||||||
|
private _routes = new Map<string, (arg0: http.IncomingMessage, arg1: http.ServerResponse) => any>();
|
||||||
|
private _auths = new Map<string, { username: string; password: string; }>();
|
||||||
|
private _csp = new Map<string, string>();
|
||||||
|
private _extraHeaders = new Map<string, object>();
|
||||||
|
private _gzipRoutes = new Set<string>();
|
||||||
|
private _requestSubscribers = new Map<string, Promise<any>>();
|
||||||
|
readonly PORT: number;
|
||||||
|
readonly PREFIX: string;
|
||||||
|
readonly CROSS_PROCESS_PREFIX: string;
|
||||||
|
readonly EMPTY_PAGE: string;
|
||||||
|
|
||||||
|
static async create(dirPath: string, port: number, loopback?: string): Promise<TestServer> {
|
||||||
const server = new TestServer(dirPath, port, loopback);
|
const server = new TestServer(dirPath, port, loopback);
|
||||||
await new Promise(x => server._server.once('listening', x));
|
await new Promise(x => server._server.once('listening', x));
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static async createHTTPS(dirPath: string, port: number, loopback?: string): Promise<TestServer> {
|
||||||
* @param {string} dirPath
|
|
||||||
* @param {number} port
|
|
||||||
* @param {string=} loopback
|
|
||||||
* @return {!Promise<TestServer>}
|
|
||||||
*/
|
|
||||||
static async createHTTPS(dirPath, port, loopback) {
|
|
||||||
const server = new TestServer(dirPath, port, loopback, {
|
const server = new TestServer(dirPath, port, loopback, {
|
||||||
key: await fs.promises.readFile(path.join(__dirname, 'key.pem')),
|
key: await fs.promises.readFile(path.join(__dirname, 'key.pem')),
|
||||||
cert: await fs.promises.readFile(path.join(__dirname, 'cert.pem')),
|
cert: await fs.promises.readFile(path.join(__dirname, 'cert.pem')),
|
||||||
|
|
@ -58,21 +66,15 @@ class TestServer {
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
constructor(dirPath: string, port: number, loopback?: string, sslOptions?: object) {
|
||||||
* @param {string} dirPath
|
|
||||||
* @param {number} port
|
|
||||||
* @param {string=} loopback
|
|
||||||
* @param {!Object=} sslOptions
|
|
||||||
*/
|
|
||||||
constructor(dirPath, port, loopback, sslOptions) {
|
|
||||||
if (sslOptions)
|
if (sslOptions)
|
||||||
this._server = https.createServer(sslOptions, this._onRequest.bind(this));
|
this._server = https.createServer(sslOptions, this._onRequest.bind(this));
|
||||||
else
|
else
|
||||||
this._server = http.createServer(this._onRequest.bind(this));
|
this._server = http.createServer(this._onRequest.bind(this));
|
||||||
this._server.on('connection', socket => this._onSocket(socket));
|
this._server.on('connection', socket => this._onSocket(socket));
|
||||||
this._wsServer = new WebSocketServer({ noServer: true });
|
this._wsServer = new ws.WebSocketServer({ noServer: true });
|
||||||
this._server.on('upgrade', async (request, socket, head) => {
|
this._server.on('upgrade', async (request, socket, head) => {
|
||||||
const pathname = url.parse(request.url).pathname;
|
const pathname = url.parse(request.url!).path;
|
||||||
if (pathname === '/ws-slow')
|
if (pathname === '/ws-slow')
|
||||||
await new Promise(f => setTimeout(f, 2000));
|
await new Promise(f => setTimeout(f, 2000));
|
||||||
if (!['/ws', '/ws-slow'].includes(pathname)) {
|
if (!['/ws', '/ws-slow'].includes(pathname)) {
|
||||||
|
|
@ -92,21 +94,6 @@ class TestServer {
|
||||||
this._startTime = new Date();
|
this._startTime = new Date();
|
||||||
this._cachedPathPrefix = null;
|
this._cachedPathPrefix = null;
|
||||||
|
|
||||||
/** @type {!Set<!NodeJS.Socket>} */
|
|
||||||
this._sockets = new Set();
|
|
||||||
/** @type {!Map<string, function(!http.IncomingMessage,http.ServerResponse)>} */
|
|
||||||
this._routes = new Map();
|
|
||||||
/** @type {!Map<string, !{username:string, password:string}>} */
|
|
||||||
this._auths = new Map();
|
|
||||||
/** @type {!Map<string, string>} */
|
|
||||||
this._csp = new Map();
|
|
||||||
/** @type {!Map<string, Object>} */
|
|
||||||
this._extraHeaders = new Map();
|
|
||||||
/** @type {!Set<string>} */
|
|
||||||
this._gzipRoutes = new Set();
|
|
||||||
/** @type {!Map<string, !Promise>} */
|
|
||||||
this._requestSubscribers = new Map();
|
|
||||||
|
|
||||||
const cross_origin = loopback || '127.0.0.1';
|
const cross_origin = loopback || '127.0.0.1';
|
||||||
const same_origin = loopback || 'localhost';
|
const same_origin = loopback || 'localhost';
|
||||||
const protocol = sslOptions ? 'https' : 'http';
|
const protocol = sslOptions ? 'https' : 'http';
|
||||||
|
|
@ -116,51 +103,35 @@ class TestServer {
|
||||||
this.EMPTY_PAGE = `${protocol}://${same_origin}:${port}/empty.html`;
|
this.EMPTY_PAGE = `${protocol}://${same_origin}:${port}/empty.html`;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onSocket(socket) {
|
_onSocket(socket: net.Socket) {
|
||||||
this._sockets.add(socket);
|
this._sockets.add(socket);
|
||||||
// ECONNRESET and HPE_INVALID_EOF_STATE are legit errors given
|
// ECONNRESET and HPE_INVALID_EOF_STATE are legit errors given
|
||||||
// that tab closing aborts outgoing connections to the server.
|
// that tab closing aborts outgoing connections to the server.
|
||||||
socket.on('error', error => {
|
socket.on('error', error => {
|
||||||
if (error.code !== 'ECONNRESET' && error.code !== 'HPE_INVALID_EOF_STATE')
|
if ((error as any).code !== 'ECONNRESET' && (error as any).code !== 'HPE_INVALID_EOF_STATE')
|
||||||
throw error;
|
throw error;
|
||||||
});
|
});
|
||||||
socket.once('close', () => this._sockets.delete(socket));
|
socket.once('close', () => this._sockets.delete(socket));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
enableHTTPCache(pathPrefix: string) {
|
||||||
* @param {string} pathPrefix
|
|
||||||
*/
|
|
||||||
enableHTTPCache(pathPrefix) {
|
|
||||||
this._cachedPathPrefix = pathPrefix;
|
this._cachedPathPrefix = pathPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
setAuth(path: string, username: string, password: string) {
|
||||||
* @param {string} path
|
|
||||||
* @param {string} username
|
|
||||||
* @param {string} password
|
|
||||||
*/
|
|
||||||
setAuth(path, username, password) {
|
|
||||||
this.debugServer(`set auth for ${path} to ${username}:${password}`);
|
this.debugServer(`set auth for ${path} to ${username}:${password}`);
|
||||||
this._auths.set(path, {username, password});
|
this._auths.set(path, { username, password });
|
||||||
}
|
}
|
||||||
|
|
||||||
enableGzip(path) {
|
enableGzip(path: string) {
|
||||||
this._gzipRoutes.add(path);
|
this._gzipRoutes.add(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
setCSP(path: string, csp: string) {
|
||||||
* @param {string} path
|
|
||||||
* @param {string} csp
|
|
||||||
*/
|
|
||||||
setCSP(path, csp) {
|
|
||||||
this._csp.set(path, csp);
|
this._csp.set(path, csp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
setExtraHeaders(path: string, object: Record<string, string>) {
|
||||||
* @param {string} path
|
|
||||||
* @param {Object<string, string>} object
|
|
||||||
*/
|
|
||||||
setExtraHeaders(path, object) {
|
|
||||||
this._extraHeaders.set(path, object);
|
this._extraHeaders.set(path, object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -172,31 +143,19 @@ class TestServer {
|
||||||
await new Promise(x => this._server.close(x));
|
await new Promise(x => this._server.close(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
setRoute(path: string, handler: (arg0: http.IncomingMessage & { postBody: Promise<Buffer> }, arg1: http.ServerResponse) => any) {
|
||||||
* @param {string} path
|
|
||||||
* @param {function(!http.IncomingMessage,http.ServerResponse)} handler
|
|
||||||
*/
|
|
||||||
setRoute(path, handler) {
|
|
||||||
this._routes.set(path, handler);
|
this._routes.set(path, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
setRedirect(from: string, to: string) {
|
||||||
* @param {string} from
|
|
||||||
* @param {string} to
|
|
||||||
*/
|
|
||||||
setRedirect(from, to) {
|
|
||||||
this.setRoute(from, (req, res) => {
|
this.setRoute(from, (req, res) => {
|
||||||
let headers = this._extraHeaders.get(req.url) || {};
|
const headers = this._extraHeaders.get(req.url!) || {};
|
||||||
res.writeHead(302, { ...headers, location: to });
|
res.writeHead(302, { ...headers, location: to });
|
||||||
res.end();
|
res.end();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
waitForRequest(path: string): Promise<http.IncomingMessage & { postBody: Promise<Buffer> }> {
|
||||||
* @param {string} path
|
|
||||||
* @return {!Promise<!http.IncomingMessage>}
|
|
||||||
*/
|
|
||||||
waitForRequest(path) {
|
|
||||||
let promise = this._requestSubscribers.get(path);
|
let promise = this._requestSubscribers.get(path);
|
||||||
if (promise)
|
if (promise)
|
||||||
return promise;
|
return promise;
|
||||||
|
|
@ -223,28 +182,24 @@ class TestServer {
|
||||||
this._requestSubscribers.clear();
|
this._requestSubscribers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
_onRequest(request: http.IncomingMessage, response: http.ServerResponse) {
|
||||||
* @param {http.IncomingMessage} request
|
|
||||||
* @param {http.ServerResponse} response
|
|
||||||
*/
|
|
||||||
_onRequest(request, response) {
|
|
||||||
request.on('error', error => {
|
request.on('error', error => {
|
||||||
if (error.code === 'ECONNRESET')
|
if ((error as any).code === 'ECONNRESET')
|
||||||
response.end();
|
response.end();
|
||||||
else
|
else
|
||||||
throw error;
|
throw error;
|
||||||
});
|
});
|
||||||
request.postBody = new Promise(resolve => {
|
(request as any).postBody = new Promise(resolve => {
|
||||||
const chunks = [];
|
const chunks: Buffer[] = [];
|
||||||
request.on('data', chunk => {
|
request.on('data', chunk => {
|
||||||
chunks.push(chunk);
|
chunks.push(chunk);
|
||||||
});
|
});
|
||||||
request.on('end', () => resolve(Buffer.concat(chunks)));
|
request.on('end', () => resolve(Buffer.concat(chunks)));
|
||||||
});
|
});
|
||||||
const path = url.parse(request.url).path;
|
const path = url.parse(request.url!).path;
|
||||||
this.debugServer(`request ${request.method} ${path}`);
|
this.debugServer(`request ${request.method} ${path}`);
|
||||||
if (this._auths.has(path)) {
|
if (this._auths.has(path)) {
|
||||||
const auth = this._auths.get(path);
|
const auth = this._auths.get(path)!;
|
||||||
const credentials = Buffer.from((request.headers.authorization || '').split(' ')[1] || '', 'base64').toString();
|
const credentials = Buffer.from((request.headers.authorization || '').split(' ')[1] || '', 'base64').toString();
|
||||||
this.debugServer(`request credentials ${credentials}`);
|
this.debugServer(`request credentials ${credentials}`);
|
||||||
this.debugServer(`actual credentials ${auth.username}:${auth.password}`);
|
this.debugServer(`actual credentials ${auth.username}:${auth.password}`);
|
||||||
|
|
@ -257,24 +212,18 @@ class TestServer {
|
||||||
}
|
}
|
||||||
// Notify request subscriber.
|
// Notify request subscriber.
|
||||||
if (this._requestSubscribers.has(path)) {
|
if (this._requestSubscribers.has(path)) {
|
||||||
this._requestSubscribers.get(path)[fulfillSymbol].call(null, request);
|
this._requestSubscribers.get(path)![fulfillSymbol].call(null, request);
|
||||||
this._requestSubscribers.delete(path);
|
this._requestSubscribers.delete(path);
|
||||||
}
|
}
|
||||||
const handler = this._routes.get(path);
|
const handler = this._routes.get(path);
|
||||||
if (handler) {
|
if (handler)
|
||||||
handler.call(null, request, response);
|
handler.call(null, request, response);
|
||||||
} else {
|
else
|
||||||
this.serveFile(request, response);
|
this.serveFile(request, response);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
async serveFile(request: http.IncomingMessage, response: http.ServerResponse, filePath?: string) {
|
||||||
* @param {!http.IncomingMessage} request
|
let pathName = url.parse(request.url!).path;
|
||||||
* @param {!http.ServerResponse} response
|
|
||||||
* @param {string|undefined} filePath
|
|
||||||
*/
|
|
||||||
async serveFile(request, response, filePath) {
|
|
||||||
let pathName = url.parse(request.url).path;
|
|
||||||
if (!filePath) {
|
if (!filePath) {
|
||||||
if (pathName === '/')
|
if (pathName === '/')
|
||||||
pathName = '/index.html';
|
pathName = '/index.html';
|
||||||
|
|
@ -293,7 +242,7 @@ class TestServer {
|
||||||
response.setHeader('Cache-Control', 'no-cache, no-store');
|
response.setHeader('Cache-Control', 'no-cache, no-store');
|
||||||
}
|
}
|
||||||
if (this._csp.has(pathName))
|
if (this._csp.has(pathName))
|
||||||
response.setHeader('Content-Security-Policy', this._csp.get(pathName));
|
response.setHeader('Content-Security-Policy', this._csp.get(pathName)!);
|
||||||
|
|
||||||
if (this._extraHeaders.has(pathName)) {
|
if (this._extraHeaders.has(pathName)) {
|
||||||
const object = this._extraHeaders.get(pathName);
|
const object = this._extraHeaders.get(pathName);
|
||||||
|
|
@ -301,7 +250,7 @@ class TestServer {
|
||||||
response.setHeader(key, object[key]);
|
response.setHeader(key, object[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {err, data} = await fs.promises.readFile(filePath).then(data => ({data})).catch(err => ({err}));
|
const { err, data } = await fs.promises.readFile(filePath).then(data => ({ data, err: undefined })).catch(err => ({ data: undefined, err }));
|
||||||
// The HTTP transaction might be already terminated after async hop here - do nothing in this case.
|
// The HTTP transaction might be already terminated after async hop here - do nothing in this case.
|
||||||
if (response.writableEnded)
|
if (response.writableEnded)
|
||||||
return;
|
return;
|
||||||
|
|
@ -332,7 +281,7 @@ class TestServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForWebSocketConnectionRequest() {
|
waitForWebSocketConnectionRequest() {
|
||||||
return new Promise(fullfil => {
|
return new Promise<http.IncomingMessage & { headers: http.IncomingHttpHeaders }>(fullfil => {
|
||||||
this._wsServer.once('connection', (ws, req) => fullfil(req));
|
this._wsServer.once('connection', (ws, req) => fullfil(req));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -341,5 +290,3 @@ class TestServer {
|
||||||
this.onceWebSocketConnection(ws => ws.send(data));
|
this.onceWebSocketConnection(ws => ws.send(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {TestServer};
|
|
||||||
Loading…
Reference in a new issue