diff --git a/fetch.js b/fetch.js index c54b4814d12868de3ecb991531a3c4b716811ff7..e5284a7e44757b1525c15f8d6f585a175289e56a 100644 --- a/fetch.js +++ b/fetch.js @@ -69,56 +69,79 @@ body.bodyUsed = true } + var blobSupport = self.FileReader && (function() { + try { + new Blob(); + return true + } catch(e) { + return false + } + })(); + function Body() { this._body = null this.bodyUsed = false - this.arrayBuffer = function() { - throw new Error('Not implemented yet') - } + if (blobSupport) { + this.arrayBuffer = function() { + var rejected = consumed(this) + if (rejected) { + return rejected + } - var blobSupport = (function() { - try { - new Blob(); - return true - } catch(e) { - return false + var body = this._body + return new Promise(function(resolve, reject) { + var fileReader = new FileReader() + fileReader.readAsArrayBuffer(body) + fileReader.onloadend = function(e) { + if (e.target.error) { + reject(e.target.error) + } else { + resolve(e.target.result) + } + } + }) } - })(); - if (blobSupport) { this.blob = function() { var rejected = consumed(this) - return rejected ? rejected : Promise.resolve(new Blob([this._body])) + return rejected ? rejected : Promise.resolve(this._body) } - } - if (self.FormData) { - this.formData = function() { + this.text = function() { + var rejected = consumed(this) + if (rejected) { + return rejected + } + + var body = this._body + return new Promise(function(resolve, reject) { + var fileReader = new FileReader() + fileReader.readAsText(body) + fileReader.onloadend = function(e) { + if (e.target.error) { + reject(e.target.error) + } else { + resolve(e.target.result) + } + } + }) + } + } else { + this.text = function() { var rejected = consumed(this) - return rejected ? rejected : Promise.resolve(decode(this._body)) + return rejected ? rejected : Promise.resolve(this._body) } } - this.json = function() { - var rejected = consumed(this) - if (rejected) { - return rejected + if (self.FormData) { + this.formData = function() { + return this.text().then(function(text) { return decode(text) }) } - - var body = this._body - return new Promise(function(resolve, reject) { - try { - resolve(JSON.parse(body)) - } catch (ex) { - reject(ex) - } - }) } - this.text = function() { - var rejected = consumed(this) - return rejected ? rejected : Promise.resolve(this._body) + this.json = function() { + return this.text().then(function(text) { return JSON.parse(text) }) } return this @@ -186,7 +209,7 @@ headers: headers(xhr), url: xhr.responseURL || xhr.getResponseHeader('X-Request-URL') } - resolve(new Response(xhr.responseText, options)) + resolve(new Response(blobSupport ? xhr.response: xhr.responseText, options)) } xhr.onerror = function() { @@ -194,6 +217,9 @@ } xhr.open(self.method, self.url) + if (blobSupport) { + xhr.responseType = 'blob' + } self.headers.forEach(function(name, values) { values.forEach(function(value) { diff --git a/test/server.js b/test/server.js index afc091b15a0e11746922c8cffc5d96c885a1ed80..489fee456023e3cd20db1113aa7677aa0a3186b8 100755 --- a/test/server.js +++ b/test/server.js @@ -28,6 +28,14 @@ var routes = { }); res.end('hi'); }, + '/nonascii': function(res) { + res.writeHead(200, {'Content-Type': 'text/plain'}); + var buf = new Buffer(256); + for (var i = 0; i< 256; i++) { + buf[i] = i; + } + res.end(buf); + }, '/redirect/301': function(res) { res.writeHead(301, {'Location': '/hello'}); res.end(); diff --git a/test/test.js b/test/test.js index dacf7cc690d1518f5fd878441115bcf213c13602..df649ec27f92629faf414f3bb5f28375f6b84b0e 100644 --- a/test/test.js +++ b/test/test.js @@ -63,6 +63,40 @@ suite('Response', function() { // https://fetch.spec.whatwg.org/#body-mixin suite('Body mixin', function() { + ;(Response.prototype.arrayBuffer ? suite : suite.skip)('arrayBuffer', function() { + test('resolves arrayBuffer promise', function() { + return fetch('/hello').then(function(response) { + return response.arrayBuffer() + }).then(function(buf) { + assert(buf instanceof ArrayBuffer, 'buf is an ArrayBuffer instance') + assert.equal(buf.byteLength, 2) + }) + }) + + test('arrayBuffer handles non-ascii data', function() { + return fetch('/nonascii').then(function(response) { + return response.arrayBuffer() + }).then(function(buf) { + assert(buf instanceof ArrayBuffer, 'buf is an ArrayBuffer instance') + assert.equal(buf.byteLength, 256, 'buf.byteLength is correct') + var view = new Uint8Array(buf) + for (var i = 0; i< 256; i++) { + assert.equal(view[i], i) + } + }) + }) + + test('rejects arrayBuffer promise after body is consumed', function() { + return fetch('/hello').then(function(response) { + assert(response.arrayBuffer, 'Body does not implement arrayBuffer') + response.blob() + return response.arrayBuffer() + }).catch(function(error) { + assert(error instanceof TypeError, 'Promise rejected after body consumed') + }) + }) + }) + ;(Response.prototype.blob ? suite : suite.skip)('blob', function() { test('resolves blob promise', function() { return fetch('/hello').then(function(response) { @@ -73,6 +107,15 @@ suite('Body mixin', function() { }) }) + test('blob handles non-ascii data', function() { + return fetch('/nonascii').then(function(response) { + return response.blob() + }).then(function(blob) { + assert(blob instanceof Blob, 'blob is a Blob instance') + assert.equal(blob.size, 256, 'blob.size is correct') + }) + }) + test('rejects blob promise after body is consumed', function() { return fetch('/hello').then(function(response) { assert(response.blob, 'Body does not implement blob')