mongrel 0.3.12.4 → 0.3.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. data/Rakefile +44 -33
  2. data/bin/mongrel_rails +67 -34
  3. data/doc/rdoc/classes/IO.html +10 -10
  4. data/doc/rdoc/classes/IO.src/{M000005.html → M000001.html} +5 -5
  5. data/doc/rdoc/classes/IO.src/{M000006.html → M000002.html} +5 -5
  6. data/doc/rdoc/classes/Kernel.html +10 -10
  7. data/doc/rdoc/classes/Kernel.src/{M000025.html → M000020.html} +5 -5
  8. data/doc/rdoc/classes/Kernel.src/M000021.html +23 -0
  9. data/doc/rdoc/classes/Mongrel.html +25 -4
  10. data/doc/rdoc/classes/Mongrel/CGIWrapper.html +383 -0
  11. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000096.html +11 -5
  12. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000097.html +34 -5
  13. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000098.html +21 -6
  14. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/{M000093.html → M000099.html} +13 -13
  15. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/{M000094.html → M000100.html} +11 -11
  16. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/{M000095.html → M000101.html} +4 -4
  17. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000102.html +18 -0
  18. data/doc/rdoc/classes/Mongrel/{URIClassifier.src/M000084.html → CGIWrapper.src/M000103.html} +5 -5
  19. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000104.html +19 -0
  20. data/doc/rdoc/classes/Mongrel/Camping.html +5 -5
  21. data/doc/rdoc/classes/Mongrel/Camping.src/M000046.html +22 -0
  22. data/doc/rdoc/classes/Mongrel/Camping/CampingHandler.html +10 -10
  23. data/doc/rdoc/classes/Mongrel/Camping/CampingHandler.src/{M000049.html → M000047.html} +4 -4
  24. data/doc/rdoc/classes/Mongrel/Camping/CampingHandler.src/M000048.html +51 -0
  25. data/doc/rdoc/classes/Mongrel/Command.html +13 -0
  26. data/doc/rdoc/classes/Mongrel/Command/Base.html +95 -50
  27. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000024.html +24 -0
  28. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000025.html +42 -0
  29. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000026.html +18 -0
  30. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000027.html +18 -0
  31. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000028.html +18 -0
  32. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000029.html +5 -11
  33. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000030.html +9 -28
  34. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000031.html +5 -5
  35. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000032.html +5 -5
  36. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000033.html +5 -5
  37. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000034.html +11 -9
  38. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000035.html +11 -5
  39. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000036.html +5 -5
  40. data/doc/rdoc/classes/Mongrel/Command/Registry.html +15 -15
  41. data/doc/rdoc/classes/Mongrel/Command/Registry.src/M000037.html +20 -0
  42. data/doc/rdoc/classes/Mongrel/Command/Registry.src/{M000040.html → M000038.html} +11 -11
  43. data/doc/rdoc/classes/Mongrel/Command/Registry.src/M000039.html +37 -7
  44. data/doc/rdoc/classes/Mongrel/Configurator.html +651 -0
  45. data/doc/rdoc/classes/Mongrel/Configurator.src/M000105.html +16 -20
  46. data/doc/rdoc/classes/Mongrel/Configurator.src/M000106.html +18 -5
  47. data/doc/rdoc/classes/Mongrel/Configurator.src/M000107.html +7 -11
  48. data/doc/rdoc/classes/Mongrel/Configurator.src/M000108.html +7 -6
  49. data/doc/rdoc/classes/Mongrel/Configurator.src/M000109.html +10 -9
  50. data/doc/rdoc/classes/Mongrel/Configurator.src/M000110.html +5 -8
  51. data/doc/rdoc/classes/Mongrel/Configurator.src/M000111.html +21 -5
  52. data/doc/rdoc/classes/Mongrel/Configurator.src/M000112.html +6 -15
  53. data/doc/rdoc/classes/Mongrel/Configurator.src/M000113.html +17 -5
  54. data/doc/rdoc/classes/Mongrel/Configurator.src/M000114.html +20 -33
  55. data/doc/rdoc/classes/Mongrel/Configurator.src/M000115.html +5 -5
  56. data/doc/rdoc/classes/Mongrel/Configurator.src/M000116.html +24 -0
  57. data/doc/rdoc/classes/Mongrel/Configurator.src/M000117.html +19 -0
  58. data/doc/rdoc/classes/Mongrel/Configurator.src/M000118.html +22 -0
  59. data/doc/rdoc/classes/Mongrel/Configurator.src/M000119.html +25 -0
  60. data/doc/rdoc/classes/Mongrel/Configurator.src/M000120.html +18 -0
  61. data/doc/rdoc/classes/Mongrel/Configurator.src/M000121.html +35 -0
  62. data/doc/rdoc/classes/Mongrel/Configurator.src/M000122.html +18 -0
  63. data/doc/rdoc/classes/Mongrel/Configurator.src/M000123.html +33 -0
  64. data/doc/rdoc/classes/Mongrel/Configurator.src/M000124.html +18 -0
  65. data/doc/rdoc/classes/Mongrel/Const.html +39 -7
  66. data/doc/rdoc/classes/Mongrel/DeflateFilter.html +181 -0
  67. data/doc/rdoc/classes/Mongrel/DeflateFilter.src/M000094.html +19 -0
  68. data/doc/rdoc/classes/Mongrel/DeflateFilter.src/M000095.html +28 -0
  69. data/doc/rdoc/classes/Mongrel/DirHandler.html +48 -33
  70. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000056.html +21 -0
  71. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000057.html +43 -0
  72. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000058.html +29 -7
  73. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000059.html +50 -29
  74. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000060.html +26 -27
  75. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000061.html +5 -38
  76. data/doc/rdoc/classes/Mongrel/Error404Handler.html +171 -0
  77. data/doc/rdoc/classes/Mongrel/Error404Handler.src/{M000116.html → M000125.html} +4 -4
  78. data/doc/rdoc/classes/Mongrel/Error404Handler.src/{M000117.html → M000126.html} +4 -4
  79. data/doc/rdoc/classes/Mongrel/HeaderOut.html +185 -0
  80. data/doc/rdoc/classes/Mongrel/HeaderOut.src/{M000072.html → M000069.html} +4 -4
  81. data/doc/rdoc/classes/Mongrel/HeaderOut.src/{M000073.html → M000070.html} +4 -4
  82. data/doc/rdoc/classes/Mongrel/HttpHandler.html +170 -0
  83. data/doc/rdoc/classes/Mongrel/HttpHandler.src/{M000074.html → M000075.html} +3 -3
  84. data/doc/rdoc/classes/Mongrel/HttpHandlerPlugin.html +20 -10
  85. data/doc/rdoc/classes/Mongrel/HttpHandlerPlugin.src/{M000027.html → M000022.html} +5 -4
  86. data/doc/rdoc/classes/Mongrel/HttpHandlerPlugin.src/{M000028.html → M000023.html} +3 -3
  87. data/doc/rdoc/classes/Mongrel/HttpParser.html +41 -36
  88. data/doc/rdoc/classes/Mongrel/HttpParser.src/{M000056.html → M000049.html} +6 -5
  89. data/doc/rdoc/classes/Mongrel/HttpParser.src/{M000057.html → M000050.html} +7 -6
  90. data/doc/rdoc/classes/Mongrel/HttpParser.src/M000051.html +7 -6
  91. data/doc/rdoc/classes/Mongrel/HttpParser.src/M000052.html +37 -7
  92. data/doc/rdoc/classes/Mongrel/HttpParser.src/M000053.html +5 -7
  93. data/doc/rdoc/classes/Mongrel/HttpParser.src/M000054.html +6 -22
  94. data/doc/rdoc/classes/Mongrel/HttpParser.src/M000055.html +6 -5
  95. data/doc/rdoc/classes/Mongrel/HttpRequest.html +239 -0
  96. data/doc/rdoc/classes/Mongrel/HttpRequest.src/M000130.html +52 -0
  97. data/doc/rdoc/classes/Mongrel/HttpRequest.src/{M000119.html → M000131.html} +6 -6
  98. data/doc/rdoc/classes/Mongrel/HttpRequest.src/{M000120.html → M000132.html} +6 -6
  99. data/doc/rdoc/classes/Mongrel/HttpRequest.src/{M000121.html → M000133.html} +18 -18
  100. data/doc/rdoc/classes/Mongrel/HttpResponse.html +439 -0
  101. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000076.html +12 -7
  102. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000077.html +7 -12
  103. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000078.html +12 -9
  104. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000079.html +9 -9
  105. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000080.html +9 -10
  106. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000081.html +9 -5
  107. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000082.html +18 -7
  108. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000083.html +8 -5
  109. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000084.html +20 -0
  110. data/doc/rdoc/classes/Mongrel/{HttpServer.src/M000069.html → HttpResponse.src/M000085.html} +7 -5
  111. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000086.html +20 -0
  112. data/doc/rdoc/classes/Mongrel/{Configurator.src/M000101.html → HttpResponse.src/M000087.html} +5 -5
  113. data/doc/rdoc/classes/Mongrel/HttpServer.html +68 -59
  114. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000062.html +25 -0
  115. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000063.html +83 -0
  116. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000064.html +13 -11
  117. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000065.html +50 -53
  118. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000066.html +21 -11
  119. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000067.html +5 -47
  120. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000068.html +9 -15
  121. data/doc/rdoc/classes/Mongrel/Rails/RailsConfigurator.html +16 -16
  122. data/doc/rdoc/classes/Mongrel/Rails/RailsConfigurator.src/M000040.html +39 -0
  123. data/doc/rdoc/classes/Mongrel/Rails/RailsConfigurator.src/{M000043.html → M000041.html} +11 -11
  124. data/doc/rdoc/classes/Mongrel/Rails/RailsConfigurator.src/M000042.html +13 -22
  125. data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.html +17 -17
  126. data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.src/M000043.html +22 -0
  127. data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.src/M000044.html +51 -0
  128. data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.src/M000045.html +10 -9
  129. data/doc/rdoc/classes/Mongrel/StatisticsFilter.html +211 -0
  130. data/doc/rdoc/classes/Mongrel/StatisticsFilter.src/M000127.html +24 -0
  131. data/doc/rdoc/classes/Mongrel/StatisticsFilter.src/M000128.html +24 -0
  132. data/doc/rdoc/classes/Mongrel/StatisticsFilter.src/M000129.html +18 -0
  133. data/doc/rdoc/classes/{Class.html → Mongrel/StatusHandler.html} +42 -45
  134. data/doc/rdoc/classes/Mongrel/StatusHandler.src/M000071.html +18 -0
  135. data/doc/rdoc/classes/Mongrel/StatusHandler.src/M000072.html +24 -0
  136. data/doc/rdoc/classes/Mongrel/StatusHandler.src/M000073.html +42 -0
  137. data/doc/rdoc/classes/Mongrel/StatusHandler.src/M000074.html +20 -0
  138. data/doc/rdoc/classes/Mongrel/StopServer.html +117 -0
  139. data/doc/rdoc/classes/Mongrel/URIClassifier.html +314 -0
  140. data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000088.html +5 -23
  141. data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000089.html +5 -70
  142. data/doc/rdoc/classes/Mongrel/URIClassifier.src/{M000086.html → M000090.html} +0 -0
  143. data/doc/rdoc/classes/Mongrel/URIClassifier.src/{M000087.html → M000091.html} +0 -0
  144. data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000092.html +36 -0
  145. data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000093.html +83 -0
  146. data/doc/rdoc/classes/MongrelDbg.html +25 -25
  147. data/doc/rdoc/classes/MongrelDbg.src/{M000020.html → M000015.html} +5 -5
  148. data/doc/rdoc/classes/MongrelDbg.src/{M000021.html → M000016.html} +6 -6
  149. data/doc/rdoc/classes/MongrelDbg.src/{M000022.html → M000017.html} +8 -8
  150. data/doc/rdoc/classes/MongrelDbg.src/{M000023.html → M000018.html} +7 -7
  151. data/doc/rdoc/classes/MongrelDbg.src/{M000024.html → M000019.html} +4 -4
  152. data/doc/rdoc/classes/ObjectTracker.html +10 -40
  153. data/doc/rdoc/classes/ObjectTracker.src/M000013.html +27 -0
  154. data/doc/rdoc/classes/ObjectTracker.src/M000014.html +44 -0
  155. data/doc/rdoc/classes/RequestLog.html +115 -0
  156. data/doc/rdoc/classes/RequestLog/Access.html +151 -0
  157. data/doc/rdoc/classes/RequestLog/Access.src/{M000122.html → M000134.html} +5 -5
  158. data/doc/rdoc/classes/RequestLog/Files.html +144 -0
  159. data/doc/rdoc/classes/RequestLog/Files.src/{M000123.html → M000135.html} +5 -5
  160. data/doc/rdoc/classes/RequestLog/Objects.html +144 -0
  161. data/doc/rdoc/classes/RequestLog/Objects.src/{M000124.html → M000137.html} +5 -5
  162. data/doc/rdoc/classes/RequestLog/Params.html +144 -0
  163. data/doc/rdoc/classes/RequestLog/Params.src/{M000125.html → M000138.html} +5 -5
  164. data/doc/rdoc/classes/RequestLog/Threads.html +144 -0
  165. data/doc/rdoc/classes/RequestLog/Threads.src/M000136.html +34 -0
  166. data/doc/rdoc/classes/Stats.html +54 -36
  167. data/doc/rdoc/classes/Stats.src/{M000013.html → M000005.html} +6 -5
  168. data/doc/rdoc/classes/Stats.src/M000006.html +23 -0
  169. data/doc/rdoc/classes/Stats.src/M000007.html +26 -0
  170. data/doc/rdoc/classes/Stats.src/M000008.html +18 -0
  171. data/doc/rdoc/classes/Stats.src/M000009.html +5 -6
  172. data/doc/rdoc/classes/Stats.src/M000010.html +5 -10
  173. data/doc/rdoc/classes/Stats.src/M000011.html +10 -13
  174. data/doc/rdoc/classes/Stats.src/M000012.html +7 -5
  175. data/doc/rdoc/classes/TCPServer.html +9 -9
  176. data/doc/rdoc/classes/TCPServer.src/M000003.html +19 -0
  177. data/doc/rdoc/created.rid +1 -1
  178. data/doc/rdoc/files/COPYING.html +1 -1
  179. data/doc/rdoc/files/LICENSE.html +1 -1
  180. data/doc/rdoc/files/README.html +1 -1
  181. data/doc/rdoc/files/ext/http11/http11_c.html +1 -1
  182. data/doc/rdoc/files/lib/mongrel/camping_rb.html +28 -1
  183. data/doc/rdoc/files/lib/mongrel/cgi_rb.html +28 -1
  184. data/doc/rdoc/files/lib/mongrel/command_rb.html +28 -1
  185. data/doc/rdoc/files/lib/mongrel/debug_rb.html +28 -1
  186. data/doc/rdoc/files/lib/mongrel/handlers_rb.html +3 -3
  187. data/doc/rdoc/files/lib/mongrel/init_rb.html +28 -1
  188. data/doc/rdoc/files/lib/mongrel/rails_rb.html +28 -1
  189. data/doc/rdoc/files/lib/mongrel/stats_rb.html +21 -10
  190. data/doc/rdoc/files/lib/mongrel/tcphack_rb.html +23 -4
  191. data/doc/rdoc/files/lib/mongrel_rb.html +33 -1
  192. data/doc/rdoc/fr_class_index.html +65 -0
  193. data/doc/rdoc/fr_file_index.html +40 -0
  194. data/doc/rdoc/fr_method_index.html +164 -0
  195. data/doc/rdoc/index.html +24 -0
  196. data/examples/camping/blog.rb +1 -1
  197. data/examples/camping/tepee.rb +4 -3
  198. data/examples/simpletest.rb +15 -14
  199. data/ext/http11/http11.c +52 -18
  200. data/ext/http11/http11_parser.c +194 -177
  201. data/ext/http11/http11_parser.h +23 -5
  202. data/lib/mongrel.rb +295 -134
  203. data/lib/mongrel/camping.rb +48 -6
  204. data/lib/mongrel/cgi.rb +20 -2
  205. data/lib/mongrel/command.rb +76 -27
  206. data/lib/mongrel/debug.rb +42 -13
  207. data/lib/mongrel/handlers.rb +218 -59
  208. data/lib/mongrel/init.rb +18 -0
  209. data/lib/mongrel/rails.rb +61 -44
  210. data/lib/mongrel/stats.rb +30 -2
  211. data/lib/mongrel/tcphack.rb +18 -0
  212. data/setup.rb +799 -574
  213. data/test/mime.yaml +3 -0
  214. data/test/mongrel.conf +1 -0
  215. data/test/test_command.rb +101 -0
  216. data/test/test_conditional.rb +124 -0
  217. data/test/test_configurator.rb +56 -22
  218. data/test/test_debug.rb +18 -2
  219. data/test/test_handlers.rb +105 -0
  220. data/test/test_http11.rb +27 -9
  221. data/test/test_response.rb +38 -0
  222. data/test/test_stats.rb +21 -2
  223. data/test/test_uriclassifier.rb +18 -0
  224. data/test/test_ws.rb +105 -19
  225. data/test/testhelp.rb +36 -0
  226. data/tools/rakehelp.rb +52 -46
  227. data/tools/trickletest.rb +37 -0
  228. metadata +127 -76
  229. data/doc/rdoc/classes/Class.src/M000001.html +0 -24
  230. data/doc/rdoc/classes/Class.src/M000002.html +0 -62
  231. data/doc/rdoc/classes/Class.src/M000003.html +0 -21
  232. data/doc/rdoc/classes/Class.src/M000004.html +0 -20
  233. data/doc/rdoc/classes/Kernel.src/M000026.html +0 -25
  234. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000090.html +0 -24
  235. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000091.html +0 -47
  236. data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000092.html +0 -34
  237. data/doc/rdoc/classes/Mongrel/Camping.src/M000048.html +0 -22
  238. data/doc/rdoc/classes/Mongrel/Camping/CampingHandler.src/M000050.html +0 -27
  239. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000037.html +0 -18
  240. data/doc/rdoc/classes/Mongrel/Command/Base.src/M000038.html +0 -18
  241. data/doc/rdoc/classes/Mongrel/Command/Registry.src/M000041.html +0 -46
  242. data/doc/rdoc/classes/Mongrel/Configurator.src/M000099.html +0 -24
  243. data/doc/rdoc/classes/Mongrel/Configurator.src/M000100.html +0 -23
  244. data/doc/rdoc/classes/Mongrel/Configurator.src/M000102.html +0 -32
  245. data/doc/rdoc/classes/Mongrel/Configurator.src/M000103.html +0 -19
  246. data/doc/rdoc/classes/Mongrel/Configurator.src/M000104.html +0 -31
  247. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000062.html +0 -40
  248. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000063.html +0 -18
  249. data/doc/rdoc/classes/Mongrel/HttpRequest.src/M000118.html +0 -28
  250. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000075.html +0 -26
  251. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000070.html +0 -22
  252. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000071.html +0 -18
  253. data/doc/rdoc/classes/Mongrel/Rails/RailsConfigurator.src/M000044.html +0 -32
  254. data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.src/M000046.html +0 -48
  255. data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.src/M000047.html +0 -23
  256. data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000085.html +0 -18
  257. data/doc/rdoc/classes/ObjectTracker.src/M000016.html +0 -22
  258. data/doc/rdoc/classes/ObjectTracker.src/M000017.html +0 -18
  259. data/doc/rdoc/classes/ObjectTracker.src/M000018.html +0 -18
  260. data/doc/rdoc/classes/ObjectTracker.src/M000019.html +0 -44
  261. data/doc/rdoc/classes/Stats.src/M000014.html +0 -19
  262. data/doc/rdoc/classes/Stats.src/M000015.html +0 -20
  263. data/doc/rdoc/classes/TCPServer.src/M000007.html +0 -19
  264. data/lib/mongrel/#rails.rb# +0 -178
@@ -1,3 +1,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