Skip to content

Commit 1b89c0a

Browse files
committed
Merge pull request #29 from jsnbuchanan/master
Add unique request part names to Content-Disposition header
2 parents 43d0317 + 707a802 commit 1b89c0a

File tree

5 files changed

+128
-6
lines changed

5 files changed

+128
-6
lines changed

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,28 @@ For instance java servlet <= 3.1 parses multipart requests looking for the Conte
170170

171171
See notes on running this with java servlet <= 3.1
172172

173+
####uniqueRequestName
174+
175+
An optional parameter to set a unique parameter name on the Content-Disposition header.
176+
This requires the use of `batchPartRequestHeaders` sending in a Content-Disposition header. Sample configuration:
177+
178+
```language-javascript
179+
{
180+
...
181+
batchPartRequestHeaders: {'Content-Disposition': 'form-data' },
182+
uniqueRequestName: "batchRequest"
183+
...
184+
}
185+
```
186+
187+
Some backend servers may require that each part be named in this manner.
188+
If the configuration above is used, then each part will have a header like this:
189+
```Content-Disposition: form-data; name=batchRequest0```
190+
191+
If a Content-Disposition header is not added in the `batchPartRequestHeaders` then this parameter
192+
is silently ignored.
193+
194+
173195
####sendCookies
174196
False by default to reduce request size. If this is set to true cookies available on the document.cookie property will be set
175197
in each segment of a batch request. Note that only non HTTPOnly cookies will be sent as HTTPOnly cookies cannot be access by JavaScript

src/providers/httpBatchConfig.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ function HttpBatchConfigFn() {
88
ignoredVerbs: ['head'],
99
sendCookies: false,
1010
enabled: true,
11-
adapter: defaultBatchAdapter
11+
adapter: defaultBatchAdapter,
12+
uniqueRequestName: null
1213
};
1314

