Postman / Newman retry in case of failure - postman

In Newman I want to test to ensure that the response code is correct, response time is reasonable and response values are correct.
In some cases, due to network hiccups or other system conditions, some requests might end up with timeouts or incorrect values that will resolve if the same request was processed a few seconds later.
in such cases, I would like to retry the exact request x times with a Y timeout between requests.
If an iteration pass after a retry, I would like the Newman exit code to be 0 (successful run).

After few hours I had ended up with a function like this:
function retryOnFailure(successCode, numberOfRetrys) {
var key = request.name + '_counter';
var execCounter = postman.getEnvironmentVariable(key) || 1;
var sleepDuration = 1000;
var waitUntilTime = new Date().getTime() + sleepDuration;
if (responseCode.code !== successCode && execCounter <= numberOfRetrys) {
while (new Date().getTime() < waitUntilTime) {
// Do Nothing -> Wait
}
console.log('Retrying: ' + request.name + '\nGot: ' + responseCode.code + ' Expected: ' + successCode + '\nWaited: ' + sleepDuration / 1000 + 'sec \nRetry Number: ' + execCounter + ' of ' + numberOfRetrys);
execCounter++;
postman.setEnvironmentVariable(key, execCounter);
postman.setNextRequest(request.name);
}
}
Usage:
retryOnFailure(404, 4);

You can setup a request workflow like this:
Create a collection with a request, then:
In the pre-request tab you can implement a counter:
// Counter for number of requests
var counter = environment.counter ? _.parseInt(environment.counter) + 1 : 1;
postman.setEnvironmentVariable("counter", counter);
Your tests tab would look like this:
const code = (responseCode.code === 200);
if (code === 200 && environment.counter < X) {
// Stop execution
tests["Status code is 200"] = code;
postman.setNextRequest();
}
else {
// retry the same request
postman.setNextRequest("Name of this request");
}
A timeout for the request itself can be configured with the newman CLI:
newman run myCollection.json --timeout-request Y

Here is the reusable function for the same
postmanFunctions.common.retryOnFailure(predicate,retryCount,waitbetweenRetrySec,ReroutetorequestifNeeded ,postmanAssertions);
predicate function decides success or failure
assertion function has all postman assertion
if reroute is blank then after retry attempts assertions gets executed.
Flexible polling with retrycount and waittime(if predicate passed no more
polling/reflow)
There is a maxflow counter(env var) which limits the number of flow jumps to
avoid infinite loop
Store the below function in Globals or env:
() => {
var sleep = (sleepDuration) => {
var startTime = new Date().getTime();
while (new Date().getTime() - startTime < sleepDuration) {}
}
var sleepByAsyncDelayTime = () => {
var sleepDuration = postman.getEnvironmentVariable('asyncDelayTime') || 0;
sleep(sleepDuration);
}
var retryOnFailure = (predicate, numberOfRetrys, sleepDuration, reRouteRequestName, postmanAssertions) => {
var retryCountPerReq_key = request.name + '_retry_count';
var retryCountPerReq = pm.environment.get(retryCountPerReq_key) || 0;
var reflowCountPerReq_key = request.name + '_reflow_count';
var reflowCountPerReq = pm.environment.get(reflowCountPerReq_key) || 0;
var totalReflowCount_key = 'totalReflowCount';
var totalReflowCount = pm.environment.get(totalReflowCount_key) || 0;
var maxReflowCounter = postman.getEnvironmentVariable('maxReflowCounter') || 0;
var maxReflowCounterPerReq = postman.getEnvironmentVariable('maxReflowCounterPerReq') || 0;
function clearAndExit() {
pm.environment.unset(retryCountPerReq_key);
pm.environment.unset(reflowCountPerReq_key);
postmanAssertions();
}
function retry() {
sleep(sleepDuration);
pm.environment.set(retryCountPerReq_key, ++retryCountPerReq);
postman.setNextRequest(request.name);
}
function reFlow() {
if (totalReflowCount < maxReflowCounter && reflowCountPerReq < maxReflowCounterPerReq) {
pm.environment.unset(retryCountPerReq_key);
pm.environment.set(totalReflowCount_key, ++totalReflowCount);
pm.environment.set(reflowCountPerReq_key, ++reflowCountPerReq);
postman.setNextRequest(reRouteRequestName);
} else clearAndExit();
}
if (predicate()) clearAndExit();
else if (retryCountPerReq < numberOfRetrys) retry();
else if (reRouteRequestName != '') reFlow();
else clearAndExit();
}
return {
common: {
sleepByAsyncDelayTime,
sleep,
retryOnFailure
}
};
}

