Skip to content

Commit c8aa8d7

Browse files
Merge pull request #371 from Distributive-Network/Xmader/fix/xhr-poll-error
Make easier debugging HTTP errors (such as `DCPError: no transports defined`)
2 parents 6d9a396 + 9e5794b commit c8aa8d7

File tree

3 files changed

+47
-14
lines changed

3 files changed

+47
-14
lines changed

python/pythonmonkey/builtin_modules/XMLHttpRequest-internal.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ export declare function request(
4646
// callbacks for known exceptions
4747
onTimeoutError: (err: Error) => void,
4848
onNetworkError: (err: Error) => void,
49+
// the debug logging function
50+
/** See `pm.bootstrap.require("debug")` */
51+
debug: (selector: string) => ((...args: string[]) => void),
4952
): Promise<void>;
5053

5154
/**

python/pythonmonkey/builtin_modules/XMLHttpRequest-internal.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@ async def request(
4444
# callbacks for known exceptions
4545
onTimeoutError: Callable[[asyncio.TimeoutError], None],
4646
onNetworkError: Callable[[aiohttp.ClientError], None],
47+
# the debug logging function, see `pm.bootstrap.require("debug")`
48+
debug: Callable[[str], Callable[..., None]],
4749
/
4850
):
49-
debug = pm.bootstrap.require("debug")
5051

5152
# to support HTTP-Keep-Alive
5253
global keepAliveConnector

python/pythonmonkey/builtin_modules/XMLHttpRequest.js

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const debug = globalThis.python.eval('__import__("pythonmonkey").bootstrap.requi
2020
* @param {any} what The thing to truncate; must have a slice method and index property.
2121
* Works with string, array, typedarray, etc.
2222
* @param {number} maxlen The maximum length for truncation
23-
* @param {boolean} coerce Not false = coerce to printable character codes
23+
* @param {boolean=} coerce Not false = coerce to printable character codes
2424
* @returns {string}
2525
*/
2626
function trunc(what, maxlen, coerce)
@@ -104,6 +104,34 @@ class XMLHttpRequest extends XMLHttpRequestEventTarget
104104
/** @type {EventListenerFn} */
105105
onreadystatechange = null;
106106

107+
//
108+
// debugging
109+
//
110+
/** The unique connection id to identify each XHR connection when debugging */
111+
#connectionId = Math.random().toString(16).slice(2, 9); // random 7-character hex string
112+
113+
/**
114+
* Wrapper to print debug logs with connection id information
115+
* @param {string} selector
116+
*/
117+
#debug(selector)
118+
{
119+
return (...args) => debug(selector)(`Conn<${this.#connectionId}>:`, ...args);
120+
}
121+
122+
/**
123+
* Allowing others to inspect the internal properties
124+
*/
125+
get _requestMetadata()
126+
{
127+
return {
128+
method: this.#requestMethod,
129+
url: this.#requestURL.toString(),
130+
headers: this.#requestHeaders,
131+
body: this.#requestBody,
132+
};
133+
}
134+
107135
//
108136
// states
109137
//
@@ -142,7 +170,7 @@ class XMLHttpRequest extends XMLHttpRequestEventTarget
142170
*/
143171
open(method, url, async = true, username = null, password = null)
144172
{
145-
debug('xhr:open')('open start, method=' + method);
173+
this.#debug('xhr:open')('open start, method=' + method);
146174
// Normalize the method.
147175
// @ts-expect-error
148176
method = method.toString().toUpperCase();
@@ -156,7 +184,7 @@ class XMLHttpRequest extends XMLHttpRequestEventTarget
156184
parsedURL.username = username;
157185
if (password)
158186
parsedURL.password = password;
159-
debug('xhr:open')('url is ' + parsedURL.href);
187+
this.#debug('xhr:open')('url is ' + parsedURL.href);
160188

161189
// step 11
162190
this.#sendFlag = false;
@@ -176,7 +204,7 @@ class XMLHttpRequest extends XMLHttpRequestEventTarget
176204
this.#state = XMLHttpRequest.OPENED;
177205
this.dispatchEvent(new Event('readystatechange'));
178206
}
179-
debug('xhr:open')('finished open, state is ' + this.#state);
207+
this.#debug('xhr:open')('finished open, state is ' + this.#state);
180208
}
181209