1415
/**

src/services/adapters/httpAdapter.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ function HttpBatchAdapter($document, $window, httpBatchConfig) {
88
singleSpace: ' ',
99
forwardSlash: '/',
1010
doubleDash: '--',
11-
colon: ':'
11+
colon: ':',
12+
semiColon: ';',
13+
requestName: 'name='
1214
};
1315

1416
self.key = 'httpBatchAdapter';
@@ -47,7 +49,13 @@ function HttpBatchAdapter($document, $window, httpBatchConfig) {
4749
batchBody.push(constants.doubleDash + boundary);
4850
if (config.batchPartRequestHeaders) {
4951
for (header in config.batchPartRequestHeaders) {
50-
batchBody.push(header + constants.colon + constants.singleSpace + config.batchPartRequestHeaders[header]);
52+
if (config.batchPartRequestHeaders.hasOwnProperty(header)) {
53+
var currHeader = header + constants.colon + constants.singleSpace + config.batchPartRequestHeaders[header];
54+
if (header.toLowerCase() === "content-disposition" && config.uniqueRequestName !== null && config.uniqueRequestName !== undefined) {
55+
currHeader += constants.semiColon + constants.singleSpace + constants.requestName + config.uniqueRequestName + i;
56+
}
57+
batchBody.push(currHeader);
58+
}
5159
}
5260
}
5361

@@ -170,7 +178,8 @@ function HttpBatchAdapter($document, $window, httpBatchConfig) {
170178
function findResponseBoundary(contentType) {
171179
var boundaryText = 'boundary=',
172180
startIndex = contentType.indexOf(boundaryText),
173-
boundary = contentType.substring(startIndex + boundaryText.length);
181+
endIndex = contentType.indexOf(';', startIndex),
182+
boundary = contentType.substring(startIndex + boundaryText.length, endIndex > 0 ? endIndex : contentType.length);
174183

175184
// the boundary might be quoted so remove the quotes
176185
boundary = boundary.replace(/"/g, constants.emptyString);

tests/providers/httpBatchConfig.spec.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@
5252
batchEndpointUrl: batchEndpointUrl,
5353
enabled: true,
5454
sendCookies: false,
55-
adapter: 'httpBatchAdapter'
55+
adapter: 'httpBatchAdapter',
56+
uniqueRequestName: null
5657
});
5758
});
5859

@@ -71,7 +72,8 @@
7172
batchEndpointUrl: batchEndpointUrl,
7273
enabled: true,
7374
sendCookies: false,
74-
adapter: 'httpBatchAdapter'
75+
adapter: 'httpBatchAdapter',
76+
uniqueRequestName: null
7577
});
7678
});
7779

tests/services/adapters/httpAdapter.spec.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,60 @@
120120
'GET api/some-method?params=123 HTTP/1.1\r\nHost: localhost:9876\r\n\r\n\r\n--boundary123--');
121121
});
122122

123+
it('should build the correct request that includes custom headers and a unique request name', function () {
124+
var rawRequest = {
125+
url: 'api/some-method?params=123',
126+
method: 'GET'
127+
},
128+
config = {
129+
batchEndpointUrl: 'batchEndpointUrl',
130+
batchRequestHeaders: {
131+
'Content-disposition': 'form-data'
132+
},
133+
batchPartRequestHeaders: {
134+
'Content-disposition': 'form-data'
135+
},
136+
uniqueRequestName: "veryUniqueRequestName"
137+
};
138+
139+
var result = httpAdapter.buildRequest([rawRequest], config);
140+
141+
expect(result.method).to.equal('POST');
142+
expect(result.url).to.equal(config.batchEndpointUrl);
143+
expect(result.cache).to.equal(false);
144+
expect(result.headers['Content-Type']).to.equal('multipart/mixed; boundary=boundary123');
145+
expect(result.headers['Content-disposition']).to.equal('form-data');
146+
expect(result.data).to.equal('--boundary123\r\nContent-disposition: form-data; name=veryUniqueRequestName0\r\nContent-Type: application/http; msgtype=request\r\n\r\n' +
147+
'GET api/some-method?params=123 HTTP/1.1\r\nHost: localhost:9876\r\n\r\n\r\n--boundary123--');
148+
});
149+
150+
it('should ignore unique request name if content disposition is not sent', function () {
151+
var rawRequest = {
152+
url: 'api/some-method?params=123',
153+
method: 'GET'
154+
},
155+
config = {
156+
batchEndpointUrl: 'batchEndpointUrl',
157+
batchRequestHeaders: {
158+
'Custom-Header': 'sweet'
159+
},
160+
batchPartRequestHeaders: {
161+
'Custom-Header': 'sweet'
162+
},
163+
uniqueRequestName: "veryUniqueRequestName"
164+
};
165+
166+
var result = httpAdapter.buildRequest([rawRequest], config);
167+
168+
expect(result.method).to.equal('POST');
169+
expect(result.url).to.equal(config.batchEndpointUrl);
170+
expect(result.cache).to.equal(false);
171+
expect(result.headers['Content-Type']).to.equal('multipart/mixed; boundary=boundary123');
172+
expect(result.headers['Custom-Header']).to.equal('sweet');
173+
expect(result.data).to.equal('--boundary123\r\nCustom-Header: sweet\r\nContent-Type: application/http; msgtype=request\r\n\r\n' +
174+
'GET api/some-method?params=123 HTTP/1.1\r\nHost: localhost:9876\r\n\r\n\r\n--boundary123--');
175+
});
176+
123177
it('should build the correct request that includes uri ecoding the urls', function () {
124178
var rawRequest = {
125179
url: 'api/some method?params=123',
@@ -181,6 +235,40 @@
181235
expect(results[0].data[1].Id).to.equal(2);
182236
expect(results[0].data[2].Id).to.equal(3);
183237
});
238+
239+
it('should parse a single response with extra data on the Content-Type header', function () {
240+
var rawRequest = {
241+
url: 'api/some-method?params=123',
242+
method: 'GET'
243+
},
244+
config = {
245+
batchEndpointUrl: 'batchEndpointUrl'
246+
},
247+
responseData = '--boundary123\r\nContent-Type: application/http; msgtype=response\r\n\r\n' +
248+
'HTTP/1.1 200 OK\r\nContent-Type: application/json; charset=utf-8\r\n\r\n' +
249+
'[{"Name":"Product 1","Id":1,"StockQuantity":100},{"Name":"Product 2","Id":2,"StockQuantity":2},{"Name":"Product 3","Id":3,"StockQuantity":32432}]' +
250+
'\r\n--boundary123--\r\n',
251+
response = {
252+
data: responseData,
253+
headers: function () {
254+
return {
255+
'content-type': 'multipart/mixed; boundary="boundary123"; charset=UTF-8'
256+
};
257+
}
258+
};
259+
260+
var results = httpAdapter.parseResponse([rawRequest], response, config);
261+
262+
expect(results.length).to.equal(1);
263+
expect(results[0].request).to.deep.equal(rawRequest);
264+
expect(results[0].statusCode).to.equal(200);
265+
expect(results[0].statusText).to.equal('OK');
266+
expect(results[0].headers['Content-Type']).to.equal('json; charset=utf-8');
267+
expect(results[0].data.length).to.equal(3);
268+
expect(results[0].data[0].Id).to.equal(1);
269+
expect(results[0].data[1].Id).to.equal(2);
270+
expect(results[0].data[2].Id).to.equal(3);
271+
});
184272
});
185273

186274
describe('canBatchRequest', function () {

0 commit comments

Comments
 (0)