server.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /**
  2. * Guzzle node.js test server to return queued responses to HTTP requests and
  3. * expose a RESTful API for enqueueing responses and retrieving the requests
  4. * that have been received.
  5. *
  6. * - Delete all requests that have been received:
  7. * > DELETE /guzzle-server/requests
  8. * > Host: 127.0.0.1:8125
  9. *
  10. * - Enqueue responses
  11. * > PUT /guzzle-server/responses
  12. * > Host: 127.0.0.1:8125
  13. * >
  14. * > [{'status': 200, 'reason': 'OK', 'headers': {}, 'body': '' }]
  15. *
  16. * - Get the received requests
  17. * > GET /guzzle-server/requests
  18. * > Host: 127.0.0.1:8125
  19. *
  20. * < HTTP/1.1 200 OK
  21. * <
  22. * < [{'http_method': 'GET', 'uri': '/', 'headers': {}, 'body': 'string'}]
  23. *
  24. * - Attempt access to the secure area
  25. * > GET /secure/by-digest/qop-auth/guzzle-server/requests
  26. * > Host: 127.0.0.1:8125
  27. *
  28. * < HTTP/1.1 401 Unauthorized
  29. * < WWW-Authenticate: Digest realm="Digest Test", qop="auth", nonce="0796e98e1aeef43141fab2a66bf4521a", algorithm="MD5", stale="false"
  30. * <
  31. * < 401 Unauthorized
  32. *
  33. * - Shutdown the server
  34. * > DELETE /guzzle-server
  35. * > Host: 127.0.0.1:8125
  36. *
  37. * @package Guzzle PHP <http://www.guzzlephp.org>
  38. * @license See the LICENSE file that was distributed with this source code.
  39. */
  40. var http = require('http');
  41. var url = require('url');
  42. /**
  43. * Guzzle node.js server
  44. * @class
  45. */
  46. var GuzzleServer = function(port, log) {
  47. this.port = port;
  48. this.log = log;
  49. this.responses = [];
  50. this.requests = [];
  51. var that = this;
  52. var md5 = function(input) {
  53. var crypto = require('crypto');
  54. var hasher = crypto.createHash('md5');
  55. hasher.update(input);
  56. return hasher.digest('hex');
  57. }
  58. /**
  59. * Node.js HTTP server authentication module.
  60. *
  61. * It is only initialized on demand (by loadAuthentifier). This avoids
  62. * requiring the dependency to http-auth on standard operations, and the
  63. * performance hit at startup.
  64. */
  65. var auth;
  66. /**
  67. * Provides authentication handlers (Basic, Digest).
  68. */
  69. var loadAuthentifier = function(type, options) {
  70. var typeId = type;
  71. if (type == 'digest') {
  72. typeId += '.'+(options && options.qop ? options.qop : 'none');
  73. }
  74. if (!loadAuthentifier[typeId]) {
  75. if (!auth) {
  76. try {
  77. auth = require('http-auth');
  78. } catch (e) {
  79. if (e.code == 'MODULE_NOT_FOUND') {
  80. return;
  81. }
  82. }
  83. }
  84. switch (type) {
  85. case 'digest':
  86. var digestParams = {
  87. realm: 'Digest Test',
  88. login: 'me',
  89. password: 'test'
  90. };
  91. if (options && options.qop) {
  92. digestParams.qop = options.qop;
  93. }
  94. loadAuthentifier[typeId] = auth.digest(digestParams, function(username, callback) {
  95. callback(md5(digestParams.login + ':' + digestParams.realm + ':' + digestParams.password));
  96. });
  97. break
  98. }
  99. }
  100. return loadAuthentifier[typeId];
  101. };
  102. var firewallRequest = function(request, req, res, requestHandlerCallback) {
  103. var securedAreaUriParts = request.uri.match(/^\/secure\/by-(digest)(\/qop-([^\/]*))?(\/.*)$/);
  104. if (securedAreaUriParts) {
  105. var authentifier = loadAuthentifier(securedAreaUriParts[1], { qop: securedAreaUriParts[2] });
  106. if (!authentifier) {
  107. res.writeHead(501, 'HTTP authentication not implemented', { 'Content-Length': 0 });
  108. res.end();
  109. return;
  110. }
  111. authentifier.check(req, res, function(req, res) {
  112. req.url = securedAreaUriParts[4];
  113. requestHandlerCallback(request, req, res);
  114. });
  115. } else {
  116. requestHandlerCallback(request, req, res);
  117. }
  118. };
  119. var controlRequest = function(request, req, res) {
  120. if (req.url == '/guzzle-server/perf') {
  121. res.writeHead(200, 'OK', {'Content-Length': 16});
  122. res.end('Body of response');
  123. } else if (req.method == 'DELETE') {
  124. if (req.url == '/guzzle-server/requests') {
  125. // Clear the received requests
  126. that.requests = [];
  127. res.writeHead(200, 'OK', { 'Content-Length': 0 });
  128. res.end();
  129. if (that.log) {
  130. console.log('Flushing requests');
  131. }
  132. } else if (req.url == '/guzzle-server') {
  133. // Shutdown the server
  134. res.writeHead(200, 'OK', { 'Content-Length': 0, 'Connection': 'close' });
  135. res.end();
  136. if (that.log) {
  137. console.log('Shutting down');
  138. }
  139. that.server.close();
  140. }
  141. } else if (req.method == 'GET') {
  142. if (req.url === '/guzzle-server/requests') {
  143. if (that.log) {
  144. console.log('Sending received requests');
  145. }
  146. // Get received requests
  147. var body = JSON.stringify(that.requests);
  148. res.writeHead(200, 'OK', { 'Content-Length': body.length });
  149. res.end(body);
  150. }
  151. } else if (req.method == 'PUT' && req.url == '/guzzle-server/responses') {
  152. if (that.log) {
  153. console.log('Adding responses...');
  154. }
  155. if (!request.body) {
  156. if (that.log) {
  157. console.log('No response data was provided');
  158. }
  159. res.writeHead(400, 'NO RESPONSES IN REQUEST', { 'Content-Length': 0 });
  160. } else {
  161. that.responses = eval('(' + request.body + ')');
  162. for (var i = 0; i < that.responses.length; i++) {
  163. if (that.responses[i].body) {
  164. that.responses[i].body = new Buffer(that.responses[i].body, 'base64');
  165. }
  166. }
  167. if (that.log) {
  168. console.log(that.responses);
  169. }
  170. res.writeHead(200, 'OK', { 'Content-Length': 0 });
  171. }
  172. res.end();
  173. }
  174. };
  175. var receivedRequest = function(request, req, res) {
  176. if (req.url.indexOf('/guzzle-server') === 0) {
  177. controlRequest(request, req, res);
  178. } else if (req.url.indexOf('/guzzle-server') == -1 && !that.responses.length) {
  179. res.writeHead(500);
  180. res.end('No responses in queue');
  181. } else {
  182. if (that.log) {
  183. console.log('Returning response from queue and adding request');
  184. }
  185. that.requests.push(request);
  186. var response = that.responses.shift();
  187. res.writeHead(response.status, response.reason, response.headers);
  188. res.end(response.body);
  189. }
  190. };
  191. this.start = function() {
  192. that.server = http.createServer(function(req, res) {
  193. var parts = url.parse(req.url, false);
  194. var request = {
  195. http_method: req.method,
  196. scheme: parts.scheme,
  197. uri: parts.pathname,
  198. query_string: parts.query,
  199. headers: req.headers,
  200. version: req.httpVersion,
  201. body: ''
  202. };
  203. // Receive each chunk of the request body
  204. req.addListener('data', function(chunk) {
  205. request.body += chunk;
  206. });
  207. // Called when the request completes
  208. req.addListener('end', function() {
  209. firewallRequest(request, req, res, receivedRequest);
  210. });
  211. });
  212. that.server.listen(this.port, '127.0.0.1');
  213. if (this.log) {
  214. console.log('Server running at http://127.0.0.1:8125/');
  215. }
  216. };
  217. };
  218. // Get the port from the arguments
  219. port = process.argv.length >= 3 ? process.argv[2] : 8125;
  220. log = process.argv.length >= 4 ? process.argv[3] : false;
  221. // Start the server
  222. server = new GuzzleServer(port, log);
  223. server.start();