Navigation Menu

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core(driver): add timeout to getRequestContent #4718

Merged
merged 5 commits into from Mar 13, 2018

Conversation

paulirish
Copy link
Member

@paulirish paulirish commented Mar 9, 2018

fixes #4258

I was able to repro on all the URLs mentioned in the ticket:

Network.getResponseBody is taking >5s to respond when provided particular requestIds. But it's quite odd. If i have devtools open at the same time, the devtools frontend can issue the exact same protocol method with the same requestId and it has no problem. (and apparently devtools and CLI also have no problem)

image

So this is particular to the extension.. sort of.

Update: the root bug appears to be an encoding issue in how the extension handles chrome.debugger payloads. Below is a bunch of details that supports that argument:

here's the 4 scripts that will reliably cause a problem:

I believe these files all have some characters in common that are mishandled somewhere in encoding.

I ran this across these 4 files:

var codepoints = []; 
for (const char of filesource){ 
	var code = char.codePointAt(0); 
	if (code > 30000) codepoints.push(code) 
}

And they all have in common some fairly high codepoints:

bundle.js 
[ 127828, 127860, 127860, 127861, 127864, 127866, 128722, 129366, 65306, 65306, 65374, 65374, 65534 ]

smartchat
[ 65311, 65311, 65311, 65311, 65520, 65521, 65522, 65523, 65524, 65525, 65526, 65527, 65528, 65529, 65530, 65531, 65532, 65533, 65534, 65535 ]

firebase firestore
[ 65535 ]

alfabank
[ 65533, 65533, 65533, 65533, 65534 ]

They all have in common 65534 or 65535.

And... check this out. Run this in any DevTools console:

String.fromCodePoint(65535)

Now take that resulting string, copy/paste it back and try to evaluate it. DevTools won't. :)


So, since this appears to be rather an involved bug. My easy workaround is to add a 2s timeout.

I did observe a 300ms successful response time for this method on a fairly big file so 2000ms seems an OK maximum.

return new Promise((resolve, reject) => {
// If this takes more than 2s, reject the Promise.
const err = new Error('Fetching resource content has exceeded the allotted time of 2s');
const asyncTimeout = setTimeout((_ => reject(err)), 2000);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be cool going down to 1s too to keep things moving

}).then(result => result.body);
return new Promise((resolve, reject) => {
// If this takes more than 2s, reject the Promise.
const err = new Error('Fetching resource content has exceeded the allotted time of 2s');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we try to give this thing an error code and be consistent about timeout stuff moving forward?

Copy link
Collaborator

@patrickhulce patrickhulce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very nice sleuthing 👏

Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

// Ignoring result.base64Encoded, which indicates if body is already encoded
}).then(result => result.body);
return new Promise((resolve, reject) => {
// If this takes more than 2s, reject the Promise.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add a note with link back to your investigation for why this method in particular can be problematic?

Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

two last things

return driverStub.getRequestContent(0, MAX_WAIT_FOR_PROTOCOL).then(_ => {
assert.ok(false, 'long-running getRequestContent supposed to reject');
}).catch(e => {
assert.equal(e.code, 'REQUEST_CONTENT_TIMEOUT');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will the above assert message get eaten by this comparison? could also do the two function then()

return driverStub.getRequestContent(0).then(value => {
  assert.ok(false, 'long-running getRequestContent supposed to reject');
}, e => {
  assert.equal(e.code, 'REQUEST_CONTENT_TIMEOUT');
});

@@ -714,13 +715,25 @@ class Driver {
/**
* Return the body of the response with the given ID.
* @param {string} requestId
* @param {?number} timeout
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{number=}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Lighthouse hangs indefinitely on Retrieving Scripts
4 participants