mongrel 0.3.12.4 → 0.3.13

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 (264) hide show
  1. data/Rakefile +44 -33
  2. data/bin/mongrel_rails +67 -34
  3. data/doc/rdoc/classes/IO.html +10 -10
  4. data/doc/rdoc/classes/IO.src/{M000005.html → M000001.html} +5 -5
  5. data/doc/rdoc/classes/IO.src/{M000006.html → M000002.html} +5 -5
  6. data/doc/rdoc/classes/Kernel.html +10 -10
  7. data/doc/rdoc/classes/Kernel.src/{M000025.html → M000020.html} +5 -5
  8. data/doc/rdoc/classes/Kernel.src/M000021.html +23 -0
  9. data/doc/rdoc/classes/Mongrel.html +25 -4
  10. data/doc/rdoc/classes/Mongrel/CGIWrapper.html +383 -0
  11. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000096.html +11 -5
  12. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000097.html +34 -5
  13. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000098.html +21 -6
  14. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/{M000093.html → M000099.html} +13 -13
  15. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/{M000094.html → M000100.html} +11 -11
  16. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/{M000095.html → M000101.html} +4 -4
  17. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000102.html +18 -0
  18. data/doc/rdoc/classes/Mongrel/{URIClassifier.src/M000084.html → CGIWrapper.src/M000103.html} +5 -5
  19. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000104.html +19 -0
  20. data/doc/rdoc/classes/Mongrel/Camping.html +5 -5
  21. data/doc/rdoc/classes/Mongrel/Camping.src/M000046.html +22 -0
  22. data/doc/rdoc/classes/Mongrel/Camping/CampingHandler.html +10 -10
  23. data/doc/rdoc/classes/Mongrel/Camping/CampingHandler.src/{M000049.html → M000047.html} +4 -4
  24. data/doc/rdoc/classes/Mongrel/Camping/CampingHandler.src/M000048.html +51 -0
  25. data/doc/rdoc/classes/Mongrel/Command.html +13 -0
  26. data/doc/rdoc/classes/Mongrel/Command/Base.html +95 -50
  27. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000024.html +24 -0
  28. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000025.html +42 -0
  29. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000026.html +18 -0
  30. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000027.html +18 -0
  31. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000028.html +18 -0
  32. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000029.html +5 -11
  33. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000030.html +9 -28
  34. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000031.html +5 -5
  35. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000032.html +5 -5
  36. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000033.html +5 -5
  37. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000034.html +11 -9
  38. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000035.html +11 -5
  39. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000036.html +5 -5
  40. data/doc/rdoc/classes/Mongrel/Command/Registry.html +15 -15
  41. data/doc/rdoc/classes/Mongrel/Command/Registry.src/M000037.html +20 -0
  42. data/doc/rdoc/classes/Mongrel/Command/Registry.src/{M000040.html → M000038.html} +11 -11
  43. data/doc/rdoc/classes/Mongrel/Command/Registry.src/M000039.html +37 -7
  44. data/doc/rdoc/classes/Mongrel/Configurator.html +651 -0
  45. data/doc/rdoc/classes/Mongrel/Configurator.src/M000105.html +16 -20
  46. data/doc/rdoc/classes/Mongrel/Configurator.src/M000106.html +18 -5
  47. data/doc/rdoc/classes/Mongrel/Configurator.src/M000107.html +7 -11
  48. data/doc/rdoc/classes/Mongrel/Configurator.src/M000108.html +7 -6
  49. data/doc/rdoc/classes/Mongrel/Configurator.src/M000109.html +10 -9
  50. data/doc/rdoc/classes/Mongrel/Configurator.src/M000110.html +5 -8
  51. data/doc/rdoc/classes/Mongrel/Configurator.src/M000111.html +21 -5
  52. data/doc/rdoc/classes/Mongrel/Configurator.src/M000112.html +6 -15
  53. data/doc/rdoc/classes/Mongrel/Configurator.src/M000113.html +17 -5
  54. data/doc/rdoc/classes/Mongrel/Configurator.src/M000114.html +20 -33
  55. data/doc/rdoc/classes/Mongrel/Configurator.src/M000115.html +5 -5
  56. data/doc/rdoc/classes/Mongrel/Configurator.src/M000116.html +24 -0
  57. data/doc/rdoc/classes/Mongrel/Configurator.src/M000117.html +19 -0
  58. data/doc/rdoc/classes/Mongrel/Configurator.src/M000118.html +22 -0
  59. data/doc/rdoc/classes/Mongrel/Configurator.src/M000119.html +25 -0
  60. data/doc/rdoc/classes/Mongrel/Configurator.src/M000120.html +18 -0
  61. data/doc/rdoc/classes/Mongrel/Configurator.src/M000121.html +35 -0
  62. data/doc/rdoc/classes/Mongrel/Configurator.src/M000122.html +18 -0
  63. data/doc/rdoc/classes/Mongrel/Configurator.src/M000123.html +33 -0
  64. data/doc/rdoc/classes/Mongrel/Configurator.src/M000124.html +18 -0
  65. data/doc/rdoc/classes/Mongrel/Const.html +39 -7
  66. data/doc/rdoc/classes/Mongrel/DeflateFilter.html +181 -0
  67. data/doc/rdoc/classes/Mongrel/DeflateFilter.src/M000094.html +19 -0
  68. data/doc/rdoc/classes/Mongrel/DeflateFilter.src/M000095.html +28 -0
  69. data/doc/rdoc/classes/Mongrel/DirHandler.html +48 -33
  70. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000056.html +21 -0
  71. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000057.html +43 -0
  72. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000058.html +29 -7
  73. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000059.html +50 -29
  74. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000060.html +26 -27
  75. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000061.html +5 -38
  76. data/doc/rdoc/classes/Mongrel/Error404Handler.html +171 -0
  77. data/doc/rdoc/classes/Mongrel/Error404Handler.src/{M000116.html → M000125.html} +4 -4
  78. data/doc/rdoc/classes/Mongrel/Error404Handler.src/{M000117.html → M000126.html} +4 -4
  79. data/doc/rdoc/classes/Mongrel/HeaderOut.html +185 -0
  80. data/doc/rdoc/classes/Mongrel/HeaderOut.src/{M000072.html → M000069.html} +4 -4
  81. data/doc/rdoc/classes/Mongrel/HeaderOut.src/{M000073.html → M000070.html} +4 -4
  82. data/doc/rdoc/classes/Mongrel/HttpHandler.html +170 -0
  83. data/doc/rdoc/classes/Mongrel/HttpHandler.src/{M000074.html → M000075.html} +3 -3
  84. data/doc/rdoc/classes/Mongrel/HttpHandlerPlugin.html +20 -10
  85. data/doc/rdoc/classes/Mongrel/HttpHandlerPlugin.src/{M000027.html → M000022.html} +5 -4
  86. data/doc/rdoc/classes/Mongrel/HttpHandlerPlugin.src/{M000028.html → M000023.html} +3 -3
  87. data/doc/rdoc/classes/Mongrel/HttpParser.html +41 -36
  88. data/doc/rdoc/classes/Mongrel/HttpParser.src/{M000056.html → M000049.html} +6 -5
  89. data/doc/rdoc/classes/Mongrel/HttpParser.src/{M000057.html → M000050.html} +7 -6
  90. data/doc/rdoc/classes/Mongrel/HttpParser.src/M000051.html +7 -6
  91. data/doc/rdoc/classes/Mongrel/HttpParser.src/M000052.html +37 -7
  92. data/doc/rdoc/classes/Mongrel/HttpParser.src/M000053.html +5 -7
  93. data/doc/rdoc/classes/Mongrel/HttpParser.src/M000054.html +6 -22
  94. data/doc/rdoc/classes/Mongrel/HttpParser.src/M000055.html +6 -5
  95. data/doc/rdoc/classes/Mongrel/HttpRequest.html +239 -0
  96. data/doc/rdoc/classes/Mongrel/HttpRequest.src/M000130.html +52 -0
  97. data/doc/rdoc/classes/Mongrel/HttpRequest.src/{M000119.html → M000131.html} +6 -6
  98. data/doc/rdoc/classes/Mongrel/HttpRequest.src/{M000120.html → M000132.html} +6 -6
  99. data/doc/rdoc/classes/Mongrel/HttpRequest.src/{M000121.html → M000133.html} +18 -18
  100. data/doc/rdoc/classes/Mongrel/HttpResponse.html +439 -0
  101. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000076.html +12 -7
  102. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000077.html +7 -12
  103. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000078.html +12 -9
  104. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000079.html +9 -9
  105. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000080.html +9 -10
  106. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000081.html +9 -5
  107. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000082.html +18 -7
  108. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000083.html +8 -5
  109. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000084.html +20 -0
  110. data/doc/rdoc/classes/Mongrel/{HttpServer.src/M000069.html → HttpResponse.src/M000085.html} +7 -5
  111. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000086.html +20 -0
  112. data/doc/rdoc/classes/Mongrel/{Configurator.src/M000101.html → HttpResponse.src/M000087.html} +5 -5
  113. data/doc/rdoc/classes/Mongrel/HttpServer.html +68 -59
  114. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000062.html +25 -0
  115. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000063.html +83 -0
  116. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000064.html +13 -11
  117. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000065.html +50 -53
  118. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000066.html +21 -11
  119. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000067.html +5 -47
  120. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000068.html +9 -15
  121. data/doc/rdoc/classes/Mongrel/Rails/RailsConfigurator.html +16 -16
  122. data/doc/rdoc/classes/Mongrel/Rails/RailsConfigurator.src/M000040.html +39 -0
  123. data/doc/rdoc/classes/Mongrel/Rails/RailsConfigurator.src/{M000043.html → M000041.html} +11 -11
  124. data/doc/rdoc/classes/Mongrel/Rails/RailsConfigurator.src/M000042.html +13 -22
  125. data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.html +17 -17
  126. data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.src/M000043.html +22 -0
  127. data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.src/M000044.html +51 -0
  128. data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.src/M000045.html +10 -9
  129. data/doc/rdoc/classes/Mongrel/StatisticsFilter.html +211 -0
  130. data/doc/rdoc/classes/Mongrel/StatisticsFilter.src/M000127.html +24 -0
  131. data/doc/rdoc/classes/Mongrel/StatisticsFilter.src/M000128.html +24 -0
  132. data/doc/rdoc/classes/Mongrel/StatisticsFilter.src/M000129.html +18 -0
  133. data/doc/rdoc/classes/{Class.html → Mongrel/StatusHandler.html} +42 -45
  134. data/doc/rdoc/classes/Mongrel/StatusHandler.src/M000071.html +18 -0
  135. data/doc/rdoc/classes/Mongrel/StatusHandler.src/M000072.html +24 -0
  136. data/doc/rdoc/classes/Mongrel/StatusHandler.src/M000073.html +42 -0
  137. data/doc/rdoc/classes/Mongrel/StatusHandler.src/M000074.html +20 -0
  138. data/doc/rdoc/classes/Mongrel/StopServer.html +117 -0
  139. data/doc/rdoc/classes/Mongrel/URIClassifier.html +314 -0
  140. data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000088.html +5 -23
  141. data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000089.html +5 -70
  142. data/doc/rdoc/classes/Mongrel/URIClassifier.src/{M000086.html → M000090.html} +0 -0
  143. data/doc/rdoc/classes/Mongrel/URIClassifier.src/{M000087.html → M000091.html} +0 -0
  144. data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000092.html +36 -0
  145. data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000093.html +83 -0
  146. data/doc/rdoc/classes/MongrelDbg.html +25 -25
  147. data/doc/rdoc/classes/MongrelDbg.src/{M000020.html → M000015.html} +5 -5
  148. data/doc/rdoc/classes/MongrelDbg.src/{M000021.html → M000016.html} +6 -6
  149. data/doc/rdoc/classes/MongrelDbg.src/{M000022.html → M000017.html} +8 -8
  150. data/doc/rdoc/classes/MongrelDbg.src/{M000023.html → M000018.html} +7 -7
  151. data/doc/rdoc/classes/MongrelDbg.src/{M000024.html → M000019.html} +4 -4
  152. data/doc/rdoc/classes/ObjectTracker.html +10 -40
  153. data/doc/rdoc/classes/ObjectTracker.src/M000013.html +27 -0
  154. data/doc/rdoc/classes/ObjectTracker.src/M000014.html +44 -0
  155. data/doc/rdoc/classes/RequestLog.html +115 -0
  156. data/doc/rdoc/classes/RequestLog/Access.html +151 -0
  157. data/doc/rdoc/classes/RequestLog/Access.src/{M000122.html → M000134.html} +5 -5
  158. data/doc/rdoc/classes/RequestLog/Files.html +144 -0
  159. data/doc/rdoc/classes/RequestLog/Files.src/{M000123.html → M000135.html} +5 -5
  160. data/doc/rdoc/classes/RequestLog/Objects.html +144 -0
  161. data/doc/rdoc/classes/RequestLog/Objects.src/{M000124.html → M000137.html} +5 -5
  162. data/doc/rdoc/classes/RequestLog/Params.html +144 -0
  163. data/doc/rdoc/classes/RequestLog/Params.src/{M000125.html → M000138.html} +5 -5
  164. data/doc/rdoc/classes/RequestLog/Threads.html +144 -0
  165. data/doc/rdoc/classes/RequestLog/Threads.src/M000136.html +34 -0
  166. data/doc/rdoc/classes/Stats.html +54 -36
  167. data/doc/rdoc/classes/Stats.src/{M000013.html → M000005.html} +6 -5
  168. data/doc/rdoc/classes/Stats.src/M000006.html +23 -0
  169. data/doc/rdoc/classes/Stats.src/M000007.html +26 -0
  170. data/doc/rdoc/classes/Stats.src/M000008.html +18 -0
  171. data/doc/rdoc/classes/Stats.src/M000009.html +5 -6
  172. data/doc/rdoc/classes/Stats.src/M000010.html +5 -10
  173. data/doc/rdoc/classes/Stats.src/M000011.html +10 -13
  174. data/doc/rdoc/classes/Stats.src/M000012.html +7 -5
  175. data/doc/rdoc/classes/TCPServer.html +9 -9
  176. data/doc/rdoc/classes/TCPServer.src/M000003.html +19 -0
  177. data/doc/rdoc/created.rid +1 -1
  178. data/doc/rdoc/files/COPYING.html +1 -1
  179. data/doc/rdoc/files/LICENSE.html +1 -1
  180. data/doc/rdoc/files/README.html +1 -1
  181. data/doc/rdoc/files/ext/http11/http11_c.html +1 -1
  182. data/doc/rdoc/files/lib/mongrel/camping_rb.html +28 -1
  183. data/doc/rdoc/files/lib/mongrel/cgi_rb.html +28 -1
  184. data/doc/rdoc/files/lib/mongrel/command_rb.html +28 -1
  185. data/doc/rdoc/files/lib/mongrel/debug_rb.html +28 -1
  186. data/doc/rdoc/files/lib/mongrel/handlers_rb.html +3 -3
  187. data/doc/rdoc/files/lib/mongrel/init_rb.html +28 -1
  188. data/doc/rdoc/files/lib/mongrel/rails_rb.html +28 -1
  189. data/doc/rdoc/files/lib/mongrel/stats_rb.html +21 -10
  190. data/doc/rdoc/files/lib/mongrel/tcphack_rb.html +23 -4
  191. data/doc/rdoc/files/lib/mongrel_rb.html +33 -1
  192. data/doc/rdoc/fr_class_index.html +65 -0
  193. data/doc/rdoc/fr_file_index.html +40 -0
  194. data/doc/rdoc/fr_method_index.html +164 -0
  195. data/doc/rdoc/index.html +24 -0
  196. data/examples/camping/blog.rb +1 -1
  197. data/examples/camping/tepee.rb +4 -3
  198. data/examples/simpletest.rb +15 -14
  199. data/ext/http11/http11.c +52 -18
  200. data/ext/http11/http11_parser.c +194 -177
  201. data/ext/http11/http11_parser.h +23 -5
  202. data/lib/mongrel.rb +295 -134
  203. data/lib/mongrel/camping.rb +48 -6
  204. data/lib/mongrel/cgi.rb +20 -2
  205. data/lib/mongrel/command.rb +76 -27
  206. data/lib/mongrel/debug.rb +42 -13
  207. data/lib/mongrel/handlers.rb +218 -59
  208. data/lib/mongrel/init.rb +18 -0
  209. data/lib/mongrel/rails.rb +61 -44
  210. data/lib/mongrel/stats.rb +30 -2
  211. data/lib/mongrel/tcphack.rb +18 -0
  212. data/setup.rb +799 -574
  213. data/test/mime.yaml +3 -0
  214. data/test/mongrel.conf +1 -0
  215. data/test/test_command.rb +101 -0
  216. data/test/test_conditional.rb +124 -0
  217. data/test/test_configurator.rb +56 -22
  218. data/test/test_debug.rb +18 -2
  219. data/test/test_handlers.rb +105 -0
  220. data/test/test_http11.rb +27 -9
  221. data/test/test_response.rb +38 -0
  222. data/test/test_stats.rb +21 -2
  223. data/test/test_uriclassifier.rb +18 -0
  224. data/test/test_ws.rb +105 -19
  225. data/test/testhelp.rb +36 -0
  226. data/tools/rakehelp.rb +52 -46
  227. data/tools/trickletest.rb +37 -0
  228. metadata +127 -76
  229. data/doc/rdoc/classes/Class.src/M000001.html +0 -24
  230. data/doc/rdoc/classes/Class.src/M000002.html +0 -62
  231. data/doc/rdoc/classes/Class.src/M000003.html +0 -21
  232. data/doc/rdoc/classes/Class.src/M000004.html +0 -20
  233. data/doc/rdoc/classes/Kernel.src/M000026.html +0 -25
  234. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000090.html +0 -24
  235. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000091.html +0 -47
  236. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000092.html +0 -34
  237. data/doc/rdoc/classes/Mongrel/Camping.src/M000048.html +0 -22
  238. data/doc/rdoc/classes/Mongrel/Camping/CampingHandler.src/M000050.html +0 -27
  239. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000037.html +0 -18
  240. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000038.html +0 -18
  241. data/doc/rdoc/classes/Mongrel/Command/Registry.src/M000041.html +0 -46
  242. data/doc/rdoc/classes/Mongrel/Configurator.src/M000099.html +0 -24
  243. data/doc/rdoc/classes/Mongrel/Configurator.src/M000100.html +0 -23
  244. data/doc/rdoc/classes/Mongrel/Configurator.src/M000102.html +0 -32
  245. data/doc/rdoc/classes/Mongrel/Configurator.src/M000103.html +0 -19
  246. data/doc/rdoc/classes/Mongrel/Configurator.src/M000104.html +0 -31
  247. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000062.html +0 -40
  248. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000063.html +0 -18
  249. data/doc/rdoc/classes/Mongrel/HttpRequest.src/M000118.html +0 -28
  250. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000075.html +0 -26
  251. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000070.html +0 -22
  252. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000071.html +0 -18
  253. data/doc/rdoc/classes/Mongrel/Rails/RailsConfigurator.src/M000044.html +0 -32
  254. data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.src/M000046.html +0 -48
  255. data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.src/M000047.html +0 -23
  256. data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000085.html +0 -18
  257. data/doc/rdoc/classes/ObjectTracker.src/M000016.html +0 -22
  258. data/doc/rdoc/classes/ObjectTracker.src/M000017.html +0 -18
  259. data/doc/rdoc/classes/ObjectTracker.src/M000018.html +0 -18
  260. data/doc/rdoc/classes/ObjectTracker.src/M000019.html +0 -44
  261. data/doc/rdoc/classes/Stats.src/M000014.html +0 -19
  262. data/doc/rdoc/classes/Stats.src/M000015.html +0 -20
  263. data/doc/rdoc/classes/TCPServer.src/M000007.html +0 -19
  264. data/lib/mongrel/#rails.rb# +0 -178
