pax_global_header 0000666 0000000 0000000 00000000064 13056054646 0014523 g ustar 00root root 0000000 0000000 52 comment=d4ed806fdcbdeaef707d27f6c88943f0336a647d
fetch-2.0.3/ 0000775 0000000 0000000 00000000000 13056054646 0012616 5 ustar 00root root 0000000 0000000 fetch-2.0.3/.gitignore 0000664 0000000 0000000 00000000106 13056054646 0014603 0 ustar 00root root 0000000 0000000 .env
bower_components/
node_modules/
sauce_connect/
sauce_connect.log
fetch-2.0.3/.jshintrc 0000664 0000000 0000000 00000000624 13056054646 0014445 0 ustar 00root root 0000000 0000000 {
"curly": true,
"eqeqeq": true,
"es3": true,
"immed": true,
"indent": 2,
"latedef": true,
"newcap": true,
"noarg": true,
"quotmark": true,
"undef": true,
"unused": true,
"strict": true,
"trailing": true,
"asi": true,
"boss": true,
"esnext": true,
"eqnull": true,
"browser": true,
"worker": true,
"globals": {
"JSON": false,
"URLSearchParams": false
}
}
fetch-2.0.3/.travis.yml 0000664 0000000 0000000 00000000555 13056054646 0014734 0 ustar 00root root 0000000 0000000 sudo: false
language: node_js
cache:
directories:
- phantomjs
deploy:
provider: npm
email: mislav.marohnic@gmail.com
api_key:
secure: gt9g5/bXhxSKjxfFSPCdpWGJKBrSG8zdGRYgPouUgRqNeD2Ff4Nc8HGQTxp0OLKnP/jJ5FIru5jUur6LWzJCyEd+aNUEvFf5J078m3pzHN9AP2fiWUkKXcc5lKV0PQnI+JDRxJwd/PggtjubrneGfCzyFoys9apRrd/TzTGEtGw=
on:
tags: true
repo: github/fetch
fetch-2.0.3/LICENSE 0000664 0000000 0000000 00000002045 13056054646 0013624 0 ustar 00root root 0000000 0000000 Copyright (c) 2014-2016 GitHub, Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
fetch-2.0.3/MAINTAINING.md 0000664 0000000 0000000 00000001207 13056054646 0014676 0 ustar 00root root 0000000 0000000 # Maintaining
## Releasing a new version
This project follows [semver](http://semver.org/). So if you are making a bug
fix, only increment the patch level "1.0.x". If any new files are added, a
minor version "1.x.x" bump is in order.
### Make a release commit
To prepare the release commit:
1. Update the npm [package.json](https://github.com/github/fetch/blob/master/package.json)
`version` value.
2. Make a single commit with the description as "Fetch 2.x.x".
3. Finally, tag the commit with `v2.x.x`.
```
$ git pull
$ vim package.json
$ git add package.json
$ git commit -m "Fetch 1.x.x"
$ git tag v1.x.x
$ git push
$ git push --tags
```
fetch-2.0.3/Makefile 0000664 0000000 0000000 00000001360 13056054646 0014256 0 ustar 00root root 0000000 0000000 test: node_modules/ lint
./script/test
lint: node_modules/
./node_modules/.bin/jshint *.js test/*.js
node_modules/:
npm install
clean:
rm -rf ./bower_components ./node_modules
ifeq ($(shell uname -s),Darwin)
sauce_connect/bin/sc:
wget https://saucelabs.com/downloads/sc-4.3.16-osx.zip
unzip sc-4.3.16-osx.zip
mv sc-4.3.16-osx sauce_connect
rm sc-4.3.16-osx.zip
else
sauce_connect/bin/sc:
mkdir -p sauce_connect
curl -fsSL http://saucelabs.com/downloads/sc-4.3.16-linux.tar.gz | tar xz -C sauce_connect --strip-components 1
endif
phantomjs/bin/phantomjs:
mkdir -p phantomjs
wget https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2 -O- | tar xj -C phantomjs --strip-components 1
.PHONY: clean lint test
fetch-2.0.3/README.md 0000664 0000000 0000000 00000020664 13056054646 0014105 0 ustar 00root root 0000000 0000000 # window.fetch polyfill
The `fetch()` function is a Promise-based mechanism for programmatically making
web requests in the browser. This project is a polyfill that implements a subset
of the standard [Fetch specification][], enough to make `fetch` a viable
replacement for most uses of XMLHttpRequest in traditional web applications.
This project adheres to the [Open Code of Conduct][]. By participating, you are
expected to uphold this code.
## Table of Contents
* [Read this first](#read-this-first)
* [Installation](#installation)
* [Usage](#usage)
* [HTML](#html)
* [JSON](#json)
* [Response metadata](#response-metadata)
* [Post form](#post-form)
* [Post JSON](#post-json)
* [File upload](#file-upload)
* [Caveats](#caveats)
* [Handling HTTP error statuses](#handling-http-error-statuses)
* [Sending cookies](#sending-cookies)
* [Receiving cookies](#receiving-cookies)
* [Obtaining the Response URL](#obtaining-the-response-url)
* [Browser Support](#browser-support)
## Read this first
* If you believe you found a bug with how `fetch` behaves in Chrome or Firefox,
please **avoid opening an issue in this repository**. This project is a
_polyfill_, and since Chrome and Firefox both implement the `window.fetch`
function natively, no code from this project actually takes any effect in
these browsers. See [Browser support](#browser-support) for detailed
information.
* If you have trouble **making a request to another domain** (a different
subdomain or port number also constitutes as another domain), please
familiarize yourself with all the intricacies and limitations of [CORS][]
requests. Because CORS requires participation of the server by implementing
specific HTTP response headers, it is often nontrivial to set up or debug.
CORS is exclusively handled by the browser's internal mechanisms which this
polyfill cannot influence.
* If you have trouble **maintaining the user's session** or [CSRF][] protection
through `fetch` requests, please ensure that you've read and understood the
[Sending cookies](#sending-cookies) section.
* If this polyfill **doesn't work under Node.js environments**, that is expected,
because this project is meant for web browsers only. You should ensure that your
application doesn't try to package and run this on the server.
* If you have an idea for a new feature of `fetch`, please understand that we
are only ever going to add features and APIs that are a part of the
[Fetch specification][]. You should **submit your feature requests** to the
[repository of the specification](https://github.com/whatwg/fetch/issues)
itself, rather than this repository.
## Installation
* `npm install whatwg-fetch --save`; or
* `bower install fetch`.
You will also need a Promise polyfill for [older browsers](http://caniuse.com/#feat=promises).
We recommend [taylorhakes/promise-polyfill](https://github.com/taylorhakes/promise-polyfill)
for its small size and Promises/A+ compatibility.
For use with webpack, add this package in the `entry` configuration option
before your application entry point:
```javascript
entry: ['whatwg-fetch', ...]
```
For Babel and ES2015+, make sure to import the file:
```javascript
import 'whatwg-fetch'
```
## Usage
For a more comprehensive API reference that this polyfill supports, refer to
https://github.github.io/fetch/.
### HTML
```javascript
fetch('/users.html')
.then(function(response) {
return response.text()
}).then(function(body) {
document.body.innerHTML = body
})
```
### JSON
```javascript
fetch('/users.json')
.then(function(response) {
return response.json()
}).then(function(json) {
console.log('parsed json', json)
}).catch(function(ex) {
console.log('parsing failed', ex)
})
```
### Response metadata
```javascript
fetch('/users.json').then(function(response) {
console.log(response.headers.get('Content-Type'))
console.log(response.headers.get('Date'))
console.log(response.status)
console.log(response.statusText)
})
```
### Post form
```javascript
var form = document.querySelector('form')
fetch('/users', {
method: 'POST',
body: new FormData(form)
})
```
### Post JSON
```javascript
fetch('/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Hubot',
login: 'hubot',
})
})
```
### File upload
```javascript
var input = document.querySelector('input[type="file"]')
var data = new FormData()
data.append('file', input.files[0])
data.append('user', 'hubot')
fetch('/avatars', {
method: 'POST',
body: data
})
```
### Caveats
The `fetch` specification differs from `jQuery.ajax()` in mainly two ways that
bear keeping in mind:
* The Promise returned from `fetch()` **won't reject on HTTP error status**
even if the response is an HTTP 404 or 500. Instead, it will resolve normally,
and it will only reject on network failure or if anything prevented the
request from completing.
* By default, `fetch` **won't send or receive any cookies** from the server,
resulting in unauthenticated requests if the site relies on maintaining a user
session. See [Sending cookies](#sending-cookies) for how to opt into cookie
handling.
#### Handling HTTP error statuses
To have `fetch` Promise reject on HTTP error statuses, i.e. on any non-2xx
status, define a custom response handler:
```javascript
function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response
} else {
var error = new Error(response.statusText)
error.response = response
throw error
}
}
function parseJSON(response) {
return response.json()
}
fetch('/users')
.then(checkStatus)
.then(parseJSON)
.then(function(data) {
console.log('request succeeded with JSON response', data)
}).catch(function(error) {
console.log('request failed', error)
})
```
#### Sending cookies
To automatically send cookies for the current domain, the `credentials` option
must be provided:
```javascript
fetch('/users', {
credentials: 'same-origin'
})
```
The "same-origin" value makes `fetch` behave similarly to XMLHttpRequest with
regards to cookies. Otherwise, cookies won't get sent, resulting in these
requests not preserving the authentication session.
For [CORS][] requests, use the "include" value to allow sending credentials to
other domains:
```javascript
fetch('https://example.com:1234/users', {
credentials: 'include'
})
```
#### Receiving cookies
As with XMLHttpRequest, the `Set-Cookie` response header returned from the
server is a [forbidden header name][] and therefore can't be programmatically
read with `response.headers.get()`. Instead, it's the browser's responsibility
to handle new cookies being set (if applicable to the current URL). Unless they
are HTTP-only, new cookies will be available through `document.cookie`.
Bear in mind that the default behavior of `fetch` is to ignore the `Set-Cookie`
header completely. To opt into accepting cookies from the server, you must use
the `credentials` option.
#### Obtaining the Response URL
Due to limitations of XMLHttpRequest, the `response.url` value might not be
reliable after HTTP redirects on older browsers.
The solution is to configure the server to set the response HTTP header
`X-Request-URL` to the current URL after any redirect that might have happened.
It should be safe to set it unconditionally.
``` ruby
# Ruby on Rails controller example
response.headers['X-Request-URL'] = request.url
```
This server workaround is necessary if you need reliable `response.url` in
Firefox < 32, Chrome < 37, Safari, or IE.
## Browser Support
- Chrome
- Firefox
- Safari 6.1+
- Internet Explorer 10+
Note: modern browsers such as Chrome, Firefox, and Microsoft Edge contain native
implementations of `window.fetch`, therefore the code from this polyfill doesn't
have any effect on those browsers. If you believe you've encountered an error
with how `window.fetch` is implemented in any of these browsers, you should file
an issue with that browser vendor instead of this project.
[fetch specification]: https://fetch.spec.whatwg.org
[open code of conduct]: http://todogroup.org/opencodeofconduct/#fetch/opensource@github.com
[cors]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
"Cross-origin resource sharing"
[csrf]: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
"Cross-site request forgery"
[forbidden header name]: https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name
fetch-2.0.3/bower.json 0000664 0000000 0000000 00000000245 13056054646 0014630 0 ustar 00root root 0000000 0000000 {
"name": "fetch",
"main": "fetch.js",
"ignore": [
".*",
"*.md",
"examples/",
"Makefile",
"package.json",
"script/",
"test/"
]
}
fetch-2.0.3/examples/ 0000775 0000000 0000000 00000000000 13056054646 0014434 5 ustar 00root root 0000000 0000000 fetch-2.0.3/examples/index.html 0000664 0000000 0000000 00000000765 13056054646 0016441 0 ustar 00root root 0000000 0000000
fetch-2.0.3/fetch.js 0000664 0000000 0000000 00000030610 13056054646 0014245 0 ustar 00root root 0000000 0000000 (function(self) {
'use strict';
if (self.fetch) {
return
}
var support = {
searchParams: 'URLSearchParams' in self,
iterable: 'Symbol' in self && 'iterator' in Symbol,
blob: 'FileReader' in self && 'Blob' in self && (function() {
try {
new Blob()
return true
} catch(e) {
return false
}
})(),
formData: 'FormData' in self,
arrayBuffer: 'ArrayBuffer' in self
}
if (support.arrayBuffer) {
var viewClasses = [
'[object Int8Array]',
'[object Uint8Array]',
'[object Uint8ClampedArray]',
'[object Int16Array]',
'[object Uint16Array]',
'[object Int32Array]',
'[object Uint32Array]',
'[object Float32Array]',
'[object Float64Array]'
]
var isDataView = function(obj) {
return obj && DataView.prototype.isPrototypeOf(obj)
}
var isArrayBufferView = ArrayBuffer.isView || function(obj) {
return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1
}
}
function normalizeName(name) {
if (typeof name !== 'string') {
name = String(name)
}
if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) {
throw new TypeError('Invalid character in header field name')
}
return name.toLowerCase()
}
function normalizeValue(value) {
if (typeof value !== 'string') {
value = String(value)
}
return value
}
// Build a destructive iterator for the value list
function iteratorFor(items) {
var iterator = {
next: function() {
var value = items.shift()
return {done: value === undefined, value: value}
}
}
if (support.iterable) {
iterator[Symbol.iterator] = function() {
return iterator
}
}
return iterator
}
function Headers(headers) {
this.map = {}
if (headers instanceof Headers) {
headers.forEach(function(value, name) {
this.append(name, value)
}, this)
} else if (Array.isArray(headers)) {
headers.forEach(function(header) {
this.append(header[0], header[1])
}, this)
} else if (headers) {
Object.getOwnPropertyNames(headers).forEach(function(name) {
this.append(name, headers[name])
}, this)
}
}
Headers.prototype.append = function(name, value) {
name = normalizeName(name)
value = normalizeValue(value)
var oldValue = this.map[name]
this.map[name] = oldValue ? oldValue+','+value : value
}
Headers.prototype['delete'] = function(name) {
delete this.map[normalizeName(name)]
}
Headers.prototype.get = function(name) {
name = normalizeName(name)
return this.has(name) ? this.map[name] : null
}
Headers.prototype.has = function(name) {
return this.map.hasOwnProperty(normalizeName(name))
}
Headers.prototype.set = function(name, value) {
this.map[normalizeName(name)] = normalizeValue(value)
}
Headers.prototype.forEach = function(callback, thisArg) {
for (var name in this.map) {
if (this.map.hasOwnProperty(name)) {
callback.call(thisArg, this.map[name], name, this)
}
}
}
Headers.prototype.keys = function() {
var items = []
this.forEach(function(value, name) { items.push(name) })
return iteratorFor(items)
}
Headers.prototype.values = function() {
var items = []
this.forEach(function(value) { items.push(value) })
return iteratorFor(items)
}
Headers.prototype.entries = function() {
var items = []
this.forEach(function(value, name) { items.push([name, value]) })
return iteratorFor(items)
}
if (support.iterable) {
Headers.prototype[Symbol.iterator] = Headers.prototype.entries
}
function consumed(body) {
if (body.bodyUsed) {
return Promise.reject(new TypeError('Already read'))
}
body.bodyUsed = true
}
function fileReaderReady(reader) {
return new Promise(function(resolve, reject) {
reader.onload = function() {
resolve(reader.result)
}
reader.onerror = function() {
reject(reader.error)
}
})
}
function readBlobAsArrayBuffer(blob) {
var reader = new FileReader()
var promise = fileReaderReady(reader)
reader.readAsArrayBuffer(blob)
return promise
}
function readBlobAsText(blob) {
var reader = new FileReader()
var promise = fileReaderReady(reader)
reader.readAsText(blob)
return promise
}
function readArrayBufferAsText(buf) {
var view = new Uint8Array(buf)
var chars = new Array(view.length)
for (var i = 0; i < view.length; i++) {
chars[i] = String.fromCharCode(view[i])
}
return chars.join('')
}
function bufferClone(buf) {
if (buf.slice) {
return buf.slice(0)
} else {
var view = new Uint8Array(buf.byteLength)
view.set(new Uint8Array(buf))
return view.buffer
}
}
function Body() {
this.bodyUsed = false
this._initBody = function(body) {
this._bodyInit = body
if (!body) {
this._bodyText = ''
} else if (typeof body === 'string') {
this._bodyText = body
} else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
this._bodyBlob = body
} else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
this._bodyFormData = body
} else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
this._bodyText = body.toString()
} else if (support.arrayBuffer && support.blob && isDataView(body)) {
this._bodyArrayBuffer = bufferClone(body.buffer)
// IE 10-11 can't handle a DataView body.
this._bodyInit = new Blob([this._bodyArrayBuffer])
} else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
this._bodyArrayBuffer = bufferClone(body)
} else {
throw new Error('unsupported BodyInit type')
}
if (!this.headers.get('content-type')) {
if (typeof body === 'string') {
this.headers.set('content-type', 'text/plain;charset=UTF-8')
} else if (this._bodyBlob && this._bodyBlob.type) {
this.headers.set('content-type', this._bodyBlob.type)
} else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8')
}
}
}
if (support.blob) {
this.blob = function() {
var rejected = consumed(this)
if (rejected) {
return rejected
}
if (this._bodyBlob) {
return Promise.resolve(this._bodyBlob)
} else if (this._bodyArrayBuffer) {
return Promise.resolve(new Blob([this._bodyArrayBuffer]))
} else if (this._bodyFormData) {
throw new Error('could not read FormData body as blob')
} else {
return Promise.resolve(new Blob([this._bodyText]))
}
}
this.arrayBuffer = function() {
if (this._bodyArrayBuffer) {
return consumed(this) || Promise.resolve(this._bodyArrayBuffer)
} else {
return this.blob().then(readBlobAsArrayBuffer)
}
}
}
this.text = function() {
var rejected = consumed(this)
if (rejected) {
return rejected
}
if (this._bodyBlob) {
return readBlobAsText(this._bodyBlob)
} else if (this._bodyArrayBuffer) {
return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer))
} else if (this._bodyFormData) {
throw new Error('could not read FormData body as text')
} else {
return Promise.resolve(this._bodyText)
}
}
if (support.formData) {
this.formData = function() {
return this.text().then(decode)
}
}
this.json = function() {
return this.text().then(JSON.parse)
}
return this
}
// HTTP methods whose capitalization should be normalized
var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT']
function normalizeMethod(method) {
var upcased = method.toUpperCase()
return (methods.indexOf(upcased) > -1) ? upcased : method
}
function Request(input, options) {
options = options || {}
var body = options.body
if (input instanceof Request) {
if (input.bodyUsed) {
throw new TypeError('Already read')
}
this.url = input.url
this.credentials = input.credentials
if (!options.headers) {
this.headers = new Headers(input.headers)
}
this.method = input.method
this.mode = input.mode
if (!body && input._bodyInit != null) {
body = input._bodyInit
input.bodyUsed = true
}
} else {
this.url = String(input)
}
this.credentials = options.credentials || this.credentials || 'omit'
if (options.headers || !this.headers) {
this.headers = new Headers(options.headers)
}
this.method = normalizeMethod(options.method || this.method || 'GET')
this.mode = options.mode || this.mode || null
this.referrer = null
if ((this.method === 'GET' || this.method === 'HEAD') && body) {
throw new TypeError('Body not allowed for GET or HEAD requests')
}
this._initBody(body)
}
Request.prototype.clone = function() {
return new Request(this, { body: this._bodyInit })
}
function decode(body) {
var form = new FormData()
body.trim().split('&').forEach(function(bytes) {
if (bytes) {
var split = bytes.split('=')
var name = split.shift().replace(/\+/g, ' ')
var value = split.join('=').replace(/\+/g, ' ')
form.append(decodeURIComponent(name), decodeURIComponent(value))
}
})
return form
}
function parseHeaders(rawHeaders) {
var headers = new Headers()
rawHeaders.split(/\r?\n/).forEach(function(line) {
var parts = line.split(':')
var key = parts.shift().trim()
if (key) {
var value = parts.join(':').trim()
headers.append(key, value)
}
})
return headers
}
Body.call(Request.prototype)
function Response(bodyInit, options) {
if (!options) {
options = {}
}
this.type = 'default'
this.status = 'status' in options ? options.status : 200
this.ok = this.status >= 200 && this.status < 300
this.statusText = 'statusText' in options ? options.statusText : 'OK'
this.headers = new Headers(options.headers)
this.url = options.url || ''
this._initBody(bodyInit)
}
Body.call(Response.prototype)
Response.prototype.clone = function() {
return new Response(this._bodyInit, {
status: this.status,
statusText: this.statusText,
headers: new Headers(this.headers),
url: this.url
})
}
Response.error = function() {
var response = new Response(null, {status: 0, statusText: ''})
response.type = 'error'
return response
}
var redirectStatuses = [301, 302, 303, 307, 308]
Response.redirect = function(url, status) {
if (redirectStatuses.indexOf(status) === -1) {
throw new RangeError('Invalid status code')
}
return new Response(null, {status: status, headers: {location: url}})
}
self.Headers = Headers
self.Request = Request
self.Response = Response
self.fetch = function(input, init) {
return new Promise(function(resolve, reject) {
var request = new Request(input, init)
var xhr = new XMLHttpRequest()
xhr.onload = function() {
var options = {
status: xhr.status,
statusText: xhr.statusText,
headers: parseHeaders(xhr.getAllResponseHeaders() || '')
}
options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL')
var body = 'response' in xhr ? xhr.response : xhr.responseText
resolve(new Response(body, options))
}
xhr.onerror = function() {
reject(new TypeError('Network request failed'))
}
xhr.ontimeout = function() {
reject(new TypeError('Network request failed'))
}
xhr.open(request.method, request.url, true)
if (request.credentials === 'include') {
xhr.withCredentials = true
}
if ('responseType' in xhr && support.blob) {
xhr.responseType = 'blob'
}
request.headers.forEach(function(value, name) {
xhr.setRequestHeader(name, value)
})
xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)
})
}
self.fetch.polyfill = true
})(typeof self !== 'undefined' ? self : this);
fetch-2.0.3/package.json 0000664 0000000 0000000 00000000707 13056054646 0015110 0 ustar 00root root 0000000 0000000 {
"name": "whatwg-fetch",
"description": "A window.fetch polyfill.",
"version": "2.0.3",
"main": "fetch.js",
"repository": "github/fetch",
"license": "MIT",
"devDependencies": {
"chai": "1.10.0",
"jshint": "2.8.0",
"mocha": "2.1.0",
"mocha-phantomjs-core": "2.0.1",
"promise-polyfill": "6.0.2",
"url-search-params": "0.6.1"
},
"files": [
"LICENSE",
"fetch.js"
],
"scripts": {
"test": "make"
}
}
fetch-2.0.3/script/ 0000775 0000000 0000000 00000000000 13056054646 0014122 5 ustar 00root root 0000000 0000000 fetch-2.0.3/script/phantomjs 0000775 0000000 0000000 00000001342 13056054646 0016053 0 ustar 00root root 0000000 0000000 #!/bin/bash
set -e
port=3900
# Find next available port
while lsof -i :$((++port)) >/dev/null; do true; done
# Spin a test server in the background
node ./script/server $port &>/dev/null &
server_pid=$!
trap "kill $server_pid" INT EXIT
STATUS=0
reporter=dot
[ -z "$CI" ] || reporter=spec
if [ -n "$TRAVIS" ]; then
make phantomjs/bin/phantomjs
export PATH="$PWD/phantomjs/bin:$PATH"
fi
run() {
phantomjs ./node_modules/mocha-phantomjs-core/mocha-phantomjs-core.js \
"$1" $reporter "{\"useColors\":true, \"hooks\":\"$PWD/test/mocha-phantomjs-hooks.js\"}" \
|| STATUS=$?
}
[ -z "$CI" ] || echo "phantomjs $(phantomjs -v)"
run "http://localhost:$port/"
run "http://localhost:$port/test/test-worker.html"
exit $STATUS
fetch-2.0.3/script/saucelabs 0000775 0000000 0000000 00000003265 13056054646 0016020 0 ustar 00root root 0000000 0000000 #!/bin/bash
set -e
port=8080
# Spin a test server in the background
node ./script/server $port &>/dev/null &
server_pid=$!
trap "kill $server_pid" INT EXIT
make sauce_connect/bin/sc
sauce_ready="${TMPDIR:-/tmp}/sauce-ready.$$"
sauce_connect/bin/sc -u "$SAUCE_USERNAME" -k "$SAUCE_ACCESS_KEY" \
-i "$TRAVIS_JOB_NUMBER" -l sauce_connect.log -f "$sauce_ready" &>/dev/null &
sauce_pid=$!
trap "kill $sauce_pid" INT EXIT
sauce_waited=0
while [ ! -f "$sauce_ready" ]; do
if [ "$sauce_waited" -gt 60000 ]; then
echo "sauce_connect failed to start within 60 seconds" >&2
exit 1
fi
sleep .01
sauce_waited=$((sauce_waited + 10))
done
echo "sauce_connect started within $sauce_waited ms"
rm -f "$sauce_ready"
job="$(./script/saucelabs-api --raw "js-tests" <&2
exit 1
fi
grep -q "^completed: true" <<<"$result" && break
echo -n "."
done
echo
awk '
/result\.tests:/ { tests+=$(NF) }
/result\.passes:/ { passes+=$(NF) }
/result\.pending:/ { pending+=$(NF) }
/result\.failures:/ { failures+=$(NF) }
/\.url:/ { print $(NF) }
END {
printf "%d passed, %d pending, %d failures\n", passes, pending, failures
if (failures > 0 || tests != passes + pending || tests == 0) exit 1
}
' <<<"$result"
fetch-2.0.3/script/saucelabs-api 0000775 0000000 0000000 00000001261 13056054646 0016561 0 ustar 00root root 0000000 0000000 #!/bin/bash
set -e
set -o pipefail
raw=""
if [ "$1" = "--raw" ]; then
raw="1"
shift 1
fi
endpoint="$1"
curl -fsS -X POST "https://saucelabs.com/rest/v1/$SAUCE_USERNAME/${endpoint}" \
-u "$SAUCE_USERNAME:$SAUCE_ACCESS_KEY" \
-H "Content-Type: application/json" -d "@-" | \
{
if [ -n "$raw" ]; then
cat
else
ruby -rjson -e '
dump = lambda do |obj, ns|
case obj
when Array then obj.each_with_index { |v, i| dump.call(v, [ns, i]) }
when Hash then obj.each { |k, v| dump.call(v, [ns, k]) }
else puts "%s: %s" % [ ns.flatten.compact.join("."), obj.to_s ]
end
end
dump.call JSON.parse(STDIN.read), nil
'
fi
}
fetch-2.0.3/script/server 0000775 0000000 0000000 00000007620 13056054646 0015363 0 ustar 00root root 0000000 0000000 #!/usr/bin/env node
var port = Number(process.argv[2] || 3000)
var fs = require('fs')
var http = require('http');
var url = require('url');
var querystring = require('querystring');
var routes = {
'/request': function(res, req) {
res.writeHead(200, {'Content-Type': 'application/json'});
var data = ''
req.on('data', function(c) { data += c })
req.on('end', function() {
res.end(JSON.stringify({
method: req.method,
url: req.url,
headers: req.headers,
data: data
}));
})
},
'/hello': function(res, req) {
res.writeHead(200, {
'Content-Type': 'text/plain',
'X-Request-URL': 'http://' + req.headers.host + req.url
});
res.end('hi');
},
'/hello/utf8': function(res) {
res.writeHead(200, {
'Content-Type': 'text/plain; charset=utf-8'
});
// "hello"
var buf = new Buffer([104, 101, 108, 108, 111]);
res.end(buf);
},
'/hello/utf16le': function(res) {
res.writeHead(200, {
'Content-Type': 'text/plain; charset=utf-16le'
});
// "hello"
var buf = new Buffer([104, 0, 101, 0, 108, 0, 108, 0, 111, 0]);
res.end(buf);
},
'/binary': function(res) {
res.writeHead(200, {'Content-Type': 'application/octet-stream'});
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();
},
'/redirect/302': function(res) {
res.writeHead(302, {'Location': '/hello'});
res.end();
},
'/redirect/303': function(res) {
res.writeHead(303, {'Location': '/hello'});
res.end();
},
'/redirect/307': function(res) {
res.writeHead(307, {'Location': '/hello'});
res.end();
},
'/redirect/308': function(res) {
res.writeHead(308, {'Location': '/hello'});
res.end();
},
'/boom': function(res) {
res.writeHead(500, {'Content-Type': 'text/plain'});
res.end('boom');
},
'/empty': function(res) {
res.writeHead(204);
res.end();
},
'/error': function(res) {
res.destroy();
},
'/form': function(res) {
res.writeHead(200, {'Content-Type': 'application/x-www-form-urlencoded'});
res.end('number=1&space=one+two&empty=&encoded=a%2Bb&');
},
'/json': function(res) {
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify({name: 'Hubot', login: 'hubot'}));
},
'/json-error': function(res) {
res.writeHead(200, {'Content-Type': 'application/json'});
res.end('not json {');
},
'/cookie': function(res, req) {
var setCookie, cookie
var params = querystring.parse(url.parse(req.url).query);
if (params.name && params.value) {
setCookie = [params.name, params.value].join('=');
}
if (params.name) {
cookie = querystring.parse(req.headers['cookie'], '; ')[params.name];
}
res.writeHead(200, {'Content-Type': 'text/plain', 'Set-Cookie': setCookie});
res.end(cookie);
},
'/headers': function(res) {
res.writeHead(200, {
'Date': 'Mon, 13 Oct 2014 21:02:27 GMT',
'Content-Type': 'text/html; charset=utf-8'
});
res.end();
}
};
var types = {
js: 'application/javascript',
css: 'text/css',
html: 'text/html',
txt: 'text/plain'
};
server = http.createServer(function(req, res) {
var pathname = url.parse(req.url).pathname;
var route = routes[pathname];
if (route) {
route(res, req);
} else {
if (pathname == '/') pathname = '/test/test.html'
fs.readFile(__dirname + '/..' + pathname, function(err, data) {
if (err) {
res.writeHead(404, {'Content-Type': types.txt});
res.end('Not Found');
} else {
var ext = (pathname.match(/\.([^\/]+)$/) || [])[1]
res.writeHead(200, {'Content-Type': types[ext] || types.txt});
res.end(data);
}
});
}
});
console.warn("Started test server on localhost:" + port);
server.listen(port);
fetch-2.0.3/script/test 0000775 0000000 0000000 00000000150 13056054646 0015023 0 ustar 00root root 0000000 0000000 #!/bin/bash
set -e
if [ -n "$SAUCE_BROWSER" ]; then
./script/saucelabs
else
./script/phantomjs
fi
fetch-2.0.3/test/ 0000775 0000000 0000000 00000000000 13056054646 0013575 5 ustar 00root root 0000000 0000000 fetch-2.0.3/test/.gitignore 0000664 0000000 0000000 00000000013 13056054646 0015557 0 ustar 00root root 0000000 0000000 server.pid
fetch-2.0.3/test/.jshintrc 0000664 0000000 0000000 00000000514 13056054646 0015422 0 ustar 00root root 0000000 0000000 {
"extends": "../.jshintrc",
"es3": false,
"strict": false,
"sub": true,
"globals": {
"fetch": false,
"Headers": false,
"Request": false,
"Response": false,
"mocha": false,
"chai": false,
"suite": false,
"setup": false,
"suiteSetup": false,
"test": false,
"assert": false
}
}
fetch-2.0.3/test/mocha-phantomjs-hooks.js 0000664 0000000 0000000 00000000406 13056054646 0020344 0 ustar 00root root 0000000 0000000 /* globals exports */
exports.beforeStart = function(context) {
var originalResourceError = context.page.onResourceError
context.page.onResourceError = function(resErr) {
if (!/\/boom$/.test(resErr.url)) {
originalResourceError(resErr)
}
}
}
fetch-2.0.3/test/test-worker.html 0000664 0000000 0000000 00000002114 13056054646 0016747 0 ustar 00root root 0000000 0000000
Fetch Worker Tests
fetch-2.0.3/test/test.html 0000664 0000000 0000000 00000003342 13056054646 0015444 0 ustar 00root root 0000000 0000000
Fetch Tests
fetch-2.0.3/test/test.js 0000664 0000000 0000000 00000115344 13056054646 0015122 0 ustar 00root root 0000000 0000000 var support = {
searchParams: 'URLSearchParams' in self,
url: (function(url) {
try {
return new URL(url).toString() === url
} catch(e) {
return false
}
})('http://example.com/'),
blob: 'FileReader' in self && 'Blob' in self && (function() {
try {
new Blob()
return true
} catch(e) {
return false
}
})(),
formData: 'FormData' in self,
arrayBuffer: 'ArrayBuffer' in self,
patch: !/PhantomJS/.test(navigator.userAgent),
permanentRedirect: !/PhantomJS|Trident/.test(navigator.userAgent)
}
function readBlobAsText(blob) {
if ('FileReader' in self) {
return new Promise(function(resolve, reject) {
var reader = new FileReader()
reader.onload = function() {
resolve(reader.result)
}
reader.onerror = function() {
reject(reader.error)
}
reader.readAsText(blob)
})
} else if ('FileReaderSync' in self) {
return new FileReaderSync().readAsText(blob)
} else {
throw new ReferenceError('FileReader is not defined')
}
}
function readBlobAsBytes(blob) {
if ('FileReader' in self) {
return new Promise(function(resolve, reject) {
var reader = new FileReader()
reader.onload = function() {
var view = new Uint8Array(reader.result)
resolve(Array.prototype.slice.call(view))
}
reader.onerror = function() {
reject(reader.error)
}
reader.readAsArrayBuffer(blob)
})
} else if ('FileReaderSync' in self) {
return new FileReaderSync().readAsArrayBuffer(blob)
} else {
throw new ReferenceError('FileReader is not defined')
}
}
function arrayBufferFromText(text) {
var buf = new ArrayBuffer(text.length)
var view = new Uint8Array(buf)
for (var i = 0; i < text.length; i++) {
view[i] = text.charCodeAt(i)
}
return buf
}
function readArrayBufferAsText(buf) {
var view = new Uint8Array(buf)
var chars = new Array(view.length)
for (var i = 0; i < view.length; i++) {
chars[i] = String.fromCharCode(view[i])
}
return chars.join('')
}
var preservedGlobals = {}
var keepGlobals = ['fetch', 'Headers', 'Request', 'Response']
var exercise = ['polyfill']
// If native fetch implementation exists, save it and allow it to be replaced
// by the polyfill. Native implementation will be exercised additionally.
if (self.fetch) {
keepGlobals.forEach(function(name) {
preservedGlobals[name] = self[name]
})
self.fetch = undefined
exercise.push('native')
}
var slice = Array.prototype.slice
function featureDependent(testOrSuite, condition) {
(condition ? testOrSuite : testOrSuite.skip).apply(this, slice.call(arguments, 2))
}
exercise.forEach(function(exerciseMode) {
suite(exerciseMode, function() {
if (exerciseMode === 'native') {
suiteSetup(function() {
keepGlobals.forEach(function(name) {
self[name] = preservedGlobals[name]
})
})
}
var nativeChrome = /Chrome\//.test(navigator.userAgent) && exerciseMode === 'native'
var polyfillFirefox = /Firefox\//.test(navigator.userAgent) && exerciseMode === 'polyfill'
// https://fetch.spec.whatwg.org/#concept-bodyinit-extract
function testBodyExtract(factory) {
suite('body extract', function() {
var expected = 'Hello World!'
var inputs = [['type USVString', expected]]
if (support.blob) {
inputs.push(['type Blob', new Blob([expected])])
}
if (support.arrayBuffer) {
inputs = inputs.concat([
['type ArrayBuffer', arrayBufferFromText(expected)],
['type TypedArray', new Uint8Array(arrayBufferFromText(expected))],
['type DataView', new DataView(arrayBufferFromText(expected))],
])
}
inputs.forEach(function(input) {
var typeLabel = input[0], body = input[1]
suite(typeLabel, function() {
featureDependent(test, support.blob, 'consume as blob', function() {
var r = factory(body)
return r.blob().then(readBlobAsText).then(function(text) {
assert.equal(text, expected)
})
})
test('consume as text', function() {
var r = factory(body)
return r.text().then(function(text) {
assert.equal(text, expected)
})
})
featureDependent(test, support.arrayBuffer, 'consume as array buffer', function() {
var r = factory(body)
return r.arrayBuffer().then(readArrayBufferAsText).then(function(text) {
assert.equal(text, expected)
})
})
})
})
})
}
// https://fetch.spec.whatwg.org/#headers-class
suite('Headers', function() {
test('constructor copies headers', function() {
var original = new Headers()
original.append('Accept', 'application/json')
original.append('Accept', 'text/plain')
original.append('Content-Type', 'text/html')
var headers = new Headers(original)
assert.equal(headers.get('Accept'), 'application/json,text/plain')
assert.equal(headers.get('Content-type'), 'text/html')
})
test('constructor works with arrays', function() {
var array = [
['Content-Type', 'text/xml'],
['Breaking-Bad', '<3']
]
var headers = new Headers(array)
assert.equal(headers.get('Content-Type'), 'text/xml')
assert.equal(headers.get('Breaking-Bad'), '<3')
})
test('headers are case insensitive', function() {
var headers = new Headers({'Accept': 'application/json'})
assert.equal(headers.get('ACCEPT'), 'application/json')
assert.equal(headers.get('Accept'), 'application/json')
assert.equal(headers.get('accept'), 'application/json')
})
test('appends to existing', function() {
var headers = new Headers({'Accept': 'application/json'})
assert.isFalse(headers.has('Content-Type'))
headers.append('Content-Type', 'application/json')
assert.isTrue(headers.has('Content-Type'))
assert.equal(headers.get('Content-Type'), 'application/json')
})
test('appends values to existing header name', function() {
var headers = new Headers({'Accept': 'application/json'})
headers.append('Accept', 'text/plain')
assert.equal(headers.get('Accept'), 'application/json,text/plain')
})
test('sets header name and value', function() {
var headers = new Headers()
headers.set('Content-Type', 'application/json')
assert.equal(headers.get('Content-Type'), 'application/json')
})
test('returns null on no header found', function() {
var headers = new Headers()
assert.isNull(headers.get('Content-Type'))
})
test('has headers that are set', function() {
var headers = new Headers()
headers.set('Content-Type', 'application/json')
assert.isTrue(headers.has('Content-Type'))
})
test('deletes headers', function() {
var headers = new Headers()
headers.set('Content-Type', 'application/json')
assert.isTrue(headers.has('Content-Type'))
headers.delete('Content-Type')
assert.isFalse(headers.has('Content-Type'))
assert.isNull(headers.get('Content-Type'))
})
test('converts field name to string on set and get', function() {
var headers = new Headers()
headers.set(1, 'application/json')
assert.isTrue(headers.has('1'))
assert.equal(headers.get(1), 'application/json')
})
test('converts field value to string on set and get', function() {
var headers = new Headers()
headers.set('Content-Type', 1)
headers.set('X-CSRF-Token', undefined)
assert.equal(headers.get('Content-Type'), '1')
assert.equal(headers.get('X-CSRF-Token'), 'undefined')
})
test('throws TypeError on invalid character in field name', function() {
assert.throws(function() { new Headers({'': 'application/json'}) }, TypeError)
assert.throws(function() { new Headers({'Accept:': 'application/json'}) }, TypeError)
assert.throws(function() {
var headers = new Headers()
headers.set({field: 'value'}, 'application/json')
}, TypeError)
})
test('is iterable with forEach', function() {
var headers = new Headers()
headers.append('Accept', 'application/json')
headers.append('Accept', 'text/plain')
headers.append('Content-Type', 'text/html')
var results = []
headers.forEach(function(value, key, object) {
results.push({value: value, key: key, object: object})
})
assert.equal(results.length, 2)
assert.deepEqual({key: 'accept', value: 'application/json,text/plain', object: headers}, results[0])
assert.deepEqual({key: 'content-type', value: 'text/html', object: headers}, results[1])
})
test('forEach accepts second thisArg argument', function() {
var headers = new Headers({'Accept': 'application/json'})
var thisArg = 42
headers.forEach(function() {
assert.equal(this, thisArg)
}, thisArg)
})
test('is iterable with keys', function() {
var headers = new Headers()
headers.append('Accept', 'application/json')
headers.append('Accept', 'text/plain')
headers.append('Content-Type', 'text/html')
var iterator = headers.keys()
assert.deepEqual({done: false, value: 'accept'}, iterator.next())
assert.deepEqual({done: false, value: 'content-type'}, iterator.next())
assert.deepEqual({done: true, value: undefined}, iterator.next())
})
test('is iterable with values', function() {
var headers = new Headers()
headers.append('Accept', 'application/json')
headers.append('Accept', 'text/plain')
headers.append('Content-Type', 'text/html')
var iterator = headers.values()
assert.deepEqual({done: false, value: 'application/json,text/plain'}, iterator.next())
assert.deepEqual({done: false, value: 'text/html'}, iterator.next())
assert.deepEqual({done: true, value: undefined}, iterator.next())
})
test('is iterable with entries', function() {
var headers = new Headers()
headers.append('Accept', 'application/json')
headers.append('Accept', 'text/plain')
headers.append('Content-Type', 'text/html')
var iterator = headers.entries()
assert.deepEqual({done: false, value: ['accept', 'application/json,text/plain']}, iterator.next())
assert.deepEqual({done: false, value: ['content-type', 'text/html']}, iterator.next())
assert.deepEqual({done: true, value: undefined}, iterator.next())
})
})
// https://fetch.spec.whatwg.org/#request-class
suite('Request', function() {
test('construct with string url', function() {
var request = new Request('https://fetch.spec.whatwg.org/')
assert.equal(request.url, 'https://fetch.spec.whatwg.org/')
})
featureDependent(test, support.url, 'construct with URL instance', function() {
var url = new URL('https://fetch.spec.whatwg.org/')
url.pathname = 'cors'
var request = new Request(url)
assert.equal(request.url, 'https://fetch.spec.whatwg.org/cors')
})
test('construct with non-Request object', function() {
var url = { toString: function() { return 'https://fetch.spec.whatwg.org/' } }
var request = new Request(url)
assert.equal(request.url, 'https://fetch.spec.whatwg.org/')
})
test('construct with Request', function() {
var request1 = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
body: 'I work out',
headers: {
accept: 'application/json',
'Content-Type': 'text/plain'
}
})
var request2 = new Request(request1)
return request2.text().then(function(body2) {
assert.equal(body2, 'I work out')
assert.equal(request2.method, 'POST')
assert.equal(request2.url, 'https://fetch.spec.whatwg.org/')
assert.equal(request2.headers.get('accept'), 'application/json')
assert.equal(request2.headers.get('content-type'), 'text/plain')
return request1.text().then(function() {
assert(false, 'original request body should have been consumed')
}, function(error) {
assert(error instanceof TypeError, 'expected TypeError for already read body')
})
})
})
test('construct with Request and override headers', function() {
var request1 = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
body: 'I work out',
headers: {
accept: 'application/json',
'X-Request-ID': '123'
}
})
var request2 = new Request(request1, {
headers: { 'x-test': '42' }
})
assert.equal(request2.headers.get('accept'), undefined)
assert.equal(request2.headers.get('x-request-id'), undefined)
assert.equal(request2.headers.get('x-test'), '42')
})
test('construct with Request and override body', function() {
var request1 = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
body: 'I work out',
headers: {
'Content-Type': 'text/plain'
}
})
var request2 = new Request(request1, {
body: '{"wiggles": 5}',
headers: { 'Content-Type': 'application/json' }
})
return request2.json().then(function(data) {
assert.equal(data.wiggles, 5)
assert.equal(request2.headers.get('content-type'), 'application/json')
})
})
featureDependent(test, !nativeChrome, 'construct with used Request body', function() {
var request1 = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
body: 'I work out'
})
return request1.text().then(function() {
assert.throws(function() {
new Request(request1)
}, TypeError)
})
})
test('GET should not have implicit Content-Type', function() {
var req = new Request('https://fetch.spec.whatwg.org/')
assert.equal(req.headers.get('content-type'), undefined)
})
test('POST with blank body should not have implicit Content-Type', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post'
})
assert.equal(req.headers.get('content-type'), undefined)
})
test('construct with string body sets Content-Type header', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
body: 'I work out'
})
assert.equal(req.headers.get('content-type'), 'text/plain;charset=UTF-8')
})
featureDependent(test, support.blob, 'construct with Blob body and type sets Content-Type header', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
body: new Blob(['test'], { type: 'image/png' })
})
assert.equal(req.headers.get('content-type'), 'image/png')
})
test('construct with body and explicit header uses header', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
headers: { 'Content-Type': 'image/png' },
body: 'I work out'
})
assert.equal(req.headers.get('content-type'), 'image/png')
})
featureDependent(test, support.blob, 'construct with Blob body and explicit Content-Type header', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
headers: { 'Content-Type': 'image/png' },
body: new Blob(['test'], { type: 'text/plain' })
})
assert.equal(req.headers.get('content-type'), 'image/png')
})
featureDependent(test, support.searchParams, 'construct with URLSearchParams body sets Content-Type header', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
body: new URLSearchParams('a=1&b=2')
})
assert.equal(req.headers.get('content-type'), 'application/x-www-form-urlencoded;charset=UTF-8')
})
featureDependent(test, support.searchParams, 'construct with URLSearchParams body and explicit Content-Type header', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
headers: { 'Content-Type': 'image/png' },
body: new URLSearchParams('a=1&b=2')
})
assert.equal(req.headers.get('content-type'), 'image/png')
})
test('clone GET request', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
headers: {'content-type': 'text/plain'}
})
var clone = req.clone()
assert.equal(clone.url, req.url)
assert.equal(clone.method, 'GET')
assert.equal(clone.headers.get('content-type'), 'text/plain')
assert.notEqual(clone.headers, req.headers)
assert.isFalse(req.bodyUsed)
})
test('clone POST request', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
headers: {'content-type': 'text/plain'},
body: 'I work out'
})
var clone = req.clone()
assert.equal(clone.method, 'POST')
assert.equal(clone.headers.get('content-type'), 'text/plain')
assert.notEqual(clone.headers, req.headers)
assert.equal(req.bodyUsed, false)
return Promise.all([clone.text(), req.clone().text()]).then(function(bodies) {
assert.deepEqual(bodies, ['I work out', 'I work out'])
})
})
featureDependent(test, !nativeChrome, 'clone with used Request body', function() {
var req = new Request('https://fetch.spec.whatwg.org/', {
method: 'post',
body: 'I work out'
})
return req.text().then(function() {
assert.throws(function() {
req.clone()
}, TypeError)
})
})
testBodyExtract(function(body) {
return new Request('', { method: 'POST', body: body })
})
})
// https://fetch.spec.whatwg.org/#response-class
suite('Response', function() {
test('default status is 200 OK', function() {
var res = new Response()
assert.equal(res.status, 200)
assert.equal(res.statusText, 'OK')
assert.isTrue(res.ok)
})
testBodyExtract(function(body) {
return new Response(body)
})
test('creates Headers object from raw headers', function() {
var r = new Response('{"foo":"bar"}', {headers: {'content-type': 'application/json'}})
assert.equal(r.headers instanceof Headers, true)
return r.json().then(function(json){
assert.equal(json.foo, 'bar')
return json
})
})
test('always creates a new Headers instance', function() {
var headers = new Headers({ 'x-hello': 'world' })
var res = new Response('', {headers: headers})
assert.equal(res.headers.get('x-hello'), 'world')
assert.notEqual(res.headers, headers)
})
test('clone text response', function() {
var res = new Response('{"foo":"bar"}', {
headers: {'content-type': 'application/json'}
})
var clone = res.clone()
assert.notEqual(clone.headers, res.headers, 'headers were cloned')
assert.equal(clone.headers.get('content-type'), 'application/json')
return Promise.all([clone.json(), res.json()]).then(function(jsons){
assert.deepEqual(jsons[0], jsons[1], 'json of cloned object is the same as original')
})
})
featureDependent(test, support.blob, 'clone blob response', function() {
var req = new Request(new Blob(['test']))
req.clone()
assert.equal(req.bodyUsed, false)
})
test('error creates error Response', function() {
var r = Response.error()
assert(r instanceof Response)
assert.equal(r.status, 0)
assert.equal(r.statusText, '')
assert.equal(r.type, 'error')
})
test('redirect creates redirect Response', function() {
var r = Response.redirect('https://fetch.spec.whatwg.org/', 301)
assert(r instanceof Response)
assert.equal(r.status, 301)
assert.equal(r.headers.get('Location'), 'https://fetch.spec.whatwg.org/')
})
test('construct with string body sets Content-Type header', function() {
var r = new Response('I work out')
assert.equal(r.headers.get('content-type'), 'text/plain;charset=UTF-8')
})
featureDependent(test, support.blob, 'construct with Blob body and type sets Content-Type header', function() {
var r = new Response(new Blob(['test'], { type: 'text/plain' }))
assert.equal(r.headers.get('content-type'), 'text/plain')
})
test('construct with body and explicit header uses header', function() {
var r = new Response('I work out', {
headers: {
'Content-Type': 'text/plain'
},
})
assert.equal(r.headers.get('content-type'), 'text/plain')
})
})
// https://fetch.spec.whatwg.org/#body-mixin
suite('Body mixin', function() {
featureDependent(suite, support.blob, '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 binary data', function() {
return fetch('/binary').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('arrayBuffer handles utf-8 data', function() {
return fetch('/hello/utf8').then(function(response) {
return response.arrayBuffer()
}).then(function(buf) {
assert(buf instanceof ArrayBuffer, 'buf is an ArrayBuffer instance')
assert.equal(buf.byteLength, 5, 'buf.byteLength is correct')
var octets = Array.prototype.slice.call(new Uint8Array(buf))
assert.deepEqual(octets, [104, 101, 108, 108, 111])
})
})
test('arrayBuffer handles utf-16le data', function() {
return fetch('/hello/utf16le').then(function(response) {
return response.arrayBuffer()
}).then(function(buf) {
assert(buf instanceof ArrayBuffer, 'buf is an ArrayBuffer instance')
assert.equal(buf.byteLength, 10, 'buf.byteLength is correct')
var octets = Array.prototype.slice.call(new Uint8Array(buf))
assert.deepEqual(octets, [104, 0, 101, 0, 108, 0, 108, 0, 111, 0])
})
})
test('rejects arrayBuffer promise after body is consumed', function() {
return fetch('/hello').then(function(response) {
assert.equal(response.bodyUsed, false)
response.blob()
assert.equal(response.bodyUsed, true)
return response.arrayBuffer()
}).catch(function(error) {
assert(error instanceof TypeError, 'Promise rejected after body consumed')
})
})
})
featureDependent(suite, support.blob, 'blob', function() {
test('resolves blob promise', function() {
return fetch('/hello').then(function(response) {
return response.blob()
}).then(function(blob) {
assert(blob instanceof Blob, 'blob is a Blob instance')
assert.equal(blob.size, 2)
})
})
test('blob handles binary data', function() {
return fetch('/binary').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('blob handles utf-8 data', function() {
return fetch('/hello/utf8').then(function(response) {
return response.blob()
}).then(readBlobAsBytes).then(function(octets) {
assert.equal(octets.length, 5, 'blob.size is correct')
assert.deepEqual(octets, [104, 101, 108, 108, 111])
})
})
test('blob handles utf-16le data', function() {
return fetch('/hello/utf16le').then(function(response) {
return response.blob()
}).then(readBlobAsBytes).then(function(octets) {
assert.equal(octets.length, 10, 'blob.size is correct')
assert.deepEqual(octets, [104, 0, 101, 0, 108, 0, 108, 0, 111, 0])
})
})
test('rejects blob promise after body is consumed', function() {
return fetch('/hello').then(function(response) {
assert(response.blob, 'Body does not implement blob')
assert.equal(response.bodyUsed, false)
response.text()
assert.equal(response.bodyUsed, true)
return response.blob()
}).catch(function(error) {
assert(error instanceof TypeError, 'Promise rejected after body consumed')
})
})
})
featureDependent(suite, support.formData, 'formData', function() {
test('post sets content-type header', function() {
return fetch('/request', {
method: 'post',
body: new FormData()
}).then(function(response) {
return response.json()
}).then(function(json) {
assert.equal(json.method, 'POST')
assert(/^multipart\/form-data;/.test(json.headers['content-type']))
})
})
featureDependent(test, !nativeChrome, 'rejects formData promise after body is consumed', function() {
return fetch('/json').then(function(response) {
assert(response.formData, 'Body does not implement formData')
response.formData()
return response.formData()
}).catch(function(error) {
if (error instanceof chai.AssertionError) {
throw error
} else {
assert(error instanceof TypeError, 'Promise rejected after body consumed')
}
})
})
featureDependent(test, !nativeChrome, 'parses form encoded response', function() {
return fetch('/form').then(function(response) {
return response.formData()
}).then(function(form) {
assert(form instanceof FormData, 'Parsed a FormData object')
})
})
})
suite('json', function() {
test('parses json response', function() {
return fetch('/json').then(function(response) {
return response.json()
}).then(function(json) {
assert.equal(json.name, 'Hubot')
assert.equal(json.login, 'hubot')
})
})
test('rejects json promise after body is consumed', function() {
return fetch('/json').then(function(response) {
assert(response.json, 'Body does not implement json')
assert.equal(response.bodyUsed, false)
response.text()
assert.equal(response.bodyUsed, true)
return response.json()
}).catch(function(error) {
assert(error instanceof TypeError, 'Promise rejected after body consumed')
})
})
featureDependent(test, !polyfillFirefox, 'handles json parse error', function() {
return fetch('/json-error').then(function(response) {
return response.json()
}).catch(function(error) {
assert(error instanceof Error, 'JSON exception is an Error instance')
assert(error.message, 'JSON exception has an error message')
})
})
})
suite('text', function() {
test('handles 204 No Content response', function() {
return fetch('/empty').then(function(response) {
assert.equal(response.status, 204)
return response.text()
}).then(function(body) {
assert.equal(body, '')
})
})
test('resolves text promise', function() {
return fetch('/hello').then(function(response) {
return response.text()
}).then(function(text) {
assert.equal(text, 'hi')
})
})
test('rejects text promise after body is consumed', function() {
return fetch('/hello').then(function(response) {
assert(response.text, 'Body does not implement text')
assert.equal(response.bodyUsed, false)
response.text()
assert.equal(response.bodyUsed, true)
return response.text()
}).catch(function(error) {
assert(error instanceof TypeError, 'Promise rejected after body consumed')
})
})
})
})
suite('fetch method', function() {
suite('promise resolution', function() {
test('resolves promise on 500 error', function() {
return fetch('/boom').then(function(response) {
assert.equal(response.status, 500)
assert.equal(response.ok, false)
return response.text()
}).then(function(body) {
assert.equal(body, 'boom')
})
})
test.skip('rejects promise for network error', function() {
return fetch('/error').then(function(response) {
assert(false, 'HTTP status ' + response.status + ' was treated as success')
}).catch(function(error) {
assert(error instanceof TypeError, 'Rejected with Error')
})
})
test('rejects when Request constructor throws', function() {
return fetch('/request', { method: 'GET', body: 'invalid' }).then(function() {
assert(false, 'Invalid Request init was accepted')
}).catch(function(error) {
assert(error instanceof TypeError, 'Rejected with Error')
})
})
})
suite('request', function() {
test('sends headers', function() {
return fetch('/request', {
headers: {
'Accept': 'application/json',
'X-Test': '42'
}
}).then(function(response) {
return response.json()
}).then(function(json) {
assert.equal(json.headers['accept'], 'application/json')
assert.equal(json.headers['x-test'], '42')
})
})
test('with Request as argument', function() {
var request = new Request('/request', {
headers: {
'Accept': 'application/json',
'X-Test': '42'
}
})
return fetch(request).then(function(response) {
return response.json()
}).then(function(json) {
assert.equal(json.headers['accept'], 'application/json')
assert.equal(json.headers['x-test'], '42')
})
})
test('reusing same Request multiple times', function() {
var request = new Request('/request', {
headers: {
'Accept': 'application/json',
'X-Test': '42'
}
})
var responses = []
return fetch(request).then(function(response) {
responses.push(response)
return fetch(request)
}).then(function(response) {
responses.push(response)
return fetch(request)
}).then(function(response) {
responses.push(response)
return Promise.all(responses.map(function(r) { return r.json() }))
}).then(function(jsons) {
jsons.forEach(function(json) {
assert.equal(json.headers['accept'], 'application/json')
assert.equal(json.headers['x-test'], '42')
})
})
})
featureDependent(suite, support.arrayBuffer, 'ArrayBuffer', function() {
test('ArrayBuffer body', function() {
return fetch('/request', {
method: 'post',
body: arrayBufferFromText('name=Hubot')
}).then(function(response) {
return response.json()
}).then(function(request) {
assert.equal(request.method, 'POST')
assert.equal(request.data, 'name=Hubot')
})
})
test('DataView body', function() {
return fetch('/request', {
method: 'post',
body: new DataView(arrayBufferFromText('name=Hubot'))
}).then(function(response) {
return response.json()
}).then(function(request) {
assert.equal(request.method, 'POST')
assert.equal(request.data, 'name=Hubot')
})
})
test('TypedArray body', function() {
return fetch('/request', {
method: 'post',
body: new Uint8Array(arrayBufferFromText('name=Hubot'))
}).then(function(response) {
return response.json()
}).then(function(request) {
assert.equal(request.method, 'POST')
assert.equal(request.data, 'name=Hubot')
})
})
})
featureDependent(test, support.searchParams, 'sends URLSearchParams body', function() {
return fetch('/request', {
method: 'post',
body: new URLSearchParams('a=1&b=2')
}).then(function(response) {
return response.json()
}).then(function(request) {
assert.equal(request.method, 'POST')
assert.equal(request.data, 'a=1&b=2')
})
})
})
suite('response', function() {
test('populates body', function() {
return fetch('/hello').then(function(response) {
assert.equal(response.status, 200)
assert.equal(response.ok, true)
return response.text()
}).then(function(body) {
assert.equal(body, 'hi')
})
})
test('parses headers', function() {
return fetch('/headers?' + new Date().getTime()).then(function(response) {
assert.equal(response.headers.get('Date'), 'Mon, 13 Oct 2014 21:02:27 GMT')
assert.equal(response.headers.get('Content-Type'), 'text/html; charset=utf-8')
})
})
})
// https://fetch.spec.whatwg.org/#methods
suite('HTTP methods', function() {
test('supports HTTP GET', function() {
return fetch('/request', {
method: 'get',
}).then(function(response) {
return response.json()
}).then(function(request) {
assert.equal(request.method, 'GET')
assert.equal(request.data, '')
})
})
test('GET with body throws TypeError', function() {
assert.throw(function() {
new Request('', {
method: 'get',
body: 'invalid'
})
}, TypeError)
})
test('HEAD with body throws TypeError', function() {
assert.throw(function() {
new Request('', {
method: 'head',
body: 'invalid'
})
}, TypeError)
})
test('supports HTTP POST', function() {
return fetch('/request', {
method: 'post',
body: 'name=Hubot'
}).then(function(response) {
return response.json()
}).then(function(request) {
assert.equal(request.method, 'POST')
assert.equal(request.data, 'name=Hubot')
})
})
test('supports HTTP PUT', function() {
return fetch('/request', {
method: 'put',
body: 'name=Hubot'
}).then(function(response) {
return response.json()
}).then(function(request) {
assert.equal(request.method, 'PUT')
assert.equal(request.data, 'name=Hubot')
})
})
featureDependent(test, support.patch, 'supports HTTP PATCH', function() {
return fetch('/request', {
method: 'PATCH',
body: 'name=Hubot'
}).then(function(response) {
return response.json()
}).then(function(request) {
assert.equal(request.method, 'PATCH')
assert.equal(request.data, 'name=Hubot')
})
})
test('supports HTTP DELETE', function() {
return fetch('/request', {
method: 'delete',
}).then(function(response) {
return response.json()
}).then(function(request) {
assert.equal(request.method, 'DELETE')
assert.equal(request.data, '')
})
})
})
// https://fetch.spec.whatwg.org/#atomic-http-redirect-handling
suite('Atomic HTTP redirect handling', function() {
test('handles 301 redirect response', function() {
return fetch('/redirect/301').then(function(response) {
assert.equal(response.status, 200)
assert.equal(response.ok, true)
assert.match(response.url, /\/hello/)
return response.text()
}).then(function(body) {
assert.equal(body, 'hi')
})
})
test('handles 302 redirect response', function() {
return fetch('/redirect/302').then(function(response) {
assert.equal(response.status, 200)
assert.equal(response.ok, true)
assert.match(response.url, /\/hello/)
return response.text()
}).then(function(body) {
assert.equal(body, 'hi')
})
})
test('handles 303 redirect response', function() {
return fetch('/redirect/303').then(function(response) {
assert.equal(response.status, 200)
assert.equal(response.ok, true)
assert.match(response.url, /\/hello/)
return response.text()
}).then(function(body) {
assert.equal(body, 'hi')
})
})
test('handles 307 redirect response', function() {
return fetch('/redirect/307').then(function(response) {
assert.equal(response.status, 200)
assert.equal(response.ok, true)
assert.match(response.url, /\/hello/)
return response.text()
}).then(function(body) {
assert.equal(body, 'hi')
})
})
featureDependent(test, support.permanentRedirect, 'handles 308 redirect response', function() {
return fetch('/redirect/308').then(function(response) {
assert.equal(response.status, 200)
assert.equal(response.ok, true)
assert.match(response.url, /\/hello/)
return response.text()
}).then(function(body) {
assert.equal(body, 'hi')
})
})
})
// https://fetch.spec.whatwg.org/#concept-request-credentials-mode
suite('credentials mode', function() {
setup(function() {
return fetch('/cookie?name=foo&value=reset', {credentials: 'same-origin'})
})
featureDependent(suite, exerciseMode === 'native', 'omit', function() {
test('request credentials defaults to omit', function() {
var request = new Request('')
assert.equal(request.credentials, 'omit')
})
test('does not accept cookies with implicit omit credentials', function() {
return fetch('/cookie?name=foo&value=bar').then(function() {
return fetch('/cookie?name=foo', {credentials: 'same-origin'})
}).then(function(response) {
return response.text()
}).then(function(data) {
assert.equal(data, 'reset')
})
})
test('does not accept cookies with omit credentials', function() {
return fetch('/cookie?name=foo&value=bar', {credentials: 'omit'}).then(function() {
return fetch('/cookie?name=foo', {credentials: 'same-origin'})
}).then(function(response) {
return response.text()
}).then(function(data) {
assert.equal(data, 'reset')
})
})
test('does not send cookies with implicit omit credentials', function() {
return fetch('/cookie?name=foo&value=bar', {credentials: 'same-origin'}).then(function() {
return fetch('/cookie?name=foo')
}).then(function(response) {
return response.text()
}).then(function(data) {
assert.equal(data, '')
})
})
test('does not send cookies with omit credentials', function() {
return fetch('/cookie?name=foo&value=bar').then(function() {
return fetch('/cookie?name=foo', {credentials: 'omit'})
}).then(function(response) {
return response.text()
}).then(function(data) {
assert.equal(data, '')
})
})
})
suite('same-origin', function() {
test('request credentials uses inits member', function() {
var request = new Request('', {credentials: 'same-origin'})
assert.equal(request.credentials, 'same-origin')
})
test('send cookies with same-origin credentials', function() {
return fetch('/cookie?name=foo&value=bar', {credentials: 'same-origin'}).then(function() {
return fetch('/cookie?name=foo', {credentials: 'same-origin'})
}).then(function(response) {
return response.text()
}).then(function(data) {
assert.equal(data, 'bar')
})
})
})
suite('include', function() {
test('send cookies with include credentials', function() {
return fetch('/cookie?name=foo&value=bar', {credentials: 'include'}).then(function() {
return fetch('/cookie?name=foo', {credentials: 'include'})
}).then(function(response) {
return response.text()
}).then(function(data) {
assert.equal(data, 'bar')
})
})
})
})
})
})
})
fetch-2.0.3/test/worker.js 0000664 0000000 0000000 00000001516 13056054646 0015447 0 ustar 00root root 0000000 0000000 importScripts('/node_modules/chai/chai.js')
importScripts('/node_modules/mocha/mocha.js')
mocha.setup('tdd')
self.assert = chai.assert
importScripts('/node_modules/promise-polyfill/promise.js')
importScripts('/test/test.js')
importScripts('/fetch.js')
function title(test) {
return test.fullTitle().replace(/#/g, '');
}
function reporter(runner) {
runner.on('pending', function(test){
self.postMessage({name: 'pending', title: title(test)});
});
runner.on('pass', function(test){
self.postMessage({name: 'pass', title: title(test)});
});
runner.on('fail', function(test, err){
self.postMessage({
name: 'fail',
title: title(test),
message: err.message,
stack: err.stack
});
});
runner.on('end', function(){
self.postMessage({name: 'end'});
});
}
mocha.reporter(reporter).run()