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

fix(net): Fix HEAD requests in new Chromium #5180

Merged
merged 1 commit into from
Apr 25, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 69 additions & 57 deletions lib/net/http_fetch_plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,65 +120,77 @@ shaka.net.HttpFetchPlugin = class {
// not the body yet.
headersReceived(shaka.net.HttpFetchPlugin.headersToGenericObject_(
response.headers));
// Getting the reader in this way allows us to observe the process of
// downloading the body, instead of just waiting for an opaque promise to
// resolve.
// We first clone the response because calling getReader locks the body
// stream; if we didn't clone it here, we would be unable to get the
// response's arrayBuffer later.
const reader = response.clone().body.getReader();

const contentLengthRaw = response.headers.get('Content-Length');
const contentLength =
contentLengthRaw ? parseInt(contentLengthRaw, 10) : 0;

const start = (controller) => {
const push = async () => {
let readObj;
try {
readObj = await reader.read();
} catch (e) {
// If we abort the request, we'll get an error here. Just ignore it
// since real errors will be reported when we read the buffer below.
shaka.log.v1('error reading from stream', e.message);
return;
}

if (!readObj.done) {
loaded += readObj.value.byteLength;
if (streamDataCallback) {
await streamDataCallback(readObj.value);

// In new versions of Chromium, HEAD requests now have a response body
// that is null.
// So just don't try to download the body at all, if it's a HEAD request,
// to avoid null reference errors.
// See: https://crbug.com/1297060
if (init.method != 'HEAD') {
goog.asserts.assert(response.body,
'non-HEAD responses should have a body');

// Getting the reader in this way allows us to observe the process of
// downloading the body, instead of just waiting for an opaque promise
// to resolve.
// We first clone the response because calling getReader locks the body
// stream; if we didn't clone it here, we would be unable to get the
// response's arrayBuffer later.
const reader = response.clone().body.getReader();

const contentLengthRaw = response.headers.get('Content-Length');
const contentLength =
contentLengthRaw ? parseInt(contentLengthRaw, 10) : 0;

const start = (controller) => {
const push = async () => {
let readObj;
try {
readObj = await reader.read();
} catch (e) {
// If we abort the request, we'll get an error here. Just ignore
// it since real errors will be reported when we read the buffer
// below.
shaka.log.v1('error reading from stream', e.message);
return;
}

if (!readObj.done) {
loaded += readObj.value.byteLength;
if (streamDataCallback) {
await streamDataCallback(readObj.value);
}
}

const currentTime = Date.now();
// If the time between last time and this time we got progress event
// is long enough, or if a whole segment is downloaded, call
// progressUpdated().
if (currentTime - lastTime > 100 || readObj.done) {
progressUpdated(currentTime - lastTime, loaded - lastLoaded,
contentLength - loaded);
lastLoaded = loaded;
lastTime = currentTime;
}

if (readObj.done) {
goog.asserts.assert(!readObj.value,
'readObj should be unset when "done" is true.');
controller.close();
} else {
controller.enqueue(readObj.value);
push();
}
}

const currentTime = Date.now();
// If the time between last time and this time we got progress event
// is long enough, or if a whole segment is downloaded, call
// progressUpdated().
if (currentTime - lastTime > 100 || readObj.done) {
progressUpdated(currentTime - lastTime, loaded - lastLoaded,
contentLength - loaded);
lastLoaded = loaded;
lastTime = currentTime;
}

if (readObj.done) {
goog.asserts.assert(!readObj.value,
'readObj should be unset when "done" is true.');
controller.close();
} else {
controller.enqueue(readObj.value);
push();
}
};
push();
};
push();
};
// Create a ReadableStream to use the reader. We don't need to use the
// actual stream for anything, though, as we are using the response's
// arrayBuffer method to get the body, so we don't store the
// ReadableStream.
new ReadableStream({start}); // eslint-disable-line no-new
arrayBuffer = await response.arrayBuffer();
// Create a ReadableStream to use the reader. We don't need to use the
// actual stream for anything, though, as we are using the response's
// arrayBuffer method to get the body, so we don't store the
// ReadableStream.
new ReadableStream({start}); // eslint-disable-line no-new
arrayBuffer = await response.arrayBuffer();
}
} catch (error) {
if (abortStatus.canceled) {
throw new shaka.util.Error(
Expand Down