@@ -1,3 +1,22 @@
1
+ /* Mongrel Web Server - A Mostly Ruby Webserver and Library
2
+ *
3
+ * Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
4
+ *
5
+ * This library is free software; you can redistribute it and/or
6
+ * modify it under the terms of the GNU Lesser General Public
7
+ * License as published by the Free Software Foundation; either
8
+ * version 2.1 of the License, or (at your option) any later version.
9
+ *
10
+ * This library is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ * Lesser General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Lesser General Public
16
+ * License along with this library; if not, write to the Free Software
17
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
+ */
19
+
1
20
  #ifndef http11_parser_h
2
21
  #define http11_parser_h
3
22
 
@@ -12,11 +31,11 @@ typedef void (*field_cb)(void *data, const char *field, size_t flen, const char
12
31
 
13
32
  typedef struct http_parser {
14
33
  int cs;
15
- const char *body_start;
34
+ size_t body_start;
16
35
  int content_len;
17
36
  size_t nread;
18
- const char *mark;
19
- const char *field_start;
37
+ size_t mark;
38
+ size_t field_start;
20
39
  size_t field_len;
21
40
 
22
41
  void *data;
@@ -32,10 +51,9 @@ typedef struct http_parser {
32
51
 
33
52
  int http_parser_init(http_parser *parser);
34
53
  int http_parser_finish(http_parser *parser);
35
- size_t http_parser_execute(http_parser *parser, const char *data, size_t len );
54
+ size_t http_parser_execute(http_parser *parser, const char *data, size_t len, size_t off);
36
55
  int http_parser_has_error(http_parser *parser);
37
56
  int http_parser_is_finished(http_parser *parser);
38
- void http_parser_destroy(http_parser *parser);
39
57
 
40
58
  #define http_parser_nread(parser) (parser)->nread
41
59
 
@@ -1,5 +1,24 @@
1
+ # Mongrel Web Server - A Mostly Ruby Webserver and Library
2
+ #
3
+ # Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
+
1
19
  require 'socket'
2
20
  require 'http11'
21
+ require 'tempfile'
3
22
  require 'thread'
4
23
  require 'stringio'
5
24
  require 'mongrel/cgi'
@@ -8,6 +27,17 @@ require 'mongrel/command'
8
27
  require 'mongrel/tcphack'
9
28
  require 'yaml'
10
29
  require 'time'
30
+ require 'rubygems'
31
+ require 'etc'
32
+
33
+
34
+ begin
35
+ require 'sendfile'
36
+ STDERR.puts "** You have sendfile installed, will use that to serve files."
37
+ rescue Object
38
+ # do nothing
39
+ end
40
+
11
41
 
12
42
  # Mongrel module containing all of the classes (include C extensions) for running
13
43
  # a Mongrel web server. It contains a minimalist HTTP server with just enough
@@ -15,6 +45,8 @@ require 'time'
15
45
  module Mongrel
16
46
 
17
47
  class URIClassifier
48
+ attr_reader :handler_map
49
+
18
50
  # Returns the URIs that have been registered with this classifier so far.
19
51
  # The URIs returned should not be modified as this will cause a memory leak.
20
52
  # You can use this to inspect the contents of the URIClassifier.
@@ -77,7 +109,7 @@ module Mongrel
77
109
  505 => 'HTTP Version not supported'
78
110
  }
79
111
 
80
-
112
+
81
113
 
82
114
  # Frequently used constants when constructing requests or responses. Many times
83
115
  # the constant just refers to a string with the same contents. Using these constants
@@ -93,13 +125,16 @@ module Mongrel
93
125
  # This is the part of the path after the SCRIPT_NAME. URIClassifier will determine this.
94
126
  PATH_INFO="PATH_INFO".freeze
95
127
 
96
- # This is the intial part that your handler is identified as by URIClassifier.
128
+ # This is the initial part that your handler is identified as by URIClassifier.
97
129
  SCRIPT_NAME="SCRIPT_NAME".freeze
98
130
 
99
131
  # The original URI requested by the client. Passed to URIClassifier to build PATH_INFO and SCRIPT_NAME.
100
132
  REQUEST_URI='REQUEST_URI'.freeze
101
133
 
102
- MONGREL_VERSION="0.3.12.4".freeze
134
+ MONGREL_VERSION="0.3.13".freeze
135
+
136
+ # TODO: this use of a base for tempfiles needs to be looked at for security problems
137
+ MONGREL_TMP_BASE="mongrel".freeze
103
138
 
104
139
  # The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
105
140
  ERROR_404_RESPONSE="HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: #{MONGREL_VERSION}\r\n\r\nNOT FOUND".freeze
@@ -116,6 +151,9 @@ module Mongrel
116
151
  # this, but we'd also like to do this as well.
117
152
  MAX_HEADER=1024 * (80 + 32)
118
153
 
154
+ # Maximum request body size before it is moved out of memory and into a tempfile for reading.
155
+ MAX_BODY=MAX_HEADER
156
+
119
157
  # A frozen format for this is about 15% faster
120
158
  STATUS_FORMAT = "HTTP/1.1 %d %s\r\nContent-Length: %d\r\nConnection: close\r\n".freeze
121
159
  CONTENT_TYPE = "Content-Type".freeze
@@ -131,6 +169,8 @@ module Mongrel
131
169
  LINE_END="\r\n".freeze
132
170
  REMOTE_ADDR="REMOTE_ADDR".freeze
133
171
  HTTP_X_FORWARDED_FOR="HTTP_X_FORWARDED_FOR".freeze
172
+ HTTP_IF_UNMODIFIED_SINCE="HTTP_IF_UNMODIFIED_SINCE".freeze
173
+ HTTP_IF_NONE_MATCH="HTTP_IF_NONE_MATCH".freeze
134
174
  end
135
175
 
136
176
 
@@ -140,30 +180,58 @@ module Mongrel
140
180
  # HttpRequest.params Hash that matches common CGI params, and a HttpRequest.body
141
181
  # which is a string containing the request body (raw for now).
142
182
  #
143
- # Mongrel really only supports small-ish request bodies right now since really
144
- # huge ones have to be completely read off the wire and put into a string.
145
- # Later there will be several options for efficiently handling large file
146
- # uploads.
183
+ # The HttpRequest.initialize method will convert any request that is larger than
184
+ # Const::MAX_BODY into a Tempfile and use that as the body. Otherwise it uses
185
+ # a StringIO object. To be safe, you should assume it works like a file.
147
186
  class HttpRequest
148
187
  attr_reader :body, :params
149
188
 
150
189
  # You don't really call this. It's made for you.
151
190
  # Main thing it does is hook up the params, and store any remaining
152
191
  # body data into the HttpRequest.body attribute.
192
+ #
193
+ # TODO: Implement tempfile removal when the request is done.
153
194
  def initialize(params, initial_body, socket)
154
- @body = initial_body || ""
155
195
  @params = params
156
196
  @socket = socket
157
197
 
158
- # now, if the initial_body isn't long enough for the content length we have to fill it
159
- # TODO: adapt for big ass stuff by writing to a temp file
160
- clen = params[Const::CONTENT_LENGTH].to_i
161
- if @body.length < clen
162
- @body << @socket.read(clen - @body.length)
198
+ clen = params[Const::CONTENT_LENGTH].to_i - initial_body.length
199
+
200
+ if clen > Const::MAX_BODY
201
+ @body = Tempfile.new(Const::MONGREL_TMP_BASE)
202
+ @body.binmode
203
+ else
204
+ @body = StringIO.new
163
205
  end
164
206
 
207
+ begin
208
+ @body.write(initial_body)
209
+
210
+ # write the odd sized chunk first
211
+ clen -= @body.write(@socket.read(clen % Const::CHUNK_SIZE))
212
+
213
+ # then stream out nothing but perfectly sized chunks
214
+ while clen > 0
215
+ data = @socket.read(Const::CHUNK_SIZE)
216
+ # have to do it this way since @socket.eof? causes it to block
217
+ raise "Socket closed or read failure" if not data or data.length != Const::CHUNK_SIZE
218
+ clen -= @body.write(data)
219
+ # ASSUME: we are writing to a disk and these writes always write the requested amount
220
+ end
221
+
222
+ # rewind to keep the world happy
223
+ @body.rewind
224
+ rescue Object
225
+ # any errors means we should delete the file, including if the file is dumped
226
+ STDERR.puts "Error reading request: #$!"
227
+ @body.delete if @body.class == Tempfile
228
+ @body = nil # signals that there was a problem
229
+ end
165
230
  end
166
231
 
232
+ # Performs URI escaping so that you can construct proper
233
+ # query strings faster. Use this rather than the cgi.rb
234
+ # version since it's faster. (Stolen from Camping).
167
235
  def self.escape(s)
168
236
  s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
169
237
  '%'+$1.unpack('H2'*$1.size).join('%').upcase
@@ -171,13 +239,17 @@ module Mongrel
171
239
  end
172
240
 
173
241
 
242
+ # Unescapes a URI escaped string. (Stolen from Camping).
174
243
  def self.unescape(s)
175
244
  s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
176
245
  [$1.delete('%')].pack('H*')
177
246
  }
178
247
  end
179
248
 
180
-
249
+ # Parses a query string by breaking it up at the '&'
250
+ # and ';' characters. You can also use this to parse
251
+ # cookies by changing the characters used in the second
252
+ # parameter (which defaults to '&;'.
181
253
  def self.query_parse(qs, d = '&;')
182
254
  params = {}
183
255
  (qs||'').split(/[#{d}] */n).inject(params) { |h,p|
@@ -195,8 +267,6 @@ module Mongrel
195
267
 
196
268
  return params
197
269
  end
198
-
199
-
200
270
  end
201
271
 
202
272
 
@@ -218,7 +288,6 @@ module Mongrel
218
288
  def[]=(key,value)
219
289
  @out.write(Const::HEADER_FORMAT % [key, value])
220
290
  end
221
-
222
291
  end
223
292
 
224
293
  # Writes and controls your response to the client using the HTTP/1.1 specification.
@@ -253,13 +322,14 @@ module Mongrel
253
322
  class HttpResponse
254
323
  attr_reader :socket
255
324
  attr_reader :body
325
+ attr_writer :body
256
326
  attr_reader :header
257
327
  attr_reader :status
258
328
  attr_writer :status
259
329
  attr_reader :body_sent
260
330
  attr_reader :header_sent
261
331
  attr_reader :status_sent
262
-
332
+
263
333
  def initialize(socket)
264
334
  @socket = socket
265
335
  @body = StringIO.new
@@ -301,8 +371,8 @@ module Mongrel
301
371
 
302
372
  def send_status(content_length=nil)
303
373
  if not @status_sent
304
- content_length ||= @body.length
305
- @socket.write(Const::STATUS_FORMAT % [status, HTTP_STATUS_CODES[@status], content_length])
374
+ content_length ||= @body.length
375
+ write(Const::STATUS_FORMAT % [status, HTTP_STATUS_CODES[@status], content_length])
306
376
  @status_sent = true
307
377
  end
308
378
  end
@@ -310,7 +380,7 @@ module Mongrel
310
380
  def send_header
311
381
  if not @header_sent
312
382
  @header.out.rewind
313
- @socket.write(@header.out.read + Const::LINE_END)
383
+ write(@header.out.read + Const::LINE_END)
314
384
  @header_sent = true
315
385
  end
316
386
  end
@@ -318,14 +388,47 @@ module Mongrel
318
388
  def send_body
319
389
  if not @body_sent
320
390
  @body.rewind
321
- # connection: close is also added to ensure that the client does not pipeline.
322
- @socket.write(@body.read)
391
+ write(@body.read)
323
392
  @body_sent = true
324
393
  end
325
394
  end
326
395
 
396
+ # Appends the contents of +path+ to the response stream. The file is opened for binary
397
+ # reading and written in chunks to the socket. If the
398
+ # <a href="http://rubyforge.org/projects/ruby-sendfile">sendfile</a> library is found,
399
+ # it is used to send the file, often with greater speed and less memory/cpu usage.
400
+ #
401
+ # The presence of ruby-sendfile is determined by @socket.response_to? :sendfile, which means
402
+ # that if you have your own sendfile implementation you can use it without changing this function,
403
+ # just make sure it follows the ruby-sendfile signature.
404
+ def send_file(path)
405
+ File.open(path, "rb") do |f|
406
+ if @socket.respond_to? :sendfile
407
+ begin
408
+ @socket.sendfile(f)
409
+ rescue => details
410
+ socket_error(details)
411
+ end
412
+ else
413
+ while chunk = f.read(Const::CHUNK_SIZE) and chunk.length > 0
414
+ write(chunk)
415
+ end
416
+ end
417
+ @body_send = true
418
+ end
419
+ end
420
+
421
+ def socket_error(details)
422
+ # ignore these since it means the client closed off early
423
+ @socket.close unless @socket.closed?
424
+ done = true
425
+ raise details
426
+ end
427
+
327
428
  def write(data)
328
429
  @socket.write(data)
430
+ rescue => details
431
+ socket_error(details)
329
432
  end
330
433
 
331
434
  # This takes whatever has been done to header and body and then writes it in the
@@ -336,14 +439,19 @@ module Mongrel
336
439
  send_body
337
440
  end
338
441
 
442
+ # Used during error conditions to mark the response as "done" so there isn't any more processing
443
+ # sent to the client.
444
+ def done=(val)
445
+ @status_sent = true
446
+ @header_sent = true
447
+ @body_sent = true
448
+ end
449
+
339
450
  def done
340
451
  (@status_sent and @header_sent and @body_sent)
341
452
  end
342
453
 
343
454
  end
344
-
345
-
346
-
347
455
 
348
456
  # This is the main driver of Mongrel, while the Mognrel::HttpParser and Mongrel::URIClassifier
349
457
  # make up the majority of how the server functions. It's a very simple class that just
@@ -353,7 +461,7 @@ module Mongrel
353
461
  # You use it by doing the following:
354
462
  #
355
463
  # server = HttpServer.new("0.0.0.0", 3000)
356
- # server.register("/stuff", MyNifterHandler.new)
464
+ # server.register("/stuff", MyNiftyHandler.new)
357
465
  # server.run.join
358
466
  #
359
467
  # The last line can be just server.run if you don't want to join the thread used.
@@ -369,6 +477,8 @@ module Mongrel
369
477
  attr_reader :classifier
370
478
  attr_reader :host
371
479
  attr_reader :port
480
+ attr_reader :timeout
481
+ attr_reader :num_processors
372
482
 
373
483
  # Creates a working server on host:port (strange things happen if port isn't a Number).
374
484
  # Use HttpServer::run to start the server and HttpServer.acceptor.join to
@@ -383,6 +493,8 @@ module Mongrel
383
493
  # The timeout parameter is a sleep timeout (in hundredths of a second) that is placed between
384
494
  # socket.accept calls in order to give the server a cheap throttle time. It defaults to 0 and
385
495
  # actually if it is 0 then the sleep is not done at all.
496
+ #
497
+ # TODO: Find out if anyone actually uses the timeout option since it seems to cause problems on FBSD.
386
498
  def initialize(host, port, num_processors=(2**30-1), timeout=0)
387
499
  @socket = TCPServer.new(host, port)
388
500
  @classifier = URIClassifier.new
@@ -393,7 +505,7 @@ module Mongrel
393
505
  @num_processors = num_processors
394
506
  @death_time = 60
395
507
  end
396
-
508
+
397
509
 
398
510
  # Does the majority of the IO processing. It has been written in Ruby using
399
511
  # about 7 different IO processing strategies and no matter how it's done
@@ -406,9 +518,14 @@ module Mongrel
406
518
  params = {}
407
519
 
408
520
  data = client.readpartial(Const::CHUNK_SIZE)
521
+ nparsed = 0
409
522
 
410
- while true
411
- nread = parser.execute(params, data)
523
+ # Assumption: nparsed will always be less since data will get filled with more
524
+ # after each parsing. If it doesn't get more then there was a problem
525
+ # with the read operation on the client socket. Effect is to stop processing when the
526
+ # socket can't fill the buffer for further parsing.
527
+ while nparsed < data.length
528
+ nparsed = parser.execute(params, data, nparsed)
412
529
 
413
530
  if parser.finished?
414
531
  script_name, path_info, handlers = @classifier.resolve(params[Const::REQUEST_URI])
@@ -418,44 +535,49 @@ module Mongrel
418
535
  params[Const::SCRIPT_NAME] = script_name
419
536
  params[Const::REMOTE_ADDR] = params[Const::HTTP_X_FORWARDED_FOR] || client.peeraddr.last
420
537
 
421
- request = HttpRequest.new(params, data[nread ... data.length], client)
538
+ # TODO: Find a faster/better way to carve out the range, preferably without copying.
539
+ request = HttpRequest.new(params, data[nparsed ... data.length] || "", client)
540
+
541
+ # in the case of large file uploads the user could close the socket, so skip those requests
542
+ break if request.body == nil # nil signals from HttpRequest::initialize that the request was aborted
543
+
544
+ # request is good so far, continue processing the response
422
545
  response = HttpResponse.new(client)
423
-
546
+
547
+ # Process each handler in registered order until we run out or one finalizes the response.
424
548
  handlers.each do |handler|
425
549
  handler.process(request, response)
426
- break if response.done
550
+ break if response.done or client.closed?
427
551
  end
428
552
 
429
- if not response.done
553
+ # And finally, if nobody closed the response off, we finalize it.
554
+ unless response.done or client.closed?
430
555
  response.finished
431
556
  end
432
-
433
557
  else
558
+ # Didn't find it, return a stock 404 response.
559
+ # TODO: Implement customer 404 files (but really they should use a real web server).
434
560
  client.write(Const::ERROR_404_RESPONSE)
435
561
  end
436
-
562
+
437
563
  break #done
438
564
  else
439
- # gotta stream and read again until we can get the parser to be character safe
440
- # TODO: make this more efficient since this means we're parsing a lot repeatedly
565
+ # Parser is not done, queue up more data to read and continue parsing
566
+ data << client.readpartial(Const::CHUNK_SIZE)
441
567
  if data.length >= Const::MAX_HEADER
442
568
  raise HttpParserError.new("HEADER is longer than allowed, aborting client early.")
443
569
  end
444
-
445
- parser.reset
446
- data << client.readpartial(Const::CHUNK_SIZE)
447
570
  end
448
571
  end
449
- rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL
572
+ rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF
450
573
  # ignored
451
574
  rescue HttpParserError
452
- STDERR.puts "BAD CLIENT (#{client.peeraddr.last}): #$!"
453
- STDERR.puts "REQUEST DATA: #{data}"
575
+ STDERR.puts "#{Time.now}: BAD CLIENT (#{params[Const::HTTP_X_FORWARDED_FOR] || client.peeraddr.last}): #$!"
454
576
  rescue => details
455
- STDERR.puts "ERROR: #$!"
577
+ STDERR.puts "#{Time.now}: ERROR: #$!"
456
578
  STDERR.puts details.backtrace.join("\n")
457
579
  ensure
458
- client.close
580
+ client.close unless client.closed?
459
581
  end
460
582
  end
461
583
 
@@ -473,7 +595,7 @@ module Mongrel
473
595
  end
474
596
  end
475
597
  end
476
-
598
+
477
599
 
478
600
  # Runs the thing. It returns the thread used so you can "join" it. You can also
479
601
  # access the HttpServer::acceptor attribute to get the thread later.
@@ -494,15 +616,14 @@ module Mongrel
494
616
  thread = Thread.new do
495
617
  process_client(client)
496
618
  end
497
-
619
+
498
620
  thread[:started_on] = Time.now
499
621
  thread.priority=1
500
622
  @workers.add(thread)
501
-
623
+
502
624
  sleep @timeout/100 if @timeout > 0
503
625
  end
504
626
  rescue StopServer
505
- STDERR.puts "Server stopped. Exiting."
506
627
  @socket.close if not @socket.closed?
507
628
  break
508
629
  rescue Errno::EMFILE
@@ -511,13 +632,11 @@ module Mongrel
511
632
  end
512
633
  end
513
634
 
514
- # now that processing is done we feed enough false onto the request queue to get
515
- # each processor to exit and stop processing.
516
-
517
- # finally we wait until the queue is empty (but only about 10 seconds)
635
+ # troll through the threads that are waiting and kill any that take too long
636
+ # TODO: Allow for death time to be set if people ask for it.
518
637
  @death_time = 10
519
638
  shutdown_start = Time.now
520
-
639
+
521
640
  while @workers.list.length > 0
522
641
  waited_for = (Time.now - shutdown_start).ceil
523
642
  STDERR.print "Shutdown waited #{waited_for} for #{@workers.list.length} requests, could take #{@death_time + @timeout} seconds.\r" if @workers.list.length > 0
@@ -529,12 +648,13 @@ module Mongrel
529
648
  return @acceptor
530
649
  end
531
650
 
532
-
651
+
533
652
  # Simply registers a handler with the internal URIClassifier. When the URI is
534
653
  # found in the prefix of a request then your handler's HttpHandler::process method
535
654
  # is called. See Mongrel::URIClassifier#register for more information.
536
655
  #
537
656
  # If you set in_front=true then the passed in handler will be put in front in the list.
657
+ # Otherwise it's placed at the end of the list.
538
658
  def register(uri, handler, in_front=false)
539
659
  script_name, path_info, handlers = @classifier.resolve(uri)
540
660
 
@@ -551,6 +671,8 @@ module Mongrel
551
671
  @classifier.register(uri, [handler])
552
672
  end
553
673
  end
674
+
675
+ handler.listener = self
554
676
  end
555
677
 
556
678
  # Removes any handlers registered at the given URI. See Mongrel::URIClassifier#unregister
@@ -601,6 +723,8 @@ module Mongrel
601
723
  # A major thing about Configurator is that it actually lets you configure
602
724
  # multiple listeners for any hosts and ports you want. These are kept in a
603
725
  # map config.listeners so you can get to them.
726
+ #
727
+ # * :pid_file => Where to write the process ID.
604
728
  class Configurator
605
729
  attr_reader :listeners
606
730
  attr_reader :defaults
@@ -608,31 +732,68 @@ module Mongrel
608
732
 
609
733
  # You pass in initial defaults and then a block to continue configuring.
610
734
  def initialize(defaults={}, &blk)
735
+ @listener = nil
736
+ @listener_name = nil
611
737
  @listeners = {}
612
738
  @defaults = defaults
613
739
  @needs_restart = false
740
+ @pid_file = defaults[:pid_file]
741
+
742
+ change_privilege(@defaults[:user], @defaults[:group])
614
743
 
615
744
  if blk
616
745
  cloaker(&blk).bind(self).call
617
746
  end
618
747
  end
619
748
 
749
+ # Change privilege of the process to specified user and group.
750
+ def change_privilege(user, group)
751
+ begin
752
+ if group
753
+ log "Changing group to #{group}."
754
+ Process::GID.change_privilege(Etc.getgrnam(group).gid)
755
+ end
756
+
757
+ if user
758
+ log "Changing user to #{user}."
759
+ Process::UID.change_privilege(Etc.getpwnam(user).uid)
760
+ end
761
+ rescue Errno::EPERM
762
+ log "FAILED to change user:group #{user}:#{group}: #$!"
763
+ exit 1
764
+ end
765
+ end
766
+
767
+ # Writes the PID file but only if we're on windows.
768
+ def write_pid_file
769
+ if RUBY_PLATFORM !~ /mswin/
770
+ open(@pid_file,"w") {|f| f.write(Process.pid) }
771
+ end
772
+ end
773
+
774
+ # generates a class for cloaking the current self and making the DSL nicer
775
+ def cloaking_class
776
+ class << self
777
+ self
778
+ end
779
+ end
780
+
620
781
  # Do not call this. You were warned.
621
- def cloaker &blk
622
- (class << self; self; end).class_eval do
782
+ def cloaker(&blk)
783
+ cloaking_class.class_eval do
623
784
  define_method :cloaker_, &blk
624
785
  meth = instance_method( :cloaker_ )
625
786
  remove_method :cloaker_
626
787
  meth
627
788
  end
628
789
  end
629
-
790
+
630
791
  # This will resolve the given options against the defaults.
631
792
  # Normally just used internally.
632
793
  def resolve_defaults(options)
633
794
  options.merge(@defaults)
634
795
  end
635
-
796
+
636
797
  # Starts a listener block. This is the only one that actually takes
637
798
  # a block and then you make Configurator.uri calls in order to setup
638
799
  # your URIs and handlers. If you write your Handlers as GemPlugins
@@ -643,9 +804,10 @@ module Mongrel
643
804
  # * :host => Host name to bind.
644
805
  # * :port => Port to bind.
645
806
  # * :num_processors => The maximum number of concurrent threads allowed. (950 default)
646
- # * :timeout => 1/100th of a second timeout between requests. (10 is 1/10th, 0 is not timeout)
807
+ # * :timeout => 1/100th of a second timeout between requests. (10 is 1/10th, 0 is timeout)
647
808
  #
648
809
  def listener(options={},&blk)
810
+ raise "Cannot call listener inside another listener block." if (@listener or @listener_name)
649
811
  ops = resolve_defaults(options)
650
812
  ops[:num_processors] ||= 950
651
813
  ops[:timeout] ||= 0
@@ -653,29 +815,31 @@ module Mongrel
653
815
  @listener = Mongrel::HttpServer.new(ops[:host], ops[:port].to_i, ops[:num_processors].to_i, ops[:timeout].to_i)
654
816
  @listener_name = "#{ops[:host]}:#{ops[:port]}"
655
817
  @listeners[@listener_name] = @listener
656
-
818
+
819
+ # Does the actual cloaking operation to give the new implicit self.
657
820
  if blk
658
821
  cloaker(&blk).bind(self).call
659
822
  end
660
-
661
- # all done processing this listener setup
823
+
824
+ # all done processing this listener setup, reset implicit variables
662
825
  @listener = nil
663
826
  @listener_name = nil
664
827
  end
665
-
666
-
828
+
829
+
667
830
  # Called inside a Configurator.listener block in order to
668
831
  # add URI->handler mappings for that listener. Use this as
669
832
  # many times as you like. It expects the following options
670
833
  # or defaults:
671
834
  #
672
- # * :handler => Handler to use for this location.
835
+ # * :handler => HttpHandler -- Handler to use for this location.
836
+ # * :in_front => true/false -- Rather than appending, it prepends this handler.
673
837
  def uri(location, options={})
674
838
  ops = resolve_defaults(options)
675
839
  @listener.register(location, ops[:handler], in_front=ops[:in_front])
676
840
  end
677
-
678
-
841
+
842
+
679
843
  # Daemonizes the current Ruby script turning all the
680
844
  # listeners into an actual "server" or detached process.
681
845
  # You must call this *before* frameworks that open files
@@ -687,35 +851,33 @@ module Mongrel
687
851
  #
688
852
  # * :cwd => Directory to change to.
689
853
  # * :log_file => Where to write STDOUT and STDERR.
690
- # * :pid_file => Where to write the process ID.
691
854
  #
692
- # It is safe to call this on win32 as it will only require daemons
693
- # if NOT win32.
855
+ # It is safe to call this on win32 as it will only require the daemons
856
+ # gem/library if NOT win32.
694
857
  def daemonize(options={})
695
858
  ops = resolve_defaults(options)
696
859
  # save this for later since daemonize will hose it
697
860
  if RUBY_PLATFORM !~ /mswin/
698
861
  require 'daemons/daemonize'
699
-
862
+
700
863
  Daemonize.daemonize(log_file=File.join(ops[:cwd], ops[:log_file]))
701
-
864
+
702
865
  # change back to the original starting directory
703
866
  Dir.chdir(ops[:cwd])
704
-
705
- open(ops[:pid_file],"w") {|f| f.write(Process.pid) }
867
+
706
868
  else
707
869
  log "WARNING: Win32 does not support daemon mode."
708
870
  end
709
871
  end
710
-
711
-
872
+
873
+
712
874
  # Uses the GemPlugin system to easily load plugins based on their
713
875
  # gem dependencies. You pass in either an :includes => [] or
714
876
  # :excludes => [] setting listing the names of plugins to include
715
- # or exclude from the loading.
877
+ # or exclude from the when determining the dependencies.
716
878
  def load_plugins(options={})
717
879
  ops = resolve_defaults(options)
718
-
880
+
719
881
  load_settings = {}
720
882
  if ops[:includes]
721
883
  ops[:includes].each do |plugin|
@@ -731,14 +893,14 @@ module Mongrel
731
893
 
732
894
  GemPlugin::Manager.instance.load(load_settings)
733
895
  end
734
-
735
-
896
+
897
+
736
898
  # Easy way to load a YAML file and apply default settings.
737
899
  def load_yaml(file, default={})
738
900
  default.merge(YAML.load_file(file))
739
901
  end
740
-
741
-
902
+
903
+
742
904
  # Loads the MIME map file and checks that it is correct
743
905
  # on loading. This is commonly passed to Mongrel::DirHandler
744
906
  # or any framework handler that uses DirHandler to serve files.
@@ -748,14 +910,14 @@ module Mongrel
748
910
  def load_mime_map(file, mime={})
749
911
  # configure any requested mime map
750
912
  mime = load_yaml(file, mime)
751
-
913
+
752
914
  # check all the mime types to make sure they are the right format
753
915
  mime.each {|k,v| log "WARNING: MIME type #{k} must start with '.'" if k.index(".") != 0 }
754
-
916
+
755
917
  return mime
756
918
  end
757
-
758
-
919
+
920
+
759
921
  # Loads and creates a plugin for you based on the given
760
922
  # name and configured with the selected options. The options
761
923
  # are merged with the defaults prior to passing them in.
@@ -763,26 +925,32 @@ module Mongrel
763
925
  ops = resolve_defaults(options)
764
926
  GemPlugin::Manager.instance.create(name, ops)
765
927
  end
766
-
767
-
928
+
929
+
768
930
  # Works like a meta run method which goes through all the
769
931
  # configured listeners. Use the Configurator.join method
770
932
  # to prevent Ruby from exiting until each one is done.
771
933
  def run
772
934
  @listeners.each {|name,s|
773
- log "Running #{name} listener."
774
935
  s.run
775
936
  }
776
-
937
+
938
+ $mongrel_sleeper_thread = Thread.new { loop { sleep 1 } }
777
939
  end
778
-
940
+
779
941
  # Calls .stop on all the configured listeners so they
780
- # stop processing requests (gracefully).
781
- def stop
942
+ # stop processing requests (gracefully). By default it
943
+ # assumes that you don't want to restart and that the pid file
944
+ # should be unlinked on exit.
945
+ def stop(needs_restart=false, unlink_pid_file=true)
782
946
  @listeners.each {|name,s|
783
- log "Stopping #{name} listener."
784
947
  s.stop
785
948
  }
949
+
950
+ @needs_restart = needs_restart
951
+ if unlink_pid_file
952
+ File.unlink @pid_file if (@pid_file and File.exist?(@pid_file))
953
+ end
786
954
  end
787
955
 
788
956
 
@@ -799,25 +967,32 @@ module Mongrel
799
967
  # parameters for each request. This helps you track common problems
800
968
  # found in Rails applications that are either slow or become unresponsive
801
969
  # after a little while.
802
- def debug(location)
970
+ #
971
+ # TODO: Document the optional selections from the what parameter
972
+ def debug(location, what = [:object, :rails, :files, :threads, :params])
803
973
  require 'mongrel/debug'
804
- ObjectTracker.configure
974
+ handlers = {
975
+ :object => "/handlers/requestlog::access",
976
+ :rails => "/handlers/requestlog::files",
977
+ :files => "/handlers/requestlog::objects",
978
+ :threads => "/handlers/requestlog::threads",
979
+ :params => "/handlers/requestlog::params"
980
+ }
981
+
982
+ # turn on the debugging infrastructure, and ObjectTracker is a pig
983
+ ObjectTracker.configure if what.include? :object
805
984
  MongrelDbg.configure
806
- MongrelDbg.begin_trace :objects
807
- MongrelDbg.begin_trace :rails
808
- MongrelDbg.begin_trace :files
809
- MongrelDbg.begin_trace :threads
810
-
811
- uri location, :handler => plugin("/handlers/requestlog::access")
812
- uri location, :handler => plugin("/handlers/requestlog::files")
813
- uri location, :handler => plugin("/handlers/requestlog::objects")
814
- uri location, :handler => plugin("/handlers/requestlog::params")
815
- uri location, :handler => plugin("/handlers/requestlog::threads")
985
+
986
+ # now we roll through each requested debug type, turn it on and load that plugin
987
+ what.each do |type|
988
+ MongrelDbg.begin_trace type
989
+ uri location, :handler => plugin(handlers[type])
990
+ end
816
991
  end
817
992
 
818
993
  # Used to allow you to let users specify their own configurations
819
994
  # inside your Configurator setup. You pass it a script name and
820
- # reads it in and does an eval on the contents passing in the right
995
+ # it reads it in and does an eval on the contents passing in the right
821
996
  # binding so they can put their own Configurator statements.
822
997
  def run_config(script)
823
998
  open(script) {|f| eval(f.read, proc {self}) }
@@ -827,7 +1002,7 @@ module Mongrel
827
1002
  # It only configures if the platform is not win32 and doesn't do
828
1003
  # a HUP signal since this is typically framework specific.
829
1004
  #
830
- # Requires a :pid_file option to indicate a file to delete.
1005
+ # Requires a :pid_file option given to Configurator.new to indicate a file to delete.
831
1006
  # It sets the MongrelConfig.needs_restart attribute if
832
1007
  # the start command should reload. It's up to you to detect this
833
1008
  # and do whatever is needed for a "restart".
@@ -835,33 +1010,20 @@ module Mongrel
835
1010
  # This command is safely ignored if the platform is win32 (with a warning)
836
1011
  def setup_signals(options={})
837
1012
  ops = resolve_defaults(options)
838
-
1013
+
1014
+ # forced shutdown, even if previously restarted (actually just like TERM but for CTRL-C)
1015
+ trap("INT") { log "INT signal received."; stop(need_restart=false) }
1016
+
839
1017
  if RUBY_PLATFORM !~ /mswin/
840
1018
  # graceful shutdown
841
- trap("TERM") {
842
- log "TERM signal received."
843
- stop
844
- File.unlink ops[:pid_file] if File.exist?(ops[:pid_file])
845
- }
846
-
1019
+ trap("TERM") { log "TERM signal received."; stop }
1020
+
847
1021
  # restart
848
- trap("USR2") {
849
- log "USR2 signal received."
850
- stop
851
- File.unlink ops[:pid_file] if File.exist?(ops[:pid_file])
852
- @needs_restart = true
853
- }
854
-
855
- trap("INT") {
856
- log "INT signal received."
857
- stop
858
- File.unlink ops[:pid_file] if File.exist?(ops[:pid_file])
859
- @needs_restart = false
860
- }
861
-
1022
+ trap("USR2") { log "USR2 signal received."; stop(need_restart=true) }
1023
+
862
1024
  log "Signals ready. TERM => stop. USR2 => restart. INT => stop (no restart)."
863
1025
  else
864
- log "WARNING: Win32 does not have signals support."
1026
+ log "Signals ready. INT => stop (no restart)."
865
1027
  end
866
1028
  end
867
1029
 
@@ -869,7 +1031,6 @@ module Mongrel
869
1031
  def log(msg)
870
1032
  STDERR.print "** ", msg, "\n"
871
1033
  end
872
-
873
- end
874
1034
 
1035
+ end
875
1036
  end