UNPKG

4.03 kBJavaScriptView Raw
1/**
2 * @file xhr.js
3 */
4
5/**
6 * A wrapper for videojs.xhr that tracks bandwidth.
7 *
8 * @param {Object} options options for the XHR
9 * @param {Function} callback the callback to call when done
10 * @return {Request} the xhr request that is going to be made
11 */
12import videojs from 'video.js';
13
14const {
15 xhr: videojsXHR,
16 mergeOptions
17} = videojs;
18
19const callbackWrapper = function(request, error, response, callback) {
20 const reqResponse = request.responseType === 'arraybuffer' ? request.response : request.responseText;
21
22 if (!error && reqResponse) {
23 request.responseTime = Date.now();
24 request.roundTripTime = request.responseTime - request.requestTime;
25 request.bytesReceived = reqResponse.byteLength || reqResponse.length;
26 if (!request.bandwidth) {
27 request.bandwidth =
28 Math.floor((request.bytesReceived / request.roundTripTime) * 8 * 1000);
29 }
30 }
31
32 if (response.headers) {
33 request.responseHeaders = response.headers;
34 }
35
36 // videojs.xhr now uses a specific code on the error
37 // object to signal that a request has timed out instead
38 // of setting a boolean on the request object
39 if (error && error.code === 'ETIMEDOUT') {
40 request.timedout = true;
41 }
42
43 // videojs.xhr no longer considers status codes outside of 200 and 0
44 // (for file uris) to be errors, but the old XHR did, so emulate that
45 // behavior. Status 206 may be used in response to byterange requests.
46 if (!error &&
47 !request.aborted &&
48 response.statusCode !== 200 &&
49 response.statusCode !== 206 &&
50 response.statusCode !== 0) {
51 error = new Error('XHR Failed with a response of: ' +
52 (request && (reqResponse || request.responseText)));
53 }
54
55 callback(error, request);
56};
57
58const xhrFactory = function() {
59 const xhr = function XhrFunction(options, callback) {
60 // Add a default timeout
61 options = mergeOptions({
62 timeout: 45e3
63 }, options);
64
65 // Allow an optional user-specified function to modify the option
66 // object before we construct the xhr request
67 const beforeRequest = XhrFunction.beforeRequest || videojs.Vhs.xhr.beforeRequest;
68
69 if (beforeRequest && typeof beforeRequest === 'function') {
70 const newOptions = beforeRequest(options);
71
72 if (newOptions) {
73 options = newOptions;
74 }
75 }
76
77 // Use the standard videojs.xhr() method unless `videojs.Vhs.xhr` has been overriden
78 // TODO: switch back to videojs.Vhs.xhr.name === 'XhrFunction' when we drop IE11
79 const xhrMethod = videojs.Vhs.xhr.original === true ? videojsXHR : videojs.Vhs.xhr;
80
81 const request = xhrMethod(options, function(error, response) {
82 return callbackWrapper(request, error, response, callback);
83 });
84 const originalAbort = request.abort;
85
86 request.abort = function() {
87 request.aborted = true;
88 return originalAbort.apply(request, arguments);
89 };
90 request.uri = options.uri;
91 request.requestTime = Date.now();
92 return request;
93 };
94
95 xhr.original = true;
96
97 return xhr;
98};
99
100/**
101 * Turns segment byterange into a string suitable for use in
102 * HTTP Range requests
103 *
104 * @param {Object} byterange - an object with two values defining the start and end
105 * of a byte-range
106 */
107const byterangeStr = function(byterange) {
108 // `byterangeEnd` is one less than `offset + length` because the HTTP range
109 // header uses inclusive ranges
110 const byterangeEnd = byterange.offset + byterange.length - 1;
111 const byterangeStart = byterange.offset;
112
113 return 'bytes=' + byterangeStart + '-' + byterangeEnd;
114};
115
116/**
117 * Defines headers for use in the xhr request for a particular segment.
118 *
119 * @param {Object} segment - a simplified copy of the segmentInfo object
120 * from SegmentLoader
121 */
122const segmentXhrHeaders = function(segment) {
123 const headers = {};
124
125 if (segment.byterange) {
126 headers.Range = byterangeStr(segment.byterange);
127 }
128 return headers;
129};
130
131export {segmentXhrHeaders, callbackWrapper, xhrFactory};
132
133export default xhrFactory;