Here is my retry function that I define in collection pre-request script. It only works when tests are executed via collection :
Utils = {
wait: function (that, sleepDuration){
that.setTimeout(() => {}, sleepDuration);
},
withRetry: function(that, expectedHttpStatus, maxNumberOfTries, sleepBetweenTries, businessRetryConditionCallBack, endRetryCallback){
if (!that.pm.environment.get("collection_tries")) {
that.pm.environment.set("collection_tries", 1);
}
if (((that.pm.response.code != expectedHttpStatus) || businessRetryConditionCallBack())
&& (that.pm.environment.get("collection_tries") <= maxNumberOfTries)) {
var tries = parseInt(that.pm.environment.get("collection_tries"), 10);
that.pm.environment.set("collection_tries", tries + 1);
Utils.wait(that, sleepBetweenTries, maxNumberOfTries);
that.postman.setNextRequest(that.request.name);
} else {
if(businessRetryConditionCallBack()){
// On ne passe pas à la requête suivante
that.postman.setNextRequest(null);
}
that.pm.environment.unset("collection_tries");
endRetryCallback();
}
}
};
And here is how to use it in request on pre-request or test scripts :
var expectedHttpStatus = 200;
var maxNumberOfTries = 5;
var sleepBetweenTries = 5000;
Utils.withRetry(this, expectedHttpStatus, maxNumberOfTries, sleepBetweenTries, function(){
// Retry business condition callback
return pm.response.json().length <= 0;
}, function(){
// End retry callback
pm.test("Has one result", function () {
pm.expect(pm.response.json().length).to.equals(0);
});
});
This code will retry request as long as (http statut is different from expectedHttpStatus or businessRetryConditionCallBack is true) AND maxNumberOfTries is not reached and .
When http statut condition is true and maxNumberOfTries is reached, a check is done to verify businessRetryConditionCallBack. If not true, collection execution is stopped.

Related

HTTP Cloud Function returns 500 but there is no errors in the logs

