vulcan 0.3.0 → 0.4.0

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.
Files changed (211) hide show
  1. data/bin/vulcan +3 -1
  2. data/lib/vulcan/cli.rb +22 -3
  3. data/lib/vulcan/version.rb +1 -1
  4. data/server/bin/make +1 -3
  5. data/server/lib/spawner.js +9 -4
  6. data/server/node_modules/connect-form/node_modules/formidable/Readme.md +13 -2
  7. data/server/node_modules/connect-form/node_modules/formidable/lib/incoming_form.js +19 -7
  8. data/server/node_modules/connect-form/node_modules/formidable/lib/multipart_parser.js +12 -1
  9. data/server/node_modules/connect-form/node_modules/formidable/package.json +1 -1
  10. data/server/node_modules/connect-form/node_modules/formidable/test/fixture/file/plain.txt +1 -0
  11. data/server/node_modules/connect-form/node_modules/formidable/test/fixture/http/no-filename/generic.http +13 -0
  12. data/server/node_modules/connect-form/node_modules/formidable/test/fixture/http/special-chars-in-filename/osx-chrome-13.http +0 -10
  13. data/server/node_modules/connect-form/node_modules/formidable/test/fixture/http/special-chars-in-filename/xp-chrome-12.http +0 -9
  14. data/server/node_modules/connect-form/node_modules/formidable/test/fixture/js/no-filename.js +3 -0
  15. data/server/node_modules/connect-form/node_modules/formidable/test/legacy/simple/test-multipart-parser.js +1 -1
  16. data/server/node_modules/cradle/lib/cradle.js +0 -1
  17. data/server/node_modules/cradle/package.json +3 -3
  18. data/server/node_modules/cradle/test/cache-test.js +6 -7
  19. data/server/node_modules/cradle/test/cradle-test.js +4 -5
  20. data/server/node_modules/cradle/test/response-test.js +5 -6
  21. data/server/node_modules/express/History.md +53 -1
  22. data/server/node_modules/express/Makefile +3 -9
  23. data/server/node_modules/express/Readme.md +3 -1
  24. data/server/node_modules/express/bin/express +65 -66
  25. data/server/node_modules/express/lib/express.js +1 -1
  26. data/server/node_modules/express/lib/request.js +23 -9
  27. data/server/node_modules/express/lib/response.js +3 -2
  28. data/server/node_modules/express/lib/utils.js +13 -0
  29. data/server/node_modules/express/lib/view/view.js +2 -1
  30. data/server/node_modules/express/lib/view.js +19 -16
  31. data/server/node_modules/express/node_modules/connect/lib/connect.js +1 -1
  32. data/server/node_modules/express/node_modules/connect/lib/http.js +2 -0
  33. data/server/node_modules/express/node_modules/connect/lib/middleware/bodyParser.js +135 -31
  34. data/server/node_modules/express/node_modules/connect/lib/middleware/limit.js +5 -1
  35. data/server/node_modules/express/node_modules/connect/lib/middleware/session.js +2 -2
  36. data/server/node_modules/express/node_modules/connect/lib/middleware/static.js +26 -21
  37. data/server/node_modules/express/node_modules/connect/lib/middleware/staticCache.js +91 -38
  38. data/server/node_modules/express/node_modules/connect/lib/middleware/vhost.js +1 -1
  39. data/server/node_modules/express/node_modules/connect/lib/utils.js +11 -4
  40. data/server/node_modules/express/node_modules/connect/node_modules/formidable/Makefile +14 -0
  41. data/server/node_modules/express/node_modules/connect/node_modules/formidable/Readme.md +286 -0
  42. data/server/node_modules/express/node_modules/connect/node_modules/formidable/TODO +3 -0
  43. data/server/node_modules/express/node_modules/connect/node_modules/formidable/benchmark/bench-multipart-parser.js +70 -0
  44. data/server/node_modules/express/node_modules/connect/node_modules/formidable/example/post.js +43 -0
  45. data/server/node_modules/express/node_modules/connect/node_modules/formidable/example/upload.js +48 -0
  46. data/server/node_modules/express/node_modules/connect/node_modules/formidable/index.js +1 -0
  47. data/server/node_modules/express/node_modules/connect/node_modules/formidable/lib/file.js +61 -0
  48. data/server/node_modules/express/node_modules/connect/node_modules/formidable/lib/incoming_form.js +377 -0
  49. data/server/node_modules/express/node_modules/connect/node_modules/formidable/lib/index.js +3 -0
  50. data/server/node_modules/express/node_modules/connect/node_modules/formidable/lib/multipart_parser.js +312 -0
  51. data/server/node_modules/express/node_modules/connect/node_modules/formidable/lib/querystring_parser.js +25 -0
  52. data/server/node_modules/express/node_modules/connect/node_modules/formidable/lib/util.js +6 -0
  53. data/server/node_modules/express/node_modules/connect/node_modules/formidable/package.json +22 -0
  54. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/common.js +19 -0
  55. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/file/funkyfilename.txt +1 -0
  56. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/file/plain.txt +1 -0
  57. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/no-filename/generic.http +13 -0
  58. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/info.md +3 -0
  59. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/osx-chrome-13.http +26 -0
  60. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/osx-firefox-3.6.http +24 -0
  61. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/osx-safari-5.http +23 -0
  62. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/xp-chrome-12.http +24 -0
  63. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/xp-ie-7.http +22 -0
  64. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/xp-ie-8.http +22 -0
  65. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/xp-safari-5.http +22 -0
  66. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/js/no-filename.js +3 -0
  67. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/js/special-chars-in-filename.js +21 -0
  68. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/multi_video.upload +0 -0
  69. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/multipart.js +72 -0
  70. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/integration/test-fixtures.js +89 -0
  71. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/common.js +24 -0
  72. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/integration/test-multipart-parser.js +80 -0
  73. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-file.js +104 -0
  74. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-incoming-form.js +715 -0
  75. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-multipart-parser.js +50 -0
  76. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-querystring-parser.js +45 -0
  77. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/system/test-multi-video-upload.js +72 -0
  78. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/run.js +2 -0
  79. data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/unit/test-incoming-form.js +63 -0
  80. data/server/node_modules/express/node_modules/connect/node_modules/formidable/tool/record.js +47 -0
  81. data/server/node_modules/express/node_modules/connect/package.json +7 -6
  82. data/server/node_modules/express/node_modules/connect/test.js +44 -31
  83. data/server/node_modules/express/node_modules/mime/mime.js +3 -3
  84. data/server/node_modules/express/node_modules/mime/package.json +21 -10
  85. data/server/node_modules/express/node_modules/mime/test.js +2 -3
  86. data/server/node_modules/express/node_modules/mime/{mime.types → types/mime.types} +0 -0
  87. data/server/node_modules/express/node_modules/mime/{node.types → types/node.types} +17 -0
  88. data/server/node_modules/express/node_modules/mkdirp/LICENSE +21 -0
  89. data/server/node_modules/express/node_modules/mkdirp/README.markdown +21 -0
  90. data/server/node_modules/express/node_modules/mkdirp/examples/pow.js +6 -0
  91. data/server/node_modules/express/node_modules/mkdirp/examples/pow.js.orig +6 -0
  92. data/server/node_modules/express/node_modules/mkdirp/examples/pow.js.rej +19 -0
  93. data/server/node_modules/express/node_modules/mkdirp/index.js +20 -0
  94. data/server/node_modules/express/node_modules/mkdirp/package.json +23 -0
  95. data/server/node_modules/express/node_modules/mkdirp/test/mkdirp.js +28 -0
  96. data/server/node_modules/express/node_modules/mkdirp/test/race.js +41 -0
  97. data/server/node_modules/express/node_modules/mkdirp/test/rel.js +32 -0
  98. data/server/node_modules/express/node_modules/qs/History.md +11 -0
  99. data/server/node_modules/express/node_modules/qs/Makefile +1 -3
  100. data/server/node_modules/express/node_modules/qs/Readme.md +2 -4
  101. data/server/node_modules/express/node_modules/qs/lib/querystring.js +100 -74
  102. data/server/node_modules/express/node_modules/qs/package.json +9 -2
  103. data/server/node_modules/express/node_modules/qs/test/mocha.opts +2 -0
  104. data/server/node_modules/express/node_modules/qs/test/{parse.test.js → parse.js} +1 -2
  105. data/server/node_modules/express/node_modules/qs/test/{stringify.test.js → stringify.js} +0 -0
  106. data/server/node_modules/express/package.json +9 -8
  107. data/server/node_modules/express/testing/foo/app.js +35 -0
  108. data/server/node_modules/express/testing/foo/package.json +9 -0
  109. data/server/node_modules/express/testing/foo/public/stylesheets/style.css +8 -0
  110. data/server/node_modules/express/testing/foo/routes/index.js +10 -0
  111. data/server/node_modules/express/testing/foo/views/index.jade +2 -0
  112. data/server/node_modules/express/testing/foo/views/layout.jade +6 -0
  113. data/server/node_modules/express/testing/index.js +43 -0
  114. data/server/node_modules/express/testing/public/test.txt +2971 -0
  115. data/server/node_modules/express/testing/views/page.html +1 -0
  116. data/server/node_modules/express/testing/views/page.jade +3 -0
  117. data/server/node_modules/express/testing/views/test.md +1 -0
  118. data/server/node_modules/express/testing/views/user/index.jade +1 -0
  119. data/server/node_modules/express/testing/views/user/list.jade +1 -0
  120. data/server/node_modules/node-uuid/README.md +166 -67
  121. data/server/node_modules/node-uuid/benchmark/README.md +53 -0
  122. data/server/node_modules/node-uuid/benchmark/bench.gnu +174 -0
  123. data/server/node_modules/node-uuid/benchmark/bench.sh +34 -0
  124. data/server/node_modules/node-uuid/{test → benchmark}/benchmark-native.c +0 -0
  125. data/server/node_modules/node-uuid/benchmark/benchmark.js +84 -0
  126. data/server/node_modules/node-uuid/package.json +5 -3
  127. data/server/node_modules/node-uuid/test/benchmark-native +0 -0
  128. data/server/node_modules/node-uuid/test/compare_v1.js +63 -0
  129. data/server/node_modules/node-uuid/test/test.html +3 -0
  130. data/server/node_modules/node-uuid/test/test.js +214 -57
  131. data/server/node_modules/node-uuid/uuid.js +225 -56
  132. data/server/node_modules/restler/README.md +20 -7
  133. data/server/node_modules/restler/bin/restler +1 -1
  134. data/server/node_modules/restler/lib/multipartform.js +9 -8
  135. data/server/node_modules/restler/lib/restler.js +64 -22
  136. data/server/node_modules/restler/package.json +2 -2
  137. data/server/node_modules/restler/test/restler.js +22 -2
  138. data/server/node_modules/restler/test/test_helper.js +20 -1
  139. data/server/package.json +1 -2
  140. data/server/web.js +30 -7
  141. metadata +90 -86
  142. data/server/node_modules/cradle/node_modules/vows/LICENSE +0 -20
  143. data/server/node_modules/cradle/node_modules/vows/Makefile +0 -7
  144. data/server/node_modules/cradle/node_modules/vows/README.md +0 -39
  145. data/server/node_modules/cradle/node_modules/vows/bin/vows +0 -515
  146. data/server/node_modules/cradle/node_modules/vows/lib/assert/error.js +0 -27
  147. data/server/node_modules/cradle/node_modules/vows/lib/assert/macros.js +0 -215
  148. data/server/node_modules/cradle/node_modules/vows/lib/assert/utils.js +0 -77
  149. data/server/node_modules/cradle/node_modules/vows/lib/vows/console.js +0 -131
  150. data/server/node_modules/cradle/node_modules/vows/lib/vows/context.js +0 -55
  151. data/server/node_modules/cradle/node_modules/vows/lib/vows/coverage/file.js +0 -29
  152. data/server/node_modules/cradle/node_modules/vows/lib/vows/coverage/fragments/coverage-foot.html +0 -2
  153. data/server/node_modules/cradle/node_modules/vows/lib/vows/coverage/fragments/coverage-head.html +0 -61
  154. data/server/node_modules/cradle/node_modules/vows/lib/vows/coverage/report-html.js +0 -54
  155. data/server/node_modules/cradle/node_modules/vows/lib/vows/coverage/report-json.js +0 -54
  156. data/server/node_modules/cradle/node_modules/vows/lib/vows/coverage/report-plain.js +0 -38
  157. data/server/node_modules/cradle/node_modules/vows/lib/vows/extras.js +0 -28
  158. data/server/node_modules/cradle/node_modules/vows/lib/vows/reporters/dot-matrix.js +0 -67
  159. data/server/node_modules/cradle/node_modules/vows/lib/vows/reporters/json.js +0 -16
  160. data/server/node_modules/cradle/node_modules/vows/lib/vows/reporters/silent.js +0 -8
  161. data/server/node_modules/cradle/node_modules/vows/lib/vows/reporters/spec.js +0 -44
  162. data/server/node_modules/cradle/node_modules/vows/lib/vows/reporters/watch.js +0 -39
  163. data/server/node_modules/cradle/node_modules/vows/lib/vows/reporters/xunit.js +0 -90
  164. data/server/node_modules/cradle/node_modules/vows/lib/vows/suite.js +0 -319
  165. data/server/node_modules/cradle/node_modules/vows/lib/vows.js +0 -193
  166. data/server/node_modules/cradle/node_modules/vows/node_modules/eyes/LICENSE +0 -20
  167. data/server/node_modules/cradle/node_modules/vows/node_modules/eyes/Makefile +0 -4
  168. data/server/node_modules/cradle/node_modules/vows/node_modules/eyes/README.md +0 -72
  169. data/server/node_modules/cradle/node_modules/vows/node_modules/eyes/lib/eyes.js +0 -233
  170. data/server/node_modules/cradle/node_modules/vows/node_modules/eyes/package.json +0 -14
  171. data/server/node_modules/cradle/node_modules/vows/node_modules/eyes/test/eyes-test.js +0 -55
  172. data/server/node_modules/cradle/node_modules/vows/package.json +0 -14
  173. data/server/node_modules/cradle/node_modules/vows/test/assert-test.js +0 -135
  174. data/server/node_modules/cradle/node_modules/vows/test/fixtures/isolate/failing.js +0 -18
  175. data/server/node_modules/cradle/node_modules/vows/test/fixtures/isolate/log.js +0 -18
  176. data/server/node_modules/cradle/node_modules/vows/test/fixtures/isolate/passing.js +0 -17
  177. data/server/node_modules/cradle/node_modules/vows/test/fixtures/isolate/stderr.js +0 -18
  178. data/server/node_modules/cradle/node_modules/vows/test/isolate-test.js +0 -140
  179. data/server/node_modules/cradle/node_modules/vows/test/testInherit.js +0 -133
  180. data/server/node_modules/cradle/node_modules/vows/test/vows-error-test.js +0 -51
  181. data/server/node_modules/cradle/node_modules/vows/test/vows-test.js +0 -374
  182. data/server/node_modules/express/node_modules/mime/index.js +0 -1
  183. data/server/node_modules/express/node_modules/qs/support/expresso/History.md +0 -128
  184. data/server/node_modules/express/node_modules/qs/support/expresso/Makefile +0 -53
  185. data/server/node_modules/express/node_modules/qs/support/expresso/Readme.md +0 -61
  186. data/server/node_modules/express/node_modules/qs/support/expresso/bin/expresso +0 -856
  187. data/server/node_modules/express/node_modules/qs/support/expresso/docs/api.html +0 -1080
  188. data/server/node_modules/express/node_modules/qs/support/expresso/docs/index.html +0 -377
  189. data/server/node_modules/express/node_modules/qs/support/expresso/docs/index.md +0 -290
  190. data/server/node_modules/express/node_modules/qs/support/expresso/docs/layout/foot.html +0 -3
  191. data/server/node_modules/express/node_modules/qs/support/expresso/docs/layout/head.html +0 -42
  192. data/server/node_modules/express/node_modules/qs/support/expresso/lib/bar.js +0 -4
  193. data/server/node_modules/express/node_modules/qs/support/expresso/lib/foo.js +0 -16
  194. data/server/node_modules/express/node_modules/qs/support/expresso/package.json +0 -12
  195. data/server/node_modules/express/node_modules/qs/support/expresso/test/assert.test.js +0 -91
  196. data/server/node_modules/express/node_modules/qs/support/expresso/test/async.test.js +0 -12
  197. data/server/node_modules/express/node_modules/qs/support/expresso/test/bar.test.js +0 -13
  198. data/server/node_modules/express/node_modules/qs/support/expresso/test/foo.test.js +0 -14
  199. data/server/node_modules/express/node_modules/qs/support/expresso/test/http.test.js +0 -146
  200. data/server/node_modules/express/node_modules/qs/support/expresso/test/serial/async.test.js +0 -39
  201. data/server/node_modules/express/node_modules/qs/support/expresso/test/serial/http.test.js +0 -48
  202. data/server/node_modules/express/node_modules/qs/support/should/History.md +0 -22
  203. data/server/node_modules/express/node_modules/qs/support/should/Makefile +0 -6
  204. data/server/node_modules/express/node_modules/qs/support/should/Readme.md +0 -248
  205. data/server/node_modules/express/node_modules/qs/support/should/examples/runner.js +0 -53
  206. data/server/node_modules/express/node_modules/qs/support/should/index.js +0 -2
  207. data/server/node_modules/express/node_modules/qs/support/should/lib/eql.js +0 -91
  208. data/server/node_modules/express/node_modules/qs/support/should/lib/should.js +0 -548
  209. data/server/node_modules/express/node_modules/qs/support/should/package.json +0 -8
  210. data/server/node_modules/express/node_modules/qs/support/should/test/should.test.js +0 -358
  211. data/server/node_modules/node-uuid/test/benchmark.js +0 -27
