mongrel 0.3.12.4 → 0.3.13

Sign up to get free protection for your applications and to get access to all the features.
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