diff --git a/examples/basic/index.html b/examples/basic/index.html index 62cc83f..2e1f468 100644 --- a/examples/basic/index.html +++ b/examples/basic/index.html @@ -7,5 +7,16 @@
- + + + diff --git a/examples/basic/index.js b/examples/basic/index.js index 47d12a0..f6bc4b6 100644 --- a/examples/basic/index.js +++ b/examples/basic/index.js @@ -1,6 +1,7 @@ const { get, post } = require('../../dist/index.js'); const host = 'https://httpbin.org'; +const redirectCount = 3; // Perform simple get request get(`${host}/get?foo=bar`) @@ -22,3 +23,12 @@ post(`${host}/post`, { .catch(error => { console.error(error); }); + +// Perform get request with redirects +get(`${host}//redirect/${redirectCount}`) + .then(result => { + console.log('GET request response: ', result); + }) + .catch(error => { + console.error(error); + }); diff --git a/package.json b/package.json index f05787a..a7424b0 100644 --- a/package.json +++ b/package.json @@ -13,11 +13,11 @@ "babel-preset-latest": "^6.24.1", "jasmine": "^2.6.0", "jasmine-ajax": "^3.3.1", - "rollup": "^0.41.6", + "rollup": "^0.45.2", "rollup-plugin-babel": "^2.7.1", "rollup-plugin-node-resolve": "^3.0.0", "rollup-plugin-uglify": "^2.0.1", - "rollup-watch": "^4.0.0" + "rollup-watch": "^4.3.1" }, "scripts": { "build": "rollup -c -w", diff --git a/rollup.config.js b/rollup.config.js index 54786ca..0030c4c 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -5,7 +5,8 @@ import uglify from 'rollup-plugin-uglify'; export default { entry: 'src/index.js', format: 'umd', - moduleName: "pico-ajax", + moduleName: 'pico-ajax', + footer: 'if (typeof window !== "undefined") { window.PicoAjax = window["pico-ajax"]; }', plugins: [ resolve({ jsnext: true, diff --git a/src/browser.js b/src/browser.js index 84d8039..3f2fc38 100644 --- a/src/browser.js +++ b/src/browser.js @@ -7,6 +7,13 @@ import { parseJson } from './helpers'; +/** + * Known HTTP request response types + */ +const XHR_RESPONSE_TYPES = [ + 'arraybuffer', 'blob', 'document', 'json', 'text', +]; + /** * Try to parse response * diff --git a/src/helpers.js b/src/helpers.js index b792be3..3d21d5f 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -7,9 +7,11 @@ import http from 'http'; import https from 'https'; -import { parse as nodeParseUrl } from 'url'; +import { parse as nodeParseUrl, resolve as nodeResolveUrl } from 'url'; import zlib from 'zlib'; +const MAX_REDIRECTS = 21; + /** * Try to parse json * @@ -33,14 +35,16 @@ export function parseJson(json) { * @param {string} requestUrl * @returns {Object} */ -export function parseUrl(requestUrl) { +export function parseUrl(requestUrl, baseUrl) { // Modern browsers and Node v7+ if (typeof URL !== 'undefined') { - return new URL(requestUrl); + return baseUrl ? new URL(requestUrl, baseUrl) : new URL(baseUrl); } // Node up to v6 if (typeof global !== 'undefined') { - return nodeParseUrl(requestUrl); + return baseUrl + ? nodeParseUrl(nodeResolveUrl(baseUrl, requestUrl)) + : nodeParseUrl(requestUrl); } return {}; @@ -68,27 +72,14 @@ export function decompress(response, responseBuffer) { } /** - * Response wrapper for redirects + * Request method getter + * + * @param {string} requestOptions + * @returns {function} */ -class WrappedResponse { - constructor(response) { - this.response = response; - } - on(eventName, callback) { - console.log('on', eventName, callback); - - if (eventName === 'data') { - this.response.on(eventName, callback); - } - - if (eventName === 'end') { - this.headers = this.response.headers; - this.statusCode = this.response.statusCode; - this.statusText = this.response.statusText; - this.response.on(eventName, callback); - } - }; -}; +export function getRequestMethod(requestOptions) { + return /^https/.test(requestOptions.href) ? https.request : http.request; +} /** * Request wrapper for redirects @@ -115,14 +106,30 @@ class WrappedRequest { * @param {function} originalResponseHandler * @returns {Object} */ -export function followRedirects(originalRequestOptions, originalResponseHandler) { - const requestMethod = /^https/.test(originalRequestOptions.href) ? https.request : http.request; - +export function followRedirects(originalRequestOptions, originalResponseHandler, redirectCount = 0) { const wrappedResponseHandler = (response) => { - originalResponseHandler(new WrappedResponse(response)); - } + const { headers, statusCode, statusText } = response; + + // Follow redirects until we get non-redirect response code + if (statusCode >= 300 && statusCode < 400) { + if (redirectCount >= MAX_REDIRECTS) { + response.statusText = 'Too many redirects'; + originalResponseHandler(response); + } else { + const requestOptions = Object.assign( + originalRequestOptions, + parseUrl(headers.location, `${originalRequestOptions.protocol}//${originalRequestOptions.host}`) + ); + followRedirects(requestOptions, originalResponseHandler, redirectCount + 1).end(); + } + + return; + } - const request = requestMethod(originalRequestOptions, wrappedResponseHandler); + originalResponseHandler(response); + } - return new WrappedRequest(request); + return new WrappedRequest( + getRequestMethod(originalRequestOptions)(originalRequestOptions, wrappedResponseHandler) + ); } diff --git a/src/index.js b/src/index.js index e73e01f..e1cf676 100644 --- a/src/index.js +++ b/src/index.js @@ -16,13 +16,6 @@ const REQUEST_METHODS = [ 'CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'PATCH', 'POST', 'PUT', ]; -/** - * Known HTTP request response types - */ -const XHR_RESPONSE_TYPES = [ - 'arraybuffer', 'blob', 'document', 'json', 'text', -]; - /** * Default request options */