Skip to content

Commit 968831a

Browse files
author
Denis Rechkunov
committed
Merge branch 'release/1.0.0'
2 parents 5c31417 + e96451b commit 968831a

7 files changed

Lines changed: 196 additions & 113 deletions

File tree

README.md

Lines changed: 23 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22
[![NPM](https://nodei.co/npm/catberry-uhr.png)](https://nodei.co/npm/catberry-uhr/)
33

44
##Description
5-
Catberry's modules run both at server and browser and it is very useful to have universal http(s) request implementation.
5+
Catberry's modules run both at server and in browser and it is very useful to
6+
have universal http(s) request implementation.
67

7-
It has one interface and different implementations on server and browser.
8+
It has one interface and different implementations at server and in browser.
89

9-
At server it uses node's "http.request" or "https.request" (depend on specified protocol in URL).
10-
At browser it uses jQuery AJAX implementation.
10+
At server it uses node's "http.request" or "https.request"
11+
(depend on specified protocol in URL).
12+
At browser it uses native XmlHttpRequest.
1113

12-
This module was developed using [HTTP/1.1v2 RFC 2616](http://www.w3.org/Protocols/rfc2616).
14+
This module was developed using [HTTP/1.1v2 RFC 2616]
15+
(http://www.w3.org/Protocols/rfc2616).
1316

1417
It supports:
1518

@@ -72,6 +75,7 @@ Options support:
7275

7376
```javascript
7477
{
78+
method: 'GET',
7579
timeout: 30000,
7680
headers: {
7781
Cookie: 'name=value'
@@ -88,7 +92,7 @@ In callback you always receive:
8892

8993
* Error (if it has happened)
9094
* Status object with HTTP status code, status text and response headers
91-
* Response body as plain text
95+
* Response body as plain text or object (depends on Content-Type in response headers)
9296

9397
Status object looks like this:
9498

@@ -106,38 +110,10 @@ Status object looks like this:
106110
```
107111

108112
##Usage
109-
To use this module you must register its components into catberry's [Service Locator](https://github.com/pragmadash/catberry-locator) like this:
113+
If you are using [Catberry Framework](https://github.com/pragmadash/catberry)
114+
it is already included and registered in Service Locator.
110115

111-
In server.js
112-
113-
```javascript
114-
var uhr = require('catberry-uhr'),
115-
catberry = require('catberry'),
116-
config = require('./config-server'),
117-
app = connect();
118-
cat = catberry.create(config);
119-
120-
// register UHR components
121-
uhr.registerOnServer(cat.locator);
122-
123-
app.use(cat.getMiddleware());
124-
...
125-
```
126-
127-
In client.js
128-
129-
```javascript
130-
var uhr = require('catberry-uhr'),
131-
catberry = require('catberry'),
132-
config = require('./config-client'),
133-
cat = catberry.create(config);
134-
135-
// register localization components in locator
136-
uhr.registerOnClient(cat.locator);
137-
138-
```
139-
140-
And then you can just inject $uhr into you module and use like this:
116+
You can just inject $uhr into you module and use like this:
141117

142118
```javascript
143119
function Module($uhr) {
@@ -167,11 +143,16 @@ Module.prototype.render(placeholderName, args, callback) {
167143
```
168144

169145
##Contribution
170-
If you have found a bug, please create pull request with mocha unit-test which reproduces it or describe all details in issue if you can not implement test.
171-
If you want to propose some improvements just create issue or pull request but please do not forget to use **npm test** to be sure that you code is awesome.
146+
If you have found a bug, please create pull request with [mocha]
147+
(https://www.npmjs.org/package/mocha) unit-test which reproduces it or describe
148+
all details in issue if you can not implement test. If you want to propose some
149+
improvements just create issue or pull request but please do not forget to use
150+
`npm test` to be sure that your code is awesome.
172151

173-
All changes should satisfy this [Code Style Guide](https://github.com/pragmadash/catberry/blob/master/docs/code-style.md).
152+
All changes should satisfy this [Code Style Guide]
153+
(docs/code-style-guide.md).
174154

175-
Also your changes should be covered by unit tests using [mocha](https://www.npmjs.org/package/mocha).
155+
Also your changes should be covered by unit tests using [mocha]
156+
(https://www.npmjs.org/package/mocha).
176157

177-
Denis Rechkunov <denis.rechkunov@gmail.com>
158+
Denis Rechkunov <denis.rechkunov@gmail.com>

index.js

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030

3131
'use strict';
3232

33-
var ServerUHR = require('./lib/server/UHR'),
33+
var /**no-client-bundle**/
34+
ServerUHR = require('./lib/server/UHR'),
3435
ClientUHR = require('./lib/client/UHR');
3536

3637
module.exports = {
@@ -39,12 +40,6 @@ module.exports = {
3940
* @param {ServiceLocator} locator Catberry's service locator.
4041
*/
4142
registerOnServer: function (locator) {
42-
43-
// WARNING!!!
44-
// we must do that at server TO NOT include this module into
45-
// client-bundle
46-
locator.registerInstance('serverModulePath', './lib/server/UHR');
47-
4843
var config = locator.resolve('config');
4944
locator.register('uhr', ServerUHR, config, true);
5045
},

lib/UHRBase.js

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,7 @@ UHRBase.DEFAULT_GENERAL_HEADERS = {
6262
Accept: UHRBase.TYPES.JSON + '; q=0.7, ' +
6363
UHRBase.TYPES.HTML + '; q=0.2, ' +
6464
UHRBase.TYPES.PLAIN_TEXT + '; q=0.1',
65-
Pragma: 'no-cache',
6665
'Accept-Charset': UHRBase.CHARSET + '; q=1',
67-
'Cache-Control': 'no-cache',
6866
'X-Requested-With': 'Catberry UHR'
6967
};
7068

@@ -149,6 +147,7 @@ UHRBase.prototype.delete = function (url, options, callback) {
149147
* @param {Object} parameters Request parameters.
150148
* @param {Function?} callback Callback on finish
151149
* with error, status object and data.
150+
* @return {Object} Request object with abort method.
152151
*/
153152
UHRBase.prototype.request = function (parameters, callback) {
154153
callback = callback || dummy;
@@ -161,7 +160,7 @@ UHRBase.prototype.request = function (parameters, callback) {
161160
} else {
162161
parameters.data = this._getDataToSend(parameters);
163162
}
164-
this._doRequest(parameters, callback);
163+
return this._doRequest(parameters, callback);
165164
};
166165

167166
/**
@@ -260,11 +259,49 @@ UHRBase.prototype._createHeaders = function (parameterHeaders) {
260259
* @param {Object} parameters Request parameters.
261260
* @param {Function<Error, Object, string>?} callback Callback on finish
262261
* with error, status object and data.
262+
* @return {Object} Request object with abort method.
263263
* @protected
264264
* @abstract
265265
*/
266266
UHRBase.prototype._doRequest = function (parameters, callback) {
267+
return {
268+
abort: dummy
269+
};
270+
};
267271

272+
/**
273+
* Converts response data according content type.
274+
* @param {string} contentType HTTP content type.
275+
* @param {string} responseData Data from response.
276+
* @returns {string|Object} Converted data.
277+
*/
278+
UHRBase.prototype.convertResponse = function (contentType, responseData) {
279+
contentType = typeof(contentType) === 'string' ?
280+
contentType :
281+
UHRBase.TYPES.PLAIN_TEXT;
282+
var typeAndParameters = contentType.split(';'),
283+
type = String(typeAndParameters[0]).toLowerCase();
284+
285+
switch (type) {
286+
case UHRBase.TYPES.JSON:
287+
var json;
288+
try {
289+
json = JSON.parse(responseData);
290+
} catch (e) {
291+
// nothing to do
292+
}
293+
return json || {};
294+
case UHRBase.TYPES.URL_ENCODED:
295+
var object;
296+
try {
297+
object = querystring.parse(responseData);
298+
} catch (e) {
299+
// nothing to do
300+
}
301+
return object || {};
302+
default:
303+
return responseData;
304+
}
268305
};
269306

270307
/**

lib/client/UHR.js

Lines changed: 77 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
module.exports = UHR;
3434

3535
var UHRBase = require('../UHRBase'),
36+
url = require('url'),
3637
util = require('util');
3738

3839
util.inherits(UHR, UHRBase);
@@ -42,67 +43,116 @@ var NON_SAFE_HEADERS = {
4243
'accept-charset': true
4344
};
4445

46+
var ERROR_CONNECTION = 'Connection error',
47+
ERROR_TIMEOUT = 'Request timeout',
48+
ERROR_ABORTED = 'Request aborted';
49+
4550
/**
4651
* Creates new instance of client-side HTTP(S) request implementation.
47-
* @param {jQuery} $jQuery jQuery library to use AJAX.
52+
* @param {Window} $window Current window object.
4853
* @constructor
4954
*/
50-
function UHR($jQuery) {
55+
function UHR($window) {
5156
UHRBase.call(this);
52-
this.$ = $jQuery;
57+
this.window = $window;
5358
}
5459

5560
/**
56-
* Current instance of jQuery.
57-
* @type {jQuery}
61+
* Current instance of window.
62+
* @type {Window}
5863
*/
59-
UHR.prototype.$ = null;
64+
UHR.prototype.window = null;
6065

6166
/**
6267
* Does request with specified parameters using protocol implementation.
6368
* @param {Object} parameters Request parameters.
6469
* @param {Function} callback Callback on finish.
70+
* @return {Object} Request object with abort method.
6571
* @protected
6672
*/
6773
UHR.prototype._doRequest = function (parameters, callback) {
6874
try {
69-
var ajaxParameters = Object.create(parameters);
70-
ajaxParameters.headers = this._createHeaders(parameters.headers);
71-
ajaxParameters.type = parameters.method;
72-
73-
if (parameters.headers['Content-Type']) {
74-
ajaxParameters.contentType = parameters.headers['Content-Type'];
75-
}
75+
var self = this,
76+
xhrParameters = Object.create(parameters),
77+
urlInfo = url.parse(parameters.url);
78+
xhrParameters.headers = this._createHeaders(parameters.headers);
7679

77-
Object.keys(ajaxParameters.headers)
80+
Object.keys(xhrParameters.headers)
7881
.forEach(function (name) {
7982
if (NON_SAFE_HEADERS.hasOwnProperty(name.toLowerCase())) {
80-
delete ajaxParameters.headers[name];
83+
delete xhrParameters.headers[name];
8184
}
8285
});
8386

84-
this.$.ajax(ajaxParameters)
85-
.done(function (data, textStatus, jqXHR) {
86-
callback(null, getStatusObject(jqXHR), data);
87-
})
88-
.fail(function (jqXHR, textStatus, errorThrown) {
89-
callback(errorThrown, getStatusObject(jqXHR));
87+
if (xhrParameters.data.length > 0 && (
88+
xhrParameters.method === UHRBase.METHODS.GET ||
89+
xhrParameters.method === UHRBase.METHODS.DELETE)) {
90+
xhrParameters.url +=
91+
(!urlInfo.search || urlInfo.search.length === 0 ? '?' : '&') +
92+
xhrParameters.data;
93+
}
94+
95+
var requestError = null,
96+
loginAndPass = String(urlInfo.auth || '').split(':'),
97+
xhr = new this.window.XMLHttpRequest();
98+
99+
xhr.onabort = function () {
100+
requestError = new Error(ERROR_ABORTED);
101+
};
102+
xhr.ontimeout = function () {
103+
requestError = new Error(ERROR_TIMEOUT);
104+
};
105+
xhr.onerror = function () {
106+
requestError = new Error(xhr.statusText || ERROR_CONNECTION);
107+
};
108+
xhr.onloadend = function () {
109+
xhr.onloadend = null;
110+
111+
var statusObject = getStatusObject(xhr),
112+
content = self.convertResponse(
113+
statusObject.headers['content-type'],
114+
xhr.responseText);
115+
callback(requestError, statusObject, content);
116+
};
117+
118+
xhr.open(xhrParameters.method, xhrParameters.url, true,
119+
loginAndPass[0],
120+
loginAndPass[1]);
121+
xhr.timeout = xhrParameters.timeout;
122+
123+
Object.keys(xhrParameters.headers)
124+
.forEach(function (headerName) {
125+
xhr.setRequestHeader(headerName,
126+
xhrParameters.headers[headerName]);
90127
});
91128

129+
xhr.send(xhrParameters.data);
130+
131+
return {
132+
abort: xhr.abort
133+
};
92134
} catch (e) {
93-
callback(e);
135+
callback(e, getStatusObject(), '');
94136
}
95137
};
96138

97139
/**
98140
* Gets state object for specified jQuery XHR object.
99-
* @param {Object} jqXHR jQuery XHR object.
141+
* @param {Object?} xhr XHR object.
100142
* @returns {{code: number, text: string, headers: Object}} Status object.
101143
*/
102-
function getStatusObject(jqXHR) {
144+
function getStatusObject(xhr) {
103145
var headers = {};
104146

105-
jqXHR
147+
if (!xhr) {
148+
return {
149+
code: 0,
150+
text: '',
151+
headers: headers
152+
};
153+
}
154+
155+
xhr
106156
.getAllResponseHeaders()
107157
.split('\n')
108158
.forEach(function (header) {
@@ -120,8 +170,8 @@ function getStatusObject(jqXHR) {
120170
});
121171

122172
return {
123-
code: jqXHR.status,
124-
text: jqXHR.statusText,
173+
code: xhr.status,
174+
text: xhr.statusText,
125175
headers: headers
126176
};
127177
}

0 commit comments

Comments
 (0)