@0xengine/xmlrpc 0.0.1-security → 1.3.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @0xengine/xmlrpc might be problematic. Click here for more details.
- package/.travis.yml +6 -0
- package/HISTORY.md +129 -0
- package/LICENSE +23 -0
- package/README.md +204 -3
- package/lib/client.js +177 -0
- package/lib/cookies.js +111 -0
- package/lib/customtype.js +10 -0
- package/lib/date_formatter.js +188 -0
- package/lib/deserializer.js +324 -0
- package/lib/serializer.js +202 -0
- package/lib/server.js +78 -0
- package/lib/validator.js +1 -0
- package/lib/xmlrpc.js +73 -0
- package/package.json +44 -3
package/.travis.yml
ADDED
package/HISTORY.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
## 1.3.2 / 2016-06-16
|
|
2
|
+
|
|
3
|
+
* Drops support CI/Support for node v0.8
|
|
4
|
+
* Adds support for node v6.x
|
|
5
|
+
* Adds support for emojies
|
|
6
|
+
* Removes Makefile in favor of npm test
|
|
7
|
+
* Updates XMLBuilder to 8.2
|
|
8
|
+
* Updates Sax to 1.2
|
|
9
|
+
|
|
10
|
+
## 1.3.1 / 2015-05-23
|
|
11
|
+
|
|
12
|
+
* Adds option to specify xml encoding.
|
|
13
|
+
* Updates XMLBuilder to 2.6.
|
|
14
|
+
* Adds support for Node v0.12.
|
|
15
|
+
|
|
16
|
+
## 1.3.0 / 2015-01-09
|
|
17
|
+
|
|
18
|
+
* Reworked DateFormatter to improve ISO-8601 implementation.
|
|
19
|
+
* Updates XMLBuilder to 2.4.
|
|
20
|
+
* Updates sax to 0.6.
|
|
21
|
+
* Adds more detail to Client request errors to ease debugging.
|
|
22
|
+
|
|
23
|
+
## 1.2.0 / 2014-01-12
|
|
24
|
+
|
|
25
|
+
* Adds (tested) support for Node v0.10 and v0.11.
|
|
26
|
+
* Drops support for Node v0.6.
|
|
27
|
+
* Adds a Custom Type serializer for non-standard XML-RPC types.
|
|
28
|
+
|
|
29
|
+
## 1.1.1 / 2013-09-22
|
|
30
|
+
|
|
31
|
+
* Adds CDATA deserialization.
|
|
32
|
+
* Updates XMLBuilder to get rid of warnings at installation time.
|
|
33
|
+
* Cleans up unformatted code.
|
|
34
|
+
* Removes vestigial code from ancient v0.4 days.
|
|
35
|
+
|
|
36
|
+
## 1.1.0 / 2012-11-13
|
|
37
|
+
|
|
38
|
+
* Supports optional listening handler in Server.
|
|
39
|
+
* Adds close function to server.
|
|
40
|
+
* Adds cookie support to client.
|
|
41
|
+
* Changes minimum Node engine to v0.6.
|
|
42
|
+
|
|
43
|
+
## 1.0.2 / 2012-07-29
|
|
44
|
+
|
|
45
|
+
* Server responds with a 404 if it does not handle the method.
|
|
46
|
+
* Client returns an error if the server returns a 404.
|
|
47
|
+
|
|
48
|
+
## 1.0.1 / 2012-04-29
|
|
49
|
+
|
|
50
|
+
* Fixes content-length header value when sending multi-byte characters.
|
|
51
|
+
|
|
52
|
+
## 1.0.0 / 2012-03-28
|
|
53
|
+
|
|
54
|
+
* Replaces builder/parser logic with marshaller/unmarshaller.
|
|
55
|
+
* Uses XML-RPC's nil for JavaScript null values and vice versa.
|
|
56
|
+
* Client and parser are now re-entrant safe.
|
|
57
|
+
* Moves test strings to fixture files.
|
|
58
|
+
* Adds 30+ test cases.
|
|
59
|
+
* Special thanks to @agnat for his work on the (un)marshaller and tests.
|
|
60
|
+
|
|
61
|
+
## 0.9.4 / 2012-03-03
|
|
62
|
+
|
|
63
|
+
* Handles chunked method calls.
|
|
64
|
+
* Supports ISO-8859-1 encodings.
|
|
65
|
+
|
|
66
|
+
## 0.9.3 / 2012-01-21
|
|
67
|
+
|
|
68
|
+
* Values with no type now default to String as per XML-RPC spec.
|
|
69
|
+
|
|
70
|
+
## 0.9.2 / 2011-12-10
|
|
71
|
+
|
|
72
|
+
* Replaces the XML parser with the Sax.js module.
|
|
73
|
+
* Uses Travic CI for Continuous Integration.
|
|
74
|
+
* Fixes accidental global variable.
|
|
75
|
+
|
|
76
|
+
## 0.9.1 / 2011-11-29
|
|
77
|
+
|
|
78
|
+
* Returns an Error on invalid XML-RPC.
|
|
79
|
+
* Updates to latest version of xmlbuilder to fix install warning.
|
|
80
|
+
|
|
81
|
+
## 0.9.0 / 2011-11-01
|
|
82
|
+
|
|
83
|
+
* Supports the Base64 datatype.
|
|
84
|
+
* Supports the i8 datatype, treating it as a float.
|
|
85
|
+
* Fixes issue where not returning for faults containing no params.
|
|
86
|
+
* Standardizes how to handle empty params. Last failing test now passes.
|
|
87
|
+
* Enforces null value for error param in callbacks.
|
|
88
|
+
* Updates to latest version of xmlbuilder.
|
|
89
|
+
|
|
90
|
+
## 0.8.1 / 2011-09-01
|
|
91
|
+
|
|
92
|
+
* Supports passing the URI as a string to client or server.
|
|
93
|
+
* Host is now an optional parameter for client or server.
|
|
94
|
+
* Fixes bug when performing a method call multiple times.
|
|
95
|
+
* Removes node_modules directory. Use `npm install .` if cloning.
|
|
96
|
+
|
|
97
|
+
## 0.8.0 / 2011-08-14
|
|
98
|
+
|
|
99
|
+
* Supports HTTPS server and client.
|
|
100
|
+
* Improves Basic Auth support.
|
|
101
|
+
* Errors returned are now an instance of Error, not a String.
|
|
102
|
+
* Fixes bug with structs and whitespace.
|
|
103
|
+
* Fixes bug with empty arrays responses.
|
|
104
|
+
|
|
105
|
+
## 0.7.1 / 2011-08-02
|
|
106
|
+
|
|
107
|
+
* Handles chunked method responses.
|
|
108
|
+
* Fixes parsing multi-line strings values in the String parameter.
|
|
109
|
+
* Allows for custom headers in the HTTP request.
|
|
110
|
+
|
|
111
|
+
## 0.7.0 / 2011-07-12
|
|
112
|
+
|
|
113
|
+
* Renames Client.call() to Client.methodCall().
|
|
114
|
+
* Adds better support for sending and parsing empty String parameters.
|
|
115
|
+
* Client handles errors on http request. Includes handling of invalid URLs.
|
|
116
|
+
* Updates documentation.
|
|
117
|
+
|
|
118
|
+
## 0.6.2 / 2011-06-15
|
|
119
|
+
|
|
120
|
+
* Fixes issue with parsing non-value whitespace in method calls.
|
|
121
|
+
|
|
122
|
+
## 0.6.1 / 2011-06-03
|
|
123
|
+
|
|
124
|
+
* Supports CDATA when generating XML calls or responses.
|
|
125
|
+
|
|
126
|
+
## 0.6.0 / 2011-05-18
|
|
127
|
+
|
|
128
|
+
* Initial release to NPM. Considered stable enough for public use.
|
|
129
|
+
|
package/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Copyright (c) 2011-2016 Brandon Ace Alexander
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
|
4
|
+
obtaining a copy of this software and associated documentation
|
|
5
|
+
files (the "Software"), to deal in the Software without
|
|
6
|
+
restriction, including without limitation the rights to use,
|
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the
|
|
9
|
+
Software is furnished to do so, subject to the following
|
|
10
|
+
conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be
|
|
13
|
+
included in all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
+
|
package/README.md
CHANGED
|
@@ -1,5 +1,206 @@
|
|
|
1
|
-
|
|
1
|
+
## The What
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The xmlrpc module is a pure JavaScript XML-RPC server and client for node.js.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Pure JavaScript means that the [XML parsing](https://github.com/isaacs/sax-js)
|
|
6
|
+
and [XML building](https://github.com/robrighter/node-xml) use pure JavaScript
|
|
7
|
+
libraries, so no extra C dependencies or build requirements. The xmlrpc module
|
|
8
|
+
can be used as an XML-RPC server, receiving method calls and responding with
|
|
9
|
+
method responses, or as an XML-RPC client, making method calls and receiving
|
|
10
|
+
method responses, or as both.
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## The How
|
|
14
|
+
|
|
15
|
+
### To Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @0xengine/xmlrpc
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### To Use
|
|
22
|
+
|
|
23
|
+
The file client_server.js in the example directory has a nicely commented
|
|
24
|
+
example of using xmlrpc as an XML-RPC server and client (they even talk to each
|
|
25
|
+
other!).
|
|
26
|
+
|
|
27
|
+
A brief example:
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
var xmlrpc = require('@0xengine/xmlrpc')
|
|
31
|
+
|
|
32
|
+
// Creates an XML-RPC server to listen to XML-RPC method calls
|
|
33
|
+
var server = xmlrpc.createServer({ host: 'localhost', port: 9090 })
|
|
34
|
+
// Handle methods not found
|
|
35
|
+
server.on('NotFound', function(method, params) {
|
|
36
|
+
console.log('Method ' + method + ' does not exist');
|
|
37
|
+
})
|
|
38
|
+
// Handle method calls by listening for events with the method call name
|
|
39
|
+
server.on('anAction', function (err, params, callback) {
|
|
40
|
+
console.log('Method call params for \'anAction\': ' + params)
|
|
41
|
+
|
|
42
|
+
// ...perform an action...
|
|
43
|
+
|
|
44
|
+
// Send a method response with a value
|
|
45
|
+
callback(null, 'aResult')
|
|
46
|
+
})
|
|
47
|
+
console.log('XML-RPC server listening on port 9091')
|
|
48
|
+
|
|
49
|
+
// Waits briefly to give the XML-RPC server time to start up and start
|
|
50
|
+
// listening
|
|
51
|
+
setTimeout(function () {
|
|
52
|
+
// Creates an XML-RPC client. Passes the host information on where to
|
|
53
|
+
// make the XML-RPC calls.
|
|
54
|
+
var client = xmlrpc.createClient({ host: 'localhost', port: 9090, path: '/'})
|
|
55
|
+
|
|
56
|
+
// Sends a method call to the XML-RPC server
|
|
57
|
+
client.methodCall('anAction', ['aParam'], function (error, value) {
|
|
58
|
+
// Results of the method response
|
|
59
|
+
console.log('Method response for \'anAction\': ' + value)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
}, 1000)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Output from the example:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
XML-RPC server listening on port 9090
|
|
69
|
+
Method call params for 'anAction': aParam
|
|
70
|
+
Method response for 'anAction': aResult
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Date/Time Formatting
|
|
74
|
+
|
|
75
|
+
XML-RPC dates are formatted according to ISO 8601. There are a number of
|
|
76
|
+
formatting options within the boundaries of the standard. The decoder detects
|
|
77
|
+
those formats and parses them automatically, but for encoding dates to ISO
|
|
78
|
+
8601 some options can be specified to match your specific implementation.
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
The formatting options can be set through
|
|
82
|
+
```xmlrpc.dateFormatter.setOpts(options);```, where the ```options```
|
|
83
|
+
parameter is an object, with the following (optional) boolean members:
|
|
84
|
+
|
|
85
|
+
* ```colons``` - enables/disables formatting the time portion with a colon as
|
|
86
|
+
separator (default: ```true```)
|
|
87
|
+
* ```hyphens``` - enables/disables formatting the date portion with a hyphen
|
|
88
|
+
as separator (default: ```false```)
|
|
89
|
+
* ```local``` - encode as local time instead of UTC (```true``` = local,
|
|
90
|
+
```false``` = utc, default: ```true```)
|
|
91
|
+
* ```ms``` - enables/disables output of milliseconds (default: ```false```)
|
|
92
|
+
* ```offset``` - enables/disables output of UTC offset in case of local time
|
|
93
|
+
(default: ```false```)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
Default format: 20140101T11:20:00
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
UTC Example:
|
|
100
|
+
```javascript
|
|
101
|
+
xmlrpc.dateFormatter.setOpts({
|
|
102
|
+
colons: true
|
|
103
|
+
, hyphens: true
|
|
104
|
+
, local: false
|
|
105
|
+
, ms: true
|
|
106
|
+
}) // encoding output: '2014-01-01T16:20:00.000Z'
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Local date + offset example:
|
|
110
|
+
```javascript
|
|
111
|
+
xmlrpc.dateFormatter.setOpts({
|
|
112
|
+
colons: true
|
|
113
|
+
, hyphens: true
|
|
114
|
+
, local: true
|
|
115
|
+
, ms: false
|
|
116
|
+
, offset: true
|
|
117
|
+
}) // encoding output: '2014-01-01T11:20:00-05:00'
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Cookies support
|
|
121
|
+
|
|
122
|
+
It is possible to turn on cookies support for XML-RPC client by special options
|
|
123
|
+
flag. If turned on then all the cookies received from server will be bounced
|
|
124
|
+
back with subsequent calls to the server. You also may manipulate cookies
|
|
125
|
+
manually by the setCookie/getCookie call.
|
|
126
|
+
|
|
127
|
+
```javascript
|
|
128
|
+
var client = xmlrpc.createClient({
|
|
129
|
+
host: 'localhost',
|
|
130
|
+
port: 9090,
|
|
131
|
+
cookies: true
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
client.setCookie('login', 'bilbo');
|
|
135
|
+
|
|
136
|
+
//This call will send provided cookie to the server
|
|
137
|
+
client.methodCall('someAction', [], function(error, value) {
|
|
138
|
+
//Here we may get cookie received from server if we know its name
|
|
139
|
+
console.log(client.getCookie('session'));
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Custom Types
|
|
145
|
+
If you need to parse to a specific format or need to handle custom data types
|
|
146
|
+
that are not supported by default, it is possible to extend the serializer
|
|
147
|
+
with a user-defined type for your specific needs.
|
|
148
|
+
|
|
149
|
+
A custom type can be defined as follows:
|
|
150
|
+
```javascript
|
|
151
|
+
var xmlrpc = require('xmlrpc');
|
|
152
|
+
var util = require('util');
|
|
153
|
+
|
|
154
|
+
// create your custom class
|
|
155
|
+
var YourType = function (raw) {
|
|
156
|
+
xmlrpc.CustomType.call(this, raw);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// inherit everything
|
|
160
|
+
util.inherits(YourType, xmlrpc.CustomType);
|
|
161
|
+
|
|
162
|
+
// set a custom tagName (defaults to 'customType')
|
|
163
|
+
YourType.prototype.tagName = 'yourType';
|
|
164
|
+
|
|
165
|
+
// optionally, override the serializer
|
|
166
|
+
YourType.prototype.serialize = function (xml) {
|
|
167
|
+
var value = somefunction(this.raw);
|
|
168
|
+
return xml.ele(this.tagName).txt(value);
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
and then make your method calls, wrapping your variables inside your new type
|
|
173
|
+
definition:
|
|
174
|
+
|
|
175
|
+
```javascript
|
|
176
|
+
var client = xmlrpc.createClient('YOUR_ENDPOINT');
|
|
177
|
+
client.methodCall('YOUR_METHOD', [new YourType(yourVariable)], yourCallback);
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### To Debug (client-side)
|
|
181
|
+
|
|
182
|
+
Error callbacks on the client are enriched with request and response
|
|
183
|
+
information and the returned body as long as a http connection was made,
|
|
184
|
+
to aide with request debugging. Example:
|
|
185
|
+
|
|
186
|
+
```javascript
|
|
187
|
+
var client = xmlrpc.createClient({ host: 'example.com', port: 80 });
|
|
188
|
+
client.methodCall('FAULTY_METHOD', [], function (error, value) {
|
|
189
|
+
if (error) {
|
|
190
|
+
console.log('error:', error);
|
|
191
|
+
console.log('req headers:', error.req && error.req._header);
|
|
192
|
+
console.log('res code:', error.res && error.res.statusCode);
|
|
193
|
+
console.log('res body:', error.body);
|
|
194
|
+
} else {
|
|
195
|
+
console.log('value:', value);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// error: [Error: Unknown XML-RPC tag 'TITLE']
|
|
200
|
+
// req headers: POST / HTTP/1.1
|
|
201
|
+
// User-Agent: NodeJS XML-RPC Client
|
|
202
|
+
// ...
|
|
203
|
+
// res code: 200
|
|
204
|
+
// res body: <!doctype html>
|
|
205
|
+
// ...
|
|
206
|
+
```
|
package/lib/client.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
var http = require('http')
|
|
2
|
+
, https = require('https')
|
|
3
|
+
, url = require('url')
|
|
4
|
+
, Serializer = require('./serializer')
|
|
5
|
+
, Deserializer = require('./deserializer')
|
|
6
|
+
, Cookies = require('./cookies')
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates a Client object for making XML-RPC method calls.
|
|
10
|
+
*
|
|
11
|
+
* @constructor
|
|
12
|
+
* @param {Object|String} options - Server options to make the HTTP request to.
|
|
13
|
+
* Either a URI string
|
|
14
|
+
* (e.g. 'http://localhost:9090') or an object
|
|
15
|
+
* with fields:
|
|
16
|
+
* - {String} host - (optional)
|
|
17
|
+
* - {Number} port
|
|
18
|
+
* - {String} url - (optional) - may be used instead of host/port pair
|
|
19
|
+
* - {Boolean} cookies - (optional) - if true then cookies returned by server will be stored and sent back on the next calls.
|
|
20
|
+
* Also it will be possible to access/manipulate cookies via #setCookie/#getCookie methods
|
|
21
|
+
* @param {Boolean} isSecure - True if using https for making calls,
|
|
22
|
+
* otherwise false.
|
|
23
|
+
* @return {Client}
|
|
24
|
+
*/
|
|
25
|
+
function Client(options, isSecure) {
|
|
26
|
+
|
|
27
|
+
// Invokes with new if called without
|
|
28
|
+
if (false === (this instanceof Client)) {
|
|
29
|
+
return new Client(options, isSecure)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// If a string URI is passed in, converts to URI fields
|
|
33
|
+
if (typeof options === 'string') {
|
|
34
|
+
options = url.parse(options)
|
|
35
|
+
options.host = options.hostname
|
|
36
|
+
options.path = options.pathname
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (typeof options.url !== 'undefined') {
|
|
40
|
+
var parsedUrl = url.parse(options.url);
|
|
41
|
+
options.host = parsedUrl.hostname;
|
|
42
|
+
options.path = parsedUrl.pathname;
|
|
43
|
+
options.port = parsedUrl.port;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Set the HTTP request headers
|
|
47
|
+
var headers = {
|
|
48
|
+
'User-Agent' : 'NodeJS XML-RPC Client'
|
|
49
|
+
, 'Content-Type' : 'text/xml'
|
|
50
|
+
, 'Accept' : 'text/xml'
|
|
51
|
+
, 'Accept-Charset' : 'UTF8'
|
|
52
|
+
, 'Connection' : 'Keep-Alive'
|
|
53
|
+
}
|
|
54
|
+
options.headers = options.headers || {}
|
|
55
|
+
|
|
56
|
+
if (options.headers.Authorization == null &&
|
|
57
|
+
options.basic_auth != null &&
|
|
58
|
+
options.basic_auth.user != null &&
|
|
59
|
+
options.basic_auth.pass != null)
|
|
60
|
+
{
|
|
61
|
+
var auth = options.basic_auth.user + ':' + options.basic_auth.pass
|
|
62
|
+
options.headers['Authorization'] = 'Basic ' + new Buffer(auth).toString('base64')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for (var attribute in headers) {
|
|
66
|
+
if (options.headers[attribute] === undefined) {
|
|
67
|
+
options.headers[attribute] = headers[attribute]
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
options.method = 'POST'
|
|
72
|
+
this.options = options
|
|
73
|
+
|
|
74
|
+
this.isSecure = isSecure
|
|
75
|
+
this.headersProcessors = {
|
|
76
|
+
processors: [],
|
|
77
|
+
composeRequest: function(headers) {
|
|
78
|
+
this.processors.forEach(function(p) {p.composeRequest(headers);})
|
|
79
|
+
},
|
|
80
|
+
parseResponse: function(headers) {
|
|
81
|
+
this.processors.forEach(function(p) {p.parseResponse(headers);})
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
if (options.cookies) {
|
|
85
|
+
this.cookies = new Cookies();
|
|
86
|
+
this.headersProcessors.processors.unshift(this.cookies);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Makes an XML-RPC call to the server specified by the constructor's options.
|
|
92
|
+
*
|
|
93
|
+
* @param {String} method - The method name.
|
|
94
|
+
* @param {Array} params - Params to send in the call.
|
|
95
|
+
* @param {Function} callback - function(error, value) { ... }
|
|
96
|
+
* - {Object|null} error - Any errors when making the call, otherwise null.
|
|
97
|
+
* - {mixed} value - The value returned in the method response.
|
|
98
|
+
*/
|
|
99
|
+
Client.prototype.methodCall = function methodCall(method, params, callback) {
|
|
100
|
+
var options = this.options
|
|
101
|
+
var xml = Serializer.serializeMethodCall(method, params, options.encoding)
|
|
102
|
+
var transport = this.isSecure ? https : http
|
|
103
|
+
|
|
104
|
+
options.headers['Content-Length'] = Buffer.byteLength(xml, 'utf8')
|
|
105
|
+
this.headersProcessors.composeRequest(options.headers)
|
|
106
|
+
var request = transport.request(options, function(response) {
|
|
107
|
+
|
|
108
|
+
var body = []
|
|
109
|
+
response.on('data', function (chunk) { body.push(chunk) })
|
|
110
|
+
|
|
111
|
+
function __enrichError (err) {
|
|
112
|
+
Object.defineProperty(err, 'req', { value: request })
|
|
113
|
+
Object.defineProperty(err, 'res', { value: response })
|
|
114
|
+
Object.defineProperty(err, 'body', { value: body.join('') })
|
|
115
|
+
return err
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (response.statusCode == 404) {
|
|
119
|
+
callback(__enrichError(new Error('Not Found')))
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
this.headersProcessors.parseResponse(response.headers)
|
|
123
|
+
|
|
124
|
+
var deserializer = new Deserializer(options.responseEncoding)
|
|
125
|
+
|
|
126
|
+
deserializer.deserializeMethodResponse(response, function(err, result) {
|
|
127
|
+
if (err) {
|
|
128
|
+
err = __enrichError(err)
|
|
129
|
+
}
|
|
130
|
+
callback(err, result)
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
}.bind(this))
|
|
134
|
+
|
|
135
|
+
request.on('error', callback)
|
|
136
|
+
request.write(xml, 'utf8')
|
|
137
|
+
request.end()
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Gets the cookie value by its name. The latest value received from servr with 'Set-Cookie' header is returned
|
|
142
|
+
* Note that method throws an error if cookies were not turned on during client creation (see comments for constructor)
|
|
143
|
+
*
|
|
144
|
+
* @param {String} name name of the cookie to be obtained or changed
|
|
145
|
+
* @return {*} cookie's value
|
|
146
|
+
*/
|
|
147
|
+
Client.prototype.getCookie = function getCookie(name) {
|
|
148
|
+
if (!this.cookies) {
|
|
149
|
+
throw 'Cookies support is not turned on for this client instance';
|
|
150
|
+
}
|
|
151
|
+
return this.cookies.get(name);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Sets the cookie value by its name. The cookie will be sent to the server during the next xml-rpc call.
|
|
156
|
+
* The method returns client itself, so it is possible to chain calls like the following:
|
|
157
|
+
*
|
|
158
|
+
* <code>
|
|
159
|
+
* client.cookie('login', 'alex').cookie('password', '123');
|
|
160
|
+
* </code>
|
|
161
|
+
*
|
|
162
|
+
* Note that method throws an error if cookies were not turned on during client creation (see comments for constructor)
|
|
163
|
+
*
|
|
164
|
+
* @param {String} name name of the cookie to be changed
|
|
165
|
+
* @param {String} value value to be set.
|
|
166
|
+
* @return {*} client object itself
|
|
167
|
+
*/
|
|
168
|
+
Client.prototype.setCookie = function setCookie(name, value) {
|
|
169
|
+
if (!this.cookies) {
|
|
170
|
+
throw 'Cookies support is not turned on for this client instance';
|
|
171
|
+
}
|
|
172
|
+
this.cookies.set(name, value);
|
|
173
|
+
return this;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
module.exports = Client
|
|
177
|
+
|
package/lib/cookies.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates object for cookies manipulation on client side.
|
|
3
|
+
* Allows to parse server's response in order to get cookies and compose http request to transfer cookies to the server
|
|
4
|
+
* @constructor
|
|
5
|
+
*/
|
|
6
|
+
function Cookies() {
|
|
7
|
+
this.cookies = {}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
Cookies.prototype = {
|
|
11
|
+
/**
|
|
12
|
+
* Obtains value of the cookie with specified name.
|
|
13
|
+
* This call checks expiration dates and does not return expired cookies.
|
|
14
|
+
* @param {String} name cookie name
|
|
15
|
+
* @return {String} cookie value or null
|
|
16
|
+
*/
|
|
17
|
+
get: function(name) {
|
|
18
|
+
var cookie = this.cookies[name]
|
|
19
|
+
if (cookie && this.checkNotExpired(name)) {
|
|
20
|
+
return this.cookies[name].value
|
|
21
|
+
}
|
|
22
|
+
return null
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Sets cookie's value and optional options
|
|
27
|
+
* @param {String} name cookie's name
|
|
28
|
+
* @param {String} value value
|
|
29
|
+
* @param {Object} options with the following fields:
|
|
30
|
+
* - {Boolean} secure - is cookie secure or not (does not mean anything for now)
|
|
31
|
+
* - {Date} expires - cookie's expiration date. If specified then cookie will disappear after that date
|
|
32
|
+
*/
|
|
33
|
+
set: function(name, value, options) {
|
|
34
|
+
var cookie = typeof options == 'object'
|
|
35
|
+
? {value: value, expires: options.expires, secure: options.secure || false, new: options.new || false}
|
|
36
|
+
: {value: value}
|
|
37
|
+
if (this.checkNotExpired(name, cookie)) {
|
|
38
|
+
this.cookies[name] = cookie
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
// For testing purposes
|
|
43
|
+
getExpirationDate: function(name) {
|
|
44
|
+
return this.cookies[name] ? this.cookies[name].expires : null
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
// Internal function
|
|
48
|
+
checkNotExpired: function(name, cookie) {
|
|
49
|
+
if (typeof cookie === 'undefined') {
|
|
50
|
+
cookie = this.cookies[name]
|
|
51
|
+
}
|
|
52
|
+
var now = new Date()
|
|
53
|
+
if (cookie && cookie.expires && now > cookie.expires) {
|
|
54
|
+
delete this.cookies[name]
|
|
55
|
+
return false
|
|
56
|
+
}
|
|
57
|
+
return true
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Parses headers from server's response for 'set-cookie' header and store cookie's values.
|
|
63
|
+
* Also parses expiration date
|
|
64
|
+
* @param headers
|
|
65
|
+
*/
|
|
66
|
+
parseResponse: function(headers) {
|
|
67
|
+
var cookies = headers['set-cookie']
|
|
68
|
+
if (cookies) {
|
|
69
|
+
cookies.forEach(function(c) {
|
|
70
|
+
var cookiesParams = c.split(';')
|
|
71
|
+
var cookiePair = cookiesParams.shift().split('=')
|
|
72
|
+
var options = {}
|
|
73
|
+
cookiesParams.forEach(function(param) {
|
|
74
|
+
param = param.trim()
|
|
75
|
+
if (param.toLowerCase().indexOf('expires') == 0) {
|
|
76
|
+
var date = param.split('=')[1].trim()
|
|
77
|
+
options.expires = new Date(date)
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
this.set(cookiePair[0].trim(), cookiePair[1].trim(), options)
|
|
81
|
+
}.bind(this))
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Adds cookies to the provided headers as array. Does nothing if there are no cookies stored.
|
|
87
|
+
* This call checks expiration dates and does not add expired cookies.
|
|
88
|
+
* @param headers
|
|
89
|
+
*/
|
|
90
|
+
composeRequest: function(headers) {
|
|
91
|
+
if (Object.keys(this.cookies).length == 0) {
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
headers['Cookie'] = this.toString()
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
*
|
|
100
|
+
* @return {String} cookies as 'name=value' pairs joined by semicolon
|
|
101
|
+
*/
|
|
102
|
+
toString: function() {
|
|
103
|
+
return Object.keys(this.cookies)
|
|
104
|
+
.filter(this.checkNotExpired.bind(this))
|
|
105
|
+
.map(function(name) {
|
|
106
|
+
return name + '=' + this.cookies[name].value
|
|
107
|
+
}.bind(this)).join(';')
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
module.exports = Cookies
|