From 1c824b64f671bc345b12638b18fe36e9e14d1e56 Mon Sep 17 00:00:00 2001 From: Jon Samwell Date: Tue, 11 Aug 2015 10:31:59 +1000 Subject: [PATCH] Added support for the Angular Json protection vulnerability when parsing json (see https://docs.angularjs.org/api/ng/service/$http#json-vulnerability-protection)thanks to @riann (https://github.com/riaann) for this!!Added documentation for the new 'adapter' config property and tests around the node js multifetch adapter. --- README.md | 68 ++++++++- bower.json | 2 +- dist/ChangeLog.txt | 5 + dist/angular-http-batch.js | 17 ++- dist/angular-http-batch.min.js | 4 +- package.json | 2 +- .../adapters/nodeJsMultiFetchAdapter.js | 6 +- src/services/httpBatcher.js | 15 +- .../adapters/nodeJsMultiFetchAdapter.spec.js | 137 ++++++++++++++++++ tests/services/httpBatcher.spec.js | 39 +++++ 10 files changed, 279 insertions(+), 16 deletions(-) create mode 100644 tests/services/adapters/nodeJsMultiFetchAdapter.spec.js diff --git a/README.md b/README.md index 23a3512..3ef9571 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,11 @@ Angular Http Batcher - enabling transparent HTTP batch request with AngularJS ==================== The biggest performance boost you will get with modern single page style apps is to reduce the number of HTTP request you -send. This module has been designed to batch http requests to the same endpoint following the http 1.1 batch spec. All -you need to do is configure the batch endpoint with the library and the rest is taken care of. +send. This module has been designed to batch http requests to the same endpoint following the http 1.1 batch spec and after the +1.11.0 update it can now support multiple any number of batch formats and I'm planning to implement that Facebook batch protocol +very soon. All you need to do is configure the batch endpoint with the library and the rest is taken care of! -See my original blog blog for a detailed overview - http://jonsamwell.com/batching-http-requests-in-angular/ +See my original blog post for a detailed overview - http://jonsamwell.com/batching-http-requests-in-angular/

Getting Started

@@ -51,7 +52,7 @@ angular.module('myApp', ['jcs.angular-http-batch']); The root endpoint url is simply the base address of your api and the endpoint batch address is the url of the method that can accept the batch request (usually just /batch or /$batch). You are able to pass some optional configuration paramaters to this call in the third argument (see below) -The setAllowedBatchEndpoint has some options that can be passed in as a third paramter to the call which are explained below. +The setAllowedBatchEndpoint has some options that can be passed in as a third parameter to the call which are explained below. ```language-javascript { @@ -60,10 +61,67 @@ The setAllowedBatchEndpoint has some options that can be passed in as a third pa batchRequestCollectionDelay: 100, ignoredVerbs: ['head'], sendCookies: false, - enabled: true + enabled: true, + adapter: 'httpBatchAdapter' //defaults to this value we currently also support a node js multifetch format as well } ``` +####adapter +The key for the adapter to use. Defaults to the HTTP 1.1 adapter 'httpBatchAdapter'. +Current adapters are: + 'httpBatchAdapter' - supports the HTTP 1.1 spec and used by .Net (WebAPI) and JAVA servers. + 'nodeJsMultiFetchAdapter' - supports batching GET requests to a node server that uses the multifetch library. +Coming soon: + 'facebookAdapter' - will support the facebook batching protocol. + +Please request adapters that are not present. + +Adapters convert http requests into a single batch request and parse the batch response. They consist of two methods defined below. + +This adapter parameter can also be an object with the two below functions if you need to be more specific about the way +requests and responses are handled. + +```javascript + /** + * Builds the single batch request from the given batch of pending requests. + * Returns a standard angular httpConfig object that will be use to invoke the $http service. + * See: + * https://developers.google.com/storage/docs/json_api/v1/how-tos/batch + * http://blogs.msdn.com/b/webdev/archive/2013/11/01/introducing-batch-support-in-web-api-and-web-api-odata.aspx + * + * @param requests - the collection of pending http request to build into a single http batch request. + * @param config - the http batch config. + * @returns {object} - a http config object. + */ + function buildRequestFn(requests, config) { + var httpConfig = { + method: 'POST', + url: config.batchEndpointUrl, + cache: false, + headers: config.batchRequestHeaders || {} + }; + + // do processing... + + return httpConfig; + } + + /** + * Parses the raw response into an array of HttpBatchResponseData objects. If is this methods job + * to parse the response and match it up with the orginal request object. + * @param rawResponse + * @param config + * @returns {Array.HttpBatchResponseData[]} + */ + function parseResponseFn(requests, rawResponse, config) { + var batchResponses = []; // array of HttpBatchResponseData + + //do processing.. + + return batchResponses; + } +``` + ####maxBatchedRequestPerCall The maximum number of single http request that are allow to be sent in one http batch request. If this limit is reached the call will be split up into multiple batch requests. This option defaults to 10 request per batch but it is probably worth playing around with this number to see the optimal batch size between total request size and response speed. diff --git a/bower.json b/bower.json index 4f0cece..6ff5a68 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "angular-http-batcher", - "version": "1.11.0", + "version": "1.11.1", "description": "Enables transparent HTTP batch requests with Angular", "main": "dist/angular-http-batch.js", "keywords": [ diff --git a/dist/ChangeLog.txt b/dist/ChangeLog.txt index c488f24..600cbd7 100644 --- a/dist/ChangeLog.txt +++ b/dist/ChangeLog.txt @@ -1,3 +1,8 @@ +11/08/2015 V1.11.1 +Added support for the Angular Json protection vulnerability when parsing json (see https://docs.angularjs.org/api/ng/service/$http#json-vulnerability-protection) +thanks to @riann (https://github.com/riaann) for this!! +Added documentation for the new 'adapter' config property and tests around the node js multifetch adapter. + 10/08/2015 V1.11.0 HUGE refactor of the library geared towards supporting multiple different formats of batch request and response i.e. http 1.1 batch, NodeJS, Facebook etc. The library now has the concept of batch adapters which are able to transform raw diff --git a/dist/angular-http-batch.js b/dist/angular-http-batch.js index d022e10..812a121 100644 --- a/dist/angular-http-batch.js +++ b/dist/angular-http-batch.js @@ -1,5 +1,5 @@ /* - * angular-http-batcher - v1.11.0 - 2015-08-10 + * angular-http-batcher - v1.11.1 - 2015-08-11 * https://github.com/jonsamwell/angular-http-batcher * Copyright (c) 2015 Jon Samwell */ @@ -210,6 +210,15 @@ function addRequestFn(request) { return true; } +/** + * see https://docs.angularjs.org/api/ng/service/$http#json-vulnerability-protection + * @param data + * @returns {*|void|string} + */ +function trimJsonProtectionVulnerability(data) { + return data !== undefined ? data.replace(')]}\',\n', '') : data; +} + function sendFn() { var self = this, adapter = self.getAdapter(), @@ -217,7 +226,11 @@ function sendFn() { self.sendCallback(); self.$injector.get('$http')(httpBatchConfig).then(function (response) { - var batchResponses = adapter.parseResponse(self.requests, response, self.config); + var batchResponses; + + response.data = trimJsonProtectionVulnerability(response.data); + + batchResponses = adapter.parseResponse(self.requests, response, self.config); angular.forEach(batchResponses, function (batchResponse) { batchResponse.request.callback( diff --git a/dist/angular-http-batch.min.js b/dist/angular-http-batch.min.js index 9364212..5134dfe 100644 --- a/dist/angular-http-batch.min.js +++ b/dist/angular-http-batch.min.js @@ -1,5 +1,5 @@ /* - * angular-http-batcher - v1.11.0 - 2015-08-10 + * angular-http-batcher - v1.11.1 - 2015-08-11 * https://github.com/jonsamwell/angular-http-batcher * Copyright (c) 2015 Jon Samwell;*/ -!function(a,b){"use strict";function c(){var a=[],c="httpBatchAdapter",d={maxBatchedRequestPerCall:10,minimumBatchSize:2,batchRequestCollectionDelay:100,ignoredVerbs:["head"],sendCookies:!1,enabled:!0,adapter:c};this.setAllowedBatchEndpoint=function(e,f,g){var h=b.copy(d);void 0!==g&&(b.forEach(g,function(a,b){h[b]=a}),b.forEach(h.ignoredVerbs,function(a,b){h.ignoredVerbs[b]=a.toLowerCase()})),h.serviceUrl=e,h.batchEndpointUrl=f,h.adapter=h.adapter||c,a.push(h)},this.getBatchConfig=function(b){var c,d;for(d=0;d-1));d+=1)c=void 0;return c},this.canBatchCall=function(a,b){var c=this.getBatchConfig(a),d=c?c.canBatchRequest:void 0,e=!1;return c&&c.enabled===!0&&(e=d?d(a,b):c.batchEndpointUrl!==a&&-1===a.indexOf(c.batchEndpointUrl)&&-1===c.ignoredVerbs.indexOf(b.toLowerCase())),e},this.calculateBoundary=function(){return(new Date).getTime().toString()},this.$get=[function(){return this}]}function d(a){var b,c="";for(b in a)c+=b+": "+a[b]+"\n";return c}function e(){var a=this.config.adapter;if("object"==typeof a){if(void 0===a.buildRequest||void 0===a.parseResponse)throw new Error('A custom adapter must contain the methods "buildRequest" and "parseResponse" - please see the docs')}else if("string"==typeof a&&(a=this.adapters[a],void 0===a))throw new Error("Unknown type of http batch adapter: "+this.config.adapter);return a}function f(a){return this.requests.push(a),this.requests.length>=this.config.maxBatchedRequestPerCall&&this.flush(),!0}function g(){var a=this,c=a.getAdapter(),e=c.buildRequest(a.requests,a.config);a.sendCallback(),a.$injector.get("$http")(e).then(function(e){var f=c.parseResponse(a.requests,e,a.config);b.forEach(f,function(a){a.request.callback(a.statusCode,a.data,d(a.headers),a.statusText)})},function(c){b.forEach(a.requests,function(a){a.callback(c.statusCode,c.data,c.headers,c.statusText)})})}function h(){this.$timeout.cancel(this.currentTimeoutToken),this.currentTimeoutToken=void 0,this.send()}function i(a,c,d,e,f){var g=this;this.$injector=a,this.$timeout=c,this.adapters=d,this.config=e,this.sendCallback=f,this.requests=[],this.currentTimeoutToken=c(function(){g.currentTimeoutToken=void 0,g.requests.length-1));d+=1)c=void 0;return c},this.canBatchCall=function(a,b){var c=this.getBatchConfig(a),d=c?c.canBatchRequest:void 0,e=!1;return c&&c.enabled===!0&&(e=d?d(a,b):c.batchEndpointUrl!==a&&-1===a.indexOf(c.batchEndpointUrl)&&-1===c.ignoredVerbs.indexOf(b.toLowerCase())),e},this.calculateBoundary=function(){return(new Date).getTime().toString()},this.$get=[function(){return this}]}function d(a){var b,c="";for(b in a)c+=b+": "+a[b]+"\n";return c}function e(){var a=this.config.adapter;if("object"==typeof a){if(void 0===a.buildRequest||void 0===a.parseResponse)throw new Error('A custom adapter must contain the methods "buildRequest" and "parseResponse" - please see the docs')}else if("string"==typeof a&&(a=this.adapters[a],void 0===a))throw new Error("Unknown type of http batch adapter: "+this.config.adapter);return a}function f(a){return this.requests.push(a),this.requests.length>=this.config.maxBatchedRequestPerCall&&this.flush(),!0}function g(a){return void 0!==a?a.replace(")]}',\n",""):a}function h(){var a=this,c=a.getAdapter(),e=c.buildRequest(a.requests,a.config);a.sendCallback(),a.$injector.get("$http")(e).then(function(e){var f;e.data=g(e.data),f=c.parseResponse(a.requests,e,a.config),b.forEach(f,function(a){a.request.callback(a.statusCode,a.data,d(a.headers),a.statusText)})},function(c){b.forEach(a.requests,function(a){a.callback(c.statusCode,c.data,c.headers,c.statusText)})})}function i(){this.$timeout.cancel(this.currentTimeoutToken),this.currentTimeoutToken=void 0,this.send()}function j(a,c,d,e,f){var g=this;this.$injector=a,this.$timeout=c,this.adapters=d,this.config=e,this.sendCallback=f,this.requests=[],this.currentTimeoutToken=c(function(){g.currentTimeoutToken=void 0,g.requests.length