Skip to content

Commit 91ff686

Browse files
mroswaldFacebook Github Bot 5
authored and
Facebook Github Bot 5
committedJul 13, 2016
Use HTTP range requests (responses) to serve mp4 from assets
Summary: This PR solves a problem when video assets are used from third-party React Native components (e.g. [react-native-video](https://github.com/brentvatne/react-native-video). The video will not work while the assets are served from the react native packager because the used video component (iOS) relies on HTTP range requests. I added a small fix that allows ranged requests (e.g. mp4) to be served in ranges. To test this: 1. make new react native project 1. add [react-native-video](https://github.com/brentvatne/react-native-video) to xcode project 1. add video component to your project ``` import Video from 'react-native-video'; var resolveAssetSource = require('react-native/Libraries/Image/resolveAssetSource'); /* ... /* render() { let source = resolveAssetSource(require('./someVideoFile.mp4')) || {}; return <Video /*....*/ source={source} />; } ``` That should not work (if video is smaller than a few megabytes, open app a few times). Then add my fix, that should do the trick. Closes #8219 Reviewed By: davidaurelio Differential Revision: D3542485 Pulled By: frantic fbshipit-source-id: e4f2e4d3aaafa8445e965259bf04ad107dba8a4f
1 parent 262397d commit 91ff686

File tree

3 files changed

+37
-2
lines changed

3 files changed

+37
-2
lines changed
 

‎package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,8 @@
188188
"xmldoc": "^0.4.0",
189189
"yargs": "^3.24.0",
190190
"yeoman-environment": "~1.2.7",
191-
"yeoman-generator": "^0.20.3"
191+
"yeoman-generator": "^0.20.3",
192+
"mime-types": "2.1.11"
192193
},
193194
"devDependencies": {
194195
"babel-eslint": "^6.0.0",

‎packager/react-packager/src/Server/__tests__/Server-test.js

+13
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,19 @@ describe('processRequest', () => {
349349
expect(AssetServer.prototype.get).toBeCalledWith('imgs/a.png', 'ios');
350350
expect(res.end).toBeCalledWith('i am image');
351351
});
352+
353+
it('should serve range request', () => {
354+
const req = {url: '/assets/imgs/a.png?platform=ios', headers: {range: 'bytes=0-3'}};
355+
const res = {end: jest.fn(), writeHead: jest.fn()};
356+
const mockData = 'i am image';
357+
358+
AssetServer.prototype.get.mockImpl(() => Promise.resolve(mockData));
359+
360+
server.processRequest(req, res);
361+
jest.runAllTimers();
362+
expect(AssetServer.prototype.get).toBeCalledWith('imgs/a.png', 'ios');
363+
expect(res.end).toBeCalledWith(mockData.slice(0, 3));
364+
});
352365
});
353366

354367
describe('buildbundle(options)', () => {

‎packager/react-packager/src/Server/index.js

+22-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const SourceMapConsumer = require('source-map').SourceMapConsumer;
1919
const declareOpts = require('../lib/declareOpts');
2020
const path = require('path');
2121
const url = require('url');
22+
const mime = require('mime-types');
2223

2324
function debounce(fn, delay) {
2425
var timeout;
@@ -396,13 +397,33 @@ class Server {
396397
});
397398
}
398399

400+
_rangeRequestMiddleware(req, res, data, assetPath) {
401+
if (req.headers && req.headers.range) {
402+
const [rangeStart, rangeEnd] = req.headers.range.replace(/bytes=/, '').split('-');
403+
const dataStart = parseInt(rangeStart, 10);
404+
const dataEnd = rangeEnd ? parseInt(rangeEnd, 10) : data.length - 1;
405+
const chunksize = (dataEnd - dataStart) + 1;
406+
407+
res.writeHead(206, {
408+
'Accept-Ranges': 'bytes',
409+
'Content-Length': chunksize,
410+
'Content-Range': `bytes ${dataStart}-${dataEnd}/${data.length}`,
411+
'Content-Type': mime.lookup(path.basename(assetPath[1]))
412+
});
413+
414+
return data.slice(dataStart, dataEnd);
415+
}
416+
417+
return data;
418+
}
419+
399420
_processAssetsRequest(req, res) {
400421
const urlObj = url.parse(req.url, true);
401422
const assetPath = urlObj.pathname.match(/^\/assets\/(.+)$/);
402423
const assetEvent = Activity.startEvent(`processing asset request ${assetPath[1]}`);
403424
this._assetServer.get(assetPath[1], urlObj.query.platform)
404425
.then(
405-
data => res.end(data),
426+
data => res.end(this._rangeRequestMiddleware(req, res, data, assetPath)),
406427
error => {
407428
console.error(error.stack);
408429
res.writeHead('404');

0 commit comments

Comments
 (0)
Please sign in to comment.