UploadedFile should support a callback for upload progress
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 22 Mar 2017 23:43:32 +0000 (23:43 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 22 Mar 2017 23:43:32 +0000 (23:43 +0000)
https://bugs.webkit.org/show_bug.cgi?id=169977

Reviewed by Andreas Kling.

Added a new option dictionary to CommonRemoteAPI.sendHttpRequest with uploadProgressCallback

Moved request headers and responseHandler callback in NodeRemoteAPI to this dictionary,
and updated the tests which relied on this code.

* public/shared/common-remote.js:
(CommonRemoteAPI.prototype.postJSON):
(CommonRemoteAPI.prototype.postJSONWithStatus):
(CommonRemoteAPI.prototype.postFormData):
(CommonRemoteAPI.prototype.postFormDataWithStatus):
* public/v3/privileged-api.js:
(PrivilegedAPI.prototype.sendRequest):
* public/v3/remote.js:
(BrowserRemoteAPI.prototype.sendHttpRequest):
(BrowserRemoteAPI.prototype.sendHttpRequestWithFormData):
(BrowserRemoteAPI):
* server-tests/api-uploaded-file.js:
* tools/js/remote.js:
(NodeRemoteAPI.prototype.sendHttpRequest):
(NodeRemoteAPI.prototype.sendHttpRequestWithFormData):
(NodeRemoteAPI):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@214285 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Websites/perf.webkit.org/ChangeLog
Websites/perf.webkit.org/public/shared/common-remote.js
Websites/perf.webkit.org/public/v3/models/uploaded-file.js
Websites/perf.webkit.org/public/v3/privileged-api.js
Websites/perf.webkit.org/public/v3/remote.js
Websites/perf.webkit.org/server-tests/api-uploaded-file.js
Websites/perf.webkit.org/tools/js/remote.js

index 09edbfe..9959e94 100644 (file)
@@ -1,5 +1,34 @@
 2017-03-22  Ryosuke Niwa  <rniwa@webkit.org>
 
+        UploadedFile should support a callback for upload progress
+        https://bugs.webkit.org/show_bug.cgi?id=169977
+
+        Reviewed by Andreas Kling.
+
+        Added a new option dictionary to CommonRemoteAPI.sendHttpRequest with uploadProgressCallback
+
+        Moved request headers and responseHandler callback in NodeRemoteAPI to this dictionary,
+        and updated the tests which relied on this code.
+
+        * public/shared/common-remote.js:
+        (CommonRemoteAPI.prototype.postJSON):
+        (CommonRemoteAPI.prototype.postJSONWithStatus):
+        (CommonRemoteAPI.prototype.postFormData):
+        (CommonRemoteAPI.prototype.postFormDataWithStatus):
+        * public/v3/privileged-api.js:
+        (PrivilegedAPI.prototype.sendRequest):
+        * public/v3/remote.js:
+        (BrowserRemoteAPI.prototype.sendHttpRequest):
+        (BrowserRemoteAPI.prototype.sendHttpRequestWithFormData):
+        (BrowserRemoteAPI):
+        * server-tests/api-uploaded-file.js:
+        * tools/js/remote.js:
+        (NodeRemoteAPI.prototype.sendHttpRequest):
+        (NodeRemoteAPI.prototype.sendHttpRequestWithFormData):
+        (NodeRemoteAPI):
+
+2017-03-22  Ryosuke Niwa  <rniwa@webkit.org>
+
         ComponentBase should enqueue itself to render when it becomes connected
         https://bugs.webkit.org/show_bug.cgi?id=169905
 
index e7aa85e..18a3c35 100644 (file)
@@ -1,27 +1,27 @@
 "use strict";
 
 class CommonRemoteAPI {
-    postJSON(path, data)
+    postJSON(path, data, options)
     {
-        return this._asJSON(this.sendHttpRequest(path, 'POST', 'application/json', JSON.stringify(data || {})));
+        return this._asJSON(this.sendHttpRequest(path, 'POST', 'application/json', JSON.stringify(data || {}), options));
     }
 
-    postJSONWithStatus(path, data)
+    postJSONWithStatus(path, data, options)
     {
-        return this._checkStatus(this.postJSON(path, data));
+        return this._checkStatus(this.postJSON(path, data, options));
     }
 
-    postFormData(path, data)
+    postFormData(path, data, options)
     {
         const formData = new FormData();
         for (let key in data)
             formData.append(key, data[key]);
-        return this._asJSON(this.sendHttpRequestWithFormData(path, formData));
+        return this._asJSON(this.sendHttpRequestWithFormData(path, formData, options));
     }
 
-    postFormDataWithStatus(path, data)
+    postFormDataWithStatus(path, data, options)
     {
-        return this._checkStatus(this.postFormData(path, data));
+        return this._checkStatus(this.postFormData(path, data, options));
     }
 
     getJSON(path)
@@ -34,12 +34,12 @@ class CommonRemoteAPI {
         return this._checkStatus(this.getJSON(path));
     }
 
-    sendHttpRequest(path, method, contentType, content)
+    sendHttpRequest(path, method, contentType, content, options = {})
     {
         throw 'NotImplemented';
     }
 
-    sendHttpRequestWithFormData(path, formData)
+    sendHttpRequestWithFormData(path, formData, options = {})
     {
         throw 'NotImplemented';
     }
index f401384..fd1c693 100644 (file)
@@ -12,9 +12,9 @@ class UploadedFile extends DataModelObject {
         this.ensureNamedStaticMap('sha256')[object.sha256] = this;
     }
 
-    static uploadFile(file)
+    static uploadFile(file, uploadProgressCallback = null)
     {
-        return PrivilegedAPI.sendRequest('upload-file', {'newFile': file}, {useFormData: true}).then((rawData) => {
+        return PrivilegedAPI.sendRequest('upload-file', {'newFile': file}, {useFormData: true, uploadProgressCallback}).then((rawData) => {
             return UploadedFile.ensureSingleton(rawData['uploadedFile'].id, rawData['uploadedFile']);
         });
     }
index 267f94b..0171bab 100644 (file)
@@ -10,8 +10,8 @@ class PrivilegedAPI {
 
         const fullPath = '/privileged-api/' + path;
         const post = options.useFormData
-            ? () => RemoteAPI.postFormDataWithStatus(fullPath, clonedData)
-            : () => RemoteAPI.postJSONWithStatus(fullPath, clonedData);
+            ? () => RemoteAPI.postFormDataWithStatus(fullPath, clonedData, options)
+            : () => RemoteAPI.postJSONWithStatus(fullPath, clonedData, options);
 
         return this.requestCSRFToken().then((token) => {
             clonedData['token'] = token;
index 8d9203b..6a00f18 100644 (file)
@@ -2,7 +2,7 @@
 
 class BrowserRemoteAPI extends CommonRemoteAPI {
 
-    sendHttpRequest(path, method, contentType, content)
+    sendHttpRequest(path, method, contentType, content, options = {})
     {
         console.assert(!path.startsWith('http:') && !path.startsWith('https:') && !path.startsWith('file:'));
 
@@ -26,6 +26,12 @@ class BrowserRemoteAPI extends CommonRemoteAPI {
             xhr.onabort = onerror;
             xhr.onerror = onerror;
 
+            if (content && options.uploadProgressCallback) {
+                xhr.upload.onprogress = (event) => {
+                    options.uploadProgressCallback(event.lengthComputable ? {total: event.total, loaded: event.loaded} : null);
+                }
+            }
+
             xhr.open(method, path, true);
             if (contentType)
                 xhr.setRequestHeader('Content-Type', contentType);
@@ -36,9 +42,9 @@ class BrowserRemoteAPI extends CommonRemoteAPI {
         });
     }
 
-    sendHttpRequestWithFormData(path, formData)
+    sendHttpRequestWithFormData(path, formData, options)
     {
-        return this.sendHttpRequest(path, 'POST', null, formData); // Content-type is set by the browser.
+        return this.sendHttpRequest(path, 'POST', null, formData, options); // Content-type is set by the browser.
     }
 
 }
index 5ea1e9d..415177e 100644 (file)
@@ -138,7 +138,7 @@ describe('/api/uploaded-file', function () {
             return PrivilegedAPI.sendRequest('upload-file', {newFile: stream}, {useFormData: true});
         }).then((response) => {
             const id = response['uploadedFile']['id'];
-            return TestServer.remoteAPI().sendHttpRequest(`/api/uploaded-file/${id}`, 'GET', null, null, {'Range': 'bytes=5-'});
+            return TestServer.remoteAPI().sendHttpRequest(`/api/uploaded-file/${id}`, 'GET', null, null, {headers: {'Range': 'bytes=5-'}});
         }).then((response) => {
             const headers = response.headers;
             assert.equal(response.statusCode, 206);
@@ -152,7 +152,7 @@ describe('/api/uploaded-file', function () {
             return PrivilegedAPI.sendRequest('upload-file', {newFile: stream}, {useFormData: true});
         }).then((response) => {
             const id = response['uploadedFile']['id'];
-            return TestServer.remoteAPI().sendHttpRequest(`/api/uploaded-file/${id}`, 'GET', null, null, {'Range': 'bytes=4-9'});
+            return TestServer.remoteAPI().sendHttpRequest(`/api/uploaded-file/${id}`, 'GET', null, null, {headers: {'Range': 'bytes=4-9'}});
         }).then((response) => {
             const headers = response.headers;
             assert.equal(response.statusCode, 206);
@@ -166,7 +166,7 @@ describe('/api/uploaded-file', function () {
             return PrivilegedAPI.sendRequest('upload-file', {newFile: stream}, {useFormData: true});
         }).then((response) => {
             const id = response['uploadedFile']['id'];
-            return TestServer.remoteAPI().sendHttpRequest(`/api/uploaded-file/${id}`, 'GET', null, null, {'Range': 'bytes=-4'});
+            return TestServer.remoteAPI().sendHttpRequest(`/api/uploaded-file/${id}`, 'GET', null, null, {headers: {'Range': 'bytes=-4'}});
         }).then((response) => {
             const headers = response.headers;
             assert.equal(response.statusCode, 206);
@@ -180,7 +180,7 @@ describe('/api/uploaded-file', function () {
             return PrivilegedAPI.sendRequest('upload-file', {newFile: stream}, {useFormData: true});
         }).then((response) => {
             const id = response['uploadedFile']['id'];
-            return TestServer.remoteAPI().sendHttpRequest(`/api/uploaded-file/${id}`, 'GET', null, null, {'Range': 'bytes=-100'});
+            return TestServer.remoteAPI().sendHttpRequest(`/api/uploaded-file/${id}`, 'GET', null, null, {headers: {'Range': 'bytes=-100'}});
         }).then((response) => {
             const headers = response.headers;
             assert.equal(response.statusCode, 206);
@@ -194,7 +194,7 @@ describe('/api/uploaded-file', function () {
             return PrivilegedAPI.sendRequest('upload-file', {newFile: stream}, {useFormData: true});
         }).then((response) => {
             const id = response['uploadedFile']['id'];
-            return TestServer.remoteAPI().sendHttpRequest(`/api/uploaded-file/${id}`, 'GET', null, null, {'Range': 'bytes=12-'})
+            return TestServer.remoteAPI().sendHttpRequest(`/api/uploaded-file/${id}`, 'GET', null, null, {headers: {'Range': 'bytes=12-'}})
                 .then(() => assert(false, 'should never be reached'), (error) => assert.equal(error, 416));
         });
     });
@@ -204,7 +204,7 @@ describe('/api/uploaded-file', function () {
             return PrivilegedAPI.sendRequest('upload-file', {newFile: stream}, {useFormData: true});
         }).then((response) => {
             const id = response['uploadedFile']['id'];
-            return TestServer.remoteAPI().sendHttpRequest(`/api/uploaded-file/${id}`, 'GET', null, null, {'Range': 'bytes=2-1'})
+            return TestServer.remoteAPI().sendHttpRequest(`/api/uploaded-file/${id}`, 'GET', null, null, {headers: {'Range': 'bytes=2-1'}})
                 .then(() => assert(false, 'should never be reached'), (error) => assert.equal(error, 416));
         });
     });
@@ -220,7 +220,7 @@ describe('/api/uploaded-file', function () {
             assert.equal(response.statusCode, 200);
             assert.equal(response.responseText, 'some content');
             return TestServer.remoteAPI().sendHttpRequest(`/api/uploaded-file/${id}`, 'GET', null, null,
-                {'Range': 'bytes = 9-10', 'If-Range': response.headers['last-modified']});
+                {headers: {'Range': 'bytes = 9-10', 'If-Range': response.headers['last-modified']}});
         }).then((response) => {
             const headers = response.headers;
             assert.equal(response.statusCode, 206);
@@ -240,7 +240,7 @@ describe('/api/uploaded-file', function () {
             assert.equal(response.statusCode, 200);
             assert.equal(response.responseText, 'some content');
             return TestServer.remoteAPI().sendHttpRequest(`/api/uploaded-file/${id}`, 'GET', null, null,
-                {'Range': 'bytes = 9-10', 'If-Range': response.headers['etag']});
+                {headers: {'Range': 'bytes = 9-10', 'If-Range': response.headers['etag']}});
         }).then((response) => {
             const headers = response.headers;
             assert.equal(response.statusCode, 206);
@@ -297,7 +297,7 @@ describe('/api/uploaded-file', function () {
         }).then((response) => {
             id = response['uploadedFile']['id'];
             return TestServer.remoteAPI().sendHttpRequest(`/api/uploaded-file/${id}`, 'GET', null, null,
-                {'Range': `bytes = ${startByte}-${endByte}`}, responseHandler);
+                {headers: {'Range': `bytes = ${startByte}-${endByte}`}, responseHandler});
         }).then((response) => {
             const headers = response.headers;
             assert.equal(response.statusCode, 206);
index 9d9fb99..9d20258 100644 (file)
@@ -64,7 +64,7 @@ class NodeRemoteAPI extends CommonRemoteAPI {
         });
     }
 
-    sendHttpRequest(path, method, contentType, content, headers = {}, responseHandler = null)
+    sendHttpRequest(path, method, contentType, content, requestOptions = {})
     {
         let server = this._server;
         return new Promise((resolve, reject) => {
@@ -78,8 +78,8 @@ class NodeRemoteAPI extends CommonRemoteAPI {
 
             let request = (server.scheme == 'http' ? http : https).request(options, (response) => {
                 let responseText = '';
-                if (responseHandler)
-                    responseHandler(response);
+                if (requestOptions.responseHandler)
+                    requestOptions.responseHandler(response);
                 else {
                     response.setEncoding('utf8');
                     response.on('data', (chunk) => { responseText += chunk; });
@@ -106,8 +106,11 @@ class NodeRemoteAPI extends CommonRemoteAPI {
             if (this._cookies.size)
                 request.setHeader('Cookie', Array.from(this._cookies.keys()).map((key) => `${key}=${this._cookies.get(key)}`).join('; '));
 
-            for (let headerName in headers)
-                request.setHeader(headerName, headers[headerName]);
+            if (requestOptions.headers) {
+                const requestHeaders = requestOptions.headers;
+                for (let headerName in requestHeaders)
+                    request.setHeader(headerName, requestHeaders[headerName]);
+            }
 
             if (content instanceof Function)
                 content(request);
@@ -119,11 +122,11 @@ class NodeRemoteAPI extends CommonRemoteAPI {
         });
     }
 
-    sendHttpRequestWithFormData(path, formData)
+    sendHttpRequestWithFormData(path, formData, options)
     {
         return this.sendHttpRequest(path, 'POST', `multipart/form-data; boundary=${formData.getBoundary()}`, (request) => {
             formData.pipe(request);
-        });
+        }, options);
     }
 
 };