feat(appium): Add a command line parameter to configure HTTP server request timeout (#21003)

This commit is contained in:
Mykola Mokhnach
2025-02-19 20:45:20 +01:00
committed by GitHub
parent c8ac9c40bb
commit eb1b156146
7 changed files with 55 additions and 9 deletions
+1
View File
@@ -34,6 +34,7 @@ below.
|`--driver`|Driver-specific configuration. Keys should correspond to driver package names|object|||
|`--drivers-import-chunk-size`|The maximum amount of drivers that could be imported in parallel on server startup|number|`3`||
|`--keep-alive-timeout`|Number of seconds the Appium server should apply as both the keep-alive timeout and the connection timeout for all requests. Setting this to `0` disables the timeout.|integer|`600`|`-ka`|
|`--request-timeout`|Number of seconds the Appium server should apply for receiving the entire HTTP request from the client. A value of 0 disables the timeout. Set it to a non-zero value to protect against potential Denial-of-Service attacks in case the server is deployed without a reverse proxy in front. HTTP requests that are running longer than allowed by this timeout would be rejected with the status code 408.|integer|`3600`||
|`--local-timezone`|Use local timezone for timestamps|boolean|`false`||
|`--log`|Also send log output to this file|string||`-g`|
|`--log-filters`|One or more log filtering rules|array|||
+4 -2
View File
@@ -397,8 +397,10 @@ async function main(args) {
extraMethodMap,
cliArgs: parsedArgs,
};
if (parsedArgs.keepAliveTimeout) {
serverOpts.keepAliveTimeout = parsedArgs.keepAliveTimeout * 1000;
for (const timeoutArgName of ['keepAliveTimeout', 'requestTimeout']) {
if (_.isInteger(parsedArgs[timeoutArgName])) {
serverOpts[timeoutArgName] = parsedArgs[timeoutArgName] * 1000;
}
}
let server;
const bidiServer = new WebSocketServer({noServer: true});
+1
View File
@@ -8,6 +8,7 @@ export default {
denyInsecure: [],
driversImportChunkSize: 3,
keepAliveTimeout: 600,
requestTimeout: 3600,
localTimezone: false,
logFormat: 'text',
loglevel: 'debug',
+24 -6
View File
@@ -76,7 +76,7 @@ async function createServer (app, cliArgs) {
* @param {ServerOpts} opts
* @returns {Promise<AppiumServer>}
*/
async function server(opts) {
export async function server(opts) {
const {
routeConfiguringFunction,
port,
@@ -87,6 +87,7 @@ async function server(opts) {
extraMethodMap = {},
serverUpdaters = [],
keepAliveTimeout = KEEP_ALIVE_TIMEOUT_MS,
requestTimeout,
} = opts;
const app = express();
@@ -123,7 +124,13 @@ async function server(opts) {
// want to block extensions' ability to add routes if they want.
app.all('*', catch404Handler);
await startServer({httpServer, hostname, port, keepAliveTimeout});
await startServer({
httpServer,
hostname,
port,
keepAliveTimeout,
requestTimeout,
});
resolve(appiumServer);
} catch (err) {
@@ -136,7 +143,7 @@ async function server(opts) {
* Sets up some Express middleware and stuff
* @param {ConfigureServerOpts} opts
*/
function configureServer({
export function configureServer({
app,
addRoutes,
allowCors = true,
@@ -267,13 +274,22 @@ function configureHttp({httpServer, reject, keepAliveTimeout, gracefulShutdownTi
* @param {StartServerOpts} opts
* @returns {Promise<void>}
*/
async function startServer({httpServer, port, hostname, keepAliveTimeout}) {
async function startServer({
httpServer,
port,
hostname,
keepAliveTimeout,
requestTimeout,
}) {
// If the hostname is omitted, the server will accept
// connections on any IP address
/** @type {(port: number, hostname?: string) => B<http.Server>} */
const start = B.promisify(httpServer.listen, {context: httpServer});
const startPromise = start(port, hostname);
httpServer.keepAliveTimeout = keepAliveTimeout;
if (_.isInteger(requestTimeout)) {
httpServer.requestTimeout = Number(requestTimeout);
}
// headers timeout must be greater than keepAliveTimeout
httpServer.headersTimeout = keepAliveTimeout + 5 * 1000;
await startPromise;
@@ -284,7 +300,7 @@ async function startServer({httpServer, port, hostname, keepAliveTimeout}) {
* @param {string} basePath
* @returns {string}
*/
function normalizeBasePath(basePath) {
export function normalizeBasePath(basePath) {
if (!_.isString(basePath)) {
throw new Error(`Invalid path prefix ${basePath}`);
}
@@ -302,7 +318,6 @@ function normalizeBasePath(basePath) {
return basePath;
}
export {server, configureServer, normalizeBasePath};
/**
* Options for {@linkcode startServer}.
@@ -311,6 +326,8 @@ export {server, configureServer, normalizeBasePath};
* @property {number} port - Port to run on
* @property {number} keepAliveTimeout - Keep-alive timeout in milliseconds
* @property {string} [hostname] - Optional hostname
* @property {number} [requestTimeout] - The timeout value in milliseconds for
* receiving the entire request from the client
*/
/**
@@ -344,6 +361,7 @@ export {server, configureServer, normalizeBasePath};
* @property {MethodMap} [extraMethodMap]
* @property {import('@appium/types').UpdateServerCallback[]} [serverUpdaters]
* @property {number} [keepAliveTimeout]
* @property {number} [requestTimeout]
*/
/**
+13 -1
View File
@@ -117,11 +117,23 @@ export const AppiumConfigJsonSchema = /** @type {const} */ ({
appiumCliAliases: ['ka'],
default: 600,
description:
'Number of seconds the Appium server should apply as both the keep-alive timeout and the connection timeout for all requests. A value of 0 disables the timeout.',
'Number of seconds the Appium server should apply as both the keep-alive timeout and the connection timeout ' +
'for all requests. A value of 0 disables the timeout.',
minimum: 0,
title: 'keep-alive-timeout config',
type: 'integer',
},
'request-timeout': {
default: 3600,
description:
'Number of seconds the Appium server should apply for receiving the entire HTTP request from the client. ' +
'A value of 0 disables the timeout. Set it to a non-zero value to protect against ' +
'potential Denial-of-Service attacks in case the server is deployed without a reverse proxy in front. ' +
'HTTP requests that are running longer than allowed by this timeout would be rejected with the status code 408.',
minimum: 0,
title: 'request-timeout config',
type: 'integer',
},
'local-timezone': {
default: false,
description: 'Use local timezone for timestamps',
@@ -120,6 +120,13 @@
"title": "keep-alive-timeout config",
"type": "integer"
},
"request-timeout": {
"default": 3600,
"description": "Number of seconds the Appium server should apply for receiving the entire HTTP request from the client. A value of 0 disables the timeout. Set it to a non-zero value to protect against potential Denial-of-Service attacks in case the server is deployed without a reverse proxy in front. HTTP requests that are running longer than allowed by this timeout would be rejected with the status code 408.",
"minimum": 0,
"title": "request-timeout config",
"type": "integer"
},
"local-timezone": {
"default": false,
"description": "Use local timezone for timestamps",
+5
View File
@@ -41,6 +41,10 @@ export type DenyInsecureConfig = string[];
* Number of seconds the Appium server should apply as both the keep-alive timeout and the connection timeout for all requests. A value of 0 disables the timeout.
*/
export type KeepAliveTimeoutConfig = number;
/**
* Number of seconds the Appium server should apply for receiving the entire HTTP request from the client. A value of 0 disables the timeout. Set it to a non-zero value to protect against potential Denial-of-Service attacks in case the server is deployed without a reverse proxy in front. HTTP requests that are running longer than allowed by this timeout would be rejected with the status code 408.
*/
export type RequestTimeoutConfig = number;
/**
* Use local timezone for timestamps
*/
@@ -193,6 +197,7 @@ export interface ServerConfig {
"deny-insecure"?: DenyInsecureConfig;
driver?: DriverConfig;
"keep-alive-timeout"?: KeepAliveTimeoutConfig;
"request-timeout"?: RequestTimeoutConfig;
"local-timezone"?: LocalTimezoneConfig;
log?: LogConfig;
"log-filters"?: LogFiltersConfig;