@appium/base-driver 9.16.2 → 10.0.0-beta.0
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/basedriver/helpers.d.ts +4 -3
- package/build/lib/basedriver/helpers.d.ts.map +1 -1
- package/build/lib/basedriver/helpers.js +17 -13
- package/build/lib/basedriver/helpers.js.map +1 -1
- package/build/lib/express/server.js +1 -1
- package/build/lib/express/server.js.map +1 -1
- package/build/lib/jsonwp-proxy/protocol-converter.d.ts +58 -17
- package/build/lib/jsonwp-proxy/protocol-converter.d.ts.map +1 -1
- package/build/lib/jsonwp-proxy/protocol-converter.js +50 -7
- package/build/lib/jsonwp-proxy/protocol-converter.js.map +1 -1
- package/build/lib/jsonwp-proxy/proxy-request.d.ts +12 -0
- package/build/lib/jsonwp-proxy/proxy-request.d.ts.map +1 -0
- package/build/lib/jsonwp-proxy/proxy-request.js +48 -0
- package/build/lib/jsonwp-proxy/proxy-request.js.map +1 -0
- package/build/lib/jsonwp-proxy/proxy.d.ts +70 -25
- package/build/lib/jsonwp-proxy/proxy.d.ts.map +1 -1
- package/build/lib/jsonwp-proxy/proxy.js +106 -40
- package/build/lib/jsonwp-proxy/proxy.js.map +1 -1
- package/lib/basedriver/helpers.js +17 -13
- package/lib/express/server.js +1 -1
- package/lib/jsonwp-proxy/protocol-converter.js +51 -7
- package/lib/jsonwp-proxy/proxy-request.ts +52 -0
- package/lib/jsonwp-proxy/proxy.js +121 -48
- package/package.json +6 -5
|
@@ -43,6 +43,11 @@ const {MJSONWP, W3C} = PROTOCOLS;
|
|
|
43
43
|
const DEFAULT_LOG = logger.getLogger('Protocol Converter');
|
|
44
44
|
|
|
45
45
|
class ProtocolConverter {
|
|
46
|
+
/**
|
|
47
|
+
*
|
|
48
|
+
* @param {ProxyFunction} proxyFunc
|
|
49
|
+
* @param {import('@appium/types').AppiumLogger | null} [log=null]
|
|
50
|
+
*/
|
|
46
51
|
constructor(proxyFunc, log = null) {
|
|
47
52
|
this.proxyFunc = proxyFunc;
|
|
48
53
|
this._downstreamProtocol = null;
|
|
@@ -67,7 +72,7 @@ class ProtocolConverter {
|
|
|
67
72
|
* provided in the request, we need to do 3 proxies and combine the result
|
|
68
73
|
*
|
|
69
74
|
* @param {Object} body Request body
|
|
70
|
-
* @return {
|
|
75
|
+
* @return {Object[]} Array of W3C + MJSONWP compatible timeout objects
|
|
71
76
|
*/
|
|
72
77
|
getTimeoutRequestObjects(body) {
|
|
73
78
|
if (this.downstreamProtocol === W3C && _.has(body, 'ms') && _.has(body, 'type')) {
|
|
@@ -99,9 +104,10 @@ class ProtocolConverter {
|
|
|
99
104
|
|
|
100
105
|
/**
|
|
101
106
|
* Proxy an array of timeout objects and merge the result
|
|
102
|
-
* @param {
|
|
103
|
-
* @param {
|
|
104
|
-
* @param {
|
|
107
|
+
* @param {string} url Endpoint url
|
|
108
|
+
* @param {string} method Endpoint method
|
|
109
|
+
* @param {import('@appium/types').HTTPBody} body Request body
|
|
110
|
+
* @returns {Promise<[import('@appium/types').ProxyResponse, import('@appium/types').HTTPBody]>}
|
|
105
111
|
*/
|
|
106
112
|
async proxySetTimeouts(url, method, body) {
|
|
107
113
|
let response, resBody;
|
|
@@ -127,9 +133,16 @@ class ProtocolConverter {
|
|
|
127
133
|
|
|
128
134
|
// ...Otherwise, continue to the next timeouts call
|
|
129
135
|
}
|
|
130
|
-
return [response, resBody];
|
|
136
|
+
return [/** @type {import('@appium/types').ProxyResponse} */(response), resBody];
|
|
131
137
|
}
|
|
132
138
|
|
|
139
|
+
/**
|
|
140
|
+
*
|
|
141
|
+
* @param {string} url
|
|
142
|
+
* @param {string} method
|
|
143
|
+
* @param {import('@appium/types').HTTPBody} body
|
|
144
|
+
* @returns {Promise<[import('@appium/types').ProxyResponse, import('@appium/types').HTTPBody]>}
|
|
145
|
+
*/
|
|
133
146
|
async proxySetWindow(url, method, body) {
|
|
134
147
|
const bodyObj = util.safeJsonParse(body);
|
|
135
148
|
if (_.isPlainObject(bodyObj)) {
|
|
@@ -160,6 +173,13 @@ class ProtocolConverter {
|
|
|
160
173
|
return await this.proxyFunc(url, method, body);
|
|
161
174
|
}
|
|
162
175
|
|
|
176
|
+
/**
|
|
177
|
+
*
|
|
178
|
+
* @param {string} url
|
|
179
|
+
* @param {string} method
|
|
180
|
+
* @param {import('@appium/types').HTTPBody} body
|
|
181
|
+
* @returns {Promise<[import('@appium/types').ProxyResponse, import('@appium/types').HTTPBody]>}
|
|
182
|
+
*/
|
|
163
183
|
async proxySetValue(url, method, body) {
|
|
164
184
|
const bodyObj = util.safeJsonParse(body);
|
|
165
185
|
if (_.isPlainObject(bodyObj) && (util.hasValue(bodyObj.text) || util.hasValue(bodyObj.value))) {
|
|
@@ -186,6 +206,13 @@ class ProtocolConverter {
|
|
|
186
206
|
return await this.proxyFunc(url, method, body);
|
|
187
207
|
}
|
|
188
208
|
|
|
209
|
+
/**
|
|
210
|
+
*
|
|
211
|
+
* @param {string} url
|
|
212
|
+
* @param {string} method
|
|
213
|
+
* @param {import('@appium/types').HTTPBody} body
|
|
214
|
+
* @returns {Promise<[import('@appium/types').ProxyResponse, import('@appium/types').HTTPBody]>}
|
|
215
|
+
*/
|
|
189
216
|
async proxySetFrame(url, method, body) {
|
|
190
217
|
const bodyObj = util.safeJsonParse(body);
|
|
191
218
|
return _.has(bodyObj, 'id') && _.isPlainObject(bodyObj.id)
|
|
@@ -196,6 +223,13 @@ class ProtocolConverter {
|
|
|
196
223
|
: await this.proxyFunc(url, method, body);
|
|
197
224
|
}
|
|
198
225
|
|
|
226
|
+
/**
|
|
227
|
+
*
|
|
228
|
+
* @param {string} url
|
|
229
|
+
* @param {string} method
|
|
230
|
+
* @param {import('@appium/types').HTTPBody} body
|
|
231
|
+
* @returns {Promise<[import('@appium/types').ProxyResponse, import('@appium/types').HTTPBody]>}
|
|
232
|
+
*/
|
|
199
233
|
async proxyPerformActions(url, method, body) {
|
|
200
234
|
const bodyObj = util.safeJsonParse(body);
|
|
201
235
|
return _.isPlainObject(bodyObj)
|
|
@@ -207,6 +241,12 @@ class ProtocolConverter {
|
|
|
207
241
|
: await this.proxyFunc(url, method, body);
|
|
208
242
|
}
|
|
209
243
|
|
|
244
|
+
/**
|
|
245
|
+
*
|
|
246
|
+
* @param {string} url
|
|
247
|
+
* @param {string} method
|
|
248
|
+
* @returns {Promise<[import('@appium/types').ProxyResponse, import('@appium/types').HTTPBody]>}
|
|
249
|
+
*/
|
|
210
250
|
async proxyReleaseActions(url, method) {
|
|
211
251
|
return await this.proxyFunc(url, method);
|
|
212
252
|
}
|
|
@@ -218,8 +258,8 @@ class ProtocolConverter {
|
|
|
218
258
|
* @param {string} commandName
|
|
219
259
|
* @param {string} url
|
|
220
260
|
* @param {string} method
|
|
221
|
-
* @param {
|
|
222
|
-
* @returns
|
|
261
|
+
* @param {import('@appium/types').HTTPBody} [body]
|
|
262
|
+
* @returns {Promise<[import('@appium/types').ProxyResponse, import('@appium/types').HTTPBody]>}
|
|
223
263
|
*/
|
|
224
264
|
async convertAndProxy(commandName, url, method, body) {
|
|
225
265
|
if (!this.downstreamProtocol) {
|
|
@@ -272,3 +312,7 @@ class ProtocolConverter {
|
|
|
272
312
|
}
|
|
273
313
|
|
|
274
314
|
export default ProtocolConverter;
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* @typedef {(url: string, method: string, body?: import('@appium/types').HTTPBody) => Promise<[import('@appium/types').ProxyResponse, import('@appium/types').HTTPBody]>} ProxyFunction
|
|
318
|
+
*/
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { CancellationError } from 'bluebird';
|
|
3
|
+
import EventEmitter from 'node:events';
|
|
4
|
+
|
|
5
|
+
const CANCEL_EVENT = 'cancel';
|
|
6
|
+
const FINISH_EVENT = 'finish';
|
|
7
|
+
|
|
8
|
+
export class ProxyRequest {
|
|
9
|
+
private readonly _requestConfig: axios.RawAxiosRequestConfig;
|
|
10
|
+
private readonly _ee: EventEmitter;
|
|
11
|
+
private _resultPromise: Promise<any> | null;
|
|
12
|
+
|
|
13
|
+
constructor(requestConfig: axios.RawAxiosRequestConfig<any>) {
|
|
14
|
+
this._requestConfig = requestConfig;
|
|
15
|
+
this._ee = new EventEmitter();
|
|
16
|
+
this._resultPromise = null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async execute(): Promise<axios.AxiosResponse> {
|
|
20
|
+
if (this._resultPromise) {
|
|
21
|
+
return await this._resultPromise;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
this._resultPromise = Promise.race([
|
|
26
|
+
this._makeRacingTimer(),
|
|
27
|
+
this._makeRequest(),
|
|
28
|
+
]);
|
|
29
|
+
return await this._resultPromise;
|
|
30
|
+
} finally {
|
|
31
|
+
this._ee.emit(FINISH_EVENT);
|
|
32
|
+
this._ee.removeAllListeners();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
cancel(): void {
|
|
37
|
+
this._ee.emit(CANCEL_EVENT);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private async _makeRequest(): Promise<axios.AxiosResponse> {
|
|
41
|
+
return await axios(this._requestConfig);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private async _makeRacingTimer(): Promise<void> {
|
|
45
|
+
return await new Promise((resolve, reject) => {
|
|
46
|
+
this._ee.once(FINISH_EVENT, resolve);
|
|
47
|
+
this._ee.once(CANCEL_EVENT, () => reject(new CancellationError(
|
|
48
|
+
'The request has been cancelled'
|
|
49
|
+
)));
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
import {logger, util} from '@appium/support';
|
|
3
|
-
import axios from 'axios';
|
|
4
3
|
import {getSummaryByCode} from '../jsonwp-status/status';
|
|
5
4
|
import {
|
|
6
5
|
errors,
|
|
@@ -17,7 +16,7 @@ import http from 'http';
|
|
|
17
16
|
import https from 'https';
|
|
18
17
|
import { match as pathToRegexMatch } from 'path-to-regexp';
|
|
19
18
|
import nodeUrl from 'node:url';
|
|
20
|
-
|
|
19
|
+
import { ProxyRequest } from './proxy-request';
|
|
21
20
|
|
|
22
21
|
const DEFAULT_LOG = logger.getLogger('WD Proxy');
|
|
23
22
|
const DEFAULT_REQUEST_TIMEOUT = 240000;
|
|
@@ -37,7 +36,7 @@ const ALLOWED_OPTS = [
|
|
|
37
36
|
'keepAlive',
|
|
38
37
|
];
|
|
39
38
|
|
|
40
|
-
class JWProxy {
|
|
39
|
+
export class JWProxy {
|
|
41
40
|
/** @type {string} */
|
|
42
41
|
scheme;
|
|
43
42
|
/** @type {string} */
|
|
@@ -52,13 +51,20 @@ class JWProxy {
|
|
|
52
51
|
sessionId;
|
|
53
52
|
/** @type {number} */
|
|
54
53
|
timeout;
|
|
54
|
+
/** @type {Protocol | null | undefined} */
|
|
55
|
+
_downstreamProtocol;
|
|
56
|
+
/** @type {ProxyRequest[]} */
|
|
57
|
+
_activeRequests;
|
|
55
58
|
|
|
59
|
+
/**
|
|
60
|
+
* @param {import('@appium/types').ProxyOptions} [opts={}]
|
|
61
|
+
*/
|
|
56
62
|
constructor(opts = {}) {
|
|
57
|
-
|
|
58
|
-
|
|
63
|
+
const filteredOpts = _.pick(opts, ALLOWED_OPTS);
|
|
59
64
|
// omit 'log' in the defaults assignment here because 'log' is a getter and we are going to set
|
|
60
65
|
// it to this._log (which lies behind the getter) further down
|
|
61
|
-
|
|
66
|
+
/** @type {import('@appium/types').ProxyOptions} */
|
|
67
|
+
const options = _.defaults(_.omit(filteredOpts, 'log'), {
|
|
62
68
|
scheme: 'http',
|
|
63
69
|
server: 'localhost',
|
|
64
70
|
port: 4444,
|
|
@@ -67,7 +73,7 @@ class JWProxy {
|
|
|
67
73
|
sessionId: null,
|
|
68
74
|
timeout: DEFAULT_REQUEST_TIMEOUT,
|
|
69
75
|
});
|
|
70
|
-
options.scheme = options.scheme.toLowerCase();
|
|
76
|
+
options.scheme = /** @type {string} */ (options.scheme).toLowerCase();
|
|
71
77
|
Object.assign(this, options);
|
|
72
78
|
|
|
73
79
|
this._activeRequests = [];
|
|
@@ -81,6 +87,8 @@ class JWProxy {
|
|
|
81
87
|
this.httpsAgent = new https.Agent(agentOpts);
|
|
82
88
|
this.protocolConverter = new ProtocolConverter(this.proxy.bind(this), opts.log);
|
|
83
89
|
this._log = opts.log;
|
|
90
|
+
|
|
91
|
+
this.log.debug(`${this.constructor.name} options: ${JSON.stringify(options)}`);
|
|
84
92
|
}
|
|
85
93
|
|
|
86
94
|
get log() {
|
|
@@ -97,23 +105,32 @@ class JWProxy {
|
|
|
97
105
|
* @returns {Promise<import('axios').AxiosResponse>}
|
|
98
106
|
*/
|
|
99
107
|
async request(requestConfig) {
|
|
100
|
-
const
|
|
101
|
-
this._activeRequests.push(
|
|
108
|
+
const req = new ProxyRequest(requestConfig);
|
|
109
|
+
this._activeRequests.push(req);
|
|
102
110
|
try {
|
|
103
|
-
return await
|
|
111
|
+
return await req.execute();
|
|
104
112
|
} finally {
|
|
105
|
-
_.pull(this._activeRequests,
|
|
113
|
+
_.pull(this._activeRequests, req);
|
|
106
114
|
}
|
|
107
115
|
}
|
|
108
116
|
|
|
117
|
+
/**
|
|
118
|
+
* @returns {number}
|
|
119
|
+
*/
|
|
109
120
|
getActiveRequestsCount() {
|
|
110
121
|
return this._activeRequests.length;
|
|
111
122
|
}
|
|
112
123
|
|
|
113
124
|
cancelActiveRequests() {
|
|
125
|
+
for (const ar of this._activeRequests) {
|
|
126
|
+
ar.cancel();
|
|
127
|
+
}
|
|
114
128
|
this._activeRequests = [];
|
|
115
129
|
}
|
|
116
130
|
|
|
131
|
+
/**
|
|
132
|
+
* @param {Protocol | null | undefined} value
|
|
133
|
+
*/
|
|
117
134
|
set downstreamProtocol(value) {
|
|
118
135
|
this._downstreamProtocol = value;
|
|
119
136
|
this.protocolConverter.downstreamProtocol = value;
|
|
@@ -130,32 +147,8 @@ class JWProxy {
|
|
|
130
147
|
* @returns {string}
|
|
131
148
|
*/
|
|
132
149
|
getUrlForProxy(url, method) {
|
|
133
|
-
const parsedUrl =
|
|
134
|
-
|
|
135
|
-
!parsedUrl.href || !parsedUrl.pathname
|
|
136
|
-
|| (parsedUrl.protocol && !['http:', 'https:'].includes(parsedUrl.protocol))
|
|
137
|
-
) {
|
|
138
|
-
throw new Error(`Did not know how to proxy the url '${url}'`);
|
|
139
|
-
}
|
|
140
|
-
let pathname = this.reqBasePath && parsedUrl.pathname.startsWith(this.reqBasePath)
|
|
141
|
-
? parsedUrl.pathname.replace(this.reqBasePath, '')
|
|
142
|
-
: parsedUrl.pathname;
|
|
143
|
-
const match = COMMAND_WITH_SESSION_ID_MATCHER(pathname);
|
|
144
|
-
// This is needed for the backward compatibility
|
|
145
|
-
// if drivers don't set reqBasePath properly
|
|
146
|
-
if (!this.reqBasePath) {
|
|
147
|
-
if (match && _.isArray(match.params?.prefix)) {
|
|
148
|
-
pathname = pathname.replace(`/${match.params?.prefix.join('/')}`, '');
|
|
149
|
-
} else if (_.startsWith(pathname, '/wd/hub')) {
|
|
150
|
-
pathname = pathname.replace('/wd/hub', '');
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
const normalizedPathname = _.trimEnd(
|
|
154
|
-
match && _.isArray(match.params?.command)
|
|
155
|
-
? `/${match.params.command.join('/')}`
|
|
156
|
-
: pathname,
|
|
157
|
-
'/'
|
|
158
|
-
);
|
|
150
|
+
const parsedUrl = this._parseUrl(url);
|
|
151
|
+
const normalizedPathname = this._toNormalizedPathname(parsedUrl);
|
|
159
152
|
const commandName = normalizedPathname
|
|
160
153
|
? routeToCommandName(
|
|
161
154
|
normalizedPathname,
|
|
@@ -181,8 +174,8 @@ class JWProxy {
|
|
|
181
174
|
*
|
|
182
175
|
* @param {string} url
|
|
183
176
|
* @param {string} method
|
|
184
|
-
* @param {
|
|
185
|
-
* @returns {Promise<
|
|
177
|
+
* @param {import('@appium/types').HTTPBody} [body=null]
|
|
178
|
+
* @returns {Promise<[import('@appium/types').ProxyResponse, import('@appium/types').HTTPBody]>}
|
|
186
179
|
*/
|
|
187
180
|
async proxy(url, method, body = null) {
|
|
188
181
|
method = method.toUpperCase();
|
|
@@ -255,8 +248,12 @@ class JWProxy {
|
|
|
255
248
|
// Some servers, like chromedriver may return response code 200 for non-zero JSONWP statuses
|
|
256
249
|
throwProxyError(data);
|
|
257
250
|
}
|
|
258
|
-
const
|
|
259
|
-
return [
|
|
251
|
+
const headersMap = /** @type {import('@appium/types').HTTPHeaders} */ (headers);
|
|
252
|
+
return [{
|
|
253
|
+
statusCode: status,
|
|
254
|
+
headers: headersMap,
|
|
255
|
+
body: data,
|
|
256
|
+
}, data];
|
|
260
257
|
} catch (e) {
|
|
261
258
|
// We only consider an error unexpected if this was not
|
|
262
259
|
// an async request module error or if the response cannot be cast to
|
|
@@ -279,6 +276,11 @@ class JWProxy {
|
|
|
279
276
|
}
|
|
280
277
|
}
|
|
281
278
|
|
|
279
|
+
/**
|
|
280
|
+
*
|
|
281
|
+
* @param {Record<string, any>} resObj
|
|
282
|
+
* @returns {Protocol | undefined}
|
|
283
|
+
*/
|
|
282
284
|
getProtocolFromResBody(resObj) {
|
|
283
285
|
if (_.isInteger(resObj.status)) {
|
|
284
286
|
return MJSONWP;
|
|
@@ -289,6 +291,7 @@ class JWProxy {
|
|
|
289
291
|
}
|
|
290
292
|
|
|
291
293
|
/**
|
|
294
|
+
* @deprecated This method is not used anymore and will be removed
|
|
292
295
|
*
|
|
293
296
|
* @param {string} url
|
|
294
297
|
* @param {import('@appium/types').HTTPMethod} method
|
|
@@ -322,10 +325,13 @@ class JWProxy {
|
|
|
322
325
|
*
|
|
323
326
|
* @param {string} url
|
|
324
327
|
* @param {import('@appium/types').HTTPMethod} method
|
|
325
|
-
* @param {
|
|
328
|
+
* @param {import('@appium/types').HTTPBody} [body=null]
|
|
329
|
+
* @returns {Promise<[import('@appium/types').ProxyResponse, import('@appium/types').HTTPBody]>}
|
|
326
330
|
*/
|
|
327
331
|
async proxyCommand(url, method, body = null) {
|
|
328
|
-
const
|
|
332
|
+
const parsedUrl = this._parseUrl(url);
|
|
333
|
+
const normalizedPathname = this._toNormalizedPathname(parsedUrl);
|
|
334
|
+
const commandName = normalizedPathname ? routeToCommandName(normalizedPathname, method) : '';
|
|
329
335
|
if (!commandName) {
|
|
330
336
|
return await this.proxy(url, method, body);
|
|
331
337
|
}
|
|
@@ -338,8 +344,8 @@ class JWProxy {
|
|
|
338
344
|
*
|
|
339
345
|
* @param {string} url
|
|
340
346
|
* @param {import('@appium/types').HTTPMethod} method
|
|
341
|
-
* @param {
|
|
342
|
-
* @returns {Promise<
|
|
347
|
+
* @param {import('@appium/types').HTTPBody} [body=null]
|
|
348
|
+
* @returns {Promise<import('@appium/types').HTTPBody>}
|
|
343
349
|
*/
|
|
344
350
|
async command(url, method, body = null) {
|
|
345
351
|
let response;
|
|
@@ -393,20 +399,40 @@ class JWProxy {
|
|
|
393
399
|
);
|
|
394
400
|
}
|
|
395
401
|
|
|
402
|
+
/**
|
|
403
|
+
*
|
|
404
|
+
* @param {string} url
|
|
405
|
+
* @returns {string | null}
|
|
406
|
+
*/
|
|
396
407
|
getSessionIdFromUrl(url) {
|
|
397
408
|
const match = url.match(/\/session\/([^/]+)/);
|
|
398
409
|
return match ? match[1] : null;
|
|
399
410
|
}
|
|
400
411
|
|
|
412
|
+
/**
|
|
413
|
+
*
|
|
414
|
+
* @param {import('express').Request} req
|
|
415
|
+
* @param {import('express').Response} res
|
|
416
|
+
*/
|
|
401
417
|
async proxyReqRes(req, res) {
|
|
402
418
|
// ! this method must not throw any exceptions
|
|
403
419
|
// ! make sure to call res.send before return
|
|
420
|
+
/** @type {number} */
|
|
404
421
|
let statusCode;
|
|
422
|
+
/** @type {import('@appium/types').HTTPBody} */
|
|
405
423
|
let resBodyObj;
|
|
406
424
|
try {
|
|
407
425
|
let response;
|
|
408
|
-
[response, resBodyObj] = await this.proxyCommand(
|
|
409
|
-
|
|
426
|
+
[response, resBodyObj] = await this.proxyCommand(
|
|
427
|
+
req.originalUrl,
|
|
428
|
+
/** @type {import('@appium/types').HTTPMethod} */ (req.method),
|
|
429
|
+
req.body
|
|
430
|
+
);
|
|
431
|
+
for (const [name, value] of _.toPairs(response.headers)) {
|
|
432
|
+
if (!_.isNil(value)) {
|
|
433
|
+
res.setHeader(name, _.isBoolean(value) ? String(value) : value);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
410
436
|
statusCode = response.statusCode;
|
|
411
437
|
} catch (err) {
|
|
412
438
|
[statusCode, resBodyObj] = getResponseForW3CError(
|
|
@@ -438,11 +464,58 @@ class JWProxy {
|
|
|
438
464
|
resBodyObj.value = formatResponseValue(resBodyObj.value);
|
|
439
465
|
res.status(statusCode).send(JSON.stringify(formatStatus(resBodyObj)));
|
|
440
466
|
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
*
|
|
470
|
+
* @param {string} url
|
|
471
|
+
* @returns {ParsedUrl}
|
|
472
|
+
*/
|
|
473
|
+
_parseUrl(url) {
|
|
474
|
+
const parsedUrl = nodeUrl.parse(url || '/');
|
|
475
|
+
if (
|
|
476
|
+
_.isNil(parsedUrl.href) || _.isNil(parsedUrl.pathname)
|
|
477
|
+
|| (parsedUrl.protocol && !['http:', 'https:'].includes(parsedUrl.protocol))
|
|
478
|
+
) {
|
|
479
|
+
throw new Error(`Did not know how to proxy the url '${url}'`);
|
|
480
|
+
}
|
|
481
|
+
return parsedUrl;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
*
|
|
486
|
+
* @param {ParsedUrl} parsedUrl
|
|
487
|
+
* @returns {string}
|
|
488
|
+
*/
|
|
489
|
+
_toNormalizedPathname(parsedUrl) {
|
|
490
|
+
if (!_.isString(parsedUrl.pathname)) {
|
|
491
|
+
return '';
|
|
492
|
+
}
|
|
493
|
+
let pathname = this.reqBasePath && parsedUrl.pathname.startsWith(this.reqBasePath)
|
|
494
|
+
? parsedUrl.pathname.replace(this.reqBasePath, '')
|
|
495
|
+
: parsedUrl.pathname;
|
|
496
|
+
const match = COMMAND_WITH_SESSION_ID_MATCHER(pathname);
|
|
497
|
+
// This is needed for the backward compatibility
|
|
498
|
+
// if drivers don't set reqBasePath properly
|
|
499
|
+
if (!this.reqBasePath) {
|
|
500
|
+
if (match && _.isArray(match.params?.prefix)) {
|
|
501
|
+
pathname = pathname.replace(`/${match.params?.prefix.join('/')}`, '');
|
|
502
|
+
} else if (_.startsWith(pathname, '/wd/hub')) {
|
|
503
|
+
pathname = pathname.replace('/wd/hub', '');
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return _.trimEnd(
|
|
507
|
+
match && _.isArray(match.params?.command)
|
|
508
|
+
? `/${match.params.command.join('/')}`
|
|
509
|
+
: pathname,
|
|
510
|
+
'/'
|
|
511
|
+
);
|
|
512
|
+
}
|
|
441
513
|
}
|
|
442
514
|
|
|
443
|
-
export {JWProxy};
|
|
444
515
|
export default JWProxy;
|
|
445
516
|
|
|
446
517
|
/**
|
|
447
518
|
* @typedef {Error & {response: {data: import('type-fest').JsonObject, status: import('http-status-codes').StatusCodes}}} ProxyError
|
|
519
|
+
* @typedef {nodeUrl.UrlWithStringQuery} ParsedUrl
|
|
520
|
+
* @typedef {typeof PROTOCOLS[keyof typeof PROTOCOLS]} Protocol
|
|
448
521
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appium/base-driver",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.0-beta.0",
|
|
4
4
|
"description": "Base driver class for Appium drivers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"automation",
|
|
@@ -49,7 +49,7 @@
|
|
|
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.8.1",
|
|
53
53
|
"bluebird": "3.7.2",
|
|
54
54
|
"body-parser": "1.20.3",
|
|
55
55
|
"express": "4.21.2",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"path-to-regexp": "8.2.0",
|
|
63
63
|
"serve-favicon": "2.5.0",
|
|
64
64
|
"source-map-support": "0.5.21",
|
|
65
|
-
"type-fest": "4.
|
|
65
|
+
"type-fest": "4.36.0",
|
|
66
66
|
"validate.js": "0.13.1"
|
|
67
67
|
},
|
|
68
68
|
"optionalDependencies": {
|
|
@@ -73,9 +73,10 @@
|
|
|
73
73
|
"npm": ">=8"
|
|
74
74
|
},
|
|
75
75
|
"publishConfig": {
|
|
76
|
-
"access": "public"
|
|
76
|
+
"access": "public",
|
|
77
|
+
"tag": "beta"
|
|
77
78
|
},
|
|
78
|
-
"gitHead": "
|
|
79
|
+
"gitHead": "16d16dd0f3bf9829cff2ada7525156c462970932",
|
|
79
80
|
"tsd": {
|
|
80
81
|
"directory": "test/types"
|
|
81
82
|
}
|