I'm getting a strange behaviour, I have a few http functions in Firebase Cloud Functions. They work perfectly, but there are days that they start returning status 500 for a while and then go back to working normal for a few minutes and then start returning status 500 again, this behaviour remains for the entire day.
The most strange part is that I don't get any error messages on my stack driver, in fact, there are no registries about these calls, it is as if the calls doesn't reach google's services somehow or it is just rejected and there are no registries about it.
I'll post the implementation of one of the most used functions in my application:
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp()
exports.changeOrderStatus_1 = functions.https.onRequest((request, response) =>
{
//Check Headers
const clientID = request.get('ClientID');
if(clientID === null || clientID === undefined || clientID === "")
{
console.error(new Error('clientID not provided.'));
return response.status(500).send('clientID not provided.');
}
const unitID = request.get('UnitID');
if(unitID === null || unitID === undefined || unitID === "")
{
console.error(new Error('unitID not provided.'));
return response.status(500).send('unitID not provided.');
}
//Check body
const orderID = request.body.OrderID;
if(orderID === null || orderID === undefined || orderID === "")
{
console.error(new Error('orderID not provided.'));
return response.status(500).send('orderID not provided.');
}
const orderStatus = request.body.OrderStatus;
if(orderStatus === null || orderStatus === undefined || orderStatus === "")
{
console.error(new Error('orderStatus not provided.'));
return response.status(500).send('orderStatus not provided.');
}
const orderStatusInt = Number.parseInt(String(orderStatus));
const notificationTokenString = String(request.body.NotificationToken);
const customerID = request.body.CustomerID;
const promises: any[] = [];
const p1 = admin.database().ref('Clients/' + clientID + '/UnitData/'+ unitID +'/FreshData/Orders/' + orderID + '/Status').set(orderStatusInt);
promises.push(p1);
if(notificationTokenString !== null && notificationTokenString.length !== 0 && notificationTokenString !== 'undefined' && !(customerID === null || customerID === undefined || customerID === ""))
{
const p2 = admin.database().ref('Customers/' + customerID + '/OrderHistory/' + orderID + '/Status').set(orderStatusInt);
promises.push(p2);
if(orderStatusInt > 0 && orderStatusInt < 4)
{
const p3 = admin.database().ref('Customers/' + customerID + '/ActiveOrders/' + orderID).set(orderStatusInt);
promises.push(p3);
}
else
{
const p4 = admin.database().ref('Customers/' + customerID + '/ActiveOrders/' + orderID).set(null);
promises.push(p4);
}
let title = String(request.body.NotificationTitle);
let message = String(request.body.NotificationMessage);
if(title === null || title.length === 0)
title = "?????";
if(message === null || message.length === 0)
message = "?????";
const payload =
{
notification:
{
title: title,
body: message,
icon: 'notification_icon',
sound : 'default'
}
};
const p5 = admin.messaging().sendToDevice(notificationTokenString, payload);
promises.push(p5);
}
return Promise.all(promises).then(r => { return response.status(200).send('success') })
.catch(error =>
{
console.error(new Error(error));
return response.status(500).send(error)
});
})
And this is how I invoke it, the client application is running on Xamarin Forms app usinde the c# language:
static HttpClient Client;
public static void Initialize()
{
Client = new HttpClient();
Client.BaseAddress = new Uri("My cloud functions adress");
Client.DefaultRequestHeaders.Add("UnitID", UnitService.GetUnitID());
Client.DefaultRequestHeaders.Add("ClientID", AuthenticationService.GetFirebaseAuth().User.LocalId);
}
public static async Task<bool> CallChangeOrderStatus(OrderHolder holder, int status)
{
Debug.WriteLine("CallChangeOrderStatus: " + status);
try
{
var content = new Dictionary<string, string>();
content.Add("OrderID", holder.Order.ID);
content.Add("OrderStatus", status.ToString());
if (!string.IsNullOrEmpty(holder.Order.NotificationToken) && NotificationService.ShouldSend(status))
{
content.Add("CustomerID", holder.Order.SenderID);
content.Add("NotificationToken", holder.Order.NotificationToken);
content.Add("NotificationTitle", NotificationService.GetTitle(status));
content.Add("NotificationMessage", NotificationService.GetMessage(status));
}
var result = await Client.PostAsync("changeOrderStatus_1", new FormUrlEncodedContent(content));
return result.IsSuccessStatusCode;
}
catch (HttpRequestException exc)
{
#if DEBUG
ErrorHandlerService.ShowErrorMessage(exc);
#endif
Crashes.TrackError(exc);
return false;
}
}
These functions are called several times a minute at a time but can go for up to an hour without being called.
I have sent the requests from mobile conections, wifi conections, wired conections and from various internet providers and the problem still happens nonetheless.
Am I doing something wrong? Am I missing something? Is it instability in google servers?

How to point AWS-SDK DynamoDB to a serverless DynamoDB local