@@ -10,7 +10,8 @@
10
10
  * Module dependencies.
11
11
  */
12
12
 
13
- var qs = require('qs');
13
+ var qs = require('qs')
14
+ , formidable = require('formidable');
14
15
 
15
16
  /**
16
17
  * Extract the mime type from the given request's
@@ -29,10 +30,9 @@ function mime(req) {
29
30
  /**
30
31
  * Parse request bodies.
31
32
  *
32
- * By default _application/json_ and _application/x-www-form-urlencoded_
33
- * are supported, however you may map `connect.bodyParser.parse[contentType]`
34
- * to a function of your choice to replace existing parsers, or implement
35
- * one for other content-types.
33
+ * By default _application/json_, _application/x-www-form-urlencoded_,
34
+ * and _multipart/form-data_ are supported, however you may map `connect.bodyParser.parse[contentType]`
35
+ * to a function receiving `(req, options, callback)`.
36
36
  *
37
37
  * Examples:
38
38
  *
@@ -43,36 +43,52 @@ function mime(req) {
43
43
  * }
44
44
  * );
45
45
  *
46
- * Since both _json_ and _x-www-form-urlencoded_ are supported by
47
- * default, either of the following requests would result in the response
48
- * of "viewing user tj".
49
- *
50
46
  * $ curl -d 'user[name]=tj' http://localhost/
51
47
  * $ curl -d '{"user":{"name":"tj"}}' -H "Content-Type: application/json" http://localhost/
52
48
  *
49
+ * Multipart req.files:
50
+ *
51
+ * As a security measure files are stored in a separate object, stored
52
+ * as `req.files`. This prevents attacks that may potentially alter
53
+ * filenames, and depending on the application gain access to restricted files.
54
+ *
55
+ * Multipart configuration:
56
+ *
57
+ * The `options` passed are provided to each parser function.
58
+ * The _multipart/form-data_ parser merges these with formidable's
59
+ * IncomingForm object, allowing you to tweak the upload directory,
60
+ * size limits, etc. For example you may wish to retain the file extension
61
+ * and change the upload directory:
62
+ *
63
+ * server.use(bodyParser({ uploadDir: '/www/mysite.com/uploads' }));
64
+ *
65
+ * View [node-formidable](https://github.com/felixge/node-formidable) for more information.
66
+ *
67
+ * If you wish to use formidable directly within your app, and do not
68
+ * desire this behaviour for multipart requests simply remove the
69
+ * parser:
70
+ *
71
+ * delete connect.bodyParser.parse['multipart/form-data'];
72
+ *
73
+ * Or
74
+ *
75
+ * delete express.bodyParser.parse['multipart/form-data'];
76
+ *
77
+ * @param {Object} options
53
78
  * @return {Function}
54
79
  * @api public
55
80
  */
