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,21 @@
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 'mongrel'
2
20
 
3
21
 
@@ -32,15 +50,39 @@ module Mongrel
32
50
  end
33
51
 
34
52
  def process(request, response)
35
- req = StringIO.new(request.body)
36
- controller = @klass.run(req, request.params)
37
- response.start(controller.status) do |head,out|
38
- controller.headers.each do |k, v|
53
+ controller = @klass.run(request.body, request.params)
54
+ sendfile, clength = nil
55
+ response.status = controller.status
56
+ controller.headers.each do |k, v|
57
+ if k =~ /^X-SENDFILE$/i
58
+ sendfile = v
59
+ elsif k =~ /^CONTENT-LENGTH$/i
60
+ clength = v.to_i
61
+ else
39
62
  [*v].each do |vi|
40
- head[k] = vi
63
+ response.header[k] = vi
41
64
  end
42
65
  end
43
- out << controller.body
66
+ end
67
+
68
+ if sendfile
69
+ response.send_status(File.size(sendfile))
70
+ response.send_header
71
+ response.send_file(sendfile)
72
+ elsif controller.body.respond_to? :read
73
+ response.send_status(clength)
74
+ response.send_header
75
+ while chunk = controller.body.read(16384)
76
+ response.write(chunk)
77
+ end
78
+ if controller.body.respond_to? :close
79
+ controller.body.close
80
+ end
81
+ else
82
+ body = controller.body.to_s
83
+ response.send_status(body.length)
84
+ response.send_header
85
+ response.write(body)
44
86
  end
45
87
  end
46
88
  end
@@ -1,3 +1,21 @@
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 'cgi'
2
20
 
3
21
  module Mongrel
@@ -10,7 +28,7 @@ module Mongrel
10
28
  # The CGIWrapper.handler attribute is normally not set and is available for
11
29
  # frameworks that need to get back to the handler. Rails uses this to give
12
30
  # people access to the RailsHandler#files (DirHandler really) so they can
13
- # look-up paths and do other things withthe files managed there.
31
+ # look-up paths and do other things with the files managed there.
14
32
  #
15
33
  # In Rails you can get the real file for a request with:
16
34
  #
@@ -35,7 +53,7 @@ module Mongrel
35
53
  @request = request
36
54
  @response = response
37
55
  @args = *args
38
- @input = StringIO.new(request.body)
56
+ @input = request.body
39
57
  @head = {}
40
58
  @out_called = false
41
59
  super(*args)
@@ -1,3 +1,21 @@
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 'rubygems'
2
20
  require 'singleton'
3
21
  require 'optparse'
@@ -10,11 +28,13 @@ module Mongrel
10
28
 
11
29
  module Command
12
30
 
31
+ BANNER = "Usage: mongrel_rails <command> [options]"
32
+
13
33
  # A Command pattern implementation used to create the set of command available to the user
14
34
  # from Mongrel. The script uses objects which implement this interface to do the
15
35
  # user's bidding.
16
36
  module Base
17
-
37
+
18
38
  attr_reader :valid, :done_validating, :original_args
19
39
 
20
40
  # Called by the implemented command to set the options for that command.
@@ -35,6 +55,7 @@ module Mongrel
35
55
  def initialize(options={})
36
56
  argv = options[:argv] || []
37
57
  @opt = OptionParser.new
58
+ @opt.banner = Mongrel::Command::BANNER
38
59
  @valid = true
39
60
  # this is retarded, but it has to be done this way because -h and -v exit
40
61
  @done_validating = false
@@ -47,35 +68,39 @@ module Mongrel
47
68
  @done_validating = true
48
69
  puts @opt
49
70
  end
50
-
51
- # I need to add my own -v definition to prevent the -h from exiting by default as well.
71
+
72
+ # I need to add my own -v definition to prevent the -v from exiting by default as well.
52
73
  @opt.on_tail("--version", "Show version") do
53
74
  @done_validating = true
54
75
  if VERSION
55
- puts "Version #{MONGREL_VERSION}"
76
+ puts "Version #{Mongrel::Const::MONGREL_VERSION}"
56
77
  end
57
78
  end
58
-
79
+
59
80
  @opt.parse! argv
60
81
  end
61
-
82
+
83
+ def configure
84
+ options []
85
+ end
86
+
62
87
  # Returns true/false depending on whether the command is configured properly.
63
88
  def validate
64
89
  return @valid
65
90
  end
66
-
91
+
67
92
  # Returns a help message. Defaults to OptionParser#help which should be good.