I am trying to write a script that will loop thru an array of items for a DynamoDB table and run a batch write command. My functionality is good, but I am having trouble with DynamoDB. Would be great if I could point my AWS.DynamoDB.DocumentClient() to my localhost running DynamoDB. Any tips?
Would also consider a way to just run the commands via the aws cli but I am not sure how to do that. I am running Node.js so it maybe possible?
Here is my code:
var AWS = require('aws-sdk');
AWS.config.update({ region: 'eu-central-1' });
var DynamoDB = new AWS.DynamoDB.DocumentClient()
DynamoDB.endpoint = 'http://localhost:8000';
const allItems = require('./resource.json');
const tableName = 'some-table-name';
console.log({ tableName, allItems });
var batches = [];
var currentBatch = [];
var count = 0;
for (let i = 0; i < allItems.length; i++) {
//push item to the current batch
count++;
currentBatch.push(allItems[i]);
if (count % 25 === 0) {
batches.push(currentBatch);
currentBatch = [];
}
}
//if there are still items left in the curr batch, add to the collection of batches
if (currentBatch.length > 0 && currentBatch.length !== 25) {
batches.push(currentBatch);
}
var completedRequests = 0;
var errors = false;
//request handler for DynamoDB
function requestHandler(err, data) {
console.log('In the request handler...');
return function (err, data) {
completedRequests++;
errors = errors ? true : err;
//log error
if (errors) {
console.error(JSON.stringify(err, null, 2));
console.error('Request caused a DB error.');
console.error('ERROR: ' + err);
console.error(JSON.stringify(err, null, 2));
} else {
var res = {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Methods': 'GET,POST,OPTIONS',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true,
},
body: JSON.stringify(data),
isBase64Encoded: false,
};
console.log(`Success: returned ${data}`);
return res;
}
if (completedRequests == batches.length) {
return errors;
}
};
}
//Make request
var params;
for (let j = 0; j < batches.length; j++) {
//items go in params.RequestedItems.id array
//format for the items is {PutRequest : {Item: ITEM_OBJECT}}
params = '{"RequestItems": {"' + tableName + '": []}}';
params = JSON.parse(params);
params.RequestItems[tableName] = batches[j];
console.log('before db.batchWriteItem: ', params);
//send to db
DynamoDB.batchWrite(params, requestHandler(params));
}
I figured it out and will leave this here for anyone that may need it.
var { DynamoDB } = require('aws-sdk');
var db = new DynamoDB.DocumentClient({
region: 'localhost',
endpoint: 'http://localhost:8000',
});

Postman send request entirely constructed in pre-requisite script

I have this below piece of code that generates me multiple orders in Pre-request Script section.
I dont need to have anything in "Body" section. But sending just [{}] in the body gives me 400 error.
var guid = (function() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return function() {
return 'HolCal' + s4() + '-' + s4() + '-' + s4() + '-' +
s4();
};
})();
var requestKeyNum = 2;
var orders = [];
for (var i = 0; i < requestKeyNum; i++) {
var key = guid();
orders.push({
"key": key,
"tradeDate": "2019-07-03",
"settleDate": "2019-07-04",
"transactionCode": "B",
"fundingCurrencySecurity" :{
"secId":1894823,
"secType1": "CASH",
"secType2":"NA",
"secType3":"NA",
"secType4":"NA",
"assetClass":"C",
"exchangeCode":"",
"tradeCurrencyCode":"USD",
"maturityDate":null,
"mortgageClass":null,
"investIdType":"D",
"investId":"9999USD",
"clearingHouseCode":null,
"settlementLocation":"PHY",
"expirationDate":null,
"issueCountry":""
}
})
}
postman.setEnvironmentVariable("orders", JSON.stringify(orders));
How must your generated body look like?
I assume, that you want so send you generated orders in your body.
If its true, you must reference to the orders variable in your body. Use {{orders}} in your body panel to do this.

How to choose different Lambda function while Start Streaming to Amazon Elasticsearch Service