56
81
 
57
- exports = module.exports = function bodyParser(){
82
+ exports = module.exports = function bodyParser(options){
83
+ options = options || {};
58
84
  return function bodyParser(req, res, next) {
85
+ if (req.body) return next();
86
+ req.body = {};
87
+
59
88
  if ('GET' == req.method || 'HEAD' == req.method) return next();
60
89
  var parser = exports.parse[mime(req)];
61
- if (parser && !req.body) {
62
- var data = '';
63
- req.setEncoding('utf8');
64
- req.on('data', function(chunk) { data += chunk; });
65
- req.on('end', function(){
66
- req.rawBody = data;
67
- try {
68
- req.body = data
69
- ? parser(data)
70
- : {};
71
- } catch (err) {
72
- return next(err);
73
- }
74
- next();
75
- });
90
+ if (parser) {
91
+ parser(req, options, next);
76
92
  } else {
77
93
  next();
78
94
  }
@@ -80,13 +96,101 @@ exports = module.exports = function bodyParser(){
80
96
  };
81
97
 
82
98
  /**
83
- * Supported decoders.
99
+ * Parsers.
100
+ */
101
+
102
+ exports.parse = {};
103
+
104
+ /**
105
+ * Parse application/x-www-form-urlencoded.
106
+ */
107
+
108
+ exports.parse['application/x-www-form-urlencoded'] = function(req, options, fn){
109
+ var buf = '';
110
+ req.setEncoding('utf8');
111
+ req.on('data', function(chunk){ buf += chunk });
112
+ req.on('end', function(){
113
+ try {
114
+ req.body = buf.length
115
+ ? qs.parse(buf)
116
+ : {};
117
+ fn();
118
+ } catch (err){
119
+ fn(err);
120
+ }
121
+ });
122
+ };
123
+
124
+ /**
125
+ * Parse application/json.
126
+ */
127
+
128
+ exports.parse['application/json'] = function(req, options, fn){
129
+ var buf = '';
130
+ req.setEncoding('utf8');
131
+ req.on('data', function(chunk){ buf += chunk });
132
+ req.on('end', function(){
133
+ try {
134
+ req.body = buf.length
135
+ ? JSON.parse(buf)
136
+ : {};
137
+ fn();
138
+ } catch (err){
139
+ fn(err);
140
+ }
141
+ });
142
+ };
143
+
144
+ /**
145
+ * Parse multipart/form-data.
84
146
  *
85
- * - application/x-www-form-urlencoded
86
- * - application/json
147
+ * TODO: make multiple support optional
148
+ * TODO: revisit "error" flag if it's a formidable bug
87
149
  */
88
150
 
89
- exports.parse = {
90
- 'application/x-www-form-urlencoded': qs.parse
91
- , 'application/json': JSON.parse
151
+ exports.parse['multipart/form-data'] = function(req, options, fn){
152
+ var form = new formidable.IncomingForm
153
+ , data = {}
154
+ , files = {}
155
+ , done;
156
+
157
+ Object.keys(options).forEach(function(key){
158
+ form[key] = options[key];
159
+ });
160
+
161
+ function ondata(name, val, data){
162
+ if (Array.isArray(data[name])) {
163
+ data[name].push(val);
164
+ } else if (data[name]) {
165
+ data[name] = [data[name], val];
166
+ } else {
167
+ data[name] = val;
168
+ }
169
+ }
170
+
171
+ form.on('field', function(name, val){
172
+ ondata(name, val, data);
173
+ });
174
+
175
+ form.on('file', function(name, val){
176
+ ondata(name, val, files);
177
+ });
178
+
179
+ form.on('error', function(err){
180
+ fn(err);
181
+ done = true;
182
+ });
183
+
184
+ form.on('end', function(){
185
+ if (done) return;
186
+ try {
187
+ req.body = qs.parse(data);
188
+ req.files = qs.parse(files);
189
+ fn();
190
+ } catch (err) {
191
+ fn(err);
192
+ }
193
+ });
194
+
195
+ form.parse(req);
92
196
  };
@@ -43,7 +43,11 @@ module.exports = function limit(bytes){
43
43
  req._limit = true;
44
44
 
45
45
  // limit by content-length
46
- if (len && len > bytes) deny();
46
+ if (len && len > bytes) {
47
+ res.statusCode = 413;
48
+ res.end('Request Entity Too Large');
49
+ return;
50
+ }
47
51
 
48
52
  // limit
49
53
  req.on('data', function(chunk){
@@ -55,10 +55,10 @@ function defaultFingerprint(req) {
55
55
  };
56
56
 
57
57
  /**
58
- * Paths to ignore, defaulting to `/favicon.ico`.
58
+ * Paths to ignore.
59
59
  */
60
60
 
61
- exports.ignore = ['/favicon.ico'];
61
+ exports.ignore = [];
62
62
 
63
63
  /**
64
64
  * Setup session store with the given `options`.
@@ -39,6 +39,7 @@ var fs = require('fs')
39
39
  *
40
40
  * - `maxAge` Browser cache maxAge in milliseconds. defaults to 0
41
41
  * - `hidden` Allow transfer of hidden files. defaults to false
42
+ * - `redirect` Redirect to trailing "/" when the pathname is a dir
42
43
  *
43
44
  * @param {String} root
44
45
  * @param {Object} options
@@ -101,6 +102,7 @@ var send = exports.send = function(req, res, next, options){
101
102
  , head = 'HEAD' == req.method
102
103
  , get = 'GET' == req.method
103
104
  , root = options.root ? normalize(options.root) : null
105
+ , redirect = false === options.redirect ? false : true
104
106
  , getOnly = options.getOnly
105
107
  , fn = options.callback
106
108
  , hidden = options.hidden
@@ -132,7 +134,7 @@ var send = exports.send = function(req, res, next, options){
132
134
  : utils.forbidden(res);
133
135
 
134
136
  // index.html support
135
- if ('/' == path[path.length - 1]) path += 'index.html';
137
+ if (normalize('/') == path[path.length - 1]) path += 'index.html';
136
138
 
137
139
  // "hidden" file
138
140
  if (!hidden && '.' == basename(path)[0]) return next();
@@ -149,13 +151,34 @@ var send = exports.send = function(req, res, next, options){
149
151
  : next(err);
150
152
  // redirect directory in case index.html is present
151
153
  } else if (stat.isDirectory()) {
154
+ if (!redirect) return next();
152
155
  res.statusCode = 301;
153
156
  res.setHeader('Location', url.pathname + '/');
154
157
  res.end('Redirecting to ' + url.pathname + '/');
155
158
  return;
156
159
  }
157
160
 
161
+ // header fields
162
+ if (!res.getHeader('Date')) res.setHeader('Date', new Date().toUTCString());
163
+ if (!res.getHeader('Cache-Control')) res.setHeader('Cache-Control', 'public, max-age=' + (maxAge / 1000));
164
+ if (!res.getHeader('Last-Modified')) res.setHeader('Last-Modified', stat.mtime.toUTCString());
165
+ if (!res.getHeader('ETag')) res.setHeader('ETag', utils.etag(stat));
166
+ if (!res.getHeader('content-type')) {
167
+ var charset = mime.charsets.lookup(type);
168
+ res.setHeader('Content-Type', type + (charset ? '; charset=' + charset : ''));
169
+ }
170
+ res.setHeader('Accept-Ranges', 'bytes');
171
+
172
+ // conditional GET support
173
+ if (utils.conditionalGET(req)) {
174
+ if (!utils.modified(req, res)) {
175
+ req.emit('static');
176
+ return utils.notModified(res);
177
+ }
178
+ }
179
+
158
180
  var opts = {};
181
+ var chunkSize = stat.size;
159
182
 
160
183
  // we have a Range request
161
184
  if (ranges) {
@@ -166,6 +189,7 @@ var send = exports.send = function(req, res, next, options){
166
189
  // TODO: multiple support
167
190
  opts.start = ranges[0].start;
168
191
  opts.end = ranges[0].end;
192
+ chunkSize = opts.end - opts.start + 1;
169
193
  res.statusCode = 206;
170
194
  res.setHeader('Content-Range', 'bytes '
171
195
  + opts.start
@@ -179,27 +203,9 @@ var send = exports.send = function(req, res, next, options){
179
203
  ? fn(new Error('Requested Range Not Satisfiable'))
180
204
  : invalidRange(res);
181
205
  }
182
- // stream the entire file
183
- } else {
184
- res.setHeader('Content-Length', stat.size);
185
- if (!res.getHeader('Cache-Control')) res.setHeader('Cache-Control', 'public, max-age=' + (maxAge / 1000));
186
- if (!res.getHeader('Last-Modified')) res.setHeader('Last-Modified', stat.mtime.toUTCString());
187
- if (!res.getHeader('ETag')) res.setHeader('ETag', utils.etag(stat));
188
-
189
- // conditional GET support
190
- if (utils.conditionalGET(req)) {
191
- if (!utils.modified(req, res)) {
192
- return utils.notModified(res);
193
- }
194
- }
195
206
  }
196
207
 
197
- // header fields
198
- if (!res.getHeader('content-type')) {
199
- var charset = mime.charsets.lookup(type);
200
- res.setHeader('Content-Type', type + (charset ? '; charset=' + charset : ''));
201
- }
202
- res.setHeader('Accept-Ranges', 'bytes');
208
+ res.setHeader('Content-Length', chunkSize);
203
209
 
204
210
  // transfer
205
211
  if (head) return res.end();
@@ -213,7 +219,6 @@ var send = exports.send = function(req, res, next, options){
213
219
  if (fn) {
214
220
  function callback(err) { done || fn(err); done = true }
215
221
  req.on('close', callback);
216
- req.socket.on('error', callback);
217
222
  stream.on('end', callback);
218
223
  }
219
224
  });
@@ -10,6 +10,7 @@
10
10
  */
11
11
 
12
12
  var http = require('http')
13
+ , utils = require('../utils')
13
14
  , Cache = require('../cache')
14
15
  , url = require('url')
15
16
  , fs = require('fs');
@@ -33,17 +34,15 @@ var http = require('http')
33
34
  *
34
35
  * Benchmarks:
35
36
  *
36
- * static(): 2700 rps
37
- * node-static: 5300 rps
38
- * static() + staticCache(): 7500 rps
37
+ * static(): 2700 rps
38
+ * node-static: 5300 rps
39
+ * static() + staticCache(): 7500 rps
39
40
  *
40
41
  * Options:
41
42
  *
42
43
  * - `maxObjects` max cache objects [128]
43
44
  * - `maxLength` max cache object length 256kb
44
45
  *
45
- * TODO: refactore to accept data stores (redis etc)
46
- *
47
46
  * @param {Type} name
48
47
  * @return {Type}
49
48
  * @api public
@@ -56,16 +55,82 @@ module.exports = function staticCache(options){
56
55
 
57
56
  return function staticCache(req, res, next){
58
57
  var path = url.parse(req.url).pathname
58
+ , ranges = req.headers.range
59
59
  , hit = cache.get(path)
60
- , cc = req.headers['cache-control']
61
- , header;
62
-
63
- // cache hit
64
- if (hit && hit.complete) {
65
- // TODO: finish meeee... http caching... throttling etc
66
- header = hit[0];
67
- header.Age = (new Date - hit.createdAt) / 1000 | 0;
68
-
60
+ , hitCC
61
+ , uaCC
62
+ , header
63
+ , age;
64
+
65
+ // cache static
66
+ req.on('static', function(stream){
67
+ var headers = res._headers
68
+ , cc = utils.parseCacheControl(headers['cache-control'] || '')
69
+ , contentLength = headers['content-length']
70
+ , hit;
71
+
72
+ // ignore larger files
73
+ if (!contentLength || contentLength > maxlen) return;
74
+
75
+ // dont cache items we shouldn't be
76
+ if ( cc['no-cache']
77
+ || cc['no-store']
78
+ || cc['private']
79
+ || cc['must-revalidate']) return;
80
+
81
+ // if already in cache then validate
82
+ if (hit = cache.get(path)){
83
+ if (headers.etag == hit[0].etag) {
84
+ hit[0].date = new Date;
85
+ return;
86
+ } else {
87
+ cache.remove(path);
88
+ }
89
+ }
90
+
91
+ // validation notifiactions don't contain a steam
92
+ if (null == stream) return;
93
+
94
+ // add the cache object
95
+ var arr = cache.add(path);
96
+ arr.push(headers);
97
+
98
+ // store the chunks
99
+ stream.on('data', function(chunk){
100
+ arr.push(chunk);
101
+ });
102
+
103
+ // flag it as complete
104
+ stream.on('end', function(){
105
+ arr.complete = true;
106
+ });
107
+ });
108
+
109
+ // cache hit, doesnt support range requests
110
+ if (hit && hit.complete && !ranges) {
111
+ header = utils.merge({}, hit[0]);
112
+ header.Age = age = (new Date - new Date(header.date)) / 1000 | 0;
113
+ header.date = new Date().toUTCString();
114
+
115
+ // parse cache-controls
116
+ hitCC = utils.parseCacheControl(header['cache-control'] || '');
117
+ uaCC = utils.parseCacheControl(req.headers['cache-control'] || '');
118
+
119
+ // check if we must revalidate(bypass)
120
+ if (hitCC['no-cache'] || uaCC['no-cache']) return next();
121
+
122
+ // check freshness of entity
123
+ if (isStale(hitCC, age) || isStale(uaCC, age)) return next();
124
+
125
+ // conditional GET support
126
+ if (utils.conditionalGET(req)) {
127
+ if (!utils.modified(req, res, header)) {
128
+ header['content-length'] = 0;
129
+ res.writeHead(304, header);
130
+ return res.end();
131
+ }
132
+ }
133
+
69
134
  // HEAD support
70
135
  if ('HEAD' == req.method) {
71
136
  header['content-length'] = 0;
@@ -92,31 +157,19 @@ module.exports = function staticCache(options){
92
157
  return write(1);
93
158
  }
94
159
 
95
- // cache static
96
- req.on('static', function(stream){
97
- // ignore larger files
98
- var contentLength = res._headers['content-length'];
99
- if (!contentLength || contentLength > maxlen) return;
100
-
101
- // exists
102
- if (cache.get(path)) return;
103
-
104
- // add the cache object
105
- var arr = cache.add(path);
106
- arr.push(res._headers);
107
-
108
- // store the chunks
109
- stream.on('data', function(chunk){
110
- arr.push(chunk);
111
- });
112
-
113
- // flag it as complete
114
- stream.on('end', function(){
115
- arr.complete = true;
116
- });
117
- });
118
-
119
160
  next();
120
161
  }
121
162
  };
122
163
 
164
+ /**
165
+ * Check if cache item is stale
166
+ *
167
+ * @param {Object} cc
168
+ * @param {Number} age
169
+ * @return {Boolean}
170
+ * @api private
171
+ */
172
+
173
+ function isStale(cc, age) {
174
+ return cc['max-age'] && cc['max-age'] <= age;
175
+ }
@@ -36,7 +36,7 @@ module.exports = function vhost(hostname, server){
36
36
  var host = req.headers.host.split(':')[0];
37
37
  if (req.subdomains = regexp.exec(host)) {
38
38
  req.subdomains = req.subdomains[0].split('.').slice(0, -1);
39
- server.emit("request", req, res, next);
39
+ server.emit("request", req, res);
40
40
  } else {
41
41
  next();
42
42
  }
@@ -403,14 +403,21 @@ exports.parseRange = function(size, str){
403
403
  */
404
404
 
405
405
  exports.parseCacheControl = function(str){
406
- var parts = str.split('=')
407
- , key = parts.shift()
408
- , val = parseInt(parts.shift(), 10)
406
+ var directives = str.split(',')
409
407
  , obj = {};
410
- obj[key] = isNaN(val) ? true : val;
408
+
409
+ for(var i = 0, len = directives.length; i < len; i++) {
410
+ var parts = directives[i].split('=')
411
+ , key = parts.shift().trim()
412
+ , val = parseInt(parts.shift(), 10);
413
+
414
+ obj[key] = isNaN(val) ? true : val;
415
+ }
416
+
411
417
  return obj;
412
418
  };
413
419
 
420
+
414
421
  /**
415
422
  * Convert array-like object to an `Array`.
416
423
  *
@@ -0,0 +1,14 @@
1
+ SHELL := /bin/bash
2
+
3
+ test:
4
+ @./test/run.js
5
+
6
+ build: npm test
7
+
8
+ npm:
9
+ npm install .
10
+
11
+ clean:
12
+ rm test/tmp/*
13
+
14
+ .PHONY: test clean build