@appium/base-driver 10.1.0 → 10.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/lib/express/middleware.d.ts +11 -0
- package/build/lib/express/middleware.d.ts.map +1 -1
- package/build/lib/express/middleware.js +35 -17
- package/build/lib/express/middleware.js.map +1 -1
- package/build/lib/express/server.d.ts +6 -1
- package/build/lib/express/server.d.ts.map +1 -1
- package/build/lib/express/server.js +88 -48
- package/build/lib/express/server.js.map +1 -1
- package/lib/express/middleware.js +35 -16
- package/lib/express/server.js +101 -59
- package/package.json +9 -9
|
@@ -27,6 +27,17 @@ export function handleLogContext(req: import("express").Request, res: import("ex
|
|
|
27
27
|
* @returns {any}
|
|
28
28
|
*/
|
|
29
29
|
export function defaultToJSONContentType(req: import("express").Request, res: import("express").Response, next: import("express").NextFunction): any;
|
|
30
|
+
/**
|
|
31
|
+
* Core function to handle WebSocket upgrade requests by matching the request path
|
|
32
|
+
* against registered WebSocket handlers in the webSocketsMapping.
|
|
33
|
+
*
|
|
34
|
+
* @param {import('http').IncomingMessage} req - The HTTP request
|
|
35
|
+
* @param {import('stream').Duplex} socket - The network socket
|
|
36
|
+
* @param {Buffer} head - The first packet of the upgraded stream
|
|
37
|
+
* @param {import('@appium/types').StringRecord<import('@appium/types').WSServer>} webSocketsMapping - Mapping of paths to WebSocket servers
|
|
38
|
+
* @returns {boolean} - Returns true if the upgrade was handled, false otherwise
|
|
39
|
+
*/
|
|
40
|
+
export function tryHandleWebSocketUpgrade(req: import("http").IncomingMessage, socket: import("stream").Duplex, head: Buffer, webSocketsMapping: import("@appium/types").StringRecord<import("@appium/types").WSServer>): boolean;
|
|
30
41
|
/**
|
|
31
42
|
*
|
|
32
43
|
* @param {import('@appium/types').StringRecord<import('@appium/types').WSServer>} webSocketsMapping
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../../lib/express/middleware.js"],"names":[],"mappings":"AAWA;;;;;;GAMG;AACH,sCALW,OAAO,SAAS,EAAE,OAAO,OACzB,OAAO,SAAS,EAAE,QAAQ,QAC1B,OAAO,SAAS,EAAE,YAAY,GAC5B,GAAG,CAef;AAED;;;GAGG;AACH,uDAHW,MAAM,GACJ,OAAO,SAAS,EAAE,cAAc,CAc5C;AAED;;;;;;GAMG;AACH,sCALW,OAAO,SAAS,EAAE,OAAO,OACzB,OAAO,SAAS,EAAE,QAAQ,QAC1B,OAAO,SAAS,EAAE,YAAY,GAC5B,GAAG,CAgBf;AAED;;;;;;GAMG;AACH,8CALW,OAAO,SAAS,EAAE,OAAO,OACzB,OAAO,SAAS,EAAE,QAAQ,QAC1B,OAAO,SAAS,EAAE,YAAY,GAC5B,GAAG,CAOf;AAED;;;;GAIG;AACH,iDAHW,OAAO,eAAe,EAAE,YAAY,CAAC,OAAO,eAAe,EAAE,QAAQ,CAAC,GACpE,OAAO,SAAS,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../../lib/express/middleware.js"],"names":[],"mappings":"AAWA;;;;;;GAMG;AACH,sCALW,OAAO,SAAS,EAAE,OAAO,OACzB,OAAO,SAAS,EAAE,QAAQ,QAC1B,OAAO,SAAS,EAAE,YAAY,GAC5B,GAAG,CAef;AAED;;;GAGG;AACH,uDAHW,MAAM,GACJ,OAAO,SAAS,EAAE,cAAc,CAc5C;AAED;;;;;;GAMG;AACH,sCALW,OAAO,SAAS,EAAE,OAAO,OACzB,OAAO,SAAS,EAAE,QAAQ,QAC1B,OAAO,SAAS,EAAE,YAAY,GAC5B,GAAG,CAgBf;AAED;;;;;;GAMG;AACH,8CALW,OAAO,SAAS,EAAE,OAAO,OACzB,OAAO,SAAS,EAAE,QAAQ,QAC1B,OAAO,SAAS,EAAE,YAAY,GAC5B,GAAG,CAOf;AAED;;;;;;;;;GASG;AACH,+CANW,OAAO,MAAM,EAAE,eAAe,UAC9B,OAAO,QAAQ,EAAE,MAAM,QACvB,MAAM,qBACN,OAAO,eAAe,EAAE,YAAY,CAAC,OAAO,eAAe,EAAE,QAAQ,CAAC,GACpE,OAAO,CAuBnB;AAED;;;;GAIG;AACH,iDAHW,OAAO,eAAe,EAAE,YAAY,CAAC,OAAO,eAAe,EAAE,QAAQ,CAAC,GACpE,OAAO,SAAS,EAAE,cAAc,CAS5C;AAED;;;;;GAKG;AACH,qCALW,KAAK,OACL,OAAO,SAAS,EAAE,OAAO,OACzB,OAAO,SAAS,EAAE,QAAQ,QAC1B,OAAO,SAAS,EAAE,YAAY,QAUxC;AAED;;;GAGG;AACH,qCAHW,OAAO,SAAS,EAAE,OAAO,OACzB,OAAO,SAAS,EAAE,QAAQ,QAMpC"}
|
|
@@ -8,6 +8,7 @@ exports.allowCrossDomain = allowCrossDomain;
|
|
|
8
8
|
exports.allowCrossDomainAsyncExecute = allowCrossDomainAsyncExecute;
|
|
9
9
|
exports.handleLogContext = handleLogContext;
|
|
10
10
|
exports.defaultToJSONContentType = defaultToJSONContentType;
|
|
11
|
+
exports.tryHandleWebSocketUpgrade = tryHandleWebSocketUpgrade;
|
|
11
12
|
exports.handleUpgrade = handleUpgrade;
|
|
12
13
|
exports.catchAllHandler = catchAllHandler;
|
|
13
14
|
exports.catch404Handler = catch404Handler;
|
|
@@ -85,6 +86,38 @@ function defaultToJSONContentType(req, res, next) {
|
|
|
85
86
|
}
|
|
86
87
|
next();
|
|
87
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Core function to handle WebSocket upgrade requests by matching the request path
|
|
91
|
+
* against registered WebSocket handlers in the webSocketsMapping.
|
|
92
|
+
*
|
|
93
|
+
* @param {import('http').IncomingMessage} req - The HTTP request
|
|
94
|
+
* @param {import('stream').Duplex} socket - The network socket
|
|
95
|
+
* @param {Buffer} head - The first packet of the upgraded stream
|
|
96
|
+
* @param {import('@appium/types').StringRecord<import('@appium/types').WSServer>} webSocketsMapping - Mapping of paths to WebSocket servers
|
|
97
|
+
* @returns {boolean} - Returns true if the upgrade was handled, false otherwise
|
|
98
|
+
*/
|
|
99
|
+
function tryHandleWebSocketUpgrade(req, socket, head, webSocketsMapping) {
|
|
100
|
+
if (lodash_1.default.toLower(req.headers?.upgrade) !== 'websocket') {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
let currentPathname;
|
|
104
|
+
try {
|
|
105
|
+
currentPathname = new URL(req.url ?? '', 'http://localhost').pathname;
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
currentPathname = req.url ?? '';
|
|
109
|
+
}
|
|
110
|
+
for (const [pathname, wsServer] of lodash_1.default.toPairs(webSocketsMapping)) {
|
|
111
|
+
if ((0, path_to_regexp_1.match)(pathname)(currentPathname)) {
|
|
112
|
+
wsServer.handleUpgrade(req, socket, head, (ws) => {
|
|
113
|
+
wsServer.emit('connection', ws, req);
|
|
114
|
+
});
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
logger_1.default.info(`Did not match the websocket upgrade request at ${currentPathname} to any known route`);
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
88
121
|
/**
|
|
89
122
|
*
|
|
90
123
|
* @param {import('@appium/types').StringRecord<import('@appium/types').WSServer>} webSocketsMapping
|
|
@@ -92,24 +125,9 @@ function defaultToJSONContentType(req, res, next) {
|
|
|
92
125
|
*/
|
|
93
126
|
function handleUpgrade(webSocketsMapping) {
|
|
94
127
|
return (req, res, next) => {
|
|
95
|
-
if (
|
|
96
|
-
return
|
|
97
|
-
}
|
|
98
|
-
let currentPathname;
|
|
99
|
-
try {
|
|
100
|
-
currentPathname = new URL(req.url ?? '').pathname;
|
|
101
|
-
}
|
|
102
|
-
catch {
|
|
103
|
-
currentPathname = req.url ?? '';
|
|
104
|
-
}
|
|
105
|
-
for (const [pathname, wsServer] of lodash_1.default.toPairs(webSocketsMapping)) {
|
|
106
|
-
if ((0, path_to_regexp_1.match)(pathname)(currentPathname)) {
|
|
107
|
-
return wsServer.handleUpgrade(req, req.socket, Buffer.from(''), (ws) => {
|
|
108
|
-
wsServer.emit('connection', ws, req);
|
|
109
|
-
});
|
|
110
|
-
}
|
|
128
|
+
if (tryHandleWebSocketUpgrade(req, req.socket, Buffer.from(''), webSocketsMapping)) {
|
|
129
|
+
return;
|
|
111
130
|
}
|
|
112
|
-
logger_1.default.info(`Did not match the websocket upgrade request at ${currentPathname} to any known route`);
|
|
113
131
|
next();
|
|
114
132
|
};
|
|
115
133
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../../lib/express/middleware.js"],"names":[],"mappings":";;;;;;AAkBA,4CAaC;AAMD,oEAYC;AASD,4CAcC;AASD,4DAKC;AAOD,
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../../lib/express/middleware.js"],"names":[],"mappings":"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"}
|
|
@@ -8,7 +8,7 @@ export function server(opts: ServerOpts): Promise<AppiumServer>;
|
|
|
8
8
|
* Sets up some Express middleware and stuff
|
|
9
9
|
* @param {ConfigureServerOpts} opts
|
|
10
10
|
*/
|
|
11
|
-
export function configureServer({ app, addRoutes, allowCors, basePath, extraMethodMap, webSocketsMapping, }: ConfigureServerOpts): void;
|
|
11
|
+
export function configureServer({ app, addRoutes, allowCors, basePath, extraMethodMap, webSocketsMapping, useLegacyUpgradeHandler, }: ConfigureServerOpts): void;
|
|
12
12
|
/**
|
|
13
13
|
* Normalize base path string
|
|
14
14
|
* @param {string} basePath
|
|
@@ -102,5 +102,10 @@ export type ConfigureServerOpts = {
|
|
|
102
102
|
basePath?: string | undefined;
|
|
103
103
|
extraMethodMap?: Readonly<import("@appium/types").DriverMethodMap<import("@appium/types").ExternalDriver<import("@appium/types").Constraints, string, import("@appium/types").StringRecord, import("@appium/types").StringRecord, import("@appium/types").DefaultCreateSessionResult<import("@appium/types").Constraints>, void, import("@appium/types").StringRecord>>> | undefined;
|
|
104
104
|
webSocketsMapping?: import("@appium/types").StringRecord<any> | undefined;
|
|
105
|
+
/**
|
|
106
|
+
* - Whether to use legacy Express middleware for WebSocket upgrades.
|
|
107
|
+
* Set to false when using shouldUpgradeCallback on the HTTP server (Node.js >= 22.21.0 or >= 24.9.0).
|
|
108
|
+
*/
|
|
109
|
+
useLegacyUpgradeHandler?: boolean | undefined;
|
|
105
110
|
};
|
|
106
111
|
//# sourceMappingURL=server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../lib/express/server.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../lib/express/server.js"],"names":[],"mappings":"AAkCA;;;;GAIG;AACH,6BAHW,UAAU,GACR,OAAO,CAAC,YAAY,CAAC,CAiEjC;AAED;;;GAGG;AACH,sIAFW,mBAAmB,QAuD7B;AAED;;;;GAIG;AACH,4CAHW,MAAM,GACJ,MAAM,CAkBlB;;;;;;;;gBAkLa,OAAO,MAAM,EAAE,MAAM;;;;UACrB,MAAM;;;;sBACN,MAAM;;;;;;;;;;;2BAOP,OAAO,eAAe,EAAE,YAAY;wBAIpC,OAAO,eAAe,EAAE,SAAS,CAAC,OAAO,eAAe,EAAE,cAAc,CAAC;;;;;;;;gBAMxE,OAAO,MAAM,EAAE,MAAM;;;;YACrB,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,IAAI;;;;sBACrB,MAAM;;;;;;6BACN,MAAM;;;;;;8BAQN,wBAAwB;UACxB,MAAM;;;;;;;;;;;;;6CAcT,OAAO,SAAS,EAAE,OAAO,sDAEvB,IAAI;;;;;;;;;;;;SAaH,OAAO,SAAS,EAAE,OAAO;eACzB,wBAAwB"}
|
|
@@ -23,38 +23,6 @@ const bluebird_1 = __importDefault(require("bluebird"));
|
|
|
23
23
|
const constants_1 = require("../constants");
|
|
24
24
|
const support_1 = require("@appium/support");
|
|
25
25
|
const KEEP_ALIVE_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
|
|
26
|
-
/**
|
|
27
|
-
*
|
|
28
|
-
* @param {import('express').Express} app
|
|
29
|
-
* @param {Partial<import('@appium/types').ServerArgs>} [cliArgs]
|
|
30
|
-
* @returns {Promise<http.Server>}
|
|
31
|
-
*/
|
|
32
|
-
async function createServer(app, cliArgs) {
|
|
33
|
-
const { sslCertificatePath, sslKeyPath } = cliArgs ?? {};
|
|
34
|
-
if (!sslCertificatePath && !sslKeyPath) {
|
|
35
|
-
return http_1.default.createServer(app);
|
|
36
|
-
}
|
|
37
|
-
if (!sslCertificatePath || !sslKeyPath) {
|
|
38
|
-
throw new Error(`Both certificate path and key path must be provided to enable TLS`);
|
|
39
|
-
}
|
|
40
|
-
const certKey = [sslCertificatePath, sslKeyPath];
|
|
41
|
-
const zipped = lodash_1.default.zip(await bluebird_1.default.all(certKey.map((p) => support_1.fs.exists(p))), ['certificate', 'key'], certKey);
|
|
42
|
-
for (const [exists, desc, p] of zipped) {
|
|
43
|
-
if (!exists) {
|
|
44
|
-
throw new Error(`The provided SSL ${desc} at '${p}' does not exist or is not accessible`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
const [cert, key] = await bluebird_1.default.all(certKey.map((p) => support_1.fs.readFile(p, 'utf8')));
|
|
48
|
-
logger_1.default.debug('Enabling TLS/SPDY on the server using the provided certificate');
|
|
49
|
-
return require('spdy').createServer({
|
|
50
|
-
cert,
|
|
51
|
-
key,
|
|
52
|
-
spdy: {
|
|
53
|
-
plain: false,
|
|
54
|
-
ssl: true,
|
|
55
|
-
}
|
|
56
|
-
}, app);
|
|
57
|
-
}
|
|
58
26
|
/**
|
|
59
27
|
*
|
|
60
28
|
* @param {ServerOpts} opts
|
|
@@ -77,6 +45,7 @@ async function server(opts) {
|
|
|
77
45
|
keepAliveTimeout,
|
|
78
46
|
gracefulShutdownTimeout: cliArgs.shutdownTimeout,
|
|
79
47
|
});
|
|
48
|
+
const useLegacyUpgradeHandler = !hasShouldUpgradeCallback(httpServer);
|
|
80
49
|
configureServer({
|
|
81
50
|
app,
|
|
82
51
|
addRoutes: routeConfiguringFunction,
|
|
@@ -84,6 +53,7 @@ async function server(opts) {
|
|
|
84
53
|
basePath,
|
|
85
54
|
extraMethodMap,
|
|
86
55
|
webSocketsMapping: appiumServer.webSocketsMapping,
|
|
56
|
+
useLegacyUpgradeHandler,
|
|
87
57
|
});
|
|
88
58
|
// allow extensions to update the app and http server objects
|
|
89
59
|
for (const updater of serverUpdaters) {
|
|
@@ -111,7 +81,7 @@ async function server(opts) {
|
|
|
111
81
|
* Sets up some Express middleware and stuff
|
|
112
82
|
* @param {ConfigureServerOpts} opts
|
|
113
83
|
*/
|
|
114
|
-
function configureServer({ app, addRoutes, allowCors = true, basePath = constants_1.DEFAULT_BASE_PATH, extraMethodMap = {}, webSocketsMapping = {}, }) {
|
|
84
|
+
function configureServer({ app, addRoutes, allowCors = true, basePath = constants_1.DEFAULT_BASE_PATH, extraMethodMap = {}, webSocketsMapping = {}, useLegacyUpgradeHandler = true, }) {
|
|
115
85
|
basePath = normalizeBasePath(basePath);
|
|
116
86
|
app.use(express_logging_1.endLogFormatter);
|
|
117
87
|
app.use(middleware_1.handleLogContext);
|
|
@@ -122,7 +92,12 @@ function configureServer({ app, addRoutes, allowCors = true, basePath = constant
|
|
|
122
92
|
// crash routes, for testing
|
|
123
93
|
app.use(`${basePath}/produce_error`, crash_1.produceError);
|
|
124
94
|
app.use(`${basePath}/crash`, crash_1.produceCrash);
|
|
125
|
-
|
|
95
|
+
// Only use legacy Express middleware for WebSocket upgrades if shouldUpgradeCallback is not available
|
|
96
|
+
// When shouldUpgradeCallback is available, upgrades are handled directly on the HTTP server
|
|
97
|
+
// to avoid Express middleware timeout issues with long-lived connections
|
|
98
|
+
if (useLegacyUpgradeHandler) {
|
|
99
|
+
app.use((0, middleware_1.handleUpgrade)(webSocketsMapping));
|
|
100
|
+
}
|
|
126
101
|
if (allowCors) {
|
|
127
102
|
app.use(middleware_1.allowCrossDomain);
|
|
128
103
|
}
|
|
@@ -145,6 +120,57 @@ function configureServer({ app, addRoutes, allowCors = true, basePath = constant
|
|
|
145
120
|
app.all('/test/guinea-pig-scrollable', static_1.guineaPigScrollable);
|
|
146
121
|
app.all('/test/guinea-pig-app-banner', static_1.guineaPigAppBanner);
|
|
147
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Normalize base path string
|
|
125
|
+
* @param {string} basePath
|
|
126
|
+
* @returns {string}
|
|
127
|
+
*/
|
|
128
|
+
function normalizeBasePath(basePath) {
|
|
129
|
+
if (!lodash_1.default.isString(basePath)) {
|
|
130
|
+
throw new Error(`Invalid path prefix ${basePath}`);
|
|
131
|
+
}
|
|
132
|
+
// ensure the path prefix does not end in '/', since our method map
|
|
133
|
+
// starts all paths with '/'
|
|
134
|
+
basePath = basePath.replace(/\/$/, '');
|
|
135
|
+
// likewise, ensure the path prefix does always START with /, unless the path
|
|
136
|
+
// is empty meaning no base path at all
|
|
137
|
+
if (basePath !== '' && !basePath.startsWith('/')) {
|
|
138
|
+
basePath = `/${basePath}`;
|
|
139
|
+
}
|
|
140
|
+
return basePath;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
*
|
|
144
|
+
* @param {import('express').Express} app
|
|
145
|
+
* @param {Partial<import('@appium/types').ServerArgs>} [cliArgs]
|
|
146
|
+
* @returns {Promise<http.Server>}
|
|
147
|
+
*/
|
|
148
|
+
async function createServer(app, cliArgs) {
|
|
149
|
+
const { sslCertificatePath, sslKeyPath } = cliArgs ?? {};
|
|
150
|
+
if (!sslCertificatePath && !sslKeyPath) {
|
|
151
|
+
return http_1.default.createServer(app);
|
|
152
|
+
}
|
|
153
|
+
if (!sslCertificatePath || !sslKeyPath) {
|
|
154
|
+
throw new Error(`Both certificate path and key path must be provided to enable TLS`);
|
|
155
|
+
}
|
|
156
|
+
const certKey = [sslCertificatePath, sslKeyPath];
|
|
157
|
+
const zipped = lodash_1.default.zip(await bluebird_1.default.all(certKey.map((p) => support_1.fs.exists(p))), ['certificate', 'key'], certKey);
|
|
158
|
+
for (const [exists, desc, p] of zipped) {
|
|
159
|
+
if (!exists) {
|
|
160
|
+
throw new Error(`The provided SSL ${desc} at '${p}' does not exist or is not accessible`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const [cert, key] = await bluebird_1.default.all(certKey.map((p) => support_1.fs.readFile(p, 'utf8')));
|
|
164
|
+
logger_1.default.debug('Enabling TLS/SPDY on the server using the provided certificate');
|
|
165
|
+
return require('spdy').createServer({
|
|
166
|
+
cert,
|
|
167
|
+
key,
|
|
168
|
+
spdy: {
|
|
169
|
+
plain: false,
|
|
170
|
+
ssl: true,
|
|
171
|
+
}
|
|
172
|
+
}, app);
|
|
173
|
+
}
|
|
148
174
|
/**
|
|
149
175
|
* Monkeypatches the `http.Server` instance and returns a {@linkcode AppiumServer}.
|
|
150
176
|
* This function _mutates_ the `httpServer` parameter.
|
|
@@ -165,6 +191,19 @@ function configureHttp({ httpServer, reject, keepAliveTimeout, gracefulShutdownT
|
|
|
165
191
|
// eslint-disable-next-line dot-notation
|
|
166
192
|
return Boolean(this['_spdyState']?.secure);
|
|
167
193
|
};
|
|
194
|
+
// This avoids Express middleware timeout issues with long-lived WebSocket connections
|
|
195
|
+
// See: https://github.com/appium/appium/issues/20760
|
|
196
|
+
// See: https://github.com/nodejs/node/pull/59824
|
|
197
|
+
if (hasShouldUpgradeCallback(httpServer)) {
|
|
198
|
+
// shouldUpgradeCallback only returns a boolean to indicate if the upgrade should proceed
|
|
199
|
+
// eslint-disable-next-line dot-notation
|
|
200
|
+
appiumServer['shouldUpgradeCallback'] = (req) => lodash_1.default.toLower(req.headers?.upgrade) === 'websocket';
|
|
201
|
+
appiumServer.on('upgrade', (req, socket, head) => {
|
|
202
|
+
if (!(0, middleware_1.tryHandleWebSocketUpgrade)(req, socket, head, appiumServer.webSocketsMapping)) {
|
|
203
|
+
socket.destroy();
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
168
207
|
// http.Server.close() only stops new connections, but we need to wait until
|
|
169
208
|
// all connections are closed and the `close` event is emitted
|
|
170
209
|
const originalClose = appiumServer.close.bind(appiumServer);
|
|
@@ -226,23 +265,22 @@ async function startServer({ httpServer, port, hostname, keepAliveTimeout, reque
|
|
|
226
265
|
await startPromise;
|
|
227
266
|
}
|
|
228
267
|
/**
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
* @
|
|
268
|
+
* Checks if the provided server instance supports `shouldUpgradeCallback`.
|
|
269
|
+
* This feature was added in Node.js v22.21.0 (LTS) and v24.9.0.
|
|
270
|
+
* @param {import('http').Server} server - The HTTP server instance to check
|
|
271
|
+
* @returns {boolean}
|
|
232
272
|
*/
|
|
233
|
-
function
|
|
234
|
-
if
|
|
235
|
-
|
|
273
|
+
function hasShouldUpgradeCallback(server) {
|
|
274
|
+
// Check if shouldUpgradeCallback is available on http.Server
|
|
275
|
+
// This is a runtime check that works regardless of TypeScript types
|
|
276
|
+
try {
|
|
277
|
+
// Use bracket notation to access property that may not exist in type definitions
|
|
278
|
+
// eslint-disable-next-line dot-notation
|
|
279
|
+
return typeof server['shouldUpgradeCallback'] !== 'undefined';
|
|
236
280
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
basePath = basePath.replace(/\/$/, '');
|
|
240
|
-
// likewise, ensure the path prefix does always START with /, unless the path
|
|
241
|
-
// is empty meaning no base path at all
|
|
242
|
-
if (basePath !== '' && !basePath.startsWith('/')) {
|
|
243
|
-
basePath = `/${basePath}`;
|
|
281
|
+
catch {
|
|
282
|
+
return false;
|
|
244
283
|
}
|
|
245
|
-
return basePath;
|
|
246
284
|
}
|
|
247
285
|
/**
|
|
248
286
|
* Options for {@linkcode startServer}.
|
|
@@ -306,5 +344,7 @@ function normalizeBasePath(basePath) {
|
|
|
306
344
|
* @property {string} [basePath]
|
|
307
345
|
* @property {MethodMap} [extraMethodMap]
|
|
308
346
|
* @property {import('@appium/types').StringRecord} [webSocketsMapping={}]
|
|
347
|
+
* @property {boolean} [useLegacyUpgradeHandler=true] - Whether to use legacy Express middleware for WebSocket upgrades.
|
|
348
|
+
* Set to false when using shouldUpgradeCallback on the HTTP server (Node.js >= 22.21.0 or >= 24.9.0).
|
|
309
349
|
*/
|
|
310
350
|
//# sourceMappingURL=server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../lib/express/server.js"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../lib/express/server.js"],"names":[],"mappings":"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"}
|
|
@@ -86,6 +86,39 @@ export function defaultToJSONContentType(req, res, next) {
|
|
|
86
86
|
next();
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Core function to handle WebSocket upgrade requests by matching the request path
|
|
91
|
+
* against registered WebSocket handlers in the webSocketsMapping.
|
|
92
|
+
*
|
|
93
|
+
* @param {import('http').IncomingMessage} req - The HTTP request
|
|
94
|
+
* @param {import('stream').Duplex} socket - The network socket
|
|
95
|
+
* @param {Buffer} head - The first packet of the upgraded stream
|
|
96
|
+
* @param {import('@appium/types').StringRecord<import('@appium/types').WSServer>} webSocketsMapping - Mapping of paths to WebSocket servers
|
|
97
|
+
* @returns {boolean} - Returns true if the upgrade was handled, false otherwise
|
|
98
|
+
*/
|
|
99
|
+
export function tryHandleWebSocketUpgrade(req, socket, head, webSocketsMapping) {
|
|
100
|
+
if (_.toLower(req.headers?.upgrade) !== 'websocket') {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let currentPathname;
|
|
105
|
+
try {
|
|
106
|
+
currentPathname = new URL(req.url ?? '', 'http://localhost').pathname;
|
|
107
|
+
} catch {
|
|
108
|
+
currentPathname = req.url ?? '';
|
|
109
|
+
}
|
|
110
|
+
for (const [pathname, wsServer] of _.toPairs(webSocketsMapping)) {
|
|
111
|
+
if (match(pathname)(currentPathname)) {
|
|
112
|
+
wsServer.handleUpgrade(req, socket, head, (ws) => {
|
|
113
|
+
wsServer.emit('connection', ws, req);
|
|
114
|
+
});
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
log.info(`Did not match the websocket upgrade request at ${currentPathname} to any known route`);
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
|
|
89
122
|
/**
|
|
90
123
|
*
|
|
91
124
|
* @param {import('@appium/types').StringRecord<import('@appium/types').WSServer>} webSocketsMapping
|
|
@@ -93,23 +126,9 @@ export function defaultToJSONContentType(req, res, next) {
|
|
|
93
126
|
*/
|
|
94
127
|
export function handleUpgrade(webSocketsMapping) {
|
|
95
128
|
return (req, res, next) => {
|
|
96
|
-
if (
|
|
97
|
-
return
|
|
98
|
-
}
|
|
99
|
-
let currentPathname;
|
|
100
|
-
try {
|
|
101
|
-
currentPathname = new URL(req.url ?? '').pathname;
|
|
102
|
-
} catch {
|
|
103
|
-
currentPathname = req.url ?? '';
|
|
104
|
-
}
|
|
105
|
-
for (const [pathname, wsServer] of _.toPairs(webSocketsMapping)) {
|
|
106
|
-
if (match(pathname)(currentPathname)) {
|
|
107
|
-
return wsServer.handleUpgrade(req, req.socket, Buffer.from(''), (ws) => {
|
|
108
|
-
wsServer.emit('connection', ws, req);
|
|
109
|
-
});
|
|
110
|
-
}
|
|
129
|
+
if (tryHandleWebSocketUpgrade(req, req.socket, Buffer.from(''), webSocketsMapping)) {
|
|
130
|
+
return;
|
|
111
131
|
}
|
|
112
|
-
log.info(`Did not match the websocket upgrade request at ${currentPathname} to any known route`);
|
|
113
132
|
next();
|
|
114
133
|
};
|
|
115
134
|
}
|
package/lib/express/server.js
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
allowCrossDomainAsyncExecute,
|
|
15
15
|
handleIdempotency,
|
|
16
16
|
handleUpgrade,
|
|
17
|
+
tryHandleWebSocketUpgrade,
|
|
17
18
|
catch404Handler,
|
|
18
19
|
handleLogContext,
|
|
19
20
|
} from './middleware';
|
|
@@ -31,45 +32,6 @@ import {fs, timing} from '@appium/support';
|
|
|
31
32
|
|
|
32
33
|
const KEEP_ALIVE_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
|
|
33
34
|
|
|
34
|
-
/**
|
|
35
|
-
*
|
|
36
|
-
* @param {import('express').Express} app
|
|
37
|
-
* @param {Partial<import('@appium/types').ServerArgs>} [cliArgs]
|
|
38
|
-
* @returns {Promise<http.Server>}
|
|
39
|
-
*/
|
|
40
|
-
async function createServer (app, cliArgs) {
|
|
41
|
-
const {sslCertificatePath, sslKeyPath} = cliArgs ?? {};
|
|
42
|
-
if (!sslCertificatePath && !sslKeyPath) {
|
|
43
|
-
return http.createServer(app);
|
|
44
|
-
}
|
|
45
|
-
if (!sslCertificatePath || !sslKeyPath) {
|
|
46
|
-
throw new Error(`Both certificate path and key path must be provided to enable TLS`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const certKey = [sslCertificatePath, sslKeyPath];
|
|
50
|
-
const zipped = _.zip(
|
|
51
|
-
await B.all(certKey.map((p) => fs.exists(p))),
|
|
52
|
-
['certificate', 'key'],
|
|
53
|
-
certKey,
|
|
54
|
-
);
|
|
55
|
-
for (const [exists, desc, p] of zipped) {
|
|
56
|
-
if (!exists) {
|
|
57
|
-
throw new Error(`The provided SSL ${desc} at '${p}' does not exist or is not accessible`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
const [cert, key] = await B.all(certKey.map((p) => fs.readFile(p, 'utf8')));
|
|
61
|
-
log.debug('Enabling TLS/SPDY on the server using the provided certificate');
|
|
62
|
-
|
|
63
|
-
return require('spdy').createServer({
|
|
64
|
-
cert,
|
|
65
|
-
key,
|
|
66
|
-
spdy: {
|
|
67
|
-
plain: false,
|
|
68
|
-
ssl: true,
|
|
69
|
-
}
|
|
70
|
-
}, app);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
35
|
/**
|
|
74
36
|
*
|
|
75
37
|
* @param {ServerOpts} opts
|
|
@@ -105,6 +67,7 @@ export async function server(opts) {
|
|
|
105
67
|
keepAliveTimeout,
|
|
106
68
|
gracefulShutdownTimeout: cliArgs.shutdownTimeout,
|
|
107
69
|
});
|
|
70
|
+
const useLegacyUpgradeHandler = !hasShouldUpgradeCallback(httpServer);
|
|
108
71
|
configureServer({
|
|
109
72
|
app,
|
|
110
73
|
addRoutes: routeConfiguringFunction,
|
|
@@ -112,6 +75,7 @@ export async function server(opts) {
|
|
|
112
75
|
basePath,
|
|
113
76
|
extraMethodMap,
|
|
114
77
|
webSocketsMapping: appiumServer.webSocketsMapping,
|
|
78
|
+
useLegacyUpgradeHandler,
|
|
115
79
|
});
|
|
116
80
|
// allow extensions to update the app and http server objects
|
|
117
81
|
for (const updater of serverUpdaters) {
|
|
@@ -149,6 +113,7 @@ export function configureServer({
|
|
|
149
113
|
basePath = DEFAULT_BASE_PATH,
|
|
150
114
|
extraMethodMap = {},
|
|
151
115
|
webSocketsMapping = {},
|
|
116
|
+
useLegacyUpgradeHandler = true,
|
|
152
117
|
}) {
|
|
153
118
|
basePath = normalizeBasePath(basePath);
|
|
154
119
|
|
|
@@ -164,7 +129,12 @@ export function configureServer({
|
|
|
164
129
|
app.use(`${basePath}/produce_error`, produceError);
|
|
165
130
|
app.use(`${basePath}/crash`, produceCrash);
|
|
166
131
|
|
|
167
|
-
|
|
132
|
+
// Only use legacy Express middleware for WebSocket upgrades if shouldUpgradeCallback is not available
|
|
133
|
+
// When shouldUpgradeCallback is available, upgrades are handled directly on the HTTP server
|
|
134
|
+
// to avoid Express middleware timeout issues with long-lived connections
|
|
135
|
+
if (useLegacyUpgradeHandler) {
|
|
136
|
+
app.use(handleUpgrade(webSocketsMapping));
|
|
137
|
+
}
|
|
168
138
|
if (allowCors) {
|
|
169
139
|
app.use(allowCrossDomain);
|
|
170
140
|
} else {
|
|
@@ -191,6 +161,68 @@ export function configureServer({
|
|
|
191
161
|
app.all('/test/guinea-pig-app-banner', guineaPigAppBanner);
|
|
192
162
|
}
|
|
193
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Normalize base path string
|
|
166
|
+
* @param {string} basePath
|
|
167
|
+
* @returns {string}
|
|
168
|
+
*/
|
|
169
|
+
export function normalizeBasePath(basePath) {
|
|
170
|
+
if (!_.isString(basePath)) {
|
|
171
|
+
throw new Error(`Invalid path prefix ${basePath}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ensure the path prefix does not end in '/', since our method map
|
|
175
|
+
// starts all paths with '/'
|
|
176
|
+
basePath = basePath.replace(/\/$/, '');
|
|
177
|
+
|
|
178
|
+
// likewise, ensure the path prefix does always START with /, unless the path
|
|
179
|
+
// is empty meaning no base path at all
|
|
180
|
+
if (basePath !== '' && !basePath.startsWith('/')) {
|
|
181
|
+
basePath = `/${basePath}`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return basePath;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
*
|
|
189
|
+
* @param {import('express').Express} app
|
|
190
|
+
* @param {Partial<import('@appium/types').ServerArgs>} [cliArgs]
|
|
191
|
+
* @returns {Promise<http.Server>}
|
|
192
|
+
*/
|
|
193
|
+
async function createServer (app, cliArgs) {
|
|
194
|
+
const {sslCertificatePath, sslKeyPath} = cliArgs ?? {};
|
|
195
|
+
if (!sslCertificatePath && !sslKeyPath) {
|
|
196
|
+
return http.createServer(app);
|
|
197
|
+
}
|
|
198
|
+
if (!sslCertificatePath || !sslKeyPath) {
|
|
199
|
+
throw new Error(`Both certificate path and key path must be provided to enable TLS`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const certKey = [sslCertificatePath, sslKeyPath];
|
|
203
|
+
const zipped = _.zip(
|
|
204
|
+
await B.all(certKey.map((p) => fs.exists(p))),
|
|
205
|
+
['certificate', 'key'],
|
|
206
|
+
certKey,
|
|
207
|
+
);
|
|
208
|
+
for (const [exists, desc, p] of zipped) {
|
|
209
|
+
if (!exists) {
|
|
210
|
+
throw new Error(`The provided SSL ${desc} at '${p}' does not exist or is not accessible`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const [cert, key] = await B.all(certKey.map((p) => fs.readFile(p, 'utf8')));
|
|
214
|
+
log.debug('Enabling TLS/SPDY on the server using the provided certificate');
|
|
215
|
+
|
|
216
|
+
return require('spdy').createServer({
|
|
217
|
+
cert,
|
|
218
|
+
key,
|
|
219
|
+
spdy: {
|
|
220
|
+
plain: false,
|
|
221
|
+
ssl: true,
|
|
222
|
+
}
|
|
223
|
+
}, app);
|
|
224
|
+
}
|
|
225
|
+
|
|
194
226
|
/**
|
|
195
227
|
* Monkeypatches the `http.Server` instance and returns a {@linkcode AppiumServer}.
|
|
196
228
|
* This function _mutates_ the `httpServer` parameter.
|
|
@@ -212,6 +244,20 @@ function configureHttp({httpServer, reject, keepAliveTimeout, gracefulShutdownTi
|
|
|
212
244
|
return Boolean(this['_spdyState']?.secure);
|
|
213
245
|
};
|
|
214
246
|
|
|
247
|
+
// This avoids Express middleware timeout issues with long-lived WebSocket connections
|
|
248
|
+
// See: https://github.com/appium/appium/issues/20760
|
|
249
|
+
// See: https://github.com/nodejs/node/pull/59824
|
|
250
|
+
if (hasShouldUpgradeCallback(httpServer)) {
|
|
251
|
+
// shouldUpgradeCallback only returns a boolean to indicate if the upgrade should proceed
|
|
252
|
+
// eslint-disable-next-line dot-notation
|
|
253
|
+
appiumServer['shouldUpgradeCallback'] = (req) => _.toLower(req.headers?.upgrade) === 'websocket';
|
|
254
|
+
appiumServer.on('upgrade', (req, socket, head) => {
|
|
255
|
+
if (!tryHandleWebSocketUpgrade(req, socket, head, appiumServer.webSocketsMapping)) {
|
|
256
|
+
socket.destroy();
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
215
261
|
// http.Server.close() only stops new connections, but we need to wait until
|
|
216
262
|
// all connections are closed and the `close` event is emitted
|
|
217
263
|
const originalClose = appiumServer.close.bind(appiumServer);
|
|
@@ -294,29 +340,23 @@ async function startServer({
|
|
|
294
340
|
}
|
|
295
341
|
|
|
296
342
|
/**
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
* @
|
|
343
|
+
* Checks if the provided server instance supports `shouldUpgradeCallback`.
|
|
344
|
+
* This feature was added in Node.js v22.21.0 (LTS) and v24.9.0.
|
|
345
|
+
* @param {import('http').Server} server - The HTTP server instance to check
|
|
346
|
+
* @returns {boolean}
|
|
300
347
|
*/
|
|
301
|
-
|
|
302
|
-
if
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
// likewise, ensure the path prefix does always START with /, unless the path
|
|
311
|
-
// is empty meaning no base path at all
|
|
312
|
-
if (basePath !== '' && !basePath.startsWith('/')) {
|
|
313
|
-
basePath = `/${basePath}`;
|
|
348
|
+
function hasShouldUpgradeCallback(server) {
|
|
349
|
+
// Check if shouldUpgradeCallback is available on http.Server
|
|
350
|
+
// This is a runtime check that works regardless of TypeScript types
|
|
351
|
+
try {
|
|
352
|
+
// Use bracket notation to access property that may not exist in type definitions
|
|
353
|
+
// eslint-disable-next-line dot-notation
|
|
354
|
+
return typeof server['shouldUpgradeCallback'] !== 'undefined';
|
|
355
|
+
} catch {
|
|
356
|
+
return false;
|
|
314
357
|
}
|
|
315
|
-
|
|
316
|
-
return basePath;
|
|
317
358
|
}
|
|
318
359
|
|
|
319
|
-
|
|
320
360
|
/**
|
|
321
361
|
* Options for {@linkcode startServer}.
|
|
322
362
|
* @typedef StartServerOpts
|
|
@@ -386,4 +426,6 @@ export function normalizeBasePath(basePath) {
|
|
|
386
426
|
* @property {string} [basePath]
|
|
387
427
|
* @property {MethodMap} [extraMethodMap]
|
|
388
428
|
* @property {import('@appium/types').StringRecord} [webSocketsMapping={}]
|
|
429
|
+
* @property {boolean} [useLegacyUpgradeHandler=true] - Whether to use legacy Express middleware for WebSocket upgrades.
|
|
430
|
+
* Set to false when using shouldUpgradeCallback on the HTTP server (Node.js >= 22.21.0 or >= 24.9.0).
|
|
389
431
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appium/base-driver",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.2",
|
|
4
4
|
"description": "Base driver class for Appium drivers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"automation",
|
|
@@ -44,25 +44,25 @@
|
|
|
44
44
|
"test:types": "tsd"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@appium/support": "^7.0.
|
|
48
|
-
"@appium/types": "^1.1.
|
|
47
|
+
"@appium/support": "^7.0.4",
|
|
48
|
+
"@appium/types": "^1.1.2",
|
|
49
49
|
"@colors/colors": "1.6.0",
|
|
50
50
|
"async-lock": "1.4.1",
|
|
51
51
|
"asyncbox": "3.0.0",
|
|
52
|
-
"axios": "1.
|
|
52
|
+
"axios": "1.13.2",
|
|
53
53
|
"bluebird": "3.7.2",
|
|
54
|
-
"body-parser": "2.2.
|
|
55
|
-
"express": "5.1
|
|
54
|
+
"body-parser": "2.2.1",
|
|
55
|
+
"express": "5.2.1",
|
|
56
56
|
"fastest-levenshtein": "1.0.16",
|
|
57
57
|
"http-status-codes": "2.3.0",
|
|
58
58
|
"lodash": "4.17.21",
|
|
59
|
-
"lru-cache": "11.2.
|
|
59
|
+
"lru-cache": "11.2.4",
|
|
60
60
|
"method-override": "3.0.0",
|
|
61
61
|
"morgan": "1.10.1",
|
|
62
62
|
"path-to-regexp": "8.3.0",
|
|
63
63
|
"serve-favicon": "2.5.1",
|
|
64
64
|
"source-map-support": "0.5.21",
|
|
65
|
-
"type-fest": "5.0
|
|
65
|
+
"type-fest": "5.3.0"
|
|
66
66
|
},
|
|
67
67
|
"optionalDependencies": {
|
|
68
68
|
"spdy": "4.0.2"
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"publishConfig": {
|
|
75
75
|
"access": "public"
|
|
76
76
|
},
|
|
77
|
-
"gitHead": "
|
|
77
|
+
"gitHead": "9004554879687ddad51d3afdf8c711b027efbd99",
|
|
78
78
|
"tsd": {
|
|
79
79
|
"directory": "test/types"
|
|
80
80
|
}
|