Following this Streaming CloudWatch Logs Data to Amazon Elasticsearch Service, it's working fine to stream cloud watch log to ELK having one log group and one Lambda function.
But now I want to change target lambda function for my other logs group, but I am not able to do that as there is no option in AWS console.
Any Help will be appreciated.
Thanks
I was streaming to ELK using the AWS console option which is Start Streaming to Amazon Elasticsearch Service, But I failed to change or choose different lambda function as there is only lambda function can be selected for any log group using this option.
So, I create new lambda function and set stream target to AWS lambda function,
Here is the code that all you need, Node version for lambda function is 4.* as it was some issue with the new version but the pulse point is it does not require any extra NPM packages.
// v1.1.2
var https = require('https');
var zlib = require('zlib');
var crypto = require('crypto');
var endpoint = 'search-my-test.us-west-2.es.amazonaws.com';
exports.handler = function(input, context) {
// decode input from base64
var zippedInput = new Buffer(input.awslogs.data, 'base64');
// decompress the input
zlib.gunzip(zippedInput, function(error, buffer) {
if (error) { context.fail(error); return; }
// parse the input from JSON
var awslogsData = JSON.parse(buffer.toString('utf8'));
// transform the input to Elasticsearch documents
var elasticsearchBulkData = transform(awslogsData);
// skip control messages
if (!elasticsearchBulkData) {
console.log('Received a control message');
context.succeed('Control message handled successfully');
return;
}
// post documents to the Amazon Elasticsearch Service
post(elasticsearchBulkData, function(error, success, statusCode, failedItems) {
console.log('Response: ' + JSON.stringify({
"statusCode": statusCode
}));
if (error) {
console.log('Error: ' + JSON.stringify(error, null, 2));
if (failedItems && failedItems.length > 0) {
console.log("Failed Items: " +
JSON.stringify(failedItems, null, 2));
}
context.fail(JSON.stringify(error));
} else {
console.log('Success: ' + JSON.stringify(success));
context.succeed('Success');
}
});
});
};
function transform(payload) {
if (payload.messageType === 'CONTROL_MESSAGE') {
return null;
}
var bulkRequestBody = '';
payload.logEvents.forEach(function(logEvent) {
var timestamp = new Date(1 * logEvent.timestamp);
// index name format: cwl-YYYY.MM.DD
var indexName = [
'prod-background-wo-' + timestamp.getUTCFullYear(), // year
('0' + (timestamp.getUTCMonth() + 1)).slice(-2), // month
('0' + timestamp.getUTCDate()).slice(-2) // day
].join('.');
var source = buildSource(logEvent.message, logEvent.extractedFields);
source['response_time'] = source["end"] - source["start"];
source['#id'] = logEvent.id;
source['#timestamp'] = new Date(1 * logEvent.timestamp).toISOString();
source['#message'] = logEvent.message;
source['#owner'] = payload.owner;
source['#log_group'] = payload.logGroup;
source['#log_stream'] = payload.logStream;
var action = { "index": {} };
action.index._index = indexName;
action.index._type = payload.logGroup;
action.index._id = logEvent.id;
bulkRequestBody += [
JSON.stringify(action),
JSON.stringify(source),
].join('\n') + '\n';
});
return bulkRequestBody;
}
function buildSource(message, extractedFields) {
if (extractedFields) {
var source = {};
for (var key in extractedFields) {
if (extractedFields.hasOwnProperty(key) && extractedFields[key]) {
var value = extractedFields[key];
if (isNumeric(value)) {
source[key] = 1 * value;
continue;
}
jsonSubString = extractJson(value);
if (jsonSubString !== null) {
source['$' + key] = JSON.parse(jsonSubString);
}
source[key] = value;
}
}
return source;
}
jsonSubString = extractJson(message);
if (jsonSubString !== null) {
return JSON.parse(jsonSubString);
}
return {};
}
function extractJson(message) {
var jsonStart = message.indexOf('{');
if (jsonStart < 0) return null;
var jsonSubString = message.substring(jsonStart);
return isValidJson(jsonSubString) ? jsonSubString : null;
}
function isValidJson(message) {
try {
JSON.parse(message);
} catch (e) { return false; }
return true;
}
function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
function post(body, callback) {
var requestParams = buildRequest(endpoint, body);
var request = https.request(requestParams, function(response) {
var responseBody = '';
response.on('data', function(chunk) {
responseBody += chunk;
});
response.on('end', function() {
var info = JSON.parse(responseBody);
var failedItems;
var success;
if (response.statusCode >= 200 && response.statusCode < 299) {
failedItems = info.items.filter(function(x) {
return x.index.status >= 300;
});
success = {
"attemptedItems": info.items.length,
"successfulItems": info.items.length - failedItems.length,
"failedItems": failedItems.length
};
}
var error = response.statusCode !== 200 || info.errors === true ? {
"statusCode": response.statusCode,
"responseBody": responseBody
} : null;
callback(error, success, response.statusCode, failedItems);
});
}).on('error', function(e) {
callback(e);
});
request.end(requestParams.body);
}
function buildRequest(endpoint, body) {
var endpointParts = endpoint.match(/^([^\.]+)\.?([^\.]*)\.?([^\.]*)\.amazonaws\.com$/);
var region = endpointParts[2];
var service = endpointParts[3];
var datetime = (new Date()).toISOString().replace(/[:\-]|\.\d{3}/g, '');
var date = datetime.substr(0, 8);
var kDate = hmac('AWS4' + process.env.AWS_SECRET_ACCESS_KEY, date);
var kRegion = hmac(kDate, region);
var kService = hmac(kRegion, service);
var kSigning = hmac(kService, 'aws4_request');
var request = {
host: endpoint,
method: 'POST',
path: '/_bulk',
body: body,
headers: {
'Content-Type': 'application/json',
'Host': endpoint,
'Content-Length': Buffer.byteLength(body),
'X-Amz-Security-Token': process.env.AWS_SESSION_TOKEN,
'X-Amz-Date': datetime
}
};
var canonicalHeaders = Object.keys(request.headers)
.sort(function(a, b) { return a.toLowerCase() < b.toLowerCase() ? -1 : 1; })
.map(function(k) { return k.toLowerCase() + ':' + request.headers[k]; })
.join('\n');
var signedHeaders = Object.keys(request.headers)
.map(function(k) { return k.toLowerCase(); })
.sort()
.join(';');
var canonicalString = [
request.method,
request.path, '',
canonicalHeaders, '',
signedHeaders,
hash(request.body, 'hex'),
].join('\n');
var credentialString = [ date, region, service, 'aws4_request' ].join('/');
var stringToSign = [
'AWS4-HMAC-SHA256',
datetime,
credentialString,
hash(canonicalString, 'hex')
] .join('\n');
request.headers.Authorization = [
'AWS4-HMAC-SHA256 Credential=' + process.env.AWS_ACCESS_KEY_ID + '/' + credentialString,
'SignedHeaders=' + signedHeaders,
'Signature=' + hmac(kSigning, stringToSign, 'hex')
].join(', ');
return request;
}
function hmac(key, str, encoding) {
return crypto.createHmac('sha256', key).update(str, 'utf8').digest(encoding);
}
function hash(str, encoding) {
return crypto.createHash('sha256').update(str, 'utf8').digest(encoding);
}

