Skip to content
This repository was archived by the owner on Jan 7, 2020. It is now read-only.

MAID-2488 feat/webFetch: impl multipart range requests #223

Merged
merged 6 commits into from
May 16, 2018
Merged

MAID-2488 feat/webFetch: impl multipart range requests #223

merged 6 commits into from
May 16, 2018

Conversation

hunterlester
Copy link
Contributor

@hunterlester hunterlester commented Apr 27, 2018

If a multipart range is desired, the range value may be an array instead of an object.

const range = [
        {
          start: 2,
          end: 4
        },
        {
          start: 7,
          end: 9,
        },
        {
          start: 13,
         end: 17,
        },
];

What is returned is still an object with headers and body expect the body is an array of objects with headers and body properties:

{
  "headers": {
       "Content-Type": "multipart/byteranges",
       "Content-Length": 200
   }.
   body: [
       {
           "headers": {
                "Content-Type: "video/mp4,
                "Content-Range":  bytes 4-15/200
            },
            body: <Buffer>
       },
        {
           "headers": {
                "Content-Type: "video/mp4,
                "Content-Range":  bytes 40-99/200
            },
            body: <Buffer>
       },
        {
           "headers": {
                "Content-Type: "video/mp4,
                "Content-Range":  bytes 150-175/200
            },
            body: <Buffer>
       },
   ]
}

This seemed most appropriate way to structure response based on https://stackoverflow.com/questions/41348421/how-to-properly-parse-multipart-byteranges-responses-using-typescript-javascript, https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests, and node-formidable/formidable#390

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.03%) to 91.828% when pulling e88c5d8 on hunterlester:multipart into dadd895 on maidsafe:master.

@coveralls
Copy link

coveralls commented Apr 28, 2018

Coverage Status

Coverage increased (+0.3%) to 93.176% when pulling 03dba25 on hunterlester:multipart into 3631731 on maidsafe:master.

@hunterlester hunterlester changed the title [WIP] MAID-2488 feat/webFetch: impl multipart range requests MAID-2488 feat/webFetch: impl multipart range requests Apr 28, 2018
test/browsing.js Outdated
));
const domain = await createRandomDomain(content, '/streaming.mp4');
const app = await createAnonTestApp();
return should(app.webFetch(`safe://${domain}/streaming.mp4`, { range: { start: 0, end: content.length + 5 } }))
Copy link
Contributor

Choose a reason for hiding this comment

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

I wouldn't change the end value to content.length + 5 as the test is trying to make sure that the very first byte out of range gets properly picked up as incorrect, that's where usually the issues are.

@@ -93,6 +93,14 @@ describe('Browsing', () => {
));
}).timeout(20000);

it('fetches file with non-explicit mime type', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the difference between this test and the test on line 193 named /index.html ?
Also, what do you mean by non-explicit mime type for this test?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This test differs with the one on line 193 by creating a file without an extension, so a mimetype cannot be detected.

Copy link
Contributor

Choose a reason for hiding this comment

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

We could then add the validation for the mime types to the tests

@@ -168,9 +168,6 @@ class SAFEApp extends EventEmitter {
if (!url) return Promise.reject(makeError(errConst.MISSING_URL.code, errConst.MISSING_URL.msg));

const parsedUrl = parseUrl(url);
if (!parsedUrl) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why this is not needed anymore?
Also, we need to update the documentation as it seems it now accepts either an object or an array.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This line isn't needed anymore because !url is already handled on line 168. If there is an error parsing the url it will be handled by the the parseUrl function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What is it that accepts an object or an array? The webFetch function for it's url parameter? I just see that we handle objects for options parameter.

Copy link
Contributor

Choose a reason for hiding this comment

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

When you say it will be handled by parseUrl what do you mean? if the url is not null but is an invalid url, what will happen?
The url is a string.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Currently Promise.reject(makeError(errConst.MISSING_URL.code, errConst.MISSING_URL.msg)); is returned if !parseUrl, but this condition can never be met because Node's url.parse function will throw an error if no URL string is passed to it and also the function doesn't consider any string to be an invalid URL, it will simply return an URL object.

src/app.js Outdated
data = await openedFile.read(start, lengthToRead);
}

const mimeType = multipart ? 'multipart/byteranges' : false
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need the false in the boolean operation?
I would rather break this into and if/else as it's a bit confusing and error prone.

src/app.js Outdated
const partLengthToRead = (partEnd - partStart) + 1; // account for 0 index
const byteSegment = await openedFile.read(partStart, partLengthToRead);
const partEndByte = (partEnd === fileSize - 1) ? fileSize - 1 : partEnd;
const mimeType = mime.getType(nodePath.extname(filePath))
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems you don't need this within the iteration

Copy link
Contributor Author

@hunterlester hunterlester May 1, 2018

Choose a reason for hiding this comment

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

The mimeType variable? That should be different from the parent scope mimeType.
The parent response header mimeType is multipart/byteranges while the byte range parts should have a mime type that corresponds to the file which they belong.

Copy link
Contributor

Choose a reason for hiding this comment

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

But the value you are setting to mimeType is always the same as it doesn't depend on any of the values in each iteration, right?

@@ -326,6 +333,99 @@ describe('Browsing', () => {
.be.rejectedWith('Invalid range start value')
));
}); // .timeout(20000);

it('fetches multipart ranges', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

We should be testing the headers as we do for single range tests.

@bochaco
Copy link
Contributor

bochaco commented May 1, 2018

Perhaps we can return the array with headers and bodies but with an attributed called parts rather than body, it seems that's closer to what happens in HTTP since the repose contains multiple parts, e.g.:

{
  "headers": {
       "Content-Type": "multipart/byteranges",
       "Content-Length": 200
   }.
   parts: [
       {
           "headers": {
                "Content-Type: "video/mp4,
                "Content-Range":  bytes 4-15/200
            },
            body: <Buffer>
       },
       ...
   ]
}

@hunterlester
Copy link
Contributor Author

@bochaco Yeah, I wasn't sure about this because I can't find documentation about how to encode multipart byte ranges with JSON: https://developer.mozilla.org/en-US/docs/Web/API/Response


range = options.range;
multipart = range.length > 1;
start = options.range.start || consts.pubConsts.NFS_FILE_START;
Copy link
Contributor

Choose a reason for hiding this comment

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

So are we making the start optional as well? if so we'd need to add tests for that

@bochaco bochaco merged commit 4644583 into maidsafe-archive:master May 16, 2018
@hunterlester hunterlester deleted the multipart branch June 6, 2018 18:04
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants