Skip to content

Commit 6bb6936

Browse files
peat-psuwitjsdevel
authored andcommitted
server: add option enableChunkedEncoding (#1043)
Using res.end() to write result instead of res.write() followed by res.end() will cause node not to use chunked encoding and include Content-Length header. (See nodejs/node#26005) This small distinction is important with some client. For example, Windows 10's MDM enrollment system will not accept chunked response (https://docs.microsoft.com/en-us/windows/client-management/mdm/on-premise-authentication-device-enrollment). This might improve compatibility with some other clients, too. However, there's a small chance that some client may expect the response to be chunked. So, I put this behind an option which defaults to enabled and can be disabled if needed.
1 parent 5b45068 commit 6bb6936

File tree

4 files changed

+123
-11
lines changed

4 files changed

+123
-11
lines changed

Readme.md

+1
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ Server options include the below:
219219
- `ca`: An array of strings or Buffers of trusted certificates in PEM format. If this is omitted several well known "root" CAs will be used, like VeriSign. These are used to authorize connections.
220220
- `crl` : Either a string or list of strings of PEM encoded CRLs (Certificate Revocation List)
221221
- `ciphers`: A string describing the ciphers to use or exclude, separated by :. The default cipher suite is:
222+
- `enableChunkedEncoding`: A boolean for controlling chunked transfer encoding in response. Some client (such as Windows 10's MDM enrollment SOAP client) is sensitive to transfer-encoding mode and can't accept chunked response. This option let user disable chunked transfer encoding for such a client. Default to `true` for backward compatibility.
222223

223224
``` javascript
224225
var xml = require('fs').readFileSync('myservice.wsdl', 'utf8');

lib/server.js

+26-11
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ var Server = function (server, path, services, wsdl, options) {
4040
this.suppressStack = options && options.suppressStack;
4141
this.returnFault = options && options.returnFault;
4242
this.onewayOptions = options && options.oneWay || {};
43+
this.enableChunkedEncoding =
44+
options.enableChunkedEncoding === undefined ? true : !!options.enableChunkedEncoding;
4345

4446
if (path[path.length - 1] !== '/')
4547
path += '/';
@@ -144,30 +146,22 @@ Server.prototype._processRequestXml = function (req, res, xml) {
144146
self.log("received", xml);
145147
}
146148
self._process(xml, req, function (result, statusCode) {
147-
if (statusCode) {
148-
res.statusCode = statusCode;
149-
}
150-
res.write(result);
151-
res.end();
149+
self._sendHttpResponse(res, statusCode, result);
152150
if (typeof self.log === 'function') {
153151
self.log("replied", result);
154152
}
155153
});
156154
} catch (err) {
157155
if (err.Fault !== undefined) {
158156
return self._sendError(err.Fault, function (result, statusCode) {
159-
res.statusCode = statusCode || 500;
160-
res.write(result);
161-
res.end();
157+
self._sendHttpResponse(res, statusCode || 500, result);
162158
if (typeof self.log === 'function') {
163159
self.log("error", err);
164160
}
165161
}, new Date().toISOString());
166162
} else {
167163
error = err.stack ? (self.suppressStack === true ? err.message : err.stack) : err;
168-
res.statusCode = 500;
169-
res.write(error);
170-
res.end();
164+
self._sendHttpResponse(res, /* statusCode */ 500, error);
171165
if (typeof self.log === 'function') {
172166
self.log("error", error);
173167
}
@@ -517,4 +511,25 @@ Server.prototype._sendError = function (soapFault, callback, includeTimestamp) {
517511
return callback(self._envelope(fault, '', includeTimestamp), statusCode);
518512
};
519513

514+
Server.prototype._sendHttpResponse = function (res, statusCode, result) {
515+
if (statusCode) {
516+
res.statusCode = statusCode;
517+
}
518+
519+
/*
520+
* Calling res.write(result) follow by res.end() will cause Node.js to use
521+
* chunked encoding, while calling res.end(result) directly will cause
522+
* Node.js to calculate and send Content-Length header. See
523+
* nodejs/node#26005.
524+
*/
525+
526+
if (this.enableChunkedEncoding) {
527+
res.write(result);
528+
res.end();
529+
}
530+
else {
531+
res.end(result);
532+
}
533+
}
534+
520535
exports.Server = Server;

lib/soap.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ export interface IServerOptions extends IWsdlBaseOptions {
9797
uri?: string;
9898
suppressStack?: boolean;
9999
oneWay?: IOneWayOptions;
100+
enableChunkedEncoding?: boolean;
100101
[key: string]: any;
101102
}
102103

test/server-options-test.js

+95
Original file line numberDiff line numberDiff line change
@@ -460,4 +460,99 @@ describe('SOAP Server with Options', function() {
460460
});
461461
});
462462
});
463+
it('should use chunked transfer encoding by default', function (done) {
464+
test.server.listen(15099, null, null, function() {
465+
test.soapServer = soap.listen(test.server, {
466+
path: '/stockquote',
467+
services: test.service,
468+
xml: test.wsdl,
469+
}, test.service, test.wsdl);
470+
test.baseUrl = 'http://' + test.server.address().address + ":" + test.server.address().port;
471+
472+
//windows return 0.0.0.0 as address and that is not
473+
//valid to use in a request
474+
if (test.server.address().address === '0.0.0.0' || test.server.address().address === '::') {
475+
test.baseUrl = 'http://127.0.0.1:' + test.server.address().port;
476+
}
477+
478+
soap.createClient(test.baseUrl + '/stockquote?wsdl', function(err, client) {
479+
assert.ifError(err);
480+
481+
client.on('response', function(body, response, eid) {
482+
var headers = response.headers;
483+
assert.strictEqual(headers['transfer-encoding'], 'chunked');
484+
assert.equal(headers['content-length'], undefined);
485+
})
486+
487+
client.SetTradePrice({ tickerSymbol: 'GOOG' }, function(err, result, body) {
488+
assert.ifError(err);
489+
done();
490+
});
491+
});
492+
});
493+
});
494+
it('should use chunked transfer encoding when enabled in options', function (done) {
495+
test.server.listen(15099, null, null, function() {
496+
test.soapServer = soap.listen(test.server, {
497+
path: '/stockquote',
498+
services: test.service,
499+
xml: test.wsdl,
500+
enableChunkedEncoding: true,
501+
}, test.service, test.wsdl);
502+
test.baseUrl = 'http://' + test.server.address().address + ":" + test.server.address().port;
503+
504+
//windows return 0.0.0.0 as address and that is not
505+
//valid to use in a request
506+
if (test.server.address().address === '0.0.0.0' || test.server.address().address === '::') {
507+
test.baseUrl = 'http://127.0.0.1:' + test.server.address().port;
508+
}
509+
510+
soap.createClient(test.baseUrl + '/stockquote?wsdl', function(err, client) {
511+
assert.ifError(err);
512+
513+
client.on('response', function(body, response, eid) {
514+
var headers = response.headers;
515+
assert.strictEqual(headers['transfer-encoding'], 'chunked');
516+
assert.equal(headers['content-length'], undefined);
517+
})
518+
519+
client.SetTradePrice({ tickerSymbol: 'GOOG' }, function(err, result, body) {
520+
assert.ifError(err);
521+
done();
522+
});
523+
});
524+
});
525+
});
526+
it('should not use chunked transfer encoding when disabled in options', function (done) {
527+
test.server.listen(15099, null, null, function() {
528+
test.soapServer = soap.listen(test.server, {
529+
path: '/stockquote',
530+
services: test.service,
531+
xml: test.wsdl,
532+
enableChunkedEncoding: false,
533+
}, test.service, test.wsdl);
534+
test.baseUrl = 'http://' + test.server.address().address + ":" + test.server.address().port;
535+
536+
//windows return 0.0.0.0 as address and that is not
537+
//valid to use in a request
538+
if (test.server.address().address === '0.0.0.0' || test.server.address().address === '::') {
539+
test.baseUrl = 'http://127.0.0.1:' + test.server.address().port;
540+
}
541+
542+
soap.createClient(test.baseUrl + '/stockquote?wsdl', function(err, client) {
543+
assert.ifError(err);
544+
545+
client.on('response', function(body, response, eid) {
546+
var headers = response.headers;
547+
assert.notEqual(headers['content-length'], undefined);
548+
assert.equal(headers['transfer-encoding'], undefined);
549+
})
550+
551+
client.SetTradePrice({ tickerSymbol: 'GOOG' }, function(err, result, body) {
552+
assert.ifError(err);
553+
done();
554+
});
555+
});
556+
});
557+
});
463558
});

0 commit comments

Comments
 (0)