Skip to content

Commit

Permalink
Refactor HttpFetchPlugin to clarify error outcomes
Browse files Browse the repository at this point in the history
Previously, HttpFetchPlugin would treat any non-Shaka error as
HTTP_ERROR when there were potentially other circumstances which
could have produced a non-Shaka error.

This change catches errors from fetch sooner, so that there is no
need to do a fuzzy check for error.severity. It also clears the
request timeout regardless of whether fetch throws or not.

Closes #1519
  • Loading branch information
Fillmore, Christopher (cf276p) committed Aug 8, 2018
1 parent 59eb930 commit 301c0e3
Showing 1 changed file with 72 additions and 42 deletions.
114 changes: 72 additions & 42 deletions lib/net/http_fetch_plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ goog.require('shaka.util.MapUtils');
* @export
*/
shaka.net.HttpFetchPlugin = function(uri, request, requestType) {
let headers = new shaka.net.HttpFetchPlugin.Headers_();
const headers = new shaka.net.HttpFetchPlugin.Headers_();
shaka.util.MapUtils.forEach(request.headers, function(key, value) {
headers.append(key, value);
});

let controller = new shaka.net.HttpFetchPlugin.AbortController_();
const controller = new shaka.net.HttpFetchPlugin.AbortController_();

/** @type {!RequestInit} */
let init = {
const init = {
// Edge does not treat null as undefined for body; https://bit.ly/2luyE6x
body: request.body || undefined,
headers: headers,
Expand All @@ -51,71 +51,101 @@ shaka.net.HttpFetchPlugin = function(uri, request, requestType) {
credentials: request.allowCrossSiteCredentials ? 'include' : undefined,
};

let canceled = false;
let timedOut = false;
/** @type {shaka.net.HttpFetchPlugin.AbortStatus} */
const abortStatus = {
canceled: false,
timedOut: false,
};

// The fetch API does not timeout natively, so do a timeout manually using the
// AbortController.
let timeout;
if (request.retryParameters.timeout) {
let onTimeout = function() {
timedOut = true;
abortStatus.timedOut = true;
controller.abort();
};
timeout = setTimeout(onTimeout, request.retryParameters.timeout);
}

let fetch = shaka.net.HttpFetchPlugin.fetch_;
let promise = fetch(uri, init).then(function(response) {
clearTimeout(timeout);
return response.arrayBuffer().then(function(arrayBuffer) {
let headers = {};
/** @type {Headers} */
let responseHeaders = response.headers;
responseHeaders.forEach(function(value, key) {
// Since IE/Edge incorrectly return the header with a leading new line
// character ('\n'), we trim the header here.
headers[key.trim()] = value;
});

return shaka.net.HttpPluginUtils.makeResponse(headers,
arrayBuffer, response.status, uri, response.url, requestType);
const promise = shaka.net.HttpFetchPlugin.request_(uri, requestType, init,
abortStatus, timeout);

return new shaka.util.AbortableOperation(
promise,
() => {
abortStatus.canceled = true;
controller.abort();
return Promise.resolve();
});
}).catch(function(error) {
clearTimeout(timeout);
if (canceled) {
return Promise.reject(new shaka.util.Error(
};

/**
* @param {string} uri
* @param {shaka.net.NetworkingEngine.RequestType} requestType
* @param {!RequestInit} init
* @param {shaka.net.HttpFetchPlugin.AbortStatus} abortStatus
* @param {number|undefined} timeoutId
* @return {!Promise<!shaka.extern.Response>}
*/
shaka.net.HttpFetchPlugin.request_ = async function(uri, requestType, init,
abortStatus, timeoutId) {
const fetch = shaka.net.HttpFetchPlugin.fetch_;
let response;
let arrayBuffer;

try {
response = await fetch(uri, init);
arrayBuffer = await response.arrayBuffer();
} catch (error) {
if (abortStatus.canceled) {
throw new shaka.util.Error(
shaka.util.Error.Severity.RECOVERABLE,
shaka.util.Error.Category.NETWORK,
shaka.util.Error.Code.OPERATION_ABORTED,
uri, requestType));
} else if (timedOut) {
return Promise.reject(new shaka.util.Error(
uri, requestType);
} else if (abortStatus.timedOut) {
throw new shaka.util.Error(
shaka.util.Error.Severity.RECOVERABLE,
shaka.util.Error.Category.NETWORK,
shaka.util.Error.Code.TIMEOUT,
uri, requestType));
} else if (error.severity == undefined) {
// Replace non-shaka errors with a generic HTTP_ERROR.
return Promise.reject(new shaka.util.Error(
uri, requestType);
} else {
throw new shaka.util.Error(
shaka.util.Error.Severity.RECOVERABLE,
shaka.util.Error.Category.NETWORK,
shaka.util.Error.Code.HTTP_ERROR,
uri, error, requestType));
} else {
return Promise.reject(error);
uri, error, requestType);
}
} finally {
clearTimeout(timeoutId);
}

const headers = {};
/** @type {Headers} */
const responseHeaders = response.headers;
responseHeaders.forEach(function(value, key) {
// Since IE/Edge incorrectly return the header with a leading new line
// character ('\n'), we trim the header here.
headers[key.trim()] = value;
});

return new shaka.util.AbortableOperation(
promise,
() => {
canceled = true;
controller.abort();
return Promise.resolve();
});
return shaka.net.HttpPluginUtils.makeResponse(headers,
arrayBuffer, response.status, uri, response.url, requestType);
};

/**
* @typedef {{
* canceled: boolean,
* timedOut: boolean
* }}
* @property {boolean} canceled
* Indicates if the request was canceled.
* @property {boolean} timedOut
* Indicates if the request timed out.
*/
shaka.net.HttpFetchPlugin.AbortStatus;


/**
* Determine if the Fetch API is supported in the browser. Note: this is
Expand Down

0 comments on commit 301c0e3

Please sign in to comment.