IE9: store.find is failing

I can't seem to fetch new data in Internet Explorer 9. For the purpose of an example I test the store this way:
App.__container__.lookup('store:main').find('style')
The only error I receive is the following:
SCRIPT5022: Error: Assertion Failed: [object Object]
Does Ember-data works out of the box (without polyfills, ...) in Internet Explorer 9?
versions:
Ember: 1.9.1
Ember-data: 1.0.0-beta.12
Problem solved. When doing an AJAX request with jQuery, this normally happens through the XMLHttpRequest object.
On IE8-9, this object is not present, instead it uses XDomainRequest. The simplest fix for this is adding: https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest.
ember-data works out of the box with IE8+. According to this issue:
We've been supporting IE8 with our platform (built on Ember) for a
while now. Things I know:
shim/sham is not needed, it's polyfilled by Ember and Ember-Data.
You will need it if you want additional things like .bind() on a function, then you must prepend it to the vendor file (using Brocfile)
and we only include the shim for that purpose, not the sham
Solution Synthesis
Reason :
On IE8-9, this object is not present, instead it uses XDomainRequest.
Solution :
The issue is solved. When using an AJAX request with jQuery. Normally this is done through the XMLHttpRequest object. A simple fix would be using the Open-Source jQuery-ajaxTransport-XDomainRequest
Code : Adding :
jQuery-ajaxTransport-XDomainRequest.js
/*!
* jQuery-ajaxTransport-XDomainRequest - v1.0.4 - 2015-03-05
* https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest
* Copyright (c) 2015 Jason Moon (#JSONMOON)
* Licensed MIT (/blob/master/LICENSE.txt)
*/
(function(factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as anonymous module.
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// CommonJS
module.exports = factory(require('jquery'));
} else {
// Browser globals.
factory(jQuery);
}
}(function($) {
// Only continue if we're on IE8/IE9 with jQuery 1.5+ (contains the ajaxTransport function)
if ($.support.cors || !$.ajaxTransport || !window.XDomainRequest) {
return $;
}
var httpRegEx = /^(https?:)?\/\//i;
var getOrPostRegEx = /^get|post$/i;
var sameSchemeRegEx = new RegExp('^(\/\/|' + location.protocol + ')', 'i');
// ajaxTransport exists in jQuery 1.5+
$.ajaxTransport('* text html xml json', function(options, userOptions, jqXHR) {
// Only continue if the request is: asynchronous, uses GET or POST method, has HTTP or HTTPS protocol, and has the same scheme as the calling page
if (!options.crossDomain || !options.async || !getOrPostRegEx.test(options.type) || !httpRegEx.test(options.url) || !sameSchemeRegEx.test(options.url)) {
return;
}
var xdr = null;
return {
send: function(headers, complete) {
var postData = '';
var userType = (userOptions.dataType || '').toLowerCase();
xdr = new XDomainRequest();
if (/^\d+$/.test(userOptions.timeout)) {
xdr.timeout = userOptions.timeout;
}
xdr.ontimeout = function() {
complete(500, 'timeout');
};
xdr.onload = function() {
var allResponseHeaders = 'Content-Length: ' + xdr.responseText.length + '\r\nContent-Type: ' + xdr.contentType;
var status = {
code: 200,
message: 'success'
};
var responses = {
text: xdr.responseText
};
try {
if (userType === 'html' || /text\/html/i.test(xdr.contentType)) {
responses.html = xdr.responseText;
} else if (userType === 'json' || (userType !== 'text' && /\/json/i.test(xdr.contentType))) {
try {
responses.json = $.parseJSON(xdr.responseText);
} catch(e) {
status.code = 500;
status.message = 'parseerror';
//throw 'Invalid JSON: ' + xdr.responseText;
}
} else if (userType === 'xml' || (userType !== 'text' && /\/xml/i.test(xdr.contentType))) {
var doc = new ActiveXObject('Microsoft.XMLDOM');
doc.async = false;
try {
doc.loadXML(xdr.responseText);
} catch(e) {
doc = undefined;
}
if (!doc || !doc.documentElement || doc.getElementsByTagName('parsererror').length) {
status.code = 500;
status.message = 'parseerror';
throw 'Invalid XML: ' + xdr.responseText;
}
responses.xml = doc;
}
} catch(parseMessage) {
throw parseMessage;
} finally {
complete(status.code, status.message, responses, allResponseHeaders);
}
};
// set an empty handler for 'onprogress' so requests don't get aborted
xdr.onprogress = function(){};
xdr.onerror = function() {
complete(500, 'error', {
text: xdr.responseText
});
};
if (userOptions.data) {
postData = ($.type(userOptions.data) === 'string') ? userOptions.data : $.param(userOptions.data);
}
xdr.open(options.type, options.url);
xdr.send(postData);
},
abort: function() {
if (xdr) {
xdr.abort();
}
}
};
});
return $;
}));