Skip to content
This repository has been archived by the owner on Jul 30, 2018. It is now read-only.

Commit

Permalink
Adding fetch implementation of request api, issue #139 (#243)
Browse files Browse the repository at this point in the history
* Adding fetch implementation of request api, issue #139

* Retyping fetch response body

* Fixing warning

* Code style changes

* Code style changes

* Fixing typo

* Removing iterator usage because it isnt supported in FF 39

* Style fixes
  • Loading branch information
rorticus authored Dec 13, 2016
1 parent d6b5820 commit e5b8f09
Show file tree
Hide file tree
Showing 5 changed files with 649 additions and 0 deletions.
129 changes: 129 additions & 0 deletions src/request/fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import Promise from 'dojo-shim/Promise';
import RequestTimeoutError from './errors/RequestTimeoutError';
import Task from '../async/Task';
import { Handle } from '../interfaces';
import { createHandle } from '../lang';
import { RequestOptions, Response as RequestResponse, ResponsePromise } from '../request';
import { generateRequestUrl } from './util';

declare class Headers {
append(name: string, value: string): void;
}

declare class Request {
constructor(url: string, options?: any);
}

declare function fetch(_: any): any;

export interface FetchRequestOptions extends RequestOptions {
}

export default function fetchRequest<T>(url: string, options: FetchRequestOptions = {}): ResponsePromise<T> {
const fetchRequestOptions: any = {};
const fetchRequestHeaders: Headers = new Headers();
const requestUrl = generateRequestUrl(url, options);

if ((!options.user || !options.password) && options.auth) {
const auth = options.auth.split(':');
options.user = decodeURIComponent(auth[ 0 ]);
options.password = decodeURIComponent(auth[ 1 ]);
}

if (options.user || options.password) {
fetchRequestHeaders.append('authorization', `Basic ${btoa(`${options.user}:${options.password}`)}`);
}

if (options.cacheBust) {
fetchRequestOptions.cache = 'reload';
}

if (!options.method) {
options.method = 'GET';
}

fetchRequestOptions.method = options.method;

if (options.headers) {
const headers = options.headers;
let hasContentTypeHeader = false;
let hasRequestedWithHeader = false;

for (const header in headers) {
if (header.toLowerCase() === 'content-type') {
hasContentTypeHeader = true;
} else if (header.toLowerCase() === 'x-requested-with') {
hasRequestedWithHeader = true;
}
fetchRequestHeaders.append(header.toLowerCase(), headers[ header ]);
}
}

if (options.data) {
fetchRequestOptions.body = options.data;
}

fetchRequestOptions.headers = fetchRequestHeaders;

let request = new Request(requestUrl, fetchRequestOptions);

return new Task<RequestResponse<T>>((resolve, reject) => {
let timeout: Handle;

fetch(request).then((fetchResponse: any) => {
timeout && timeout.destroy();

const { responseType = '' } = options;
let body: Promise<any>;

switch (responseType) {
case 'arraybuffer':
body = fetchResponse.arrayBuffer();
break;

case 'blob':
body = fetchResponse.blob();
break;

case 'xml':
body = fetchResponse.text()
.then((asText: string) => {
const parser = new DOMParser();
return parser.parseFromString(asText, 'text/xml');
});
break;

default:
body = fetchResponse.text();
break;
}

body.then((body: any) => {
resolve({
statusCode: fetchResponse.status,
statusText: fetchResponse.statusText,
data: body,
url: requestUrl,
nativeResponse: fetchResponse,
requestOptions: options,
getHeader(name: string) {
return fetchResponse.headers.get(name.toLowerCase());
}
});
}, reject);
}, reject);

if (options.timeout > 0 && options.timeout !== Infinity) {
timeout = ((): Handle => {
const timer = setTimeout(function (): void {
const error = new RequestTimeoutError(`Request timed out after ${options.timeout}ms`);
reject(error);
}, options.timeout);

return createHandle((): void => {
clearTimeout(timer);
});
})();
}
});
}
2 changes: 2 additions & 0 deletions src/request/has.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ import global from '../global';

add('node-buffer', 'Buffer' in global && typeof global.Buffer === 'function');

add('fetch', 'fetch' in global && typeof global.fetch === 'function');

export default has;
1 change: 1 addition & 0 deletions tests/unit/request/all.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
import './has';
import 'intern/dojo/has!host-node?./node:./xhr';
import './fetch';
Loading

0 comments on commit e5b8f09

Please sign in to comment.