182210
/**
@@ -186,7 +214,7 @@ class XMLHttpRequest extends XMLHttpRequestEventTarget
186214
*/
187215
setRequestHeader(name, value)
188216
{
189-
debug('xhr:headers')(`set header ${name}=${value}`);
217+
this.#debug('xhr:headers')(`set header ${name}=${value}`);
190218
if (this.#state !== XMLHttpRequest.OPENED)
191219
throw new DOMException('setRequestHeader can only be called when state is OPEN', 'InvalidStateError');
192220
if (this.#sendFlag)
@@ -252,7 +280,7 @@ class XMLHttpRequest extends XMLHttpRequestEventTarget
252280
*/
253281
send(body = null)
254282
{
255-
debug('xhr:send')(`sending; body length=${body?.length}`);
283+
this.#debug('xhr:send')(`sending; body length=${body?.length} «${body ? trunc(body, 100) : ''}»`);
256284
if (this.#state !== XMLHttpRequest.OPENED) // step 1
257285
throw new DOMException('connection must be opened before send() is called', 'InvalidStateError');
258286
if (this.#sendFlag) // step 2
@@ -285,7 +313,7 @@ class XMLHttpRequest extends XMLHttpRequestEventTarget
285313
if (!originalAuthorContentType && extractedContentType)
286314
this.#requestHeaders['content-type'] = extractedContentType;
287315
}
288-
debug('xhr:send')(`content-type=${this.#requestHeaders['content-type']}`);
316+
this.#debug('xhr:send')(`content-type=${this.#requestHeaders['content-type']}`);
289317

290318
// step 5
291319
if (this.#uploadObject._hasAnyListeners())
@@ -310,7 +338,7 @@ class XMLHttpRequest extends XMLHttpRequestEventTarget
310338
*/
311339
#sendAsync()
312340
{
313-
debug('xhr:send')('sending in async mode');
341+
this.#debug('xhr:send')('sending in async mode');
314342
this.dispatchEvent(new ProgressEvent('loadstart', { loaded:0, total:0 })); // step 11.1
315343

316344
let requestBodyTransmitted = 0; // step 11.2
@@ -343,7 +371,7 @@ class XMLHttpRequest extends XMLHttpRequestEventTarget
343371
let responseLength = 0;
344372
const processResponse = (response) =>
345373
{
346-
debug('xhr:response')(`response headers ----\n${response.getAllResponseHeaders()}`);
374+
this.#debug('xhr:response')(`response headers ----\n${response.getAllResponseHeaders()}`);
347375
this.#response = response; // step 11.9.1
348376
this.#state = XMLHttpRequest.HEADERS_RECEIVED; // step 11.9.4
349377
this.dispatchEvent(new Event('readystatechange')); // step 11.9.5
@@ -354,7 +382,7 @@ class XMLHttpRequest extends XMLHttpRequestEventTarget
354382

355383
const processBodyChunk = (/** @type {Uint8Array} */ bytes) =>
356384
{
357-
debug('xhr:response')(`recv chunk, ${bytes.length} bytes (${trunc(bytes, 100)})`);
385+
this.#debug('xhr:response')(`recv chunk, ${bytes.length} bytes «${trunc(bytes, 100)}»`);
358386
this.#receivedBytes.push(bytes);
359387
if (this.#state === XMLHttpRequest.HEADERS_RECEIVED)
360388
this.#state = XMLHttpRequest.LOADING;
@@ -367,7 +395,7 @@ class XMLHttpRequest extends XMLHttpRequestEventTarget
367395
*/
368396
const processEndOfBody = () =>
369397
{
370-
debug('xhr:response')(`end of body, received ${this.#receivedLength} bytes`);
398+
this.#debug('xhr:response')(`end of body, received ${this.#receivedLength} bytes`);
371399
const transmitted = this.#receivedLength; // step 3
372400
const length = responseLength || 0; // step 4
373401

@@ -380,8 +408,8 @@ class XMLHttpRequest extends XMLHttpRequestEventTarget
380408
this.dispatchEvent(new ProgressEvent(eventType, { loaded:transmitted, total:length }));
381409
};
382410

383-
debug('xhr:send')(`${this.#requestMethod} ${this.#requestURL.href}`);
384-
debug('xhr:headers')('headers=' + Object.entries(this.#requestHeaders));
411+
this.#debug('xhr:send')(`${this.#requestMethod} ${this.#requestURL.href}`);
412+
this.#debug('xhr:headers')('headers=' + Object.entries(this.#requestHeaders));
385413

386414
// send() step 6
387415
request(
@@ -397,6 +425,7 @@ class XMLHttpRequest extends XMLHttpRequestEventTarget
397425
processEndOfBody,
398426
() => (this.#timedOutFlag = true), // onTimeoutError
399427
() => (this.#response = null /* network error */), // onNetworkError
428+
this.#debug.bind(this),
400429
).catch((e) => this.#handleErrors(e));
401430
}
402431

0 commit comments

Comments
 (0)