68
93
  def help
69
94
  @opt.help
70
95
  end
71
-
96
+
72
97
  # Runs the command doing it's job. You should implement this otherwise it will
73
98
  # throw a NotImplementedError as a reminder.
74
99
  def run
75
100
  raise NotImplementedError
76
101
  end
77
-
78
-
102
+
103
+
79
104
  # Validates the given expression is true and prints the message if not, exiting.
80
105
  def valid?(exp, message)
81
106
  if not @done_validating and (not exp)
@@ -89,29 +114,49 @@ module Mongrel
89
114
  def valid_exists?(file, message)
90
115
  valid?(file != nil && File.exist?(file), message)
91
116
  end
92
-
93
-
117
+
118
+
94
119
  # Validates that the file is a file and not a directory or something else.
95
120
  def valid_file?(file, message)
96
121
  valid?(file != nil && File.file?(file), message)
97
122
  end
98
-
123
+
99
124
  # Validates that the given directory exists
100
125
  def valid_dir?(file, message)
101
126
  valid?(file != nil && File.directory?(file), message)
102
127
  end
103
128
 
129
+ def valid_user?(user)
130
+ valid?(@group, "You must also specify a group.")
131
+ begin
132
+ Etc.getpwnam(user)
133
+ rescue
134
+ failure "User does not exist: #{user}"
135
+ @valid = false
136
+ end
137
+ end
138
+
139
+ def valid_group?(group)
140
+ valid?(@user, "You must also specify a user.")
141
+ begin
142
+ Etc.getgrnam(group)
143
+ rescue
144
+ failure "Group does not exist: #{group}"
145
+ @valid = false
146
+ end
147
+ end
148
+
104
149
  # Just a simple method to display failure until something better is developed.
105
150
  def failure(message)
106
151
  STDERR.puts "!!! #{message}"
107
152
  end
108
153
  end
109
-
154
+
110
155
  # A Singleton class that manages all of the available commands
111
156
  # and handles running them.
112
157
  class Registry
113
158
  include Singleton
114
-
159
+
115
160
  # Builds a list of possible commands from the Command derivates list
116
161
  def commands
117
162
  pmgr = GemPlugin::Manager.instance
@@ -121,51 +166,55 @@ module Mongrel
121
166
 
122
167
  # Prints a list of available commands.
123
168
  def print_command_list
124
- puts "Available commands are:\n\n"
125
-
169
+ puts "#{Mongrel::Command::BANNER}\nAvailable commands are:\n\n"
170
+
126
171
  self.commands.each do |name|
127
172
  puts " - #{name[1 .. -1]}\n"
128
173
  end
129
-
174
+
130
175
  puts "\nEach command takes -h as an option to get help."
131
-
176
+
132
177
  end
133
-
134
-
178
+
179
+
135
180
  # Runs the args against the first argument as the command name.
136
181
  # If it has any errors it returns a false, otherwise it return true.
137
182
  def run(args)
138
183
  # find the command
139
184
  cmd_name = args.shift
140
-
185
+
141
186
  if !cmd_name or cmd_name == "?" or cmd_name == "help"
142
187
  print_command_list
143
188
  return true
189
+ elsif cmd_name == "--version"
190
+ STDERR.puts "Mongrel Web Server #{Mongrel::Const::MONGREL_VERSION}"
191
+ return true
144
192
  end
145
-
193
+
146
194
  # command exists, set it up and validate it
147
195
  begin
148
196
  command = GemPlugin::Manager.instance.create("/commands/#{cmd_name}", :argv => args)
149
197
  rescue
150
198
  STDERR.puts "INVALID COMMAND: #$!"
151
199
  print_command_list
152
- return
200
+ return false
153
201
  end
154
-
202
+
155
203
  # Normally the command is NOT valid right after being created
156
204
  # but sometimes (like with -h or -v) there's no further processing
157
205
  # needed so the command is already valid so we can skip it.
158
206
  if not command.done_validating
159
207
  if not command.validate
160
- STDERR.puts "#{cmd_name} reported an error. Use -h to get help."
208
+ STDERR.puts "#{cmd_name} reported an error. Use mongrel_rails #{cmd_name} -h to get help."
161
209
  return false
162
210
  else
163
211
  command.run
164
212
  end
165
213
  end
214
+
166
215
  return true
167
216
  end
168
-
217
+
169
218
  end
170
219
  end
171
220
  end
@@ -1,3 +1,21 @@
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 'logger'
2
20
  require 'set'
3
21
  require 'socket'
@@ -48,7 +66,12 @@ module ObjectTracker
48
66
  @active_objects = Set.new
49
67
 
50
68
  ObjectSpace.each_object do |obj|
51
- @active_objects << obj.object_id
69
+ begin
70
+ # believe it or not, some idiots actually alter the object_id method
71
+ @active_objects << obj.object_id
72
+ rescue Object
73
+ # skip this one, he's an idiot
74
+ end
52
75
  end
53
76
  end
54
77
 
@@ -56,12 +79,14 @@ module ObjectTracker
56
79
  ospace = Set.new
57
80
  counts = {}
58
81
 
59
- # Strings can't be tracked easily and are so numerous that they drown out all else
60
- # so we just ignore them in the counts.
61
82
  ObjectSpace.each_object do |obj|
62
- ospace << obj.object_id
63
- counts[obj.class] ||= 0
64
- counts[obj.class] += 1
83
+ begin
84
+ ospace << obj.object_id
85
+ counts[obj.class] ||= 0
86
+ counts[obj.class] += 1
87
+ rescue Object
88
+ # skip since object_id can magically get parameters
89
+ end
65
90
  end
66
91
 
67
92
  dead_objects = @active_objects - ospace
@@ -171,15 +196,19 @@ module RequestLog
171
196
  def process(request, response)
172
197
  MongrelDbg::trace(:threads, "#{Time.now} REQUEST #{request.params['PATH_INFO']}")
173
198
  ObjectSpace.each_object do |obj|
174
- if obj.class == Mongrel::HttpServer
175
- worker_list = obj.workers.list
199
+ begin
200
+ if obj.class == Mongrel::HttpServer
201
+ worker_list = obj.workers.list
176
202
 
177
- if worker_list.length > 0
178
- keys = "-----\n\tKEYS:"
179
- worker_list.each {|t| keys << "\n\t\t-- #{t}: #{t.keys.inspect}" }
180
- end
203
+ if worker_list.length > 0
204
+ keys = "-----\n\tKEYS:"
205
+ worker_list.each {|t| keys << "\n\t\t-- #{t}: #{t.keys.inspect}" }
206
+ end
181
207
 
182
- MongrelDbg::trace(:threads, "#{obj.host}:#{obj.port} -- THREADS: #{worker_list.length} #{keys}")
208
+ MongrelDbg::trace(:threads, "#{obj.host}:#{obj.port} -- THREADS: #{worker_list.length} #{keys}")
209
+ end
210
+ rescue Object
211
+ # ignore since obj.class can sometimes take parameters
183
212
  end
184
213
  end
185
214
  end
@@ -1,11 +1,23 @@
1
- require 'rubygems'
2
- begin
3
- require 'sendfile'
4
- $mongrel_has_sendfile = true
5
- STDERR.puts "** You have sendfile installed, will use that to serve files."
6
- rescue Object
7
- $mongrel_has_sendfile = false
8
- end
1
+ require 'mongrel/stats'
2
+ require 'zlib'
3
+
4
+ # Mongrel Web Server - A Mostly Ruby Webserver and Library
5
+ #
6
+ # Copyright (C) 2005 Zed A. Shaw zedshaw AT zedshaw dot com
7
+ #
8
+ # This library is free software; you can redistribute it and/or
9
+ # modify it under the terms of the GNU Lesser General Public
10
+ # License as published by the Free Software Foundation; either
11
+ # version 2.1 of the License, or (at your option) any later version.
12
+ #
13
+ # This library is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
+ # Lesser General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU Lesser General Public
19
+ # License along with this library; if not, write to the Free Software
20
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
9
21
 
10
22
  module Mongrel
11
23
 
@@ -19,6 +31,8 @@ module Mongrel
19
31
  # should be implemented using the HttpHandlerPlugin mixin.
20
32
  #
21
33
  class HttpHandler
34
+ attr_reader :header_only
35
+ attr_accessor :listener
22
36
 
23
37
  def process(request, response)
24
38
  end
@@ -31,9 +45,12 @@ module Mongrel
31
45
  # the process method later.
32
46
  module HttpHandlerPlugin
33
47
  attr_reader :options
48
+ attr_reader :header_only
49
+ attr_accessor :listener
34
50
 
35
51
  def initialize(options={})
36
52
  @options = options
53
+ @header_only = false
37
54
  end
38
55
 
39
56
  def process(request, response)
@@ -52,7 +69,7 @@ module Mongrel
52
69
  def initialize(msg)
53
70
  @response = Const::ERROR_404_RESPONSE + msg
54
71
  end
55
-
72
+
56
73
  # Just kicks back the standard 404 response with your special message.
57
74
  def process(request, response)
58
75
  response.socket.write(@response)
@@ -104,26 +121,27 @@ module Mongrel
104
121
 
105
122
  # Checks if the given path can be served and returns the full path (or nil if not).
106
123
  def can_serve(path_info)
107
- req = File.expand_path(File.join(@path,path_info), @path)
124
+ # TODO: investigate freezing the path_info to prevent double escaping
125
+ req_path = File.expand_path(File.join(@path,HttpRequest.unescape(path_info)), @path)
108
126
 
109
- if req.index(@path) == 0 and File.exist? req
127
+ if req_path.index(@path) == 0 and File.exist? req_path
110
128
  # it exists and it's in the right location
111
- if File.directory? req
129
+ if File.directory? req_path
112
130
  # the request is for a directory
113
- index = File.join(req, @index_html)
131
+ index = File.join(req_path, @index_html)
114
132
  if File.exist? index
115
133
  # serve the index
116
134
  return index
117
135
  elsif @listing_allowed
118
136
  # serve the directory
119
- req
137
+ return req_path
120
138
  else
121
139
  # do not serve anything
122
140
  return nil
123
141
  end
124
142
  else
125
143
  # it's a file and it's there
126
- return req
144
+ return req_path
127
145
  end
128
146
  else
129
147
  # does not exist or isn't in the right spot
@@ -138,6 +156,7 @@ module Mongrel
138
156
  # object to send the results on.
139
157
  def send_dir_listing(base, dir, response)
140
158
  # take off any trailing / so the links come out right
159
+ base = HttpRequest.unescape(base)
141
160
  base.chop! if base[-1] == "/"[-1]
142
161
 
143
162
  if @listing_allowed
@@ -145,12 +164,13 @@ module Mongrel
145
164
  head[Const::CONTENT_TYPE] = "text/html"
146
165
  out << "<html><head><title>Directory Listing</title></head><body>"
147
166
  Dir.entries(dir).each do |child|
167
+ child = HttpRequest.unescape(child)
148
168
  next if child == "."
149
169
 
150
170
  if child == ".."
151
171
  out << "<a href=\"#{base}/#{child}\">Up to parent..</a><br/>"
152
172
  else
153
- out << "<a href=\"#{base}/#{child}\">#{child}</a><br/>"
173
+ out << "<a href=\"#{base}/#{child}/\">#{child}</a><br/>"
154
174
  end
155
175
  end
156
176
  out << "</body></html>"
@@ -162,72 +182,81 @@ module Mongrel
162
182
  end
163
183
  end
164
184
 
165
-
185
+
166
186
  # Sends the contents of a file back to the user. Not terribly efficient since it's
167
187
  # opening and closing the file for each read.
168
- def send_file(req, response, header_only=false)
188
+ def send_file(req_path, request, response, header_only=false)
169
189
 
170
- # first we setup the headers and status then we do a very fast send on the socket directly
171
- response.status = 200
172
- stat = File.stat(req)
173
- header = response.header
190
+ stat = File.stat(req_path)
174
191
 
175
192
  # Set the last modified times as well and etag for all files
176
- header[Const::LAST_MODIFIED] = stat.mtime.httpdate
193
+ mtime = stat.mtime
177
194
  # Calculated the same as apache, not sure how well the works on win32
178
- header[Const::ETAG] = Const::ETAG_FORMAT % [stat.mtime.to_i, stat.size, stat.ino]
179
-
180
- # set the mime type from our map based on the ending
181
- dot_at = req.rindex(".")
182
- if dot_at
183
- header[Const::CONTENT_TYPE] = MIME_TYPES[req[dot_at .. -1]] || @default_content_type
184
- end
195
+ etag = Const::ETAG_FORMAT % [mtime.to_i, stat.size, stat.ino]
196
+
197
+ unmodified_since = request.params[Const::HTTP_IF_UNMODIFIED_SINCE]
198
+ none_match = request.params[Const::HTTP_IF_NONE_MATCH]
199
+
200
+ # test to see if this is a conditional request, and test if
201
+ # the response would be identical to the last response
202
+ same_response = case
203
+ when unmodified_since && !last_response_time = Time.httpdate(unmodified_since) rescue nil : false
204
+ when unmodified_since && last_response_time > Time.now : false
205
+ when unmodified_since && mtime > last_response_time : false
206
+ when none_match && none_match == '*' : false
207
+ when none_match && !none_match.strip.split(/\s*,\s*/).include?(etag) : false
208
+ else unmodified_since || none_match # validation successful if we get this far and at least one of the header exists
209
+ end
210
+
211
+ header = response.header
212
+ header[Const::ETAG] = etag
185
213
 
186
- # send a status with out content length
187
- response.send_status(stat.size)
188
- response.send_header
189
-
190
- if not header_only
191
- begin
192
- if $mongrel_has_sendfile
193
- File.open(req, "rb") { |f| response.socket.sendfile(f) }
194
- else
195
- File.open(req, "rb") { |f| response.socket.write(f.read) }
196
- end
197
- rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF
198
- # ignore these since it means the client closed off early
199
- STDERR.puts "Client closed socket requesting file #{req}: #$!"
200
- end
214
+ if same_response
215
+ response.start(304) {}
201
216
  else
202
- response.send_body # should send nothing
217
+ # first we setup the headers and status then we do a very fast send on the socket directly
218
+ response.status = 200
219
+ header[Const::LAST_MODIFIED] = mtime.httpdate
220
+
221
+ # set the mime type from our map based on the ending
222
+ dot_at = req_path.rindex('.')
223
+ if dot_at
224
+ header[Const::CONTENT_TYPE] = MIME_TYPES[req_path[dot_at .. -1]] || @default_content_type
225
+ end
226
+
227
+ # send a status with out content length
228
+ response.send_status(stat.size)
229
+ response.send_header
230
+
231
+ if not header_only
232
+ response.send_file(req_path)
233
+ end
203
234
  end
204
235
  end
205
236
 
206
-
207
237
  # Process the request to either serve a file or a directory listing
208
- # if allowed (based on the listing_allowed paramter to the constructor).
238
+ # if allowed (based on the listing_allowed parameter to the constructor).
209
239
  def process(request, response)
210
240
  req_method = request.params[Const::REQUEST_METHOD] || Const::GET
211
- req = can_serve request.params[Const::PATH_INFO]
212
- if not req
241
+ req_path = can_serve request.params[Const::PATH_INFO]
242
+ if not req_path
213
243
  # not found, return a 404
214
244
  response.start(404) do |head,out|
215
245
  out << "File not found"
216
246
  end
217
247
  else
218
248
  begin
219
- if File.directory? req
220
- send_dir_listing(request.params[Const::REQUEST_URI],req, response)
249
+ if File.directory? req_path
250
+ send_dir_listing(request.params[Const::REQUEST_URI], req_path, response)
221
251
  elsif req_method == Const::HEAD
222
- send_file(req, response, true)
223
- elsif req_method == Const::GET
224
- send_file(req, response, false)
225
- else
226
- response.start(403) {|head,out| out.write(ONLY_HEAD_GET) }
252
+ send_file(req_path, request, response, true)
253
+ elsif req_method == Const::GET
254
+ send_file(req_path, request, response, false)
255
+ else
256
+ response.start(403) {|head,out| out.write(ONLY_HEAD_GET) }
227
257
  end
228
258
  rescue => details
229
- STDERR.puts "Error accessing file #{req}: #{details}"
230
- STDERR.puts details.backtrace.join("\n")
259
+ STDERR.puts "Error sending file #{req_path}: #{details}"
231
260
  end
232
261
  end
233
262
  end
@@ -239,4 +268,134 @@ module Mongrel
239
268
  end
240
269
 
241
270
  end
271
+
272
+
273
+ # When added to a config script (-S in mongrel_rails) it will
274
+ # look at the client's allowed response types and then gzip
275
+ # compress anything that is going out.
276
+ #
277
+ # Valid option is :always_deflate => false which tells the handler to
278
+ # deflate everything even if the client can't handle it.
279
+ class DeflateFilter < HttpHandler
280
+ HTTP_ACCEPT_ENCODING = "HTTP_ACCEPT_ENCODING"
281
+
282
+ def initialize(ops={})
283
+ @options = ops
284
+ @always_deflate = ops[:always_deflate] || false
285
+ end
286
+
287
+ def process(request, response)
288
+ accepts = request.params[HTTP_ACCEPT_ENCODING]
289
+ # only process if they support compression
290
+ if @always_deflate or (accepts and (accepts.include? "deflate" and not response.body_sent))
291
+ response.header["Content-Encoding"] = "deflate"
292
+ # we can't just rewind the body and gzip it since the body could be an attached file
293
+ response.body.rewind
294
+ gzout = StringIO.new(Zlib::Deflate.deflate(response.body.read))
295
+ gzout.rewind
296
+ response.body.close
297
+ response.body = gzout
298
+ end
299
+ end
300
+ end
301
+
302
+
303
+ # Implements a few basic statistics for a particular URI. Register it anywhere
304
+ # you want in the request chain and it'll quickly gather some numbers for you
305
+ # to analyze. It is pretty fast, but don't put it out in production.
306
+ #
307
+ # You should pass the filter to StatusHandler as StatusHandler.new(:stats_filter => stats).
308
+ # This lets you then hit the status URI you want and get these stats from a browser.
309
+ #
310
+ # StatisticsFilter takes an option of :sample_rate. This is a number that's passed to
311
+ # rand and if that number gets hit then a sample is taken. This helps reduce the load
312
+ # and keeps the statistics valid (since sampling is a part of how they work).
313
+ #
314
+ # The exception to :sample_rate is that inter-request time is sampled on every request.
315
+ # If this wasn't done then it wouldn't be accurate as a measure of time between requests.
316
+ class StatisticsFilter < HttpHandler
317
+ attr_reader :stats
318
+
319
+ def initialize(ops={})
320
+ @sample_rate = ops[:sample_rate] || 300
321
+
322
+ @processors = Stats.new("processors")
323
+ @reqsize = Stats.new("request Kb")
324
+ @headcount = Stats.new("req param count")
325
+ @respsize = Stats.new("response Kb")
326
+ @interreq = Stats.new("inter-request time")
327
+ end
328
+
329
+
330
+ def process(request, response)
331
+ if rand(@sample_rate)+1 == @sample_rate
332
+ @processors.sample(listener.workers.list.length)
333
+ @headcount.sample(request.params.length)
334
+ @reqsize.sample(request.body.length / 1024.0)
335
+ @respsize.sample((response.body.length + response.header.out.length) / 1024.0)
336
+ end
337
+ @interreq.tick
338
+ end
339
+
340
+ def dump
341
+ "#{@processors.to_s}\n#{@reqsize.to_s}\n#{@headcount.to_s}\n#{@respsize.to_s}\n#{@interreq.to_s}"
342
+ end
343
+ end
344
+
345
+
346
+ # The :stats_filter is basically any configured stats filter that you've added to this same
347
+ # URI. This lets the status handler print out statistics on how Mongrel is doing.
348
+ class StatusHandler < HttpHandler
349
+ def initialize(ops={})
350
+ @stats = ops[:stats_filter]
351
+ end
352
+
353
+ def table(title, rows)
354
+ results = "<table border=\"1\"><tr><th colspan=\"#{rows[0].length}\">#{title}</th></tr>"
355
+ rows.each do |cols|
356
+ results << "<tr>"
357
+ cols.each {|col| results << "<td>#{col}</td>" }
358
+ results << "</tr>"
359
+ end
360
+ results + "</table>"
361
+ end
362
+
363
+ def describe_listener
364
+ results = ""
365
+ results << "<h1>Listener #{listener.host}:#{listener.port}</h1>"
366
+ results << table("settings", [
367
+ ["host",listener.host],
368
+ ["port",listener.port],
369
+ ["timeout",listener.timeout],
370
+ ["workers max",listener.num_processors],
371
+ ])
372
+
373
+ if @stats
374
+ results << "<h2>Statistics</h2><p>N means the number of samples, pay attention to MEAN, SD, MIN and MAX."
375
+ results << "<pre>#{@stats.dump}</pre>"
376
+ end
377
+
378
+ results << "<h2>Registered Handlers</h2>"
379
+ uris = listener.classifier.handler_map
380
+ results << table("handlers", uris.map {|uri,handlers|
381
+ [uri,
382
+ "<pre>" +
383
+ handlers.map {|h| h.class.to_s }.join("\n") +
384
+ "</pre>"
385
+ ]
386
+ })
387
+
388
+ results
389
+ end
390
+
391
+ def process(request, response)
392
+ response.start do |head,out|
393
+ out.write <<-END
394
+ <html><body><title>Mongrel Server Status</title>
395
+ #{describe_listener}
396
+ </body></html>
397
+ END
398
+ end
399
+ end
400
+ end
242
401
  end