Skip to content

Commit

Permalink
chore(http-core): pull in http core in preparation for making local c…
Browse files Browse the repository at this point in the history
…hanges
  • Loading branch information
Ian W. Remmel committed Feb 8, 2017
1 parent 432f3ad commit 8a7d60d
Show file tree
Hide file tree
Showing 3 changed files with 252 additions and 3 deletions.
3 changes: 1 addition & 2 deletions packages/http-core/package.json
Expand Up @@ -16,8 +16,7 @@
"lodash": "^4.13.1",
"qs": "^5.2.0",
"request": "^2.60.0",
"uuid": "^2.0.1",
"xhr": "^2.0.3"
"uuid": "^2.0.1"
},
"devDependencies": {
"@ciscospark/test-helper-chai": "^0.7.34",
Expand Down
250 changes: 250 additions & 0 deletions packages/http-core/src/lib/xhr.js
@@ -0,0 +1,250 @@
// Need to fork xhr to support environments with full object freezing; namely,
// SalesForce's Aura and Locker environment.

// See https://github.com/naugtur/xhr for license information

// Maintain the original code style of https://github.com/naugtur/xhr since
// we're trying to diverge as little as possible.
/* eslint-disable */

"use strict";
var window = require("global/window")
var isFunction = require("is-function")
var parseHeaders = require("parse-headers")
var xtend = require("xtend")

module.exports = createXHR
createXHR.XMLHttpRequest = window.XMLHttpRequest || noop
createXHR.XDomainRequest = "withCredentials" in (new createXHR.XMLHttpRequest()) ? createXHR.XMLHttpRequest : window.XDomainRequest

forEachArray(["get", "put", "post", "patch", "head", "delete"], function(method) {
createXHR[method === "delete" ? "del" : method] = function(uri, options, callback) {
options = initParams(uri, options, callback)
options.method = method.toUpperCase()
return _createXHR(options)
}
})

function forEachArray(array, iterator) {
for (var i = 0; i < array.length; i++) {
iterator(array[i])
}
}

function isEmpty(obj){
for(var i in obj){
if(obj.hasOwnProperty(i)) return false
}
return true
}

function initParams(uri, options, callback) {
var params = uri

if (isFunction(options)) {
callback = options
if (typeof uri === "string") {
params = {uri:uri}
}
} else {
params = xtend(options, {uri: uri})
}

params.callback = callback
return params
}

function createXHR(uri, options, callback) {
options = initParams(uri, options, callback)
return _createXHR(options)
}

function _createXHR(options) {
if(typeof options.callback === "undefined"){
throw new Error("callback argument missing")
}

var called = false
var callback = function cbOnce(err, response, body){
if(!called){
called = true
options.callback(err, response, body)
}
}

function readystatechange() {
if (xhr.readyState === 4) {
setTimeout(loadFunc, 0)
}
}

function getBody() {
// Chrome with requestType=blob throws errors arround when even testing access to responseText
var body = undefined

if (xhr.response) {
body = xhr.response
} else {
body = xhr.responseText || getXml(xhr)
}

if (isJson) {
try {
body = JSON.parse(body)
} catch (e) {}
}

return body
}

function errorFunc(evt) {
clearTimeout(timeoutTimer)
if(!(evt instanceof Error)){
evt = new Error("" + (evt || "Unknown XMLHttpRequest Error") )
}
evt.statusCode = 0
return callback(evt, failureResponse)
}

// will load the data & process the response in a special response object
function loadFunc() {
if (aborted) return
var status
clearTimeout(timeoutTimer)
if(options.useXDR && xhr.status===undefined) {
//IE8 CORS GET successful response doesn't have a status field, but body is fine
status = 200
} else {
status = (xhr.status === 1223 ? 204 : xhr.status)
}
var response = failureResponse
var err = null

if (status !== 0){
response = {
body: getBody(),
statusCode: status,
method: method,
headers: {},
url: uri,
rawRequest: xhr
}
if(xhr.getAllResponseHeaders){ //remember xhr can in fact be XDR for CORS in IE
response.headers = parseHeaders(xhr.getAllResponseHeaders())
}
} else {
err = new Error("Internal XMLHttpRequest Error")
}
return callback(err, response, response.body)
}

var xhr = options.xhr || null

if (!xhr) {
if (options.cors || options.useXDR) {
xhr = new createXHR.XDomainRequest()
}else{
xhr = new createXHR.XMLHttpRequest()
}
}

var key
var aborted
var uri = xhr.url = options.uri || options.url
var method = xhr.method = options.method || "GET"
var body = options.body || options.data
var headers = xhr.headers = options.headers || {}
var sync = !!options.sync
var isJson = false
var timeoutTimer
var failureResponse = {
body: undefined,
headers: {},
statusCode: 0,
method: method,
url: uri,
rawRequest: xhr
}

if ("json" in options && options.json !== false) {
isJson = true
headers["accept"] || headers["Accept"] || (headers["Accept"] = "application/json") //Don't override existing accept header declared by user
if (method !== "GET" && method !== "HEAD") {
headers["content-type"] || headers["Content-Type"] || (headers["Content-Type"] = "application/json") //Don't override existing accept header declared by user
body = JSON.stringify(options.json === true ? body : options.json)
}
}

xhr.onreadystatechange = readystatechange
xhr.onload = loadFunc
xhr.onerror = errorFunc
// IE9 must have onprogress be set to a unique function.
xhr.onprogress = function () {
// IE must die
}
xhr.onabort = function(){
aborted = true;
}
xhr.ontimeout = errorFunc
xhr.open(method, uri, !sync, options.username, options.password)
//has to be after open
if(!sync) {
xhr.withCredentials = !!options.withCredentials
}
// Cannot set timeout with sync request
// not setting timeout on the xhr object, because of old webkits etc. not handling that correctly
// both npm's request and jquery 1.x use this kind of timeout, so this is being consistent
if (!sync && options.timeout > 0 ) {
timeoutTimer = setTimeout(function(){
if (aborted) return
aborted = true//IE9 may still call readystatechange
xhr.abort("timeout")
var e = new Error("XMLHttpRequest timeout")
e.code = "ETIMEDOUT"
errorFunc(e)
}, options.timeout )
}

if (xhr.setRequestHeader) {
for(key in headers){
if(headers.hasOwnProperty(key)){
xhr.setRequestHeader(key, headers[key])
}
}
} else if (options.headers && !isEmpty(options.headers)) {
throw new Error("Headers cannot be set on an XDomainRequest object")
}

if ("responseType" in options) {
xhr.responseType = options.responseType
}

if ("beforeSend" in options &&
typeof options.beforeSend === "function"
) {
options.beforeSend(xhr)
}

// Microsoft Edge browser sends "undefined" when send is called with undefined value.
// XMLHttpRequest spec says to pass null as body to indicate no body
// See https://github.com/naugtur/xhr/issues/100.
xhr.send(body || null)

return xhr


}

function getXml(xhr) {
if (xhr.responseType === "document") {
return xhr.responseXML
}
var firefoxBugTakenEffect = xhr.responseXML && xhr.responseXML.documentElement.nodeName === "parsererror"
if (xhr.responseType === "" && !firefoxBugTakenEffect) {
return xhr.responseXML
}

return null
}

function noop() {}
2 changes: 1 addition & 1 deletion packages/http-core/src/request/request.shim.js
Expand Up @@ -11,7 +11,7 @@

import {defaults, isArray, pick} from 'lodash';
import qs from 'qs';
import xhr from 'xhr';
import xhr from '../lib/xhr';
import {detectSync} from '../lib/detect';

/**
Expand Down

0 comments on commit 8a7d60d

Please sign in to comment.