passenger 3.0.21 → 3.9.1.beta

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of passenger might be problematic. Click here for more details.

Files changed (835) hide show
  1. data/DEVELOPERS.TXT +4 -10
  2. data/NEWS +19 -27
  3. data/Rakefile +20 -19
  4. data/bin/passenger +3 -2
  5. data/bin/passenger-config +35 -5
  6. data/bin/passenger-install-apache2-module +12 -12
  7. data/bin/passenger-install-nginx-module +55 -38
  8. data/bin/passenger-memory-stats +3 -1
  9. data/bin/passenger-status +7 -35
  10. data/build/agents.rb +107 -21
  11. data/build/apache2.rb +11 -46
  12. data/build/basics.rb +61 -9
  13. data/build/common_library.rb +59 -142
  14. data/build/cxx_tests.rb +111 -110
  15. data/build/documentation.rb +33 -0
  16. data/build/misc.rb +30 -12
  17. data/build/nginx.rb +10 -39
  18. data/build/oxt_tests.rb +1 -0
  19. data/build/ruby_extension.rb +1 -5
  20. data/build/test_basics.rb +3 -2
  21. data/dev/copy_boost_headers.rb +2 -1
  22. data/doc/Architectural overview.html +49 -90
  23. data/doc/DebuggingAndStressTesting.txt.md +49 -0
  24. data/doc/Packaging.txt.md +254 -0
  25. data/doc/Security of user switching support.html +35 -66
  26. data/doc/Users guide Apache.html +588 -758
  27. data/doc/Users guide Apache.idmap.txt +253 -136
  28. data/doc/Users guide Apache.txt +154 -109
  29. data/doc/Users guide Nginx.html +544 -660
  30. data/doc/Users guide Nginx.idmap.txt +179 -91
  31. data/doc/Users guide Nginx.txt +192 -118
  32. data/doc/Users guide Standalone.html +65 -48
  33. data/doc/Users guide Standalone.idmap.txt +10 -2
  34. data/doc/Users guide Standalone.txt +4 -0
  35. data/doc/images/glyphicons-halflings-white.png +0 -0
  36. data/doc/images/glyphicons-halflings.png +0 -0
  37. data/doc/images/phusion_banner_small.png +0 -0
  38. data/doc/images/{smart-lv2.png → smart.png} +0 -0
  39. data/doc/images/{smart-lv2.svg → smart.svg} +0 -0
  40. data/doc/templates/bootstrap.min.css +397 -0
  41. data/doc/templates/markdown.html.erb +117 -0
  42. data/doc/users_guide_snippets/analysis_and_system_maintenance.txt +2 -1
  43. data/doc/users_guide_snippets/appendix_c_spawning_methods.txt +26 -48
  44. data/doc/users_guide_snippets/passenger_spawn_method.txt +18 -30
  45. data/doc/users_guide_snippets/support_information.txt +30 -0
  46. data/ext/apache2/Bucket.cpp +9 -26
  47. data/ext/apache2/Bucket.h +13 -10
  48. data/ext/apache2/Configuration.cpp +70 -58
  49. data/ext/apache2/Configuration.hpp +19 -47
  50. data/ext/apache2/DirectoryMapper.h +7 -7
  51. data/ext/apache2/Hooks.cpp +150 -313
  52. data/ext/boost/algorithm/string/detail/case_conv.hpp +4 -2
  53. data/ext/boost/algorithm/string/detail/find_format.hpp +20 -20
  54. data/ext/boost/algorithm/string/detail/find_format_all.hpp +23 -23
  55. data/ext/boost/algorithm/string/detail/find_format_store.hpp +2 -2
  56. data/ext/boost/algorithm/string/detail/formatter.hpp +25 -0
  57. data/ext/boost/algorithm/string/formatter.hpp +20 -3
  58. data/ext/boost/assert.hpp +85 -4
  59. data/ext/boost/bind/bind.hpp +1 -1
  60. data/ext/boost/concept/detail/backward_compatibility.hpp +1 -1
  61. data/ext/boost/concept_check.hpp +140 -64
  62. data/ext/boost/config.hpp +1 -1
  63. data/ext/boost/config/auto_link.hpp +8 -6
  64. data/ext/boost/config/compiler/borland.hpp +12 -2
  65. data/ext/boost/config/compiler/clang.hpp +89 -30
  66. data/ext/boost/config/compiler/codegear.hpp +3 -2
  67. data/ext/boost/config/compiler/common_edg.hpp +7 -5
  68. data/ext/boost/config/compiler/cray.hpp +61 -0
  69. data/ext/boost/config/compiler/digitalmars.hpp +9 -1
  70. data/ext/boost/config/compiler/gcc.hpp +33 -24
  71. data/ext/boost/config/compiler/gcc_xml.hpp +4 -0
  72. data/ext/boost/config/compiler/hp_acc.hpp +12 -1
  73. data/ext/boost/config/compiler/intel.hpp +78 -4
  74. data/ext/boost/config/compiler/metrowerks.hpp +4 -1
  75. data/ext/boost/config/compiler/mpw.hpp +4 -1
  76. data/ext/boost/config/compiler/nvcc.hpp +8 -66
  77. data/ext/boost/config/compiler/pathscale.hpp +80 -0
  78. data/ext/boost/config/compiler/pgi.hpp +5 -5
  79. data/ext/boost/config/compiler/sunpro_cc.hpp +4 -1
  80. data/ext/boost/config/compiler/vacpp.hpp +37 -13
  81. data/ext/boost/config/compiler/visualc.hpp +24 -11
  82. data/ext/boost/config/platform/bsd.hpp +1 -1
  83. data/ext/boost/config/platform/cray.hpp +18 -0
  84. data/ext/boost/config/platform/cygwin.hpp +10 -0
  85. data/ext/boost/config/platform/linux.hpp +5 -0
  86. data/ext/boost/config/platform/macos.hpp +5 -4
  87. data/ext/boost/config/platform/symbian.hpp +5 -2
  88. data/ext/boost/config/platform/vms.hpp +25 -0
  89. data/ext/boost/config/platform/win32.hpp +7 -1
  90. data/ext/boost/config/select_compiler_config.hpp +8 -25
  91. data/ext/boost/config/select_platform_config.hpp +8 -1
  92. data/ext/boost/config/select_stdlib_config.hpp +9 -1
  93. data/ext/boost/config/stdlib/dinkumware.hpp +6 -9
  94. data/ext/boost/config/stdlib/libcomo.hpp +1 -4
  95. data/ext/boost/config/stdlib/libcpp.hpp +36 -0
  96. data/ext/boost/config/stdlib/libstdcpp3.hpp +37 -11
  97. data/ext/boost/config/stdlib/modena.hpp +1 -4
  98. data/ext/boost/config/stdlib/msl.hpp +1 -4
  99. data/ext/boost/config/stdlib/roguewave.hpp +9 -6
  100. data/ext/boost/config/stdlib/sgi.hpp +12 -4
  101. data/ext/boost/config/stdlib/stlport.hpp +11 -4
  102. data/ext/boost/config/stdlib/vacpp.hpp +11 -4
  103. data/ext/boost/config/suffix.hpp +71 -6
  104. data/ext/boost/config/warning_disable.hpp +1 -1
  105. data/ext/boost/container/container_fwd.hpp +177 -0
  106. data/ext/boost/cstdint.hpp +17 -12
  107. data/ext/boost/current_function.hpp +2 -1
  108. data/ext/boost/date_time/c_time.hpp +17 -1
  109. data/ext/boost/date_time/compiler_config.hpp +13 -15
  110. data/ext/boost/date_time/date_formatting.hpp +7 -1
  111. data/ext/boost/date_time/filetime_functions.hpp +4 -4
  112. data/ext/boost/date_time/gregorian_calendar.ipp +2 -2
  113. data/ext/boost/date_time/strings_from_facet.hpp +3 -3
  114. data/ext/boost/date_time/time_facet.hpp +101 -101
  115. data/ext/boost/detail/endian.hpp +4 -2
  116. data/ext/boost/detail/fenv.hpp +74 -0
  117. data/ext/boost/detail/sp_typeinfo.hpp +6 -0
  118. data/ext/boost/exception/detail/clone_current_exception.hpp +47 -0
  119. data/ext/boost/exception/detail/exception_ptr.hpp +194 -122
  120. data/ext/boost/exception/detail/type_info.hpp +3 -3
  121. data/ext/boost/exception/diagnostic_information.hpp +37 -21
  122. data/ext/boost/exception/exception.hpp +21 -1
  123. data/ext/boost/exception/info.hpp +0 -1
  124. data/ext/boost/function.hpp +2 -2
  125. data/ext/boost/function/function_base.hpp +15 -9
  126. data/ext/boost/function/function_template.hpp +26 -48
  127. data/ext/boost/integer_fwd.hpp +0 -16
  128. data/ext/boost/integer_traits.hpp +2 -2
  129. data/ext/boost/iterator.hpp +1 -1
  130. data/ext/boost/iterator/iterator_adaptor.hpp +1 -7
  131. data/ext/boost/iterator/iterator_facade.hpp +13 -13
  132. data/ext/boost/iterator/transform_iterator.hpp +5 -20
  133. data/ext/boost/lexical_cast.hpp +1655 -673
  134. data/ext/boost/math/policies/policy.hpp +982 -0
  135. data/ext/boost/math/special_functions/detail/fp_traits.hpp +570 -0
  136. data/ext/boost/math/special_functions/detail/round_fwd.hpp +80 -0
  137. data/ext/boost/math/special_functions/fpclassify.hpp +533 -0
  138. data/ext/boost/math/special_functions/math_fwd.hpp +1070 -0
  139. data/ext/boost/math/special_functions/sign.hpp +145 -0
  140. data/ext/boost/math/tools/config.hpp +321 -0
  141. data/ext/boost/math/tools/promotion.hpp +150 -0
  142. data/ext/boost/math/tools/real_cast.hpp +29 -0
  143. data/ext/boost/math/tools/user.hpp +97 -0
  144. data/ext/boost/move/move.hpp +1222 -0
  145. data/ext/boost/mpl/O1_size.hpp +40 -0
  146. data/ext/boost/mpl/O1_size_fwd.hpp +24 -0
  147. data/ext/boost/mpl/advance.hpp +76 -0
  148. data/ext/boost/mpl/advance_fwd.hpp +28 -0
  149. data/ext/boost/mpl/at.hpp +52 -0
  150. data/ext/boost/mpl/at_fwd.hpp +24 -0
  151. data/ext/boost/mpl/aux_/O1_size_impl.hpp +87 -0
  152. data/ext/boost/mpl/aux_/advance_backward.hpp +128 -0
  153. data/ext/boost/mpl/aux_/advance_forward.hpp +127 -0
  154. data/ext/boost/mpl/aux_/arithmetic_op.hpp +92 -0
  155. data/ext/boost/mpl/aux_/at_impl.hpp +45 -0
  156. data/ext/boost/mpl/aux_/begin_end_impl.hpp +101 -0
  157. data/ext/boost/mpl/aux_/clear_impl.hpp +35 -0
  158. data/ext/boost/mpl/aux_/comparison_op.hpp +83 -0
  159. data/ext/boost/mpl/aux_/config/forwarding.hpp +27 -0
  160. data/ext/boost/mpl/aux_/config/typeof.hpp +38 -0
  161. data/ext/boost/mpl/aux_/contains_impl.hpp +61 -0
  162. data/ext/boost/mpl/aux_/find_if_pred.hpp +31 -0
  163. data/ext/boost/mpl/aux_/fold_impl.hpp +43 -0
  164. data/ext/boost/mpl/aux_/has_begin.hpp +23 -0
  165. data/ext/boost/mpl/aux_/has_size.hpp +23 -0
  166. data/ext/boost/mpl/aux_/has_tag.hpp +23 -0
  167. data/ext/boost/mpl/aux_/inserter_algorithm.hpp +159 -0
  168. data/ext/boost/mpl/aux_/is_msvc_eti_arg.hpp +64 -0
  169. data/ext/boost/mpl/aux_/iter_apply.hpp +47 -0
  170. data/ext/boost/mpl/aux_/iter_fold_if_impl.hpp +210 -0
  171. data/ext/boost/mpl/aux_/iter_fold_impl.hpp +42 -0
  172. data/ext/boost/mpl/aux_/lambda_spec.hpp +49 -0
  173. data/ext/boost/mpl/aux_/largest_int.hpp +63 -0
  174. data/ext/boost/mpl/aux_/msvc_eti_base.hpp +77 -0
  175. data/ext/boost/mpl/aux_/msvc_type.hpp +62 -0
  176. data/ext/boost/mpl/aux_/numeric_cast_utils.hpp +77 -0
  177. data/ext/boost/mpl/aux_/numeric_op.hpp +315 -0
  178. data/ext/boost/mpl/aux_/preprocessed/gcc/advance_backward.hpp +97 -0
  179. data/ext/boost/mpl/aux_/preprocessed/gcc/advance_forward.hpp +97 -0
  180. data/ext/boost/mpl/aux_/preprocessed/gcc/equal_to.hpp +94 -0
  181. data/ext/boost/mpl/aux_/preprocessed/gcc/fold_impl.hpp +180 -0
  182. data/ext/boost/mpl/aux_/preprocessed/gcc/greater.hpp +94 -0
  183. data/ext/boost/mpl/aux_/preprocessed/gcc/greater_equal.hpp +94 -0
  184. data/ext/boost/mpl/aux_/preprocessed/gcc/iter_fold_if_impl.hpp +133 -0
  185. data/ext/boost/mpl/aux_/preprocessed/gcc/iter_fold_impl.hpp +180 -0
  186. data/ext/boost/mpl/aux_/preprocessed/gcc/less.hpp +94 -0
  187. data/ext/boost/mpl/aux_/preprocessed/gcc/less_equal.hpp +94 -0
  188. data/ext/boost/mpl/aux_/preprocessed/gcc/list.hpp +323 -0
  189. data/ext/boost/mpl/aux_/preprocessed/gcc/minus.hpp +146 -0
  190. data/ext/boost/mpl/aux_/preprocessed/gcc/not_equal_to.hpp +94 -0
  191. data/ext/boost/mpl/aux_/preprocessed/gcc/plus.hpp +146 -0
  192. data/ext/boost/mpl/aux_/preprocessed/gcc/reverse_fold_impl.hpp +231 -0
  193. data/ext/boost/mpl/aux_/preprocessed/gcc/times.hpp +146 -0
  194. data/ext/boost/mpl/aux_/preprocessed/gcc/vector.hpp +323 -0
  195. data/ext/boost/mpl/aux_/preprocessor/default_params.hpp +67 -0
  196. data/ext/boost/mpl/aux_/push_back_impl.hpp +70 -0
  197. data/ext/boost/mpl/aux_/push_front_impl.hpp +71 -0
  198. data/ext/boost/mpl/aux_/reverse_fold_impl.hpp +44 -0
  199. data/ext/boost/mpl/aux_/size_impl.hpp +52 -0
  200. data/ext/boost/mpl/aux_/traits_lambda_spec.hpp +63 -0
  201. data/ext/boost/mpl/back_fwd.hpp +24 -0
  202. data/ext/boost/mpl/back_inserter.hpp +34 -0
  203. data/ext/boost/mpl/begin_end.hpp +57 -0
  204. data/ext/boost/mpl/begin_end_fwd.hpp +27 -0
  205. data/ext/boost/mpl/clear.hpp +39 -0
  206. data/ext/boost/mpl/clear_fwd.hpp +24 -0
  207. data/ext/boost/mpl/comparison.hpp +24 -0
  208. data/ext/boost/mpl/contains.hpp +41 -0
  209. data/ext/boost/mpl/contains_fwd.hpp +25 -0
  210. data/ext/boost/mpl/deref.hpp +41 -0
  211. data/ext/boost/mpl/distance.hpp +78 -0
  212. data/ext/boost/mpl/distance_fwd.hpp +28 -0
  213. data/ext/boost/mpl/empty_fwd.hpp +24 -0
  214. data/ext/boost/mpl/equal_to.hpp +21 -0
  215. data/ext/boost/mpl/find.hpp +38 -0
  216. data/ext/boost/mpl/find_if.hpp +50 -0
  217. data/ext/boost/mpl/fold.hpp +48 -0
  218. data/ext/boost/mpl/front_fwd.hpp +24 -0
  219. data/ext/boost/mpl/front_inserter.hpp +33 -0
  220. data/ext/boost/mpl/greater.hpp +21 -0
  221. data/ext/boost/mpl/greater_equal.hpp +21 -0
  222. data/ext/boost/mpl/inserter.hpp +32 -0
  223. data/ext/boost/mpl/iter_fold.hpp +49 -0
  224. data/ext/boost/mpl/iter_fold_if.hpp +117 -0
  225. data/ext/boost/mpl/iterator_range.hpp +42 -0
  226. data/ext/boost/mpl/iterator_tags.hpp +27 -0
  227. data/ext/boost/mpl/less.hpp +21 -0
  228. data/ext/boost/mpl/less_equal.hpp +21 -0
  229. data/ext/boost/mpl/limits/list.hpp +21 -0
  230. data/ext/boost/mpl/limits/vector.hpp +21 -0
  231. data/ext/boost/mpl/list.hpp +57 -0
  232. data/ext/boost/mpl/list/aux_/O1_size.hpp +33 -0
  233. data/ext/boost/mpl/list/aux_/begin_end.hpp +44 -0
  234. data/ext/boost/mpl/list/aux_/clear.hpp +34 -0
  235. data/ext/boost/mpl/list/aux_/empty.hpp +34 -0
  236. data/ext/boost/mpl/list/aux_/front.hpp +33 -0
  237. data/ext/boost/mpl/list/aux_/include_preprocessed.hpp +35 -0
  238. data/ext/boost/mpl/list/aux_/item.hpp +55 -0
  239. data/ext/boost/mpl/list/aux_/iterator.hpp +76 -0
  240. data/ext/boost/mpl/list/aux_/pop_front.hpp +34 -0
  241. data/ext/boost/mpl/list/aux_/preprocessed/plain/list10.hpp +149 -0
  242. data/ext/boost/mpl/list/aux_/preprocessed/plain/list20.hpp +169 -0
  243. data/ext/boost/mpl/list/aux_/push_back.hpp +36 -0
  244. data/ext/boost/mpl/list/aux_/push_front.hpp +39 -0
  245. data/ext/boost/mpl/list/aux_/size.hpp +33 -0
  246. data/ext/boost/mpl/list/aux_/tag.hpp +24 -0
  247. data/ext/boost/mpl/list/list0.hpp +42 -0
  248. data/ext/boost/mpl/list/list10.hpp +43 -0
  249. data/ext/boost/mpl/list/list20.hpp +43 -0
  250. data/ext/boost/mpl/long.hpp +22 -0
  251. data/ext/boost/mpl/long_fwd.hpp +27 -0
  252. data/ext/boost/mpl/minus.hpp +21 -0
  253. data/ext/boost/mpl/multiplies.hpp +53 -0
  254. data/ext/boost/mpl/negate.hpp +81 -0
  255. data/ext/boost/mpl/not_equal_to.hpp +21 -0
  256. data/ext/boost/mpl/numeric_cast.hpp +41 -0
  257. data/ext/boost/mpl/pair.hpp +70 -0
  258. data/ext/boost/mpl/plus.hpp +21 -0
  259. data/ext/boost/mpl/pop_back_fwd.hpp +24 -0
  260. data/ext/boost/mpl/pop_front_fwd.hpp +24 -0
  261. data/ext/boost/mpl/prior.hpp +19 -0
  262. data/ext/boost/mpl/push_back.hpp +53 -0
  263. data/ext/boost/mpl/push_back_fwd.hpp +24 -0
  264. data/ext/boost/mpl/push_front.hpp +52 -0
  265. data/ext/boost/mpl/push_front_fwd.hpp +24 -0
  266. data/ext/boost/mpl/remove_if.hpp +83 -0
  267. data/ext/boost/mpl/reverse_fold.hpp +50 -0
  268. data/ext/boost/mpl/same_as.hpp +55 -0
  269. data/ext/boost/mpl/sequence_tag.hpp +124 -0
  270. data/ext/boost/mpl/sequence_tag_fwd.hpp +26 -0
  271. data/ext/boost/mpl/size.hpp +42 -0
  272. data/ext/boost/mpl/size_fwd.hpp +24 -0
  273. data/ext/boost/mpl/tag.hpp +52 -0
  274. data/ext/boost/mpl/times.hpp +21 -0
  275. data/ext/boost/mpl/vector.hpp +57 -0
  276. data/ext/boost/mpl/vector/aux_/O1_size.hpp +56 -0
  277. data/ext/boost/mpl/vector/aux_/at.hpp +116 -0
  278. data/ext/boost/mpl/vector/aux_/back.hpp +59 -0
  279. data/ext/boost/mpl/vector/aux_/begin_end.hpp +49 -0
  280. data/ext/boost/mpl/vector/aux_/clear.hpp +55 -0
  281. data/ext/boost/mpl/vector/aux_/empty.hpp +68 -0
  282. data/ext/boost/mpl/vector/aux_/front.hpp +56 -0
  283. data/ext/boost/mpl/vector/aux_/include_preprocessed.hpp +55 -0
  284. data/ext/boost/mpl/vector/aux_/item.hpp +103 -0
  285. data/ext/boost/mpl/vector/aux_/iterator.hpp +130 -0
  286. data/ext/boost/mpl/vector/aux_/pop_back.hpp +40 -0
  287. data/ext/boost/mpl/vector/aux_/pop_front.hpp +40 -0
  288. data/ext/boost/mpl/vector/aux_/preprocessed/plain/vector10.hpp +829 -0
  289. data/ext/boost/mpl/vector/aux_/preprocessed/plain/vector20.hpp +1144 -0
  290. data/ext/boost/mpl/vector/aux_/preprocessed/typeof_based/vector10.hpp +139 -0
  291. data/ext/boost/mpl/vector/aux_/preprocessed/typeof_based/vector20.hpp +159 -0
  292. data/ext/boost/mpl/vector/aux_/push_back.hpp +40 -0
  293. data/ext/boost/mpl/vector/aux_/push_front.hpp +40 -0
  294. data/ext/boost/mpl/vector/aux_/size.hpp +49 -0
  295. data/ext/boost/mpl/vector/aux_/tag.hpp +32 -0
  296. data/ext/boost/mpl/vector/aux_/vector0.hpp +52 -0
  297. data/ext/boost/mpl/vector/vector0.hpp +34 -0
  298. data/ext/boost/mpl/vector/vector10.hpp +45 -0
  299. data/ext/boost/mpl/vector/vector20.hpp +45 -0
  300. data/ext/boost/none.hpp +1 -1
  301. data/ext/boost/numeric/conversion/bounds.hpp +24 -0
  302. data/ext/boost/numeric/conversion/cast.hpp +61 -0
  303. data/ext/boost/numeric/conversion/conversion_traits.hpp +39 -0
  304. data/ext/boost/numeric/conversion/converter.hpp +68 -0
  305. data/ext/boost/numeric/conversion/converter_policies.hpp +186 -0
  306. data/ext/boost/numeric/conversion/detail/bounds.hpp +58 -0
  307. data/ext/boost/numeric/conversion/detail/conversion_traits.hpp +97 -0
  308. data/ext/boost/numeric/conversion/detail/converter.hpp +602 -0
  309. data/ext/boost/numeric/conversion/detail/int_float_mixture.hpp +72 -0
  310. data/ext/boost/numeric/conversion/detail/is_subranged.hpp +234 -0
  311. data/ext/boost/numeric/conversion/detail/meta.hpp +120 -0
  312. data/ext/boost/numeric/conversion/detail/numeric_cast_traits.hpp +138 -0
  313. data/ext/boost/numeric/conversion/detail/preprocessed/numeric_cast_traits_common.hpp +1741 -0
  314. data/ext/boost/numeric/conversion/detail/preprocessed/numeric_cast_traits_long_long.hpp +347 -0
  315. data/ext/boost/numeric/conversion/detail/sign_mixture.hpp +72 -0
  316. data/ext/boost/numeric/conversion/detail/udt_builtin_mixture.hpp +69 -0
  317. data/ext/boost/numeric/conversion/int_float_mixture_enum.hpp +29 -0
  318. data/ext/boost/numeric/conversion/numeric_cast_traits.hpp +31 -0
  319. data/ext/boost/numeric/conversion/sign_mixture_enum.hpp +29 -0
  320. data/ext/boost/numeric/conversion/udt_builtin_mixture_enum.hpp +26 -0
  321. data/ext/boost/operators.hpp +3 -1
  322. data/ext/boost/optional/optional.hpp +146 -79
  323. data/ext/boost/optional/optional_fwd.hpp +8 -1
  324. data/ext/boost/preprocessor/cat.hpp +2 -2
  325. data/ext/boost/preprocessor/config/config.hpp +39 -4
  326. data/ext/boost/preprocessor/facilities/intercept.hpp +277 -0
  327. data/ext/boost/preprocessor/facilities/overload.hpp +25 -0
  328. data/ext/boost/preprocessor/iteration/detail/iter/forward1.hpp +3 -3
  329. data/ext/boost/preprocessor/iteration/iterate.hpp +3 -3
  330. data/ext/boost/preprocessor/punctuation/paren.hpp +23 -0
  331. data/ext/boost/preprocessor/repetition/enum_shifted_params.hpp +44 -0
  332. data/ext/boost/preprocessor/seq/cat.hpp +5 -4
  333. data/ext/boost/preprocessor/seq/size.hpp +0 -1
  334. data/ext/boost/preprocessor/tuple/eat.hpp +83 -34
  335. data/ext/boost/preprocessor/tuple/elem.hpp +161 -355
  336. data/ext/boost/preprocessor/tuple/rem.hpp +110 -48
  337. data/ext/boost/preprocessor/tuple/to_list.hpp +90 -36
  338. data/ext/boost/preprocessor/variadic/elem.hpp +94 -0
  339. data/ext/boost/preprocessor/variadic/size.hpp +30 -0
  340. data/ext/boost/range/begin.hpp +17 -6
  341. data/ext/boost/range/concepts.hpp +37 -2
  342. data/ext/boost/range/detail/safe_bool.hpp +72 -0
  343. data/ext/boost/range/end.hpp +14 -9
  344. data/ext/boost/range/iterator_range_core.hpp +120 -12
  345. data/ext/boost/range/size.hpp +21 -5
  346. data/ext/boost/smart_ptr/detail/shared_count.hpp +88 -0
  347. data/ext/boost/smart_ptr/detail/sp_counted_base.hpp +3 -0
  348. data/ext/boost/smart_ptr/detail/sp_counted_base_aix.hpp +142 -0
  349. data/ext/boost/smart_ptr/detail/sp_counted_base_gcc_mips.hpp +9 -0
  350. data/ext/boost/smart_ptr/detail/sp_counted_impl.hpp +10 -2
  351. data/ext/boost/smart_ptr/detail/sp_has_sync.hpp +5 -1
  352. data/ext/boost/smart_ptr/detail/spinlock.hpp +4 -1
  353. data/ext/boost/smart_ptr/detail/spinlock_gcc_arm.hpp +20 -3
  354. data/ext/boost/smart_ptr/detail/spinlock_pool.hpp +4 -0
  355. data/ext/boost/smart_ptr/make_shared.hpp +591 -22
  356. data/ext/boost/smart_ptr/shared_array.hpp +29 -1
  357. data/ext/boost/smart_ptr/shared_ptr.hpp +29 -13
  358. data/ext/boost/smart_ptr/weak_ptr.hpp +24 -12
  359. data/ext/boost/src/pthread/once.cpp +9 -7
  360. data/ext/boost/src/pthread/thread.cpp +32 -28
  361. data/ext/boost/src/pthread/timeconv.inl +4 -5
  362. data/ext/boost/src/tss_null.cpp +5 -1
  363. data/ext/boost/static_assert.hpp +8 -2
  364. data/ext/boost/thread/detail/config.hpp +19 -4
  365. data/ext/boost/thread/detail/move.hpp +11 -5
  366. data/ext/boost/thread/detail/thread.hpp +59 -43
  367. data/ext/boost/thread/exceptions.hpp +9 -9
  368. data/ext/boost/thread/future.hpp +150 -82
  369. data/ext/boost/thread/locks.hpp +101 -60
  370. data/ext/boost/thread/pthread/condition_variable.hpp +79 -32
  371. data/ext/boost/thread/pthread/condition_variable_fwd.hpp +12 -3
  372. data/ext/boost/thread/pthread/mutex.hpp +17 -14
  373. data/ext/boost/thread/pthread/once.hpp +3 -4
  374. data/ext/boost/thread/pthread/pthread_mutex_scoped_lock.hpp +12 -2
  375. data/ext/boost/thread/pthread/recursive_mutex.hpp +19 -19
  376. data/ext/boost/thread/pthread/shared_mutex.hpp +13 -13
  377. data/ext/boost/thread/pthread/thread_data.hpp +40 -12
  378. data/ext/boost/thread/thread_time.hpp +5 -0
  379. data/ext/boost/throw_exception.hpp +1 -1
  380. data/ext/boost/token_functions.hpp +34 -10
  381. data/ext/boost/type_traits/add_rvalue_reference.hpp +66 -0
  382. data/ext/boost/type_traits/alignment_of.hpp +1 -1
  383. data/ext/boost/type_traits/detail/bool_trait_def.hpp +26 -3
  384. data/ext/boost/type_traits/detail/bool_trait_undef.hpp +3 -2
  385. data/ext/boost/type_traits/detail/cv_traits_impl.hpp +1 -1
  386. data/ext/boost/type_traits/detail/size_t_trait_def.hpp +6 -4
  387. data/ext/boost/type_traits/detail/type_trait_def.hpp +8 -2
  388. data/ext/boost/type_traits/function_traits.hpp +1 -1
  389. data/ext/boost/type_traits/has_nothrow_constructor.hpp +53 -0
  390. data/ext/boost/type_traits/has_nothrow_copy.hpp +19 -5
  391. data/ext/boost/type_traits/has_trivial_constructor.hpp +51 -0
  392. data/ext/boost/type_traits/has_trivial_copy.hpp +20 -5
  393. data/ext/boost/type_traits/has_trivial_destructor.hpp +12 -5
  394. data/ext/boost/type_traits/intrinsics.hpp +119 -71
  395. data/ext/boost/type_traits/is_const.hpp +5 -5
  396. data/ext/boost/type_traits/is_convertible.hpp +14 -13
  397. data/ext/boost/type_traits/is_enum.hpp +1 -1
  398. data/ext/boost/type_traits/is_floating_point.hpp +27 -0
  399. data/ext/boost/type_traits/is_function.hpp +3 -3
  400. data/ext/boost/type_traits/is_fundamental.hpp +1 -1
  401. data/ext/boost/type_traits/is_member_function_pointer.hpp +2 -2
  402. data/ext/boost/type_traits/is_member_pointer.hpp +2 -2
  403. data/ext/boost/type_traits/is_pod.hpp +11 -3
  404. data/ext/boost/type_traits/is_pointer.hpp +2 -2
  405. data/ext/boost/type_traits/is_signed.hpp +8 -3
  406. data/ext/boost/type_traits/is_union.hpp +8 -0
  407. data/ext/boost/type_traits/is_unsigned.hpp +9 -4
  408. data/ext/boost/type_traits/is_volatile.hpp +5 -5
  409. data/ext/boost/type_traits/remove_cv.hpp +4 -3
  410. data/ext/boost/type_traits/remove_pointer.hpp +51 -2
  411. data/ext/boost/type_traits/remove_reference.hpp +2 -2
  412. data/ext/boost/type_traits/type_with_alignment.hpp +8 -2
  413. data/ext/boost/utility/declval.hpp +44 -0
  414. data/ext/boost/utility/detail/in_place_factory_prefix.hpp +36 -0
  415. data/ext/boost/utility/detail/in_place_factory_suffix.hpp +23 -0
  416. data/ext/boost/utility/detail/result_of_iterate.hpp +142 -0
  417. data/ext/boost/utility/in_place_factory.hpp +88 -0
  418. data/ext/boost/utility/result_of.hpp +103 -0
  419. data/ext/boost/utility/swap.hpp +55 -0
  420. data/ext/common/AnsiColorConstants.h +36 -0
  421. data/ext/common/ApplicationPool2/Common.h +87 -0
  422. data/ext/common/ApplicationPool2/ComponentInfo.h +53 -0
  423. data/ext/common/ApplicationPool2/Group.h +648 -0
  424. data/ext/common/ApplicationPool2/Implementation.cpp +580 -0
  425. data/ext/common/ApplicationPool2/Options.h +576 -0
  426. data/ext/common/ApplicationPool2/PipeWatcher.h +61 -0
  427. data/ext/common/ApplicationPool2/Pool.h +1181 -0
  428. data/ext/common/ApplicationPool2/Process.h +425 -0
  429. data/ext/common/ApplicationPool2/README.md +96 -0
  430. data/ext/common/ApplicationPool2/Session.h +158 -0
  431. data/ext/common/ApplicationPool2/Socket.h +246 -0
  432. data/ext/common/ApplicationPool2/Spawner.h +2212 -0
  433. data/ext/common/ApplicationPool2/SuperGroup.h +749 -0
  434. data/ext/common/BackgroundEventLoop.cpp +129 -0
  435. data/ext/common/BackgroundEventLoop.h +61 -0
  436. data/ext/common/Constants.h +3 -1
  437. data/ext/common/EventedBufferedInput.h +331 -0
  438. data/ext/common/EventedMessageServer.h +17 -34
  439. data/ext/common/EventedServer.h +2 -2
  440. data/ext/common/Exceptions.h +71 -19
  441. data/ext/common/FileDescriptor.h +8 -6
  442. data/ext/common/HttpConstants.h +167 -0
  443. data/ext/common/IniFile.h +24 -0
  444. data/ext/common/Logging.h +62 -849
  445. data/ext/common/MessageReadersWriters.h +19 -0
  446. data/ext/common/MessageServer.h +11 -14
  447. data/ext/common/MultiLibeio.cpp +198 -0
  448. data/ext/common/MultiLibeio.h +67 -0
  449. data/ext/common/ResourceLocator.h +24 -41
  450. data/ext/common/SafeLibev.h +186 -14
  451. data/ext/common/StaticString.h +23 -3
  452. data/ext/common/UnionStation.h +972 -0
  453. data/ext/common/Utils.cpp +168 -24
  454. data/ext/common/Utils.h +25 -3
  455. data/ext/common/Utils/CachedFileStat.hpp +4 -3
  456. data/ext/common/Utils/FileChangeChecker.h +2 -2
  457. data/ext/common/Utils/HashMap.h +50 -0
  458. data/ext/common/Utils/IOUtils.cpp +229 -68
  459. data/ext/common/Utils/IOUtils.h +134 -3
  460. data/ext/common/Utils/Lock.h +28 -0
  461. data/ext/common/Utils/MemoryBarrier.h +52 -0
  462. data/ext/common/Utils/PriorityQueue.h +54 -0
  463. data/ext/common/Utils/ProcessMetricsCollector.h +9 -11
  464. data/ext/common/Utils/ScopeGuard.h +50 -1
  465. data/ext/common/Utils/SmallVector.h +653 -0
  466. data/ext/common/Utils/StrIntUtils.cpp +26 -2
  467. data/ext/common/Utils/StrIntUtils.h +18 -2
  468. data/ext/common/Utils/StringMap.h +125 -8
  469. data/ext/common/Utils/Template.h +212 -0
  470. data/ext/common/Utils/fib.c +699 -0
  471. data/ext/common/Utils/fib.h +101 -0
  472. data/ext/common/Utils/fibpriv.h +67 -0
  473. data/ext/common/Utils/json-forwards.h +249 -0
  474. data/ext/common/Utils/json.h +1855 -0
  475. data/ext/common/Utils/jsoncpp.cpp +4230 -0
  476. data/ext/common/agents/Base.cpp +1126 -0
  477. data/ext/common/{AgentBase.h → agents/Base.h} +5 -1
  478. data/ext/common/agents/EnvPrinter.c +16 -0
  479. data/ext/common/agents/HelperAgent/AgentOptions.h +81 -0
  480. data/ext/common/{HelperAgent → agents/HelperAgent}/BacktracesServer.h +3 -2
  481. data/ext/common/agents/HelperAgent/FileBackedPipe.h +732 -0
  482. data/ext/common/agents/HelperAgent/Main.cpp +497 -0
  483. data/ext/common/agents/HelperAgent/RequestHandler.cpp +283 -0
  484. data/ext/common/agents/HelperAgent/RequestHandler.h +2139 -0
  485. data/ext/common/agents/HelperAgent/ScgiRequestParser.h +451 -0
  486. data/ext/common/{LoggingAgent → agents/LoggingAgent}/DataStoreId.h +1 -1
  487. data/ext/common/{LoggingAgent → agents/LoggingAgent}/FilterSupport.cpp +1 -1
  488. data/ext/common/{LoggingAgent → agents/LoggingAgent}/FilterSupport.h +0 -0
  489. data/ext/common/{LoggingAgent → agents/LoggingAgent}/LoggingServer.h +18 -16
  490. data/ext/common/{LoggingAgent → agents/LoggingAgent}/Main.cpp +15 -13
  491. data/ext/common/{LoggingAgent → agents/LoggingAgent}/RemoteSender.h +6 -6
  492. data/ext/common/agents/SpawnPreparer.cpp +127 -0
  493. data/ext/common/{Watchdog.cpp → agents/Watchdog/Main.cpp} +63 -25
  494. data/ext/libeio/Changes +72 -0
  495. data/ext/{google/COPYING → libeio/LICENSE} +17 -9
  496. data/ext/libeio/Makefile.am +15 -0
  497. data/ext/libeio/Makefile.in +694 -0
  498. data/ext/libeio/aclocal.m4 +9418 -0
  499. data/ext/libeio/autogen.sh +3 -0
  500. data/ext/libeio/config.guess +1501 -0
  501. data/ext/libeio/config.h.in +136 -0
  502. data/ext/libeio/config.sub +1705 -0
  503. data/ext/libeio/configure +14822 -0
  504. data/ext/libeio/configure.ac +22 -0
  505. data/ext/libeio/demo.c +194 -0
  506. data/ext/libeio/ecb.h +457 -0
  507. data/ext/libeio/eio.c +2816 -0
  508. data/ext/libeio/eio.h +411 -0
  509. data/ext/libeio/install-sh +520 -0
  510. data/ext/libeio/libeio.m4 +211 -0
  511. data/ext/libeio/ltmain.sh +9636 -0
  512. data/ext/libeio/missing +376 -0
  513. data/ext/libeio/xthread.h +166 -0
  514. data/ext/libev/Changes +125 -7
  515. data/ext/libev/Makefile.am +5 -3
  516. data/ext/libev/Makefile.in +209 -120
  517. data/ext/libev/aclocal.m4 +6027 -4619
  518. data/ext/libev/autogen.sh +1 -4
  519. data/ext/libev/config.h.in +11 -7
  520. data/ext/libev/configure +7312 -14993
  521. data/ext/libev/configure.ac +12 -5
  522. data/ext/libev/depcomp +630 -0
  523. data/ext/libev/ev++.h +48 -32
  524. data/ext/libev/ev.c +1173 -391
  525. data/ext/libev/ev.h +315 -181
  526. data/ext/libev/ev_epoll.c +66 -15
  527. data/ext/libev/ev_kqueue.c +20 -18
  528. data/ext/libev/ev_poll.c +27 -23
  529. data/ext/libev/ev_port.c +39 -19
  530. data/ext/libev/ev_select.c +23 -17
  531. data/ext/libev/ev_vars.h +25 -8
  532. data/ext/libev/ev_win32.c +6 -6
  533. data/ext/libev/ev_wrap.h +22 -2
  534. data/ext/libev/event.c +18 -17
  535. data/ext/libev/event.h +16 -4
  536. data/ext/libev/libev.m4 +10 -6
  537. data/ext/libev/ltmain.sh +7353 -5811
  538. data/ext/nginx/Configuration.c +74 -42
  539. data/ext/nginx/Configuration.h +3 -5
  540. data/ext/nginx/ContentHandler.c +26 -83
  541. data/ext/nginx/ContentHandler.h +1 -1
  542. data/ext/nginx/config +13 -9
  543. data/ext/nginx/ngx_http_passenger_module.c +3 -7
  544. data/ext/oxt/detail/backtrace_enabled.hpp +5 -102
  545. data/ext/oxt/detail/context.hpp +90 -0
  546. data/ext/oxt/detail/spin_lock_darwin.hpp +4 -0
  547. data/ext/oxt/detail/spin_lock_gcc_x86.hpp +4 -0
  548. data/ext/oxt/detail/spin_lock_pthreads.hpp +14 -0
  549. data/ext/oxt/detail/tracable_exception_enabled.hpp +2 -2
  550. data/ext/oxt/dynamic_thread_group.hpp +27 -1
  551. data/ext/oxt/implementation.cpp +415 -0
  552. data/ext/oxt/{thread.cpp → initialize.hpp} +13 -6
  553. data/ext/oxt/macros.hpp +32 -1
  554. data/ext/oxt/spin_lock.hpp +6 -11
  555. data/ext/oxt/system_calls.cpp +204 -16
  556. data/ext/oxt/system_calls.hpp +85 -45
  557. data/ext/oxt/thread.hpp +13 -117
  558. data/ext/ruby/passenger_native_support.c +82 -237
  559. data/helper-scripts/backtrace-sanitizer.rb +114 -0
  560. data/helper-scripts/classic-rails-loader.rb +135 -0
  561. data/helper-scripts/classic-rails-preloader.rb +161 -0
  562. data/helper-scripts/node-loader.js +314 -0
  563. data/helper-scripts/rack-loader.rb +104 -0
  564. data/helper-scripts/rack-preloader.rb +132 -0
  565. data/helper-scripts/wsgi-loader.py +231 -0
  566. data/helper-scripts/wsgi-preloader.py +1 -0
  567. data/lib/phusion_passenger.rb +159 -61
  568. data/lib/phusion_passenger/abstract_installer.rb +182 -87
  569. data/lib/phusion_passenger/admin_tools/server_instance.rb +25 -19
  570. data/lib/phusion_passenger/analytics_logger.rb +5 -4
  571. data/lib/phusion_passenger/classic_rails/{request_handler.rb → thread_handler_extension.rb} +4 -40
  572. data/lib/phusion_passenger/classic_rails_extensions/init.rb +5 -3
  573. data/lib/phusion_passenger/common_library.rb +441 -0
  574. data/lib/phusion_passenger/console_text_template.rb +4 -16
  575. data/lib/phusion_passenger/constants.rb +1 -8
  576. data/lib/phusion_passenger/debug_logging.rb +5 -2
  577. data/lib/phusion_passenger/dependencies.rb +51 -13
  578. data/lib/phusion_passenger/loader_shared_helpers.rb +318 -0
  579. data/lib/phusion_passenger/message_channel.rb +3 -47
  580. data/lib/phusion_passenger/message_client.rb +2 -2
  581. data/lib/phusion_passenger/native_support.rb +36 -15
  582. data/lib/phusion_passenger/packaging.rb +8 -11
  583. data/lib/phusion_passenger/platform_info.rb +25 -17
  584. data/lib/phusion_passenger/platform_info/apache.rb +10 -7
  585. data/lib/phusion_passenger/platform_info/binary_compatibility.rb +10 -30
  586. data/lib/phusion_passenger/platform_info/compiler.rb +93 -34
  587. data/lib/phusion_passenger/platform_info/ruby.rb +37 -97
  588. data/lib/phusion_passenger/preloader_shared_helpers.rb +121 -0
  589. data/lib/phusion_passenger/public_api.rb +1 -4
  590. data/lib/phusion_passenger/rack/{request_handler.rb → thread_handler_extension.rb} +14 -63
  591. data/lib/phusion_passenger/rails3_extensions/init.rb +9 -8
  592. data/lib/phusion_passenger/request_handler.rb +500 -0
  593. data/lib/phusion_passenger/request_handler/thread_handler.rb +360 -0
  594. data/lib/phusion_passenger/ruby_core_enhancements.rb +142 -0
  595. data/lib/phusion_passenger/standalone/command.rb +36 -15
  596. data/lib/phusion_passenger/standalone/package_runtime_command.rb +16 -8
  597. data/lib/phusion_passenger/standalone/runtime_installer.rb +169 -72
  598. data/lib/phusion_passenger/standalone/start_command.rb +44 -39
  599. data/lib/phusion_passenger/standalone/utils.rb +5 -5
  600. data/lib/phusion_passenger/utils.rb +35 -914
  601. data/lib/phusion_passenger/utils/ansi_colors.rb +59 -0
  602. data/lib/phusion_passenger/utils/file_system_watcher.rb +1 -1
  603. data/lib/phusion_passenger/utils/robust_interruption.rb +134 -0
  604. data/lib/phusion_passenger/utils/tee_input.rb +174 -0
  605. data/lib/phusion_passenger/utils/tmpio.rb +33 -0
  606. data/lib/phusion_passenger/utils/unseekable_socket.rb +6 -0
  607. data/resources/mime.types +5 -1
  608. data/{lib/phusion_passenger/templates → resources}/standalone_default_root/index.html +0 -0
  609. data/{lib/phusion_passenger → resources}/templates/apache2/apache_must_be_compiled_with_compatible_mpm.txt.erb +0 -0
  610. data/{lib/phusion_passenger → resources}/templates/apache2/config_snippets.txt.erb +0 -0
  611. data/{lib/phusion_passenger → resources}/templates/apache2/deployment_example.txt.erb +0 -0
  612. data/{lib/phusion_passenger → resources}/templates/apache2/no_write_permission_to_passenger_root.txt.erb +0 -0
  613. data/{lib/phusion_passenger → resources}/templates/apache2/possible_solutions_for_compilation_and_installation_problems.txt.erb +0 -0
  614. data/{lib/phusion_passenger → resources}/templates/apache2/run_installer_as_root.txt.erb +0 -0
  615. data/{lib/phusion_passenger → resources}/templates/apache2/welcome.txt.erb +0 -0
  616. data/{lib/phusion_passenger → resources}/templates/error_layout.css +6 -0
  617. data/resources/templates/error_layout.html.template +89 -0
  618. data/resources/templates/general_error.html.template +1 -0
  619. data/resources/templates/general_error_with_html.html.template +1 -0
  620. data/{lib/phusion_passenger → resources}/templates/nginx/ask_for_extra_configure_flags.txt.erb +0 -0
  621. data/{lib/phusion_passenger → resources}/templates/nginx/cannot_write_to_dir.txt.erb +0 -0
  622. data/{lib/phusion_passenger → resources}/templates/nginx/config_snippets.txt.erb +0 -0
  623. data/{lib/phusion_passenger → resources}/templates/nginx/config_snippets_inserted.txt.erb +0 -0
  624. data/{lib/phusion_passenger → resources}/templates/nginx/confirm_extra_configure_flags.txt.erb +0 -0
  625. data/{lib/phusion_passenger → resources}/templates/nginx/deployment_example.txt.erb +0 -0
  626. data/resources/templates/nginx/not_available_when_natively_packaged.txt.erb +8 -0
  627. data/{lib/phusion_passenger → resources}/templates/nginx/pcre_could_not_be_downloaded.txt.erb +0 -0
  628. data/{lib/phusion_passenger → resources}/templates/nginx/pcre_could_not_be_extracted.txt.erb +0 -0
  629. data/{lib/phusion_passenger → resources}/templates/nginx/possible_solutions_for_compilation_and_installation_problems.txt.erb +0 -0
  630. data/{lib/phusion_passenger → resources}/templates/nginx/possible_solutions_for_download_and_extraction_problems.txt.erb +0 -0
  631. data/{lib/phusion_passenger → resources}/templates/nginx/query_download_and_install.txt.erb +0 -0
  632. data/{lib/phusion_passenger → resources}/templates/nginx/run_installer_as_root.txt.erb +0 -0
  633. data/{lib/phusion_passenger → resources}/templates/nginx/welcome.txt.erb +0 -0
  634. data/{lib/phusion_passenger → resources}/templates/standalone/cannot_write_to_dir.txt.erb +0 -0
  635. data/{lib/phusion_passenger → resources}/templates/standalone/config.erb +26 -5
  636. data/{lib/phusion_passenger → resources}/templates/standalone/possible_solutions_for_download_and_extraction_problems.txt.erb +0 -0
  637. data/{lib/phusion_passenger → resources}/templates/standalone/run_installer_as_root.txt.erb +0 -0
  638. data/{lib/phusion_passenger → resources}/templates/standalone/welcome.txt.erb +0 -0
  639. data/resources/templates/undisclosed_error.html.template +25 -0
  640. data/test/config.json.example +42 -0
  641. data/test/cxx/ApplicationPool2/DirectSpawnerTest.cpp +86 -0
  642. data/test/cxx/ApplicationPool2/OptionsTest.cpp +44 -0
  643. data/test/cxx/ApplicationPool2/PoolTest.cpp +1234 -0
  644. data/test/cxx/ApplicationPool2/ProcessTest.cpp +131 -0
  645. data/test/cxx/ApplicationPool2/SmartSpawnerTest.cpp +229 -0
  646. data/test/cxx/ApplicationPool2/SpawnerTestCases.cpp +744 -0
  647. data/test/cxx/BufferedIOTest.cpp +7 -7
  648. data/test/cxx/CxxTestMain.cpp +65 -2
  649. data/test/cxx/FileBackedPipeTest.cpp +626 -0
  650. data/test/cxx/FileChangeCheckerTest.cpp +20 -18
  651. data/test/cxx/FilterSupportTest.cpp +5 -5
  652. data/test/cxx/IOUtilsTest.cpp +11 -4
  653. data/test/cxx/MessageReadersWritersTest.cpp +1 -1
  654. data/test/cxx/MessageServerTest.cpp +31 -30
  655. data/test/cxx/RequestHandlerTest.cpp +777 -0
  656. data/test/cxx/ScgiRequestParserTest.cpp +36 -16
  657. data/test/cxx/ServerInstanceDirTest.cpp +1 -1
  658. data/test/cxx/StringMapTest.cpp +61 -0
  659. data/test/cxx/TemplateTest.cpp +118 -0
  660. data/test/cxx/TestSupport.cpp +25 -68
  661. data/test/cxx/TestSupport.h +81 -41
  662. data/test/cxx/{LoggingTest.cpp → UnionStationTest.cpp} +79 -74
  663. data/test/cxx/UtilsTest.cpp +59 -5
  664. data/test/integration_tests/apache2_tests.rb +2 -2
  665. data/test/integration_tests/nginx_tests.rb +1 -1
  666. data/test/integration_tests/spec_helper.rb +7 -5
  667. data/test/oxt/oxt_test_main.cpp +2 -0
  668. data/test/oxt/syscall_interruption_test.cpp +1 -0
  669. data/test/ruby/classic_rails/loader_spec.rb +48 -0
  670. data/test/ruby/classic_rails/preloader_spec.rb +54 -0
  671. data/test/ruby/rack/loader_spec.rb +62 -0
  672. data/test/ruby/rack/preloader_spec.rb +74 -0
  673. data/test/ruby/{abstract_request_handler_spec.rb → request_handler_spec.rb} +31 -68
  674. data/test/ruby/shared/loader_spec.rb +241 -0
  675. data/test/ruby/shared/rails/analytics_logging_extensions_spec.rb +141 -182
  676. data/test/ruby/shared/ruby_loader_spec.rb +55 -0
  677. data/test/ruby/spec_helper.rb +8 -53
  678. data/test/ruby/utils/file_system_watcher_spec.rb +9 -1
  679. data/test/ruby/utils_spec.rb +10 -683
  680. data/test/stub/rack/config.ru +28 -3
  681. data/test/stub/rack/start.rb +47 -0
  682. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/Rakefile +0 -0
  683. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/app/controllers/application_controller.rb +0 -2
  684. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/app/controllers/bar_controller_1.rb +0 -0
  685. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/app/controllers/bar_controller_2.rb +0 -0
  686. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/app/controllers/foo_controller.rb +0 -0
  687. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/app/helpers/application_helper.rb +0 -0
  688. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/boot.rb +0 -0
  689. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/database.yml +3 -3
  690. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/environment.rb +5 -2
  691. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/environments/development.rb +0 -0
  692. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/environments/production.rb +0 -0
  693. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/environments/staging.rb +0 -0
  694. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/initializers/inflections.rb +0 -0
  695. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/initializers/mime_types.rb +0 -0
  696. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/routes.rb +1 -0
  697. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/about +0 -0
  698. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/console +0 -0
  699. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/dbconsole +0 -0
  700. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/destroy +0 -0
  701. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/generate +0 -0
  702. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/performance/benchmarker +0 -0
  703. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/performance/profiler +0 -0
  704. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/performance/request +0 -0
  705. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/plugin +0 -0
  706. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/process/inspector +0 -0
  707. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/process/reaper +0 -0
  708. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/process/spawner +0 -0
  709. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/runner +0 -0
  710. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/server +0 -0
  711. data/test/stub/{rails_apps/3.0/empty → rails3.0}/Gemfile +0 -0
  712. data/test/stub/rails3.0/Gemfile.lock +80 -0
  713. data/test/stub/{rails_apps/3.0/empty → rails3.0}/Rakefile +0 -0
  714. data/test/stub/{rails_apps/3.0/empty → rails3.0}/app/controllers/application_controller.rb +0 -0
  715. data/test/stub/{rails_apps/3.0/empty → rails3.0}/app/helpers/application_helper.rb +0 -0
  716. data/test/stub/{rails_apps/3.0/empty → rails3.0}/app/views/layouts/application.html.erb +0 -0
  717. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config.ru +0 -0
  718. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/application.rb +0 -0
  719. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/boot.rb +0 -0
  720. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/database.yml +0 -0
  721. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/environment.rb +0 -0
  722. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/environments/development.rb +0 -0
  723. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/environments/production.rb +0 -0
  724. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/environments/test.rb +0 -0
  725. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/initializers/backtrace_silencers.rb +0 -0
  726. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/initializers/inflections.rb +0 -0
  727. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/initializers/mime_types.rb +0 -0
  728. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/initializers/passenger.rb +0 -0
  729. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/initializers/secret_token.rb +0 -0
  730. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/initializers/session_store.rb +0 -0
  731. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/locales/en.yml +0 -0
  732. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/routes.rb +0 -0
  733. data/test/stub/{rails_apps/3.0/empty → rails3.0}/db/seeds.rb +0 -0
  734. data/test/stub/{rails_apps/3.0/empty → rails3.0}/doc/README_FOR_APP +0 -0
  735. data/test/stub/{rails_apps/3.0/empty → rails3.0}/public/404.html +0 -0
  736. data/test/stub/{rails_apps/3.0/empty → rails3.0}/public/422.html +0 -0
  737. data/test/stub/{rails_apps/3.0/empty → rails3.0}/public/500.html +0 -0
  738. data/test/stub/{rails_apps/3.0/empty → rails3.0}/public/favicon.ico +0 -0
  739. data/test/stub/{rails_apps/3.0/empty → rails3.0}/public/index.html +0 -0
  740. data/test/stub/{rails_apps/3.0/empty → rails3.0}/public/robots.txt +0 -0
  741. data/test/stub/{rails_apps/3.0/empty → rails3.0}/script/rails +0 -0
  742. data/test/stub/{rails_apps/3.0/empty → rails3.0}/test/performance/browsing_test.rb +0 -0
  743. data/test/stub/{rails_apps/3.0/empty → rails3.0}/test/test_helper.rb +0 -0
  744. data/test/stub/start_error.pl +24 -0
  745. data/test/stub/wsgi/passenger_wsgi.py +71 -3
  746. data/test/support/apache2_controller.rb +2 -2
  747. data/test/support/placebo-preloader.rb +88 -0
  748. data/test/support/test_helper.rb +1 -14
  749. data/test/tut/tut.h +11 -4
  750. metadata +590 -326
  751. data.tar.gz.asc +0 -12
  752. data/PACKAGING.TXT +0 -25
  753. data/build/config.rb +0 -46
  754. data/ext/apache2/HelperAgent.cpp +0 -364
  755. data/ext/boost/call_traits.hpp +0 -24
  756. data/ext/boost/detail/call_traits.hpp +0 -164
  757. data/ext/common/AbstractSpawnManager.h +0 -110
  758. data/ext/common/AgentBase.cpp +0 -432
  759. data/ext/common/ApplicationPool/Client.h +0 -788
  760. data/ext/common/ApplicationPool/Interface.h +0 -295
  761. data/ext/common/ApplicationPool/Pool.h +0 -1327
  762. data/ext/common/ApplicationPool/Server.h +0 -479
  763. data/ext/common/MessageChannel.h +0 -494
  764. data/ext/common/PoolOptions.h +0 -518
  765. data/ext/common/Process.h +0 -253
  766. data/ext/common/Session.h +0 -436
  767. data/ext/common/SpawnManager.h +0 -611
  768. data/ext/google/ChangeLog +0 -167
  769. data/ext/google/dense_hash_map +0 -310
  770. data/ext/google/dense_hash_set +0 -287
  771. data/ext/google/sparse_hash_map +0 -294
  772. data/ext/google/sparse_hash_set +0 -275
  773. data/ext/google/sparsehash/densehashtable.h +0 -1062
  774. data/ext/google/sparsehash/sparseconfig.h +0 -55
  775. data/ext/google/sparsehash/sparsehashtable.h +0 -1015
  776. data/ext/google/sparsetable +0 -1468
  777. data/ext/google/type_traits.h +0 -250
  778. data/ext/nginx/HelperAgent.cpp +0 -1355
  779. data/ext/nginx/ScgiRequestParser.h +0 -375
  780. data/ext/oxt/backtrace.cpp +0 -185
  781. data/ext/oxt/tracable_exception.cpp +0 -89
  782. data/helper-scripts/passenger-spawn-server +0 -106
  783. data/lib/phusion_passenger/abstract_request_handler.rb +0 -766
  784. data/lib/phusion_passenger/abstract_server.rb +0 -372
  785. data/lib/phusion_passenger/abstract_server_collection.rb +0 -335
  786. data/lib/phusion_passenger/app_process.rb +0 -174
  787. data/lib/phusion_passenger/classic_rails/application_spawner.rb +0 -344
  788. data/lib/phusion_passenger/classic_rails/framework_spawner.rb +0 -311
  789. data/lib/phusion_passenger/exceptions.rb +0 -103
  790. data/lib/phusion_passenger/html_template.rb +0 -107
  791. data/lib/phusion_passenger/rack/application_spawner.rb +0 -231
  792. data/lib/phusion_passenger/spawn_manager.rb +0 -359
  793. data/lib/phusion_passenger/templates/app_exited_during_initialization.html.erb +0 -38
  794. data/lib/phusion_passenger/templates/app_init_error.html.erb +0 -64
  795. data/lib/phusion_passenger/templates/database_error.html.erb +0 -66
  796. data/lib/phusion_passenger/templates/error_layout.html.erb +0 -39
  797. data/lib/phusion_passenger/templates/framework_init_error.html.erb +0 -39
  798. data/lib/phusion_passenger/templates/general_error.html.erb +0 -22
  799. data/lib/phusion_passenger/templates/load_error.html.erb +0 -46
  800. data/lib/phusion_passenger/templates/version_not_found.html.erb +0 -34
  801. data/lib/phusion_passenger/utils/rewindable_input.rb +0 -125
  802. data/lib/phusion_passenger/wsgi/application_spawner.rb +0 -108
  803. data/test/config.yml.example +0 -41
  804. data/test/cxx/ApplicationPool_PoolTest.cpp +0 -33
  805. data/test/cxx/ApplicationPool_PoolTestCases.cpp +0 -1029
  806. data/test/cxx/ApplicationPool_ServerTest.cpp +0 -308
  807. data/test/cxx/ApplicationPool_Server_PoolTest.cpp +0 -80
  808. data/test/cxx/MessageChannelTest.cpp +0 -557
  809. data/test/cxx/PoolOptionsTest.cpp +0 -116
  810. data/test/cxx/SpawnManagerTest.cpp +0 -161
  811. data/test/ruby/abstract_server_collection_spec.rb +0 -247
  812. data/test/ruby/abstract_server_spec.rb +0 -61
  813. data/test/ruby/app_process_spec.rb +0 -43
  814. data/test/ruby/classic_rails/application_spawner_spec.rb +0 -89
  815. data/test/ruby/classic_rails/framework_spawner_spec.rb +0 -92
  816. data/test/ruby/rack/application_spawner_spec.rb +0 -116
  817. data/test/ruby/shared/abstract_server_spec.rb +0 -23
  818. data/test/ruby/shared/spawners/classic_rails/framework_spawner_spec.rb +0 -38
  819. data/test/ruby/shared/spawners/classic_rails/lack_of_rails_gem_version_spec.rb +0 -19
  820. data/test/ruby/shared/spawners/classic_rails/spawner_spec.rb +0 -15
  821. data/test/ruby/shared/spawners/non_preloading_spawner_spec.rb +0 -27
  822. data/test/ruby/shared/spawners/preloading_spawner_spec.rb +0 -29
  823. data/test/ruby/shared/spawners/reload_all_spec.rb +0 -36
  824. data/test/ruby/shared/spawners/reload_single_spec.rb +0 -52
  825. data/test/ruby/shared/spawners/spawn_server_spec.rb +0 -28
  826. data/test/ruby/shared/spawners/spawner_spec.rb +0 -273
  827. data/test/ruby/shared/utils/pseudo_io_spec.rb +0 -60
  828. data/test/ruby/spawn_manager_spec.rb +0 -134
  829. data/test/ruby/wsgi/application_spawner_spec.rb +0 -50
  830. data/test/stub/message_channel.rb +0 -11
  831. data/test/stub/message_channel_2.rb +0 -12
  832. data/test/stub/message_channel_3.rb +0 -19
  833. data/test/stub/rails_apps/3.0/empty/Gemfile.lock +0 -73
  834. data/test/stub/spawn_server.rb +0 -22
  835. metadata.gz.asc +0 -12
@@ -0,0 +1,283 @@
1
+ /*
2
+ * Phusion Passenger - http://www.modrails.com/
3
+ * Copyright (c) 2011, 2012 Phusion
4
+ *
5
+ * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ * of this software and associated documentation files (the "Software"), to deal
9
+ * in the Software without restriction, including without limitation the rights
10
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the Software is
12
+ * furnished to do so, subject to the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included in
15
+ * all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ * THE SOFTWARE.
24
+ */
25
+
26
+ #include <agents/HelperAgent/RequestHandler.h>
27
+
28
+ namespace Passenger {
29
+
30
+
31
+ struct ev_loop *
32
+ Client::getLoop() const {
33
+ return requestHandler->libev->getLoop();
34
+ }
35
+
36
+ const SafeLibevPtr &
37
+ Client::getSafeLibev() const {
38
+ return requestHandler->libev;
39
+ }
40
+
41
+ unsigned int
42
+ Client::getConnectPasswordTimeout(const RequestHandler *handler) const {
43
+ return handler->connectPasswordTimeout;
44
+ }
45
+
46
+ size_t
47
+ Client::onClientInputData(const EventedBufferedInputPtr &source, const StaticString &data) {
48
+ Client *client = (Client *) source->userData;
49
+ if (client != NULL) {
50
+ return client->requestHandler->onClientInputData(client->shared_from_this(), data);
51
+ } else {
52
+ return 0;
53
+ }
54
+ }
55
+
56
+ void
57
+ Client::onClientInputError(const EventedBufferedInputPtr &source, const char *message, int errnoCode) {
58
+ Client *client = (Client *) source->userData;
59
+ if (client != NULL) {
60
+ client->requestHandler->onClientInputError(client->shared_from_this(), message, errnoCode);
61
+ }
62
+ }
63
+
64
+
65
+ void
66
+ Client::onClientBodyBufferData(const FileBackedPipePtr &source,
67
+ const char *data, size_t size,
68
+ const FileBackedPipe::ConsumeCallback &callback)
69
+ {
70
+ Client *client = (Client *) source->userData;
71
+ if (client != NULL) {
72
+ client->requestHandler->onClientBodyBufferData(client->shared_from_this(),
73
+ data, size, callback);
74
+ }
75
+ }
76
+
77
+ void
78
+ Client::onClientBodyBufferEnd(const FileBackedPipePtr &source) {
79
+ Client *client = (Client *) source->userData;
80
+ if (client != NULL) {
81
+ client->requestHandler->onClientBodyBufferEnd(client->shared_from_this());
82
+ }
83
+ }
84
+
85
+ void
86
+ Client::onClientBodyBufferError(const FileBackedPipePtr &source, int errorCode) {
87
+ Client *client = (Client *) source->userData;
88
+ if (client != NULL) {
89
+ client->requestHandler->onClientBodyBufferError(client->shared_from_this(), errorCode);
90
+ }
91
+ }
92
+
93
+ void
94
+ Client::onClientBodyBufferCommit(const FileBackedPipePtr &source) {
95
+ Client *client = (Client *) source->userData;
96
+ if (client != NULL) {
97
+ client->requestHandler->onClientBodyBufferCommit(client->shared_from_this());
98
+ }
99
+ }
100
+
101
+
102
+ void
103
+ Client::onClientOutputPipeData(const FileBackedPipePtr &source,
104
+ const char *data, size_t size,
105
+ const FileBackedPipe::ConsumeCallback &callback)
106
+ {
107
+ Client *client = (Client *) source->userData;
108
+ if (client != NULL) {
109
+ client->requestHandler->onClientOutputPipeData(client->shared_from_this(),
110
+ data, size, callback);
111
+ }
112
+ }
113
+
114
+ void
115
+ Client::onClientOutputPipeEnd(const FileBackedPipePtr &source) {
116
+ Client *client = (Client *) source->userData;
117
+ if (client != NULL) {
118
+ client->requestHandler->onClientOutputPipeEnd(client->shared_from_this());
119
+ }
120
+ }
121
+
122
+ void
123
+ Client::onClientOutputPipeError(const FileBackedPipePtr &source, int errorCode) {
124
+ Client *client = (Client *) source->userData;
125
+ if (client != NULL) {
126
+ client->requestHandler->onClientOutputPipeError(client->shared_from_this(), errorCode);
127
+ }
128
+ }
129
+
130
+ void
131
+ Client::onClientOutputPipeCommit(const FileBackedPipePtr &source) {
132
+ Client *client = (Client *) source->userData;
133
+ if (client != NULL) {
134
+ client->requestHandler->onClientOutputPipeCommit(client->shared_from_this());
135
+ }
136
+ }
137
+
138
+
139
+ void
140
+ Client::onClientOutputWritable(ev::io &io, int revents) {
141
+ assert(requestHandler != NULL);
142
+ requestHandler->onClientOutputWritable(shared_from_this());
143
+ }
144
+
145
+
146
+ size_t
147
+ Client::onAppInputData(const EventedBufferedInputPtr &source, const StaticString &data) {
148
+ Client *client = (Client *) source->userData;
149
+ if (client != NULL) {
150
+ return client->requestHandler->onAppInputData(client->shared_from_this(), data);
151
+ } else {
152
+ return 0;
153
+ }
154
+ }
155
+
156
+ void
157
+ Client::onAppInputChunk(const char *data, size_t size, void *userData) {
158
+ Client *client = (Client *) userData;
159
+ assert(client != NULL);
160
+ assert(client->requestHandler != NULL);
161
+ client->requestHandler->onAppInputChunk(client->shared_from_this(), StaticString(data, size));
162
+ }
163
+
164
+ void
165
+ Client::onAppInputError(const EventedBufferedInputPtr &source, const char *message, int errnoCode) {
166
+ Client *client = (Client *) source->userData;
167
+ if (client != NULL) {
168
+ client->requestHandler->onAppInputError(client->shared_from_this(), message, errnoCode);
169
+ }
170
+ }
171
+
172
+
173
+ void
174
+ Client::onAppOutputWritable(ev::io &io, int revents) {
175
+ assert(requestHandler != NULL);
176
+ requestHandler->onAppOutputWritable(shared_from_this());
177
+ }
178
+
179
+
180
+ void
181
+ Client::onTimeout(ev::timer &timer, int revents) {
182
+ assert(requestHandler != NULL);
183
+ requestHandler->onTimeout(shared_from_this());
184
+ }
185
+
186
+
187
+ Client *
188
+ RequestHandler::getClientPointer(const ClientPtr &client) {
189
+ return client.get();
190
+ }
191
+
192
+
193
+ } // namespace Passenger
194
+
195
+ #ifdef STANDALONE
196
+ #include <MultiLibeio.cpp>
197
+ #include <iostream>
198
+ #include <agents/Base.h>
199
+ #include <oxt/initialize.hpp>
200
+
201
+ using namespace std;
202
+ using namespace Passenger;
203
+ using namespace Passenger::ApplicationPool2;
204
+
205
+ static SafeLibevPtr libev;
206
+ static RequestHandler *handler;
207
+ static struct ev_loop *loop;
208
+ static PoolPtr pool;
209
+ static struct ev_signal sigquitwatcher, sigintwatcher;
210
+
211
+ static void
212
+ sigquit_cb(struct ev_loop *loop, ev_signal *w, int revents) {
213
+ handler->inspect(cout);
214
+ cout.flush();
215
+ }
216
+
217
+ static void
218
+ sigint_cb(struct ev_loop *loop, ev_signal *w, int revents) {
219
+ P_WARN("Exiting loop");
220
+ delete handler;
221
+ handler = NULL;
222
+ pool->destroy();
223
+ pool.reset();
224
+ ev_signal_stop(loop, &sigquitwatcher);
225
+ ev_signal_stop(loop, &sigintwatcher);
226
+ ev_break(loop, EVBREAK_ONE);
227
+ }
228
+
229
+ static void
230
+ ignoreSigpipe() {
231
+ struct sigaction action;
232
+ action.sa_handler = SIG_IGN;
233
+ action.sa_flags = 0;
234
+ sigemptyset(&action.sa_mask);
235
+ sigaction(SIGPIPE, &action, NULL);
236
+ }
237
+
238
+ int
239
+ main() {
240
+ oxt::initialize();
241
+ setup_syscall_interruption_support();
242
+ ignoreSigpipe();
243
+ //installAbortHandler();
244
+ setLogLevel(3);
245
+ MultiLibeio::init();
246
+ loop = EV_DEFAULT;
247
+ libev = make_shared<SafeLibev>(loop);
248
+ AgentOptions options;
249
+ ServerInstanceDir serverInstanceDir(getpid());
250
+ char root[PATH_MAX];
251
+ getcwd(root, sizeof(root));
252
+ #ifdef __linux__
253
+ const char *nogroup = "nogroup";
254
+ #else
255
+ const char *nogroup = "nobody";
256
+ #endif
257
+ options.passengerRoot = root;
258
+ options.loggingAgentAddress = "unix:/tmp/agent";
259
+ options.loggingAgentPassword = "1234";
260
+
261
+ SpawnerFactoryPtr spawnerFactory = make_shared<SpawnerFactory>(libev,
262
+ ResourceLocator(root),
263
+ serverInstanceDir.newGeneration(true, "nobody", nogroup, getpid(), getgid()));
264
+ UnionStation::LoggerFactoryPtr loggerFactory = make_shared<UnionStation::LoggerFactory>(options.loggingAgentAddress,
265
+ "logging", options.loggingAgentPassword);
266
+ pool = make_shared<Pool>(libev.get(), spawnerFactory, loggerFactory);
267
+ FileDescriptor requestSocket(createTcpServer("127.0.0.1", 3000));
268
+ setNonBlocking(requestSocket);
269
+ handler = new RequestHandler(libev, requestSocket, pool, options);
270
+
271
+ ev_signal_init(&sigquitwatcher, sigquit_cb, SIGQUIT);
272
+ ev_signal_start(loop, &sigquitwatcher);
273
+
274
+ ev_signal_init(&sigintwatcher, sigint_cb, SIGINT);
275
+ ev_signal_start(loop, &sigintwatcher);
276
+
277
+ P_DEBUG("Started");
278
+ ev_run(loop, 0);
279
+
280
+ MultiLibeio::shutdown();
281
+ return 0;
282
+ }
283
+ #endif
@@ -0,0 +1,2139 @@
1
+ /*
2
+ * Phusion Passenger - http://www.modrails.com/
3
+ * Copyright (c) 2011, 2012 Phusion
4
+ *
5
+ * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ * of this software and associated documentation files (the "Software"), to deal
9
+ * in the Software without restriction, including without limitation the rights
10
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the Software is
12
+ * furnished to do so, subject to the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included in
15
+ * all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ * THE SOFTWARE.
24
+ */
25
+
26
+ /*
27
+ STAGES
28
+
29
+ Accept connect password
30
+ |
31
+ \|/
32
+ Read header
33
+ |
34
+ \|/
35
+ +------+------+
36
+ | |
37
+ | |
38
+ \|/ |
39
+ Buffer |
40
+ request |
41
+ body |
42
+ | |
43
+ | |
44
+ \|/ |
45
+ Checkout <-------+
46
+ session
47
+ |
48
+ |
49
+ \|/
50
+ Send header
51
+ to app
52
+ |
53
+ |
54
+ \|/
55
+ Send request
56
+ body to app
57
+
58
+
59
+
60
+ OVERVIEW OF I/O CHANNELS, PIPES AND WATCHERS
61
+
62
+
63
+ OPTIONAL: appOutputWatcher
64
+ clientBodyBuffer (o)
65
+ | |
66
+ +----------+ | +-----------+ | +---------------+
67
+ | | ------ clientInput -----> | Request | ----------------> | |
68
+ | Client | fd | Handler | session | Application |
69
+ | | <--- clientOutputPipe --- | | <--- appInput --- | |
70
+ +----------+ | +-----------+ +---------------+
71
+ |
72
+ (o)
73
+ clientOutputWatcher
74
+
75
+ */
76
+
77
+ #ifndef _PASSENGER_REQUEST_HANDLER_H_
78
+ #define _PASSENGER_REQUEST_HANDLER_H_
79
+
80
+ #include <boost/shared_ptr.hpp>
81
+ #include <boost/weak_ptr.hpp>
82
+ #include <boost/make_shared.hpp>
83
+ #include <ev++.h>
84
+
85
+ #if defined(__GLIBCXX__) || defined(__APPLE__)
86
+ #include <cxxabi.h>
87
+ #define CXX_ABI_API_AVAILABLE
88
+ #endif
89
+
90
+ #include <sys/types.h>
91
+ #include <arpa/inet.h>
92
+ #include <sys/un.h>
93
+ #include <typeinfo>
94
+ #include <cassert>
95
+ #include <cctype>
96
+
97
+ #include <Logging.h>
98
+ #include <EventedBufferedInput.h>
99
+ #include <MessageReadersWriters.h>
100
+ #include <Constants.h>
101
+ #include <HttpConstants.h>
102
+ #include <UnionStation.h>
103
+ #include <ApplicationPool2/Pool.h>
104
+ #include <Utils/StrIntUtils.h>
105
+ #include <Utils/IOUtils.h>
106
+ #include <Utils/HttpHeaderBufferer.h>
107
+ #include <Utils/Template.h>
108
+ #include <Utils/Timer.h>
109
+ #include <Utils/Dechunker.h>
110
+ #include <agents/HelperAgent/AgentOptions.h>
111
+ #include <agents/HelperAgent/FileBackedPipe.h>
112
+ #include <agents/HelperAgent/ScgiRequestParser.h>
113
+
114
+ namespace Passenger {
115
+
116
+ using namespace std;
117
+ using namespace boost;
118
+ using namespace oxt;
119
+ using namespace ApplicationPool2;
120
+
121
+ class RequestHandler;
122
+
123
+ #define RH_WARN(client, x) P_WARN("[Client " << client->name() << "] " << x)
124
+ #define RH_DEBUG(client, x) P_DEBUG("[Client " << client->name() << "] " << x)
125
+ #define RH_TRACE(client, level, x) P_TRACE(level, "[Client " << client->name() << "] " << x)
126
+
127
+
128
+ class Client: public enable_shared_from_this<Client> {
129
+ private:
130
+ struct ev_loop *getLoop() const;
131
+ const SafeLibevPtr &getSafeLibev() const;
132
+ unsigned int getConnectPasswordTimeout(const RequestHandler *handler) const;
133
+
134
+ static size_t onClientInputData(const EventedBufferedInputPtr &source, const StaticString &data);
135
+ static void onClientInputError(const EventedBufferedInputPtr &source, const char *message, int errnoCode);
136
+
137
+ static void onClientBodyBufferData(const FileBackedPipePtr &source,
138
+ const char *data, size_t size,
139
+ const FileBackedPipe::ConsumeCallback &callback);
140
+ static void onClientBodyBufferEnd(const FileBackedPipePtr &source);
141
+ static void onClientBodyBufferError(const FileBackedPipePtr &source, int errorCode);
142
+ static void onClientBodyBufferCommit(const FileBackedPipePtr &source);
143
+
144
+ static void onClientOutputPipeData(const FileBackedPipePtr &source,
145
+ const char *data, size_t size,
146
+ const FileBackedPipe::ConsumeCallback &callback);
147
+ static void onClientOutputPipeEnd(const FileBackedPipePtr &source);
148
+ static void onClientOutputPipeError(const FileBackedPipePtr &source, int errorCode);
149
+ static void onClientOutputPipeCommit(const FileBackedPipePtr &source);
150
+
151
+ void onClientOutputWritable(ev::io &io, int revents);
152
+
153
+ static size_t onAppInputData(const EventedBufferedInputPtr &source, const StaticString &data);
154
+ static void onAppInputChunk(const char *data, size_t size, void *userData);
155
+ static void onAppInputError(const EventedBufferedInputPtr &source, const char *message, int errnoCode);
156
+
157
+ void onAppOutputWritable(ev::io &io, int revents);
158
+
159
+ void onTimeout(ev::timer &timer, int revents);
160
+
161
+
162
+ static const char *boolStr(bool val) {
163
+ static const char *strs[] = { "false", "true" };
164
+ return strs[val];
165
+ }
166
+
167
+ void resetPrimitiveFields() {
168
+ requestHandler = NULL;
169
+ state = DISCONNECTED;
170
+ backgroundOperations = 0;
171
+ requestBodyIsBuffered = false;
172
+ freeBufferedConnectPassword();
173
+ connectedAt = 0;
174
+ contentLength = 0;
175
+ clientBodyAlreadyRead = 0;
176
+ checkoutSessionAfterCommit = false;
177
+ sessionCheckedOut = false;
178
+ sessionCheckoutTry = 0;
179
+ responseHeaderSeen = false;
180
+ chunkedResponse = false;
181
+ appRoot.clear();
182
+ }
183
+
184
+ void freeScopeLogs() {
185
+ endScopeLog(&scopeLogs.requestProxying, false);
186
+ endScopeLog(&scopeLogs.getFromPool, false);
187
+ endScopeLog(&scopeLogs.bufferingRequestBody, false);
188
+ endScopeLog(&scopeLogs.requestProcessing, false);
189
+ }
190
+
191
+ public:
192
+ /** Back reference to the RequestHandler that this Client is associated with.
193
+ * NULL when this Client is not in the pool or is disconnected. */
194
+ RequestHandler *requestHandler;
195
+ /** File descriptor of the client socket. Is empty when this Client is not
196
+ * in the pool or is disconnected. */
197
+ FileDescriptor fd;
198
+ /** The last associated file descriptor number is stored here. It is not
199
+ * cleared after disassociating. Its only purpose is to make logging calls
200
+ * like RH_DEBUG() print the correct client name after disconnect() is called.
201
+ * Do not use this value for anything else as it may not refer to a valid
202
+ * file descriptor. */
203
+ int fdnum;
204
+
205
+
206
+ /***** Client <-> RequestHandler I/O channels, pipes and watchers *****/
207
+
208
+ /** Client input channel. */
209
+ EventedBufferedInputPtr clientInput;
210
+ /** If request body buffering is turned on, it will be buffered into this FileBackedPipe. */
211
+ FileBackedPipePtr clientBodyBuffer;
212
+ /** Client output pipe. */
213
+ FileBackedPipePtr clientOutputPipe;
214
+ /** Client output channel watcher. */
215
+ ev::io clientOutputWatcher;
216
+
217
+
218
+ /***** RequestHandler <-> Application I/O channels, pipes and watchers *****/
219
+
220
+ /** Application input channel. */
221
+ EventedBufferedInputPtr appInput;
222
+ string appOutputBuffer;
223
+ /** Application output channel watcher. */
224
+ ev::io appOutputWatcher;
225
+
226
+
227
+ /***** State variables *****/
228
+
229
+ enum {
230
+ BEGIN_READING_CONNECT_PASSWORD,
231
+ STILL_READING_CONNECT_PASSWORD,
232
+ READING_HEADER,
233
+ BUFFERING_REQUEST_BODY,
234
+ CHECKING_OUT_SESSION,
235
+ SENDING_HEADER_TO_APP,
236
+ FORWARDING_BODY_TO_APP,
237
+
238
+ // Special states
239
+ WRITING_SIMPLE_RESPONSE,
240
+ DISCONNECTED
241
+ } state;
242
+
243
+ /* How many background operations are currently in progress, e.g.
244
+ * an asyncGet() or bodyBuffer.add(). If the client is disconnected
245
+ * while this flag is true, then the Client object is not reassociateable
246
+ * in order to give the completion callbacks a chance to cancel properly.
247
+ */
248
+ unsigned int backgroundOperations;
249
+
250
+ struct {
251
+ char *data;
252
+ unsigned int alreadyRead;
253
+ } bufferedConnectPassword;
254
+
255
+ // Used for enforcing the connection timeout.
256
+ ev::timer timeoutTimer;
257
+
258
+ ev_tstamp connectedAt;
259
+ long long contentLength;
260
+ unsigned long long clientBodyAlreadyRead;
261
+ Options options;
262
+ ScgiRequestParser scgiParser;
263
+ SessionPtr session;
264
+ string appRoot;
265
+ struct {
266
+ UnionStation::ScopeLog
267
+ *requestProcessing,
268
+ *bufferingRequestBody,
269
+ *getFromPool,
270
+ *requestProxying;
271
+ } scopeLogs;
272
+ unsigned int sessionCheckoutTry;
273
+ bool requestBodyIsBuffered;
274
+ bool sessionCheckedOut;
275
+ bool checkoutSessionAfterCommit;
276
+
277
+ bool responseHeaderSeen;
278
+ bool chunkedResponse;
279
+ HttpHeaderBufferer responseHeaderBufferer;
280
+ Dechunker responseDechunker;
281
+
282
+
283
+ Client() {
284
+ fdnum = -1;
285
+
286
+ clientInput = make_shared< EventedBufferedInput<> >();
287
+ clientInput->onData = onClientInputData;
288
+ clientInput->onError = onClientInputError;
289
+ clientInput->userData = this;
290
+
291
+ clientBodyBuffer = make_shared<FileBackedPipe>("/tmp");
292
+ clientBodyBuffer->userData = this;
293
+ clientBodyBuffer->onData = onClientBodyBufferData;
294
+ clientBodyBuffer->onEnd = onClientBodyBufferEnd;
295
+ clientBodyBuffer->onError = onClientBodyBufferError;
296
+ clientBodyBuffer->onCommit = onClientBodyBufferCommit;
297
+
298
+ clientOutputPipe = make_shared<FileBackedPipe>("/tmp");
299
+ clientOutputPipe->userData = this;
300
+ clientOutputPipe->onData = onClientOutputPipeData;
301
+ clientOutputPipe->onEnd = onClientOutputPipeEnd;
302
+ clientOutputPipe->onError = onClientOutputPipeError;
303
+ clientOutputPipe->onCommit = onClientOutputPipeCommit;
304
+
305
+ clientOutputWatcher.set<Client, &Client::onClientOutputWritable>(this);
306
+
307
+
308
+ appInput = make_shared< EventedBufferedInput<> >();
309
+ appInput->onData = onAppInputData;
310
+ appInput->onError = onAppInputError;
311
+ appInput->userData = this;
312
+
313
+ appOutputWatcher.set<Client, &Client::onAppOutputWritable>(this);
314
+
315
+
316
+ timeoutTimer.set<Client, &Client::onTimeout>(this);
317
+
318
+
319
+ responseDechunker.onData = onAppInputChunk;
320
+ responseDechunker.userData = this;
321
+
322
+
323
+ bufferedConnectPassword.data = NULL;
324
+ bufferedConnectPassword.alreadyRead = 0;
325
+ memset(&scopeLogs, 0, sizeof(scopeLogs));
326
+ resetPrimitiveFields();
327
+ }
328
+
329
+ ~Client() {
330
+ if (requestHandler != NULL) {
331
+ discard();
332
+ }
333
+ clientInput->userData = NULL;
334
+ clientBodyBuffer->userData = NULL;
335
+ clientOutputPipe->userData = NULL;
336
+ appInput->userData = NULL;
337
+ freeBufferedConnectPassword();
338
+ freeScopeLogs();
339
+ }
340
+
341
+ void associate(RequestHandler *handler, const FileDescriptor &_fd) {
342
+ assert(requestHandler == NULL);
343
+ requestHandler = handler;
344
+ fd = _fd;
345
+ fdnum = _fd;
346
+ state = BEGIN_READING_CONNECT_PASSWORD;
347
+ connectedAt = ev_time();
348
+
349
+ clientInput->reset(getSafeLibev().get(), _fd);
350
+ clientInput->start();
351
+ clientBodyBuffer->reset(getSafeLibev());
352
+ clientOutputPipe->reset(getSafeLibev());
353
+ clientOutputPipe->start();
354
+ clientOutputWatcher.set(getLoop());
355
+ clientOutputWatcher.set(_fd, ev::WRITE);
356
+
357
+ // appOutputWatcher is initialized in initiateSession.
358
+
359
+ timeoutTimer.set(getLoop());
360
+ timeoutTimer.start(getConnectPasswordTimeout(handler) / 1000.0, 0.0);
361
+ }
362
+
363
+ void disassociate() {
364
+ assert(requestHandler != NULL);
365
+ resetPrimitiveFields();
366
+ fd = FileDescriptor();
367
+
368
+ clientInput->reset(NULL, FileDescriptor());
369
+ clientBodyBuffer->reset();
370
+ clientOutputPipe->reset();
371
+ clientOutputWatcher.stop();
372
+
373
+ appInput->reset(NULL, FileDescriptor());
374
+ appOutputBuffer.resize(0);
375
+ appOutputWatcher.stop();
376
+
377
+ timeoutTimer.stop();
378
+ scgiParser.reset();
379
+ session.reset();
380
+ responseHeaderBufferer.reset();
381
+ responseDechunker.reset();
382
+ freeScopeLogs();
383
+ }
384
+
385
+ void discard() {
386
+ assert(requestHandler != NULL);
387
+ resetPrimitiveFields();
388
+ fd = FileDescriptor();
389
+
390
+ clientInput->stop();
391
+ clientBodyBuffer->reset();
392
+ clientOutputPipe->reset();
393
+ clientOutputWatcher.stop();
394
+
395
+ appInput->stop();
396
+ appOutputWatcher.stop();
397
+
398
+ timeoutTimer.stop();
399
+
400
+ freeScopeLogs();
401
+ }
402
+
403
+ bool reassociateable() const {
404
+ return requestHandler == NULL
405
+ && backgroundOperations == 0
406
+ && clientInput->resetable()
407
+ && clientBodyBuffer->resetable()
408
+ && clientOutputPipe->resetable()
409
+ && appInput->resetable();
410
+ }
411
+
412
+ string name() const {
413
+ if (fdnum == -1) {
414
+ return "(null)";
415
+ } else {
416
+ return toString(fdnum);
417
+ }
418
+ }
419
+
420
+ bool connected() const {
421
+ return requestHandler != NULL;
422
+ }
423
+
424
+ const char *getStateName() const {
425
+ switch (state) {
426
+ case BEGIN_READING_CONNECT_PASSWORD:
427
+ return "BEGIN_READING_CONNECT_PASSWORD";
428
+ case STILL_READING_CONNECT_PASSWORD:
429
+ return "STILL_READING_CONNECT_PASSWORD";
430
+ case READING_HEADER:
431
+ return "READING_HEADER";
432
+ case BUFFERING_REQUEST_BODY:
433
+ return "BUFFERING_REQUEST_BODY";
434
+ case CHECKING_OUT_SESSION:
435
+ return "CHECKING_OUT_SESSION";
436
+ case SENDING_HEADER_TO_APP:
437
+ return "SENDING_HEADER_TO_APP";
438
+ case FORWARDING_BODY_TO_APP:
439
+ return "FORWARDING_BODY_TO_APP";
440
+ case WRITING_SIMPLE_RESPONSE:
441
+ return "WRITING_SIMPLE_RESPONSE";
442
+ case DISCONNECTED:
443
+ return "DISCONNECTED";
444
+ default:
445
+ return "UNKNOWN";
446
+ }
447
+ }
448
+
449
+ void freeBufferedConnectPassword() {
450
+ if (bufferedConnectPassword.data != NULL) {
451
+ free(bufferedConnectPassword.data);
452
+ bufferedConnectPassword.data = NULL;
453
+ bufferedConnectPassword.alreadyRead = 0;
454
+ }
455
+ }
456
+
457
+ bool shouldHalfCloseWrite() const {
458
+ return session->getProtocol() == "session";
459
+ }
460
+
461
+ bool useUnionStation() const {
462
+ return options.logger != NULL;
463
+ }
464
+
465
+ UnionStation::LoggerPtr getLogger() const {
466
+ return options.logger;
467
+ }
468
+
469
+ void beginScopeLog(UnionStation::ScopeLog **scopeLog, const char *name) {
470
+ if (options.logger != NULL) {
471
+ *scopeLog = new UnionStation::ScopeLog(options.logger, name);
472
+ }
473
+ }
474
+
475
+ void endScopeLog(UnionStation::ScopeLog **scopeLog, bool success = true) {
476
+ if (success && *scopeLog != NULL) {
477
+ (*scopeLog)->success();
478
+ }
479
+ delete *scopeLog;
480
+ *scopeLog = NULL;
481
+ }
482
+
483
+ void logMessage(const StaticString &message) {
484
+ options.logger->message(message);
485
+ }
486
+
487
+ void verifyInvariants() const {
488
+ assert((requestHandler == NULL) == (fd == -1));
489
+ assert((requestHandler == NULL) == (state == DISCONNECTED));
490
+ }
491
+
492
+ template<typename Stream>
493
+ void inspect(Stream &stream) const {
494
+ const char *indent = " ";
495
+ time_t the_time;
496
+ struct tm the_tm;
497
+ char timestr[60];
498
+
499
+ the_time = (time_t) connectedAt;
500
+ localtime_r(&the_time, &the_tm);
501
+ strftime(timestr, sizeof(timestr) - 1, "%F %H:%M:%S", &the_tm);
502
+
503
+ stream << indent << "host = " << (scgiParser.getHeader("HTTP_HOST").empty() ? "(empty)" : scgiParser.getHeader("HTTP_HOST")) << "\n";
504
+ stream << indent << "uri = " << (scgiParser.getHeader("REQUEST_URI").empty() ? "(empty)" : scgiParser.getHeader("REQUEST_URI")) << "\n";
505
+ stream << indent << "connected at = " << timestr << " (" << (unsigned long long) (ev_time() - connectedAt) << " sec ago)\n";
506
+ stream << indent << "state = " << getStateName() << "\n";
507
+ if (session == NULL) {
508
+ stream << indent << "session = NULL\n";
509
+ } else {
510
+ stream << indent << "session pid = " << session->getPid() << "\n";
511
+ stream << indent << "session gupid = " << session->getGupid() << "\n";
512
+ stream << indent << "session initiated = " << boolStr(session->initiated()) << "\n";
513
+ }
514
+ stream
515
+ << indent << "requestBodyIsBuffered = " << boolStr(requestBodyIsBuffered) << "\n"
516
+ << indent << "contentLength = " << contentLength << "\n"
517
+ << indent << "clientBodyAlreadyRead = " << clientBodyAlreadyRead << "\n"
518
+ << indent << "clientInput = " << clientInput.get() << " " << clientInput->inspect() << "\n"
519
+ << indent << "clientInput started = " << boolStr(clientInput->isStarted()) << "\n"
520
+ << indent << "clientBodyBuffer started = " << boolStr(clientBodyBuffer->isStarted()) << "\n"
521
+ << indent << "clientBodyBuffer reachedEnd = " << boolStr(clientBodyBuffer->reachedEnd()) << "\n"
522
+ << indent << "clientOutputPipe started = " << boolStr(clientOutputPipe->isStarted()) << "\n"
523
+ << indent << "clientOutputPipe reachedEnd = " << boolStr(clientOutputPipe->reachedEnd()) << "\n"
524
+ << indent << "clientOutputWatcher active = " << boolStr(clientOutputWatcher.is_active()) << "\n"
525
+ << indent << "appInput = " << appInput.get() << " " << appInput->inspect() << "\n"
526
+ << indent << "appInput started = " << boolStr(appInput->isStarted()) << "\n"
527
+ << indent << "appInput reachedEnd = " << boolStr(appInput->endReached()) << "\n"
528
+ << indent << "responseHeaderSeen = " << boolStr(responseHeaderSeen) << "\n"
529
+ << indent << "useUnionStation = " << boolStr(useUnionStation()) << "\n"
530
+ ;
531
+ }
532
+ };
533
+
534
+ typedef shared_ptr<Client> ClientPtr;
535
+
536
+
537
+ class RequestHandler {
538
+ private:
539
+ friend class Client;
540
+ typedef UnionStation::LoggerFactory LoggerFactory;
541
+ typedef UnionStation::LoggerFactoryPtr LoggerFactoryPtr;
542
+ typedef UnionStation::LoggerPtr LoggerPtr;
543
+
544
+ const SafeLibevPtr libev;
545
+ FileDescriptor requestSocket;
546
+ PoolPtr pool;
547
+ const AgentOptions &options;
548
+ const ResourceLocator resourceLocator;
549
+ LoggerFactoryPtr loggerFactory;
550
+ ev::io requestSocketWatcher;
551
+ ev::timer resumeSocketWatcherTimer;
552
+ HashMap<int, ClientPtr> clients;
553
+ Timer inactivityTimer;
554
+ bool accept4Available;
555
+
556
+
557
+ void disconnect(const ClientPtr &client) {
558
+ // Prevent Client object from being destroyed until we're done.
559
+ ClientPtr reference = client;
560
+
561
+ clients.erase(client->fd);
562
+ client->discard();
563
+ client->verifyInvariants();
564
+ RH_DEBUG(client, "Disconnected; new client count = " << clients.size());
565
+
566
+ if (clients.empty()) {
567
+ inactivityTimer.start();
568
+ }
569
+ }
570
+
571
+ void disconnectWithError(const ClientPtr &client, const StaticString &message) {
572
+ RH_WARN(client, "Disconnecting with error: " << message);
573
+ if (client->useUnionStation()) {
574
+ client->logMessage("Disconnecting with error: " + message);
575
+ }
576
+ disconnect(client);
577
+ }
578
+
579
+ void disconnectWithClientSocketWriteError(const ClientPtr &client, int e) {
580
+ stringstream message;
581
+ message << "client socket write error: ";
582
+ message << strerror(e);
583
+ message << " (errno=" << e << ")";
584
+ disconnectWithError(client, message.str());
585
+ }
586
+
587
+ void disconnectWithAppSocketWriteError(const ClientPtr &client, int e) {
588
+ stringstream message;
589
+ message << "app socket write error: ";
590
+ message << strerror(e);
591
+ message << " (errno=" << e << ")";
592
+ disconnectWithError(client, message.str());
593
+ }
594
+
595
+ void disconnectWithWarning(const ClientPtr &client, const StaticString &message) {
596
+ P_DEBUG("Disconnected client " << client->name() << " with warning: " << message);
597
+ disconnect(client);
598
+ }
599
+
600
+ // GDB helper function, implemented in .cpp file to prevent inlining.
601
+ Client *getClientPointer(const ClientPtr &client);
602
+
603
+ void getInactivityTime(unsigned long long *result) const {
604
+ *result = inactivityTimer.elapsed();
605
+ }
606
+
607
+ static bool getBoolOption(const ClientPtr &client, const StaticString &name, bool defaultValue = false) {
608
+ ScgiRequestParser::const_iterator it = client->scgiParser.getHeaderIterator(name);
609
+ if (it != client->scgiParser.end()) {
610
+ return it->second == "true";
611
+ } else {
612
+ return defaultValue;
613
+ }
614
+ }
615
+
616
+ static long long getLongLongOption(const ClientPtr &client, const StaticString &name, long long defaultValue = -1) {
617
+ ScgiRequestParser::const_iterator it = client->scgiParser.getHeaderIterator(name);
618
+ if (it != client->scgiParser.end()) {
619
+ long long result = stringToULL(it->second);
620
+ // The client may send a malicious integer, so check for this.
621
+ if (result < 0) {
622
+ return defaultValue;
623
+ } else {
624
+ return result;
625
+ }
626
+ } else {
627
+ return defaultValue;
628
+ }
629
+ }
630
+
631
+ void writeErrorResponse(const ClientPtr &client, const StaticString &message, const SpawnException *e = NULL) {
632
+ assert(client->state < Client::FORWARDING_BODY_TO_APP);
633
+ client->state = Client::WRITING_SIMPLE_RESPONSE;
634
+
635
+ string templatesDir = resourceLocator.getResourcesDir() + "/templates";
636
+ string data;
637
+
638
+ if (getBoolOption(client, "PASSENGER_FRIENDLY_ERROR_PAGES", true)) {
639
+ string cssFile = templatesDir + "/error_layout.css";
640
+ string errorLayoutFile = templatesDir + "/error_layout.html.template";
641
+ string generalErrorFile =
642
+ (e != NULL && e->isHTML())
643
+ ? templatesDir + "/general_error_with_html.html.template"
644
+ : templatesDir + "/general_error.html.template";
645
+ string css = readAll(cssFile);
646
+ StringMap<StaticString> params;
647
+
648
+ params.set("CSS", css);
649
+ params.set("APP_ROOT", client->options.appRoot);
650
+ params.set("RUBY", client->options.ruby);
651
+ params.set("ENVIRONMENT", client->options.environment);
652
+ params.set("MESSAGE", message);
653
+ params.set("IS_RUBY_APP",
654
+ (client->options.appType == "classic-rails" || client->options.appType == "rack")
655
+ ? "true" : "false");
656
+ if (e != NULL) {
657
+ params.set("TITLE", "Web application could not be started");
658
+ // Store all SpawnException annotations into 'params',
659
+ // but convert its name to uppercase.
660
+ const map<string, string> &annotations = e->getAnnotations();
661
+ map<string, string>::const_iterator it, end = annotations.end();
662
+ for (it = annotations.begin(); it != end; it++) {
663
+ string name = it->first;
664
+ for (string::size_type i = 0; i < name.size(); i++) {
665
+ name[i] = toupper(name[i]);
666
+ }
667
+ params.set(name, it->second);
668
+ }
669
+ } else {
670
+ params.set("TITLE", "Internal server error");
671
+ }
672
+ string content = Template::apply(readAll(generalErrorFile), params);
673
+ params.set("CONTENT", content);
674
+ data = Template::apply(readAll(errorLayoutFile), params);
675
+ } else {
676
+ data = readAll(templatesDir + "/undisclosed_error.html.template");
677
+ }
678
+
679
+ stringstream str;
680
+ if (getBoolOption(client, "PASSENGER_STATUS_LINE", true)) {
681
+ str << "HTTP/1.1 500 Internal Server Error\r\n";
682
+ }
683
+ str << "Status: 500 Internal Server Error\r\n";
684
+ str << "Content-Length: " << data.size() << "\r\n";
685
+ str << "Content-Type: text/html; charset=UTF-8\r\n";
686
+ str << "Cache-Control: no-cache, no-store, must-revalidate\r\n";
687
+ str << "\r\n";
688
+
689
+ const string &header = str.str();
690
+ client->clientOutputPipe->write(header.data(), header.size());
691
+ client->clientOutputPipe->write(data.data(), data.size());
692
+ client->clientOutputPipe->end();
693
+ }
694
+
695
+
696
+ /*****************************************************
697
+ * COMPONENT: appInput -> clientOutputPipe plumbing
698
+ *
699
+ * The following code receives data from appInput,
700
+ * possibly modifies it, and forwards it to
701
+ * clientOutputPipe.
702
+ *****************************************************/
703
+
704
+ struct Header {
705
+ StaticString key;
706
+ StaticString value;
707
+
708
+ Header() { }
709
+
710
+ Header(const StaticString &_key, const StaticString &_value)
711
+ : key(_key),
712
+ value(_value)
713
+ { }
714
+
715
+ bool empty() const {
716
+ return key.empty();
717
+ }
718
+
719
+ const char *begin() const {
720
+ return key.data();
721
+ }
722
+
723
+ const char *end() const {
724
+ return value.data() + value.size() + sizeof("\r\n") - 1;
725
+ }
726
+
727
+ size_t size() const {
728
+ return end() - begin();
729
+ }
730
+ };
731
+
732
+ /** Given a substring containing the start of the header value,
733
+ * extracts the substring that contains a single header value.
734
+ *
735
+ * const char *data =
736
+ * "Status: 200 OK\r\n"
737
+ * "Foo: bar\r\n";
738
+ * extractHeaderValue(data + strlen("Status:"), strlen(data) - strlen("Status:"));
739
+ * // "200 OK"
740
+ */
741
+ static StaticString extractHeaderValue(const char *data, size_t size) {
742
+ const char *start = data;
743
+ const char *end = data + size;
744
+ const char *terminator;
745
+
746
+ while (start < end && *start == ' ') {
747
+ start++;
748
+ }
749
+
750
+ terminator = (const char *) memchr(start, '\r', end - start);
751
+ if (terminator == NULL) {
752
+ return StaticString();
753
+ } else {
754
+ return StaticString(start, terminator - start);
755
+ }
756
+ }
757
+
758
+ static Header lookupHeader(const StaticString &headerData, const StaticString &name) {
759
+ string::size_type searchStart = 0;
760
+ while (searchStart < headerData.size()) {
761
+ string::size_type pos = headerData.find(name, searchStart);
762
+ if (OXT_UNLIKELY(pos == string::npos)) {
763
+ return Header();
764
+ } else if ((pos == 0 || headerData[pos - 1] == '\n')
765
+ && headerData.size() > pos + name.size()
766
+ && headerData[pos + name.size()] == ':')
767
+ {
768
+ StaticString value = extractHeaderValue(
769
+ headerData.data() + pos + name.size() + 1,
770
+ headerData.size() - pos - name.size() - 1);
771
+ return Header(headerData.substr(pos, name.size()), value);
772
+ } else {
773
+ searchStart = pos + name.size() + 1;
774
+ }
775
+ }
776
+ return Header();
777
+ }
778
+
779
+ static Header lookupHeader(const StaticString &headerData, const StaticString &name,
780
+ const StaticString &name2)
781
+ {
782
+ Header header = lookupHeader(headerData, name);
783
+ if (header.empty()) {
784
+ header = lookupHeader(headerData, name2);
785
+ }
786
+ return header;
787
+ }
788
+
789
+ bool addStatusHeaderFromStatusLine(const ClientPtr &client, string &headerData) {
790
+ string::size_type begin = headerData.find(' ');
791
+ string::size_type end = headerData.find("\r\n");
792
+ if (begin != string::npos && end != string::npos) {
793
+ StaticString status(headerData.data() + begin, end - begin);
794
+ headerData.append("Status: ");
795
+ headerData.append(status);
796
+ headerData.append("\r\n");
797
+ return true;
798
+ } else {
799
+ disconnectWithError(client, "application sent malformed response: the HTTP status line is invalid.");
800
+ return false;
801
+ }
802
+ }
803
+
804
+ static bool addReasonPhrase(string &headerData, const Header &status) {
805
+ if (status.value.find(' ') == string::npos) {
806
+ int statusCode = stringToInt(status.value);
807
+ const char *statusCodeAndReasonPhrase = getStatusCodeAndReasonPhrase(statusCode);
808
+ char newStatus[100];
809
+ char *pos = newStatus;
810
+ const char *end = newStatus + sizeof(newStatus);
811
+
812
+ pos = appendData(pos, end, "Status: ");
813
+ if (statusCodeAndReasonPhrase == NULL) {
814
+ pos = appendData(pos, end, toString(statusCode));
815
+ pos = appendData(pos, end, " Unknown Reason-Phrase\r\n");
816
+ } else {
817
+ pos = appendData(pos, end, statusCodeAndReasonPhrase);
818
+ pos = appendData(pos, end, "\r\n");
819
+ }
820
+
821
+ headerData.replace(status.begin() - headerData.data(), status.size(),
822
+ newStatus, pos - newStatus);
823
+ return true;
824
+ } else {
825
+ return false;
826
+ }
827
+ }
828
+
829
+ bool removeStatusLine(const ClientPtr &client, string &headerData) {
830
+ string::size_type end = headerData.find("\r\n");
831
+ if (end != string::npos) {
832
+ headerData.erase(0, end + 2);
833
+ return true;
834
+ } else {
835
+ disconnectWithError(client, "application sent malformed response: the HTTP status line is invalid.");
836
+ return false;
837
+ }
838
+ }
839
+
840
+ static void addStatusLineFromStatusHeader(string &headerData, const Header &status) {
841
+ char statusLine[100];
842
+ char *pos = statusLine;
843
+ const char *end = statusLine + sizeof(statusLine);
844
+
845
+ pos = appendData(pos, end, "HTTP/1.1 ");
846
+ pos = appendData(pos, end, status.value);
847
+ pos = appendData(pos, end, "\r\n");
848
+
849
+ headerData.insert(0, statusLine, pos - statusLine);
850
+ }
851
+
852
+ static void removeHeader(string &headerData, const Header &header) {
853
+ headerData.erase(header.begin() - headerData.data(), header.size());
854
+ }
855
+
856
+ /*
857
+ * Given a full header, possibly modify the header and send it to the clientOutputPipe.
858
+ */
859
+ bool processResponseHeader(const ClientPtr &client,
860
+ const StaticString &origHeaderData)
861
+ {
862
+ string headerData;
863
+ headerData.reserve(origHeaderData.size() + 100);
864
+ // Strip trailing CRLF.
865
+ headerData.append(origHeaderData.data(), origHeaderData.size() - 2);
866
+
867
+ if (startsWith(headerData, "HTTP/1.")) {
868
+ Header status = lookupHeader(headerData, "Status", "status");
869
+ if (status.empty()) {
870
+ // Add status header if necessary.
871
+ if (!addStatusHeaderFromStatusLine(client, headerData)) {
872
+ return false;
873
+ }
874
+ } else {
875
+ // Add reason phrase to existing status header if necessary.
876
+ addReasonPhrase(headerData, status);
877
+ }
878
+ // Remove status line if necesary.
879
+ if (!getBoolOption(client, "PASSENGER_STATUS_LINE", true)) {
880
+ if (!removeStatusLine(client, headerData)) {
881
+ return false;
882
+ }
883
+ }
884
+ } else {
885
+ Header status = lookupHeader(headerData, "Status", "status");
886
+ if (!status.empty()) {
887
+ // Add reason phrase to status header if necessary.
888
+ if (addReasonPhrase(headerData, status)) {
889
+ status = lookupHeader(headerData, "Status", "status");
890
+ }
891
+ // Add status line if necessary.
892
+ if (getBoolOption(client, "PASSENGER_STATUS_LINE", true)) {
893
+ addStatusLineFromStatusHeader(headerData, status);
894
+ }
895
+ } else {
896
+ disconnectWithError(client, "application sent malformed response: it didn't send an HTTP status line or a Status header.");
897
+ return false;
898
+ }
899
+ }
900
+
901
+ // Process chunked transfer encoding.
902
+ Header transferEncoding = lookupHeader(headerData, "Transfer-Encoding", "transfer-encoding");
903
+ if (!transferEncoding.empty() && transferEncoding.value == "chunked") {
904
+ P_TRACE(3, "Response with chunked transfer encoding detected.");
905
+ client->chunkedResponse = true;
906
+ removeHeader(headerData, transferEncoding);
907
+ }
908
+
909
+ // Add X-Powered-By.
910
+ if (getBoolOption(client, "PASSENGER_SHOW_VERSION_IN_HEADER", true)) {
911
+ headerData.append("X-Powered-By: Phusion Passenger " PASSENGER_VERSION "\r\n");
912
+ } else {
913
+ headerData.append("X-Powered-By: Phusion Passenger\r\n");
914
+ }
915
+
916
+ headerData.append("\r\n");
917
+ writeToClientOutputPipe(client, headerData);
918
+ return true;
919
+ }
920
+
921
+ void writeToClientOutputPipe(const ClientPtr &client, const StaticString &data) {
922
+ bool wasCommittingToDisk = client->clientOutputPipe->isCommittingToDisk();
923
+ bool nowCommittingToDisk = !client->clientOutputPipe->write(data.data(), data.size());
924
+ if (!wasCommittingToDisk && nowCommittingToDisk) {
925
+ RH_TRACE(client, 3, "Buffering response data to disk; temporarily stopping application socket.");
926
+ client->backgroundOperations++;
927
+ // If the data comes from writeErrorResponse(), then appInput is not available.
928
+ if (client->session != NULL && client->session->initiated()) {
929
+ client->appInput->stop();
930
+ }
931
+ }
932
+ }
933
+
934
+ size_t onAppInputData(const ClientPtr &client, const StaticString &data) {
935
+ if (!client->connected()) {
936
+ return 0;
937
+ }
938
+
939
+ if (!data.empty()) {
940
+ RH_TRACE(client, 3, "Application sent data: \"" << cEscapeString(data) << "\"");
941
+
942
+ // Buffer the application response until we've encountered the end of the header.
943
+ if (!client->responseHeaderSeen) {
944
+ size_t consumed = client->responseHeaderBufferer.feed(data.data(), data.size());
945
+ if (!client->responseHeaderBufferer.acceptingInput()) {
946
+ if (client->responseHeaderBufferer.hasError()) {
947
+ disconnectWithError(client, "application response format error (invalid header)");
948
+ } else {
949
+ // Now that we have a full header, do something with it.
950
+ client->responseHeaderSeen = true;
951
+ StaticString header = client->responseHeaderBufferer.getData();
952
+ if (processResponseHeader(client, header)) {
953
+ return consumed;
954
+ } else {
955
+ assert(!client->connected());
956
+ }
957
+ }
958
+ }
959
+ // The header has already been processed so forward it
960
+ // directly to clientOutputPipe, possibly through a
961
+ // dechunker first.
962
+ } else if (client->chunkedResponse) {
963
+ client->responseDechunker.feed(data.data(), data.size());
964
+ } else {
965
+ onAppInputChunk(client, data);
966
+ }
967
+ return data.size();
968
+
969
+ } else {
970
+ onAppInputEof(client);
971
+ return 0;
972
+ }
973
+ }
974
+
975
+ void onAppInputChunk(const ClientPtr &client, const StaticString &data) {
976
+ writeToClientOutputPipe(client, data);
977
+ }
978
+
979
+ void onAppInputEof(const ClientPtr &client) {
980
+ if (!client->connected()) {
981
+ return;
982
+ }
983
+
984
+ RH_DEBUG(client, "Application sent EOF");
985
+ client->endScopeLog(&client->scopeLogs.requestProxying);
986
+ client->clientOutputPipe->end();
987
+ }
988
+
989
+ void onAppInputError(const ClientPtr &client, const char *message, int errorCode) {
990
+ if (!client->connected()) {
991
+ return;
992
+ }
993
+
994
+ if (errorCode == ECONNRESET) {
995
+ // We might as well treat ECONNRESET like an EOF.
996
+ // http://stackoverflow.com/questions/2974021/what-does-econnreset-mean-in-the-context-of-an-af-local-socket
997
+ onAppInputEof(client);
998
+ } else {
999
+ stringstream message;
1000
+ message << "application socket read error: ";
1001
+ message << strerror(errorCode);
1002
+ message << " (fd=" << client->appInput->getFd();
1003
+ message << ", errno=" << errorCode << ")";
1004
+ disconnectWithError(client, message.str());
1005
+ }
1006
+ }
1007
+
1008
+ void onClientOutputPipeCommit(const ClientPtr &client) {
1009
+ if (!client->connected()) {
1010
+ return;
1011
+ }
1012
+
1013
+ RH_TRACE(client, 3, "Done buffering response data to disk; resuming application socket.");
1014
+ client->backgroundOperations--;
1015
+ // If the data comes from writeErrorResponse(), then appInput is not available.
1016
+ if (client->session != NULL && client->session->initiated()) {
1017
+ client->appInput->start();
1018
+ }
1019
+ }
1020
+
1021
+
1022
+ /*****************************************************
1023
+ * COMPONENT: clientOutputPipe -> client fd plumbing
1024
+ *
1025
+ * The following code handles forwarding data from
1026
+ * clientOutputPipe to the client socket.
1027
+ *****************************************************/
1028
+
1029
+ void onClientOutputPipeData(const ClientPtr &client, const char *data,
1030
+ size_t size, const FileBackedPipe::ConsumeCallback &consumed)
1031
+ {
1032
+ if (!client->connected()) {
1033
+ return;
1034
+ }
1035
+
1036
+ RH_TRACE(client, 3, "Forwarding " << size << " bytes of application data to client.");
1037
+ ssize_t ret = syscalls::write(client->fd, data, size);
1038
+ if (ret == -1) {
1039
+ int e = errno;
1040
+ RH_TRACE(client, 3, "Could not write to client socket: " << strerror(e) << " (errno=" << e << ")");
1041
+ if (e == EAGAIN) {
1042
+ RH_TRACE(client, 3, "Waiting until the client socket is writable again.");
1043
+ client->clientOutputWatcher.start();
1044
+ consumed(0, true);
1045
+ } else if (e == EPIPE) {
1046
+ // If the client closed the connection then disconnect quietly.
1047
+ if (client->useUnionStation()) {
1048
+ client->logMessage("Disconnecting: client stopped reading prematurely");
1049
+ }
1050
+ disconnect(client);
1051
+ } else {
1052
+ disconnectWithClientSocketWriteError(client, e);
1053
+ }
1054
+ } else {
1055
+ RH_TRACE(client, 3, "Managed to forward " << ret << " bytes.");
1056
+ consumed(ret, false);
1057
+ }
1058
+ }
1059
+
1060
+ void onClientOutputPipeEnd(const ClientPtr &client) {
1061
+ if (!client->connected()) {
1062
+ return;
1063
+ }
1064
+
1065
+ RH_TRACE(client, 2, "Client output pipe ended; disconnecting client");
1066
+ client->endScopeLog(&client->scopeLogs.requestProcessing);
1067
+ disconnect(client);
1068
+ }
1069
+
1070
+ void onClientOutputPipeError(const ClientPtr &client, int errorCode) {
1071
+ if (!client->connected()) {
1072
+ return;
1073
+ }
1074
+
1075
+ stringstream message;
1076
+ message << "client output pipe error: ";
1077
+ message << strerror(errorCode);
1078
+ message << " (errno=" << errorCode << ")";
1079
+ disconnectWithError(client, message.str());
1080
+ }
1081
+
1082
+ void onClientOutputWritable(const ClientPtr &client) {
1083
+ if (!client->connected()) {
1084
+ return;
1085
+ }
1086
+
1087
+ // Continue forwarding output data to the client.
1088
+ RH_TRACE(client, 3, "Client socket became writable again.");
1089
+ client->clientOutputWatcher.stop();
1090
+ assert(!client->clientOutputPipe->isStarted());
1091
+ client->clientOutputPipe->start();
1092
+ }
1093
+
1094
+
1095
+ /*****************************************************
1096
+ * COMPONENT: client acceptor
1097
+ *
1098
+ * The following code accepts new client connections
1099
+ * and forwards events to the appropriate functions
1100
+ * depending on the client state.
1101
+ *****************************************************/
1102
+
1103
+ FileDescriptor acceptNonBlockingSocket(int sock) {
1104
+ union {
1105
+ struct sockaddr_in inaddr;
1106
+ struct sockaddr_un unaddr;
1107
+ } u;
1108
+ socklen_t addrlen = sizeof(u);
1109
+
1110
+ if (accept4Available) {
1111
+ FileDescriptor fd(callAccept4(requestSocket,
1112
+ (struct sockaddr *) &u, &addrlen, O_NONBLOCK));
1113
+ if (fd == -1 && errno == ENOSYS) {
1114
+ accept4Available = false;
1115
+ return acceptNonBlockingSocket(sock);
1116
+ } else {
1117
+ return fd;
1118
+ }
1119
+ } else {
1120
+ FileDescriptor fd(syscalls::accept(requestSocket,
1121
+ (struct sockaddr *) &u, &addrlen));
1122
+ if (fd != -1) {
1123
+ int e = errno;
1124
+ setNonBlocking(fd);
1125
+ errno = e;
1126
+ }
1127
+ return fd;
1128
+ }
1129
+ }
1130
+
1131
+
1132
+ void onResumeSocketWatcher(ev::timer &timer, int revents) {
1133
+ P_INFO("Resuming listening on server socket.");
1134
+ resumeSocketWatcherTimer.stop();
1135
+ requestSocketWatcher.start();
1136
+ }
1137
+
1138
+ void onAcceptable(ev::io &io, int revents) {
1139
+ bool endReached = false;
1140
+ unsigned int count = 0;
1141
+
1142
+ while (!endReached && count < 10) {
1143
+ FileDescriptor fd = acceptNonBlockingSocket(requestSocket);
1144
+ if (fd == -1) {
1145
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
1146
+ endReached = true;
1147
+ } else {
1148
+ int e = errno;
1149
+ P_ERROR("Cannot accept client: " << strerror(e) <<
1150
+ " (errno=" << e << "). " <<
1151
+ "Pausing listening on server socket for 3 seconds.");
1152
+ requestSocketWatcher.stop();
1153
+ resumeSocketWatcherTimer.start();
1154
+ endReached = true;
1155
+ }
1156
+ } else {
1157
+ ClientPtr client = make_shared<Client>();
1158
+ client->associate(this, fd);
1159
+ clients.insert(make_pair<int, ClientPtr>(fd, client));
1160
+ count++;
1161
+ RH_DEBUG(client, "New client accepted; new client count = " << clients.size());
1162
+ }
1163
+ }
1164
+
1165
+ if (OXT_LIKELY(!clients.empty())) {
1166
+ inactivityTimer.stop();
1167
+ }
1168
+ }
1169
+
1170
+
1171
+ size_t onClientInputData(const ClientPtr &client, const StaticString &data) {
1172
+ RH_TRACE(client, 3, "Event: onClientInputData");
1173
+ if (!client->connected()) {
1174
+ return 0;
1175
+ }
1176
+
1177
+ if (data.empty()) {
1178
+ onClientEof(client);
1179
+ return 0;
1180
+ } else {
1181
+ return onClientRealData(client, data.data(), data.size());
1182
+ }
1183
+ }
1184
+
1185
+ size_t onClientRealData(const ClientPtr &client, const char *buf, size_t size) {
1186
+ size_t consumed = 0;
1187
+
1188
+ while (consumed < size && client->connected() && client->clientInput->isStarted()) {
1189
+ const char *data = buf + consumed;
1190
+ size_t len = size - consumed;
1191
+ size_t locallyConsumed;
1192
+
1193
+ RH_TRACE(client, 3, "Processing client data: \"" << cEscapeString(StaticString(data, len)) << "\"");
1194
+ switch (client->state) {
1195
+ case Client::BEGIN_READING_CONNECT_PASSWORD:
1196
+ locallyConsumed = state_beginReadingConnectPassword_onClientData(client, data, len);
1197
+ break;
1198
+ case Client::STILL_READING_CONNECT_PASSWORD:
1199
+ locallyConsumed = state_stillReadingConnectPassword_onClientData(client, data, len);
1200
+ break;
1201
+ case Client::READING_HEADER:
1202
+ locallyConsumed = state_readingHeader_onClientData(client, data, len);
1203
+ break;
1204
+ case Client::BUFFERING_REQUEST_BODY:
1205
+ locallyConsumed = state_bufferingRequestBody_onClientData(client, data, len);
1206
+ break;
1207
+ case Client::FORWARDING_BODY_TO_APP:
1208
+ locallyConsumed = state_forwardingBodyToApp_onClientData(client, data, len);
1209
+ break;
1210
+ default:
1211
+ abort();
1212
+ }
1213
+
1214
+ consumed += locallyConsumed;
1215
+ RH_TRACE(client, 3, "Processed client data: consumed " << locallyConsumed << " bytes");
1216
+ assert(consumed <= size);
1217
+ }
1218
+
1219
+ return consumed;
1220
+ }
1221
+
1222
+ void onClientEof(const ClientPtr &client) {
1223
+ RH_TRACE(client, 3, "Event: onClientEof; client sent EOF");
1224
+ switch (client->state) {
1225
+ case Client::BUFFERING_REQUEST_BODY:
1226
+ state_bufferingRequestBody_onClientEof(client);
1227
+ break;
1228
+ case Client::FORWARDING_BODY_TO_APP:
1229
+ state_forwardingBodyToApp_onClientEof(client);
1230
+ break;
1231
+ default:
1232
+ disconnect(client);
1233
+ break;
1234
+ }
1235
+ }
1236
+
1237
+ void onClientInputError(const ClientPtr &client, const char *message, int errnoCode) {
1238
+ RH_TRACE(client, 3, "Event: onClientInputError");
1239
+ if (!client->connected()) {
1240
+ return;
1241
+ }
1242
+
1243
+ if (errnoCode == ECONNRESET) {
1244
+ // We might as well treat ECONNRESET like an EOF.
1245
+ // http://stackoverflow.com/questions/2974021/what-does-econnreset-mean-in-the-context-of-an-af-local-socket
1246
+ onClientEof(client);
1247
+ } else {
1248
+ stringstream message;
1249
+ message << "client socket read error: ";
1250
+ message << strerror(errnoCode);
1251
+ message << " (errno=" << errnoCode << ")";
1252
+ disconnectWithError(client, message.str());
1253
+ }
1254
+ }
1255
+
1256
+
1257
+ void onClientBodyBufferData(const ClientPtr &client, const char *data, size_t size, const FileBackedPipe::ConsumeCallback &consumed) {
1258
+ RH_TRACE(client, 3, "Event: onClientBodyBufferData");
1259
+ if (!client->connected()) {
1260
+ return;
1261
+ }
1262
+
1263
+ switch (client->state) {
1264
+ case Client::FORWARDING_BODY_TO_APP:
1265
+ state_forwardingBodyToApp_onClientBodyBufferData(client, data, size, consumed);
1266
+ break;
1267
+ default:
1268
+ abort();
1269
+ }
1270
+ }
1271
+
1272
+ void onClientBodyBufferError(const ClientPtr &client, int errorCode) {
1273
+ RH_TRACE(client, 3, "Event: onClientBodyBufferError");
1274
+ if (!client->connected()) {
1275
+ return;
1276
+ }
1277
+
1278
+ stringstream message;
1279
+ message << "client body buffer error: ";
1280
+ message << strerror(errorCode);
1281
+ message << " (errno=" << errorCode << ")";
1282
+ disconnectWithError(client, message.str());
1283
+ }
1284
+
1285
+ void onClientBodyBufferEnd(const ClientPtr &client) {
1286
+ RH_TRACE(client, 3, "Event: onClientBodyBufferEnd");
1287
+ if (!client->connected()) {
1288
+ return;
1289
+ }
1290
+
1291
+ switch (client->state) {
1292
+ case Client::FORWARDING_BODY_TO_APP:
1293
+ state_forwardingBodyToApp_onClientBodyBufferEnd(client);
1294
+ break;
1295
+ default:
1296
+ abort();
1297
+ }
1298
+ }
1299
+
1300
+ void onClientBodyBufferCommit(const ClientPtr &client) {
1301
+ RH_TRACE(client, 3, "Event: onClientBodyBufferCommit");
1302
+ if (!client->connected()) {
1303
+ return;
1304
+ }
1305
+
1306
+ switch (client->state) {
1307
+ case Client::BUFFERING_REQUEST_BODY:
1308
+ state_bufferingRequestBody_onClientBodyBufferCommit(client);
1309
+ break;
1310
+ default:
1311
+ abort();
1312
+ }
1313
+ }
1314
+
1315
+ void onAppOutputWritable(const ClientPtr &client) {
1316
+ RH_TRACE(client, 3, "Event: onAppOutputWritable");
1317
+ if (!client->connected()) {
1318
+ return;
1319
+ }
1320
+
1321
+ switch (client->state) {
1322
+ case Client::SENDING_HEADER_TO_APP:
1323
+ state_sendingHeaderToApp_onAppOutputWritable(client);
1324
+ break;
1325
+ case Client::FORWARDING_BODY_TO_APP:
1326
+ state_forwardingBodyToApp_onAppOutputWritable(client);
1327
+ break;
1328
+ default:
1329
+ abort();
1330
+ }
1331
+ }
1332
+
1333
+
1334
+ void onTimeout(const ClientPtr &client) {
1335
+ RH_TRACE(client, 3, "Event: onTimeout");
1336
+ if (!client->connected()) {
1337
+ return;
1338
+ }
1339
+
1340
+ switch (client->state) {
1341
+ case Client::BEGIN_READING_CONNECT_PASSWORD:
1342
+ case Client::STILL_READING_CONNECT_PASSWORD:
1343
+ disconnectWithError(client, "no connect password received within timeout");
1344
+ break;
1345
+ default:
1346
+ disconnectWithError(client, "timeout");
1347
+ break;
1348
+ }
1349
+ }
1350
+
1351
+
1352
+ /*****************************************************
1353
+ * COMPONENT: client -> application plumbing
1354
+ *
1355
+ * The following code implements forwarding data from
1356
+ * the client to the application. Code is seperated
1357
+ * by client state.
1358
+ *****************************************************/
1359
+
1360
+
1361
+ /******* State: BEGIN_READING_CONNECT_PASSWORD *******/
1362
+
1363
+ void checkConnectPassword(const ClientPtr &client, const char *data, unsigned int len) {
1364
+ RH_TRACE(client, 3, "Given connect password: \"" << cEscapeString(StaticString(data, len)) << "\"");
1365
+ if (StaticString(data, len) == options.requestSocketPassword) {
1366
+ RH_TRACE(client, 3, "Connect password is correct; reading header");
1367
+ client->state = Client::READING_HEADER;
1368
+ client->freeBufferedConnectPassword();
1369
+ client->timeoutTimer.stop();
1370
+ } else {
1371
+ disconnectWithError(client, "wrong connect password");
1372
+ }
1373
+ }
1374
+
1375
+ size_t state_beginReadingConnectPassword_onClientData(const ClientPtr &client, const char *data, size_t size) {
1376
+ if (size >= options.requestSocketPassword.size()) {
1377
+ checkConnectPassword(client, data, options.requestSocketPassword.size());
1378
+ return options.requestSocketPassword.size();
1379
+ } else {
1380
+ client->bufferedConnectPassword.data = (char *) malloc(options.requestSocketPassword.size());
1381
+ client->bufferedConnectPassword.alreadyRead = size;
1382
+ memcpy(client->bufferedConnectPassword.data, data, size);
1383
+ client->state = Client::STILL_READING_CONNECT_PASSWORD;
1384
+ return size;
1385
+ }
1386
+ }
1387
+
1388
+
1389
+ /******* State: STILL_READING_CONNECT_PASSWORD *******/
1390
+
1391
+ size_t state_stillReadingConnectPassword_onClientData(const ClientPtr &client, const char *data, size_t size) {
1392
+ size_t consumed = std::min<size_t>(size,
1393
+ options.requestSocketPassword.size() -
1394
+ client->bufferedConnectPassword.alreadyRead);
1395
+ memcpy(client->bufferedConnectPassword.data + client->bufferedConnectPassword.alreadyRead,
1396
+ data, consumed);
1397
+ client->bufferedConnectPassword.alreadyRead += consumed;
1398
+ if (client->bufferedConnectPassword.alreadyRead == options.requestSocketPassword.size()) {
1399
+ checkConnectPassword(client, client->bufferedConnectPassword.data,
1400
+ options.requestSocketPassword.size());
1401
+ }
1402
+ return consumed;
1403
+ }
1404
+
1405
+
1406
+ /******* State: READING_HEADER *******/
1407
+
1408
+ bool modifyClientHeaders(const ClientPtr &client) {
1409
+ ScgiRequestParser &parser = client->scgiParser;
1410
+ ScgiRequestParser::HeaderMap &map = parser.getMap();
1411
+ ScgiRequestParser::iterator it, end = map.end();
1412
+ bool modified = false;
1413
+
1414
+ /* The Rack spec specifies that HTTP_CONTENT_LENGTH and HTTP_CONTENT_TYPE must
1415
+ * not exist and that their respective non-HTTP_ versions should exist instead.
1416
+ */
1417
+
1418
+ if ((it = map.find("HTTP_CONTENT_LENGTH")) != end) {
1419
+ if (map.find("CONTENT_LENGTH") == end) {
1420
+ map["CONTENT_LENGTH"] = it->second;
1421
+ map.erase("HTTP_CONTENT_LENGTH");
1422
+ } else {
1423
+ map.erase(it);
1424
+ }
1425
+ modified = true;
1426
+ }
1427
+
1428
+ if ((it = map.find("HTTP_CONTENT_TYPE")) != end) {
1429
+ if (map.find("CONTENT_TYPE") == end) {
1430
+ map["CONTENT_TYPE"] = it->second;
1431
+ map.erase("HTTP_CONTENT_TYPE");
1432
+ } else {
1433
+ map.erase(it);
1434
+ }
1435
+ modified = true;
1436
+ }
1437
+
1438
+ return modified;
1439
+ }
1440
+
1441
+ static void fillPoolOption(const ClientPtr &client, StaticString &field, const StaticString &name) {
1442
+ ScgiRequestParser::const_iterator it = client->scgiParser.getHeaderIterator(name);
1443
+ if (it != client->scgiParser.end()) {
1444
+ field = it->second;
1445
+ }
1446
+ }
1447
+
1448
+ static void fillPoolOption(const ClientPtr &client, bool &field, const StaticString &name) {
1449
+ ScgiRequestParser::const_iterator it = client->scgiParser.getHeaderIterator(name);
1450
+ if (it != client->scgiParser.end()) {
1451
+ field = it->second == "true";
1452
+ }
1453
+ }
1454
+
1455
+ static void fillPoolOption(const ClientPtr &client, unsigned long &field, const StaticString &name) {
1456
+ ScgiRequestParser::const_iterator it = client->scgiParser.getHeaderIterator(name);
1457
+ if (it != client->scgiParser.end()) {
1458
+ field = stringToUint(it->second);
1459
+ }
1460
+ }
1461
+
1462
+ static void fillPoolOption(const ClientPtr &client, long &field, const StaticString &name) {
1463
+ ScgiRequestParser::const_iterator it = client->scgiParser.getHeaderIterator(name);
1464
+ if (it != client->scgiParser.end()) {
1465
+ field = stringToInt(it->second);
1466
+ }
1467
+ }
1468
+
1469
+ void fillPoolOptions(const ClientPtr &client) {
1470
+ Options &options = client->options;
1471
+ ScgiRequestParser &parser = client->scgiParser;
1472
+ ScgiRequestParser::const_iterator it, end = client->scgiParser.end();
1473
+
1474
+ options = Options();
1475
+
1476
+ StaticString scriptName = parser.getHeader("SCRIPT_NAME");
1477
+ StaticString appRoot = parser.getHeader("PASSENGER_APP_ROOT");
1478
+ if (scriptName.empty()) {
1479
+ if (appRoot.empty()) {
1480
+ StaticString documentRoot = parser.getHeader("DOCUMENT_ROOT");
1481
+ if (documentRoot.empty()) {
1482
+ disconnectWithError(client, "no PASSENGER_APP_ROOT or DOCUMENT_ROOT headers set.");
1483
+ return;
1484
+ }
1485
+ client->appRoot = extractDirName(documentRoot);
1486
+ options.appRoot = client->appRoot;
1487
+ } else {
1488
+ options.appRoot = appRoot;
1489
+ }
1490
+ } else {
1491
+ if (appRoot.empty()) {
1492
+ client->appRoot = extractDirName(resolveSymlink(parser.getHeader("DOCUMENT_ROOT")));
1493
+ options.appRoot = client->appRoot;
1494
+ } else {
1495
+ options.appRoot = appRoot;
1496
+ }
1497
+ options.baseURI = scriptName;
1498
+ }
1499
+
1500
+ options.logLevel = getLogLevel();
1501
+ options.loggingAgentAddress = this->options.loggingAgentAddress;
1502
+ options.loggingAgentUsername = "logging";
1503
+ options.loggingAgentPassword = this->options.loggingAgentPassword;
1504
+ fillPoolOption(client, options.appGroupName, "PASSENGER_APP_GROUP_NAME");
1505
+ fillPoolOption(client, options.appType, "PASSENGER_APP_TYPE");
1506
+ fillPoolOption(client, options.environment, "PASSENGER_ENV");
1507
+ fillPoolOption(client, options.ruby, "PASSENGER_RUBY");
1508
+ fillPoolOption(client, options.user, "PASSENGER_USER");
1509
+ fillPoolOption(client, options.group, "PASSENGER_GROUP");
1510
+ fillPoolOption(client, options.minProcesses, "PASSENGER_MIN_INSTANCES");
1511
+ fillPoolOption(client, options.maxRequests, "PASSENGER_MAX_REQUESTS");
1512
+ fillPoolOption(client, options.spawnMethod, "PASSENGER_SPAWN_METHOD");
1513
+ fillPoolOption(client, options.startCommand, "PASSENGER_START_COMMAND");
1514
+ fillPoolOption(client, options.maxPreloaderIdleTime, "PASSENGER_MAX_PRELOADER_IDLE_TIME");
1515
+ fillPoolOption(client, options.statThrottleRate, "PASSENGER_STAT_THROTTLE_RATE");
1516
+ fillPoolOption(client, options.restartDir, "PASSENGER_RESTART_DIR");
1517
+ fillPoolOption(client, options.loadShellEnvvars, "PASSENGER_LOAD_SHELL_ENVVARS");
1518
+ fillPoolOption(client, options.debugger, "PASSENGER_DEBUGGER");
1519
+ fillPoolOption(client, options.raiseInternalError, "PASSENGER_RAISE_INTERNAL_ERROR");
1520
+
1521
+ for (it = client->scgiParser.begin(); it != end; it++) {
1522
+ if (!startsWith(it->first, "PASSENGER_")
1523
+ && !startsWith(it->first, "HTTP_")
1524
+ && it->first != "PATH_INFO"
1525
+ && it->first != "SCRIPT_NAME"
1526
+ && it->first != "CONTENT_LENGTH"
1527
+ && it->first != "CONTENT_TYPE")
1528
+ {
1529
+ options.environmentVariables.push_back(*it);
1530
+ }
1531
+ }
1532
+ }
1533
+
1534
+ void initializeUnionStation(const ClientPtr &client) {
1535
+ if (getBoolOption(client, "UNION_STATION_SUPPORT", false)) {
1536
+ Options &options = client->options;
1537
+ ScgiRequestParser &parser = client->scgiParser;
1538
+
1539
+ StaticString key = parser.getHeader("UNION_STATION_KEY");
1540
+ StaticString filters = parser.getHeader("UNION_STATION_FILTERS");
1541
+ if (key.empty()) {
1542
+ disconnectWithError(client, "header UNION_STATION_KEY must be set.");
1543
+ return;
1544
+ }
1545
+
1546
+ client->options.analytics = true;
1547
+ client->options.unionStationKey = key;
1548
+ client->options.logger = loggerFactory->newTransaction(
1549
+ options.getAppGroupName(), "requests", key, filters);
1550
+
1551
+ client->beginScopeLog(&client->scopeLogs.requestProcessing, "request processing");
1552
+
1553
+ StaticString staticRequestURI = parser.getHeader("REQUEST_URI");
1554
+ if (!staticRequestURI.empty()) {
1555
+ client->logMessage("URI: " + staticRequestURI);
1556
+ } else {
1557
+ string requestURI = parser.getHeader("SCRIPT_NAME");
1558
+ requestURI.append(parser.getHeader("PATH_INFO"));
1559
+ StaticString queryString = parser.getHeader("QUERY_STRING");
1560
+ if (!queryString.empty()) {
1561
+ requestURI.append("?");
1562
+ requestURI.append(queryString);
1563
+ }
1564
+ client->logMessage("URI: " + requestURI);
1565
+ }
1566
+ }
1567
+ }
1568
+
1569
+ size_t state_readingHeader_onClientData(const ClientPtr &client, const char *data, size_t size) {
1570
+ ScgiRequestParser &parser = client->scgiParser;
1571
+ size_t consumed = parser.feed(data, size);
1572
+ if (!parser.acceptingInput()) {
1573
+ if (parser.getState() == ScgiRequestParser::ERROR) {
1574
+ if (parser.getErrorReason() == ScgiRequestParser::LIMIT_REACHED) {
1575
+ disconnectWithError(client, "SCGI header too large");
1576
+ } else {
1577
+ disconnectWithError(client, "invalid SCGI header");
1578
+ }
1579
+ return consumed;
1580
+ }
1581
+
1582
+ bool modified = modifyClientHeaders(client);
1583
+ /* TODO: in case the headers are not modified, we only need to rebuild the header data
1584
+ * right now because the scgiParser buffer is invalidated as soon as onClientData exits.
1585
+ * We should figure out a way to not copy anything if we can do everything before
1586
+ * onClientData exits.
1587
+ */
1588
+ parser.rebuildData(modified);
1589
+ client->contentLength = getLongLongOption(client, "CONTENT_LENGTH");
1590
+ fillPoolOptions(client);
1591
+ if (!client->connected()) {
1592
+ return consumed;
1593
+ }
1594
+ initializeUnionStation(client);
1595
+ if (!client->connected()) {
1596
+ return consumed;
1597
+ }
1598
+
1599
+ if (getBoolOption(client, "PASSENGER_BUFFERING")) {
1600
+ RH_TRACE(client, 3, "Valid SCGI header; buffering request body");
1601
+ client->state = Client::BUFFERING_REQUEST_BODY;
1602
+ client->requestBodyIsBuffered = true;
1603
+ client->beginScopeLog(&client->scopeLogs.bufferingRequestBody, "buffering request body");
1604
+ if (client->contentLength == 0) {
1605
+ client->clientInput->stop();
1606
+ state_bufferingRequestBody_onClientEof(client);
1607
+ return 0;
1608
+ }
1609
+ } else {
1610
+ RH_TRACE(client, 3, "Valid SCGI header; not buffering request body; checking out session");
1611
+ client->clientInput->stop();
1612
+ checkoutSession(client);
1613
+ }
1614
+ }
1615
+ return consumed;
1616
+ }
1617
+
1618
+
1619
+ /******* State: BUFFERING_REQUEST_BODY *******/
1620
+
1621
+ void state_bufferingRequestBody_verifyInvariants(const ClientPtr &client) const {
1622
+ assert(client->requestBodyIsBuffered);
1623
+ assert(!client->clientBodyBuffer->isStarted());
1624
+ }
1625
+
1626
+ size_t state_bufferingRequestBody_onClientData(const ClientPtr &client, const char *data, size_t size) {
1627
+ state_bufferingRequestBody_verifyInvariants(client);
1628
+ assert(!client->clientBodyBuffer->isCommittingToDisk());
1629
+
1630
+ if (client->contentLength >= 0) {
1631
+ size = std::min<unsigned long long>(
1632
+ size,
1633
+ (unsigned long long) client->contentLength - client->clientBodyAlreadyRead
1634
+ );
1635
+ }
1636
+
1637
+ if (!client->clientBodyBuffer->write(data, size)) {
1638
+ // The pipe cannot write the data to disk quickly enough, so
1639
+ // suspend reading from the client until the pipe is done.
1640
+ client->backgroundOperations++; // TODO: figure out whether this is necessary
1641
+ client->clientInput->stop();
1642
+ }
1643
+ client->clientBodyAlreadyRead += size;
1644
+
1645
+ RH_TRACE(client, 3, "Buffered " << size << " bytes of client body data; total=" <<
1646
+ client->clientBodyAlreadyRead << ", content-length=" << client->contentLength);
1647
+ assert(client->contentLength == -1 || client->clientBodyAlreadyRead <= (unsigned long long) client->contentLength);
1648
+
1649
+ if (client->contentLength >= 0 && client->clientBodyAlreadyRead == (unsigned long long) client->contentLength) {
1650
+ if (client->clientBodyBuffer->isCommittingToDisk()) {
1651
+ RH_TRACE(client, 3, "Done buffering request body, but clientBodyBuffer not yet done committing data to disk; waiting until it's done");
1652
+ client->checkoutSessionAfterCommit = true;
1653
+ } else {
1654
+ client->clientInput->stop();
1655
+ state_bufferingRequestBody_onClientEof(client);
1656
+ }
1657
+ }
1658
+
1659
+ return size;
1660
+ }
1661
+
1662
+ void state_bufferingRequestBody_onClientEof(const ClientPtr &client) {
1663
+ state_bufferingRequestBody_verifyInvariants(client);
1664
+
1665
+ RH_TRACE(client, 3, "Done buffering request body; checking out session");
1666
+ client->clientBodyBuffer->end();
1667
+ client->endScopeLog(&client->scopeLogs.bufferingRequestBody);
1668
+ checkoutSession(client);
1669
+ }
1670
+
1671
+ void state_bufferingRequestBody_onClientBodyBufferCommit(const ClientPtr &client) {
1672
+ // Now that the pipe has committed the data to disk
1673
+ // resume reading from the client socket.
1674
+ state_bufferingRequestBody_verifyInvariants(client);
1675
+ assert(!client->clientInput->isStarted());
1676
+ client->backgroundOperations--;
1677
+ if (client->checkoutSessionAfterCommit) {
1678
+ RH_TRACE(client, 3, "Done committing request body to disk");
1679
+ state_bufferingRequestBody_onClientEof(client);
1680
+ } else {
1681
+ client->clientInput->start();
1682
+ }
1683
+ }
1684
+
1685
+
1686
+ /******* State: CHECKING_OUT_SESSION *******/
1687
+
1688
+ void state_checkingOutSession_verifyInvariants(const ClientPtr &client) {
1689
+ assert(!client->clientInput->isStarted());
1690
+ assert(!client->clientBodyBuffer->isStarted());
1691
+ }
1692
+
1693
+ void checkoutSession(const ClientPtr &client) {
1694
+ RH_TRACE(client, 2, "Checking out session: appRoot=" << client->options.appRoot);
1695
+ client->state = Client::CHECKING_OUT_SESSION;
1696
+ client->beginScopeLog(&client->scopeLogs.getFromPool, "get from pool");
1697
+ pool->asyncGet(client->options, boost::bind(&RequestHandler::sessionCheckedOut,
1698
+ this, client, _1, _2));
1699
+ if (!client->sessionCheckedOut) {
1700
+ client->backgroundOperations++;
1701
+ }
1702
+ }
1703
+
1704
+ void sessionCheckedOut(ClientPtr client, const SessionPtr &session, const ExceptionPtr &e) {
1705
+ if (!pthread_equal(pthread_self(), libev->getCurrentThread())) {
1706
+ libev->runLaterTS(boost::bind(&RequestHandler::sessionCheckedOut_real, this,
1707
+ client, session, e));
1708
+ } else {
1709
+ sessionCheckedOut_real(client, session, e);
1710
+ }
1711
+ }
1712
+
1713
+ void sessionCheckedOut_real(ClientPtr client, const SessionPtr &session, const ExceptionPtr &e) {
1714
+ if (!client->connected()) {
1715
+ return;
1716
+ }
1717
+
1718
+ state_checkingOutSession_verifyInvariants(client);
1719
+ client->backgroundOperations--;
1720
+ client->sessionCheckedOut = true;
1721
+
1722
+ if (e != NULL) {
1723
+ client->endScopeLog(&client->scopeLogs.getFromPool, false);
1724
+ shared_ptr<SpawnException> e2 = dynamic_pointer_cast<SpawnException>(e);
1725
+ if (e2 != NULL) {
1726
+ if (e2->getErrorPage().empty()) {
1727
+ RH_WARN(client, "Cannot checkout session. " << e2->what());
1728
+ writeErrorResponse(client, e2->what());
1729
+ } else {
1730
+ RH_WARN(client, "Cannot checkout session. " << e2->what() <<
1731
+ "\nError page:\n" << e2->getErrorPage());
1732
+ writeErrorResponse(client, e2->getErrorPage(), e2.get());
1733
+ }
1734
+ } else {
1735
+ string typeName;
1736
+ #ifdef CXX_ABI_API_AVAILABLE
1737
+ int status;
1738
+ char *tmp = abi::__cxa_demangle(typeid(*e).name(), 0, 0, &status);
1739
+ if (tmp != NULL) {
1740
+ typeName = tmp;
1741
+ free(tmp);
1742
+ } else {
1743
+ typeName = typeid(*e).name();
1744
+ }
1745
+ #else
1746
+ typeName = typeid(*e).name();
1747
+ #endif
1748
+
1749
+ RH_WARN(client, "Cannot checkout session (exception type " <<
1750
+ typeName << "): " << e->what());
1751
+
1752
+ string response = "An internal error occurred while trying to spawn the application.\n";
1753
+ response.append("Exception type: ");
1754
+ response.append(typeName);
1755
+ response.append("\nError message: ");
1756
+ response.append(e->what());
1757
+ shared_ptr<tracable_exception> e3 = dynamic_pointer_cast<tracable_exception>(e);
1758
+ if (e3 != NULL) {
1759
+ response.append("\nBacktrace:\n");
1760
+ response.append(e3->backtrace());
1761
+ }
1762
+
1763
+ writeErrorResponse(client, response);
1764
+ }
1765
+ } else {
1766
+ RH_DEBUG(client, "Session checked out: pid=" << session->getPid() <<
1767
+ ", gupid=" << session->getGupid());
1768
+ client->session = session;
1769
+ initiateSession(client);
1770
+ }
1771
+ }
1772
+
1773
+ void initiateSession(const ClientPtr &client) {
1774
+ assert(client->state == Client::CHECKING_OUT_SESSION);
1775
+ client->sessionCheckoutTry++;
1776
+ try {
1777
+ client->session->initiate();
1778
+ } catch (const SystemException &e2) {
1779
+ if (client->sessionCheckoutTry < 10) {
1780
+ RH_DEBUG(client, "Error checking out session (" << e2.what() <<
1781
+ "); retrying (attempt " << client->sessionCheckoutTry << ")");
1782
+ client->sessionCheckedOut = false;
1783
+ pool->asyncGet(client->options,
1784
+ boost::bind(&RequestHandler::sessionCheckedOut,
1785
+ this, client, _1, _2));
1786
+ if (!client->sessionCheckedOut) {
1787
+ client->backgroundOperations++;
1788
+ }
1789
+ } else {
1790
+ string message = "could not initiate a session (";
1791
+ message.append(e2.what());
1792
+ message.append(")");
1793
+ disconnectWithError(client, message);
1794
+ }
1795
+ return;
1796
+ }
1797
+
1798
+ if (client->useUnionStation()) {
1799
+ client->endScopeLog(&client->scopeLogs.getFromPool);
1800
+ client->logMessage("Application PID: " +
1801
+ toString(client->session->getPid()) +
1802
+ " (GUPID: " + client->session->getGupid() + ")");
1803
+ client->beginScopeLog(&client->scopeLogs.requestProxying, "request proxying");
1804
+ }
1805
+
1806
+ RH_DEBUG(client, "Session initiated: fd=" << client->session->fd());
1807
+ setNonBlocking(client->session->fd());
1808
+ client->appInput->reset(libev.get(), client->session->fd());
1809
+ client->appInput->start();
1810
+ client->appOutputWatcher.set(libev->getLoop());
1811
+ client->appOutputWatcher.set(client->session->fd(), ev::WRITE);
1812
+ sendHeaderToApp(client);
1813
+ }
1814
+
1815
+
1816
+ /******* State: SENDING_HEADER_TO_APP *******/
1817
+
1818
+ static StaticString makeStaticStringWithNull(const char *data) {
1819
+ return StaticString(data, strlen(data) + 1);
1820
+ }
1821
+
1822
+ static StaticString makeStaticStringWithNull(const string &data) {
1823
+ return StaticString(data.c_str(), data.size() + 1);
1824
+ }
1825
+
1826
+ void state_sendingHeaderToApp_verifyInvariants(const ClientPtr &client) {
1827
+ assert(!client->clientInput->isStarted());
1828
+ assert(!client->clientBodyBuffer->isStarted());
1829
+ }
1830
+
1831
+ void sendHeaderToApp(const ClientPtr &client) {
1832
+ assert(!client->clientInput->isStarted());
1833
+ assert(!client->clientBodyBuffer->isStarted());
1834
+
1835
+ RH_TRACE(client, 2, "Sending headers to application");
1836
+
1837
+ if (client->session->getProtocol() == "session") {
1838
+ char sizeField[sizeof(uint32_t)];
1839
+ SmallVector<StaticString, 10> data;
1840
+
1841
+ data.push_back(StaticString(sizeField, sizeof(uint32_t)));
1842
+ data.push_back(client->scgiParser.getHeaderData());
1843
+
1844
+ data.push_back(makeStaticStringWithNull("PASSENGER_CONNECT_PASSWORD"));
1845
+ data.push_back(makeStaticStringWithNull(client->session->getConnectPassword()));
1846
+
1847
+ if (client->options.analytics) {
1848
+ data.push_back(makeStaticStringWithNull("PASSENGER_TXN_ID"));
1849
+ data.push_back(makeStaticStringWithNull(client->options.logger->getTxnId()));
1850
+ }
1851
+
1852
+ uint32_t dataSize = 0;
1853
+ for (unsigned int i = 1; i < data.size(); i++) {
1854
+ dataSize += (uint32_t) data[i].size();
1855
+ }
1856
+ Uint32Message::generate(sizeField, dataSize);
1857
+
1858
+ ssize_t ret = gatheredWrite(client->session->fd(), &data[0],
1859
+ data.size(), client->appOutputBuffer);
1860
+ if (ret == -1 && errno != EAGAIN) {
1861
+ disconnectWithAppSocketWriteError(client, errno);
1862
+ } else if (!client->appOutputBuffer.empty()) {
1863
+ client->state = Client::SENDING_HEADER_TO_APP;
1864
+ client->appOutputWatcher.start();
1865
+ } else {
1866
+ sendBodyToApp(client);
1867
+ }
1868
+ } else {
1869
+ assert(client->session->getProtocol() == "http_session");
1870
+ const ScgiRequestParser &parser = client->scgiParser;
1871
+ ScgiRequestParser::const_iterator it, end = parser.end();
1872
+ string data;
1873
+
1874
+ data.reserve(parser.getHeaderData().size() + 128);
1875
+ data.append(parser.getHeader("REQUEST_METHOD"));
1876
+ data.append(" ");
1877
+ data.append(parser.getHeader("REQUEST_URI"));
1878
+ data.append(" HTTP/1.1\r\n");
1879
+ data.append("Connection: close\r\n");
1880
+
1881
+ for (it = parser.begin(); it != end; it++) {
1882
+ if (startsWith(it->first, "HTTP_")) {
1883
+ string subheader = it->first.substr(sizeof("HTTP_") - 1);
1884
+ string::size_type i;
1885
+ for (i = 0; i < subheader.size(); i++) {
1886
+ if (subheader[i] == '_') {
1887
+ subheader[i] = '-';
1888
+ } else if (i > 0 && subheader[i - 1] != '-') {
1889
+ subheader[i] = tolower(subheader[i]);
1890
+ }
1891
+ }
1892
+
1893
+ data.append(subheader);
1894
+ data.append(": ");
1895
+ data.append(it->second);
1896
+ data.append("\r\n");
1897
+ }
1898
+ }
1899
+
1900
+ StaticString header = parser.getHeader("CONTENT_LENGTH");
1901
+ if (!header.empty()) {
1902
+ data.append("Content-Length: ");
1903
+ data.append(header);
1904
+ data.append("\r\n");
1905
+ }
1906
+
1907
+ header = parser.getHeader("CONTENT_TYPE");
1908
+ if (!header.empty()) {
1909
+ data.append("Content-Type: ");
1910
+ data.append(header);
1911
+ data.append("\r\n");
1912
+ }
1913
+
1914
+ if (client->options.analytics) {
1915
+ data.append("Passenger-Txn-Id: ");
1916
+ data.append(client->options.logger->getTxnId());
1917
+ data.append("\r\n");
1918
+ }
1919
+
1920
+ data.append("\r\n");
1921
+
1922
+ StaticString datas[] = { data };
1923
+ ssize_t ret = gatheredWrite(client->session->fd(), datas,
1924
+ 1, client->appOutputBuffer);
1925
+ if (ret == -1 && errno != EAGAIN) {
1926
+ disconnectWithAppSocketWriteError(client, errno);
1927
+ } else if (!client->appOutputBuffer.empty()) {
1928
+ client->state = Client::SENDING_HEADER_TO_APP;
1929
+ client->appOutputWatcher.start();
1930
+ } else {
1931
+ sendBodyToApp(client);
1932
+ }
1933
+ }
1934
+ }
1935
+
1936
+ void state_sendingHeaderToApp_onAppOutputWritable(const ClientPtr &client) {
1937
+ state_sendingHeaderToApp_verifyInvariants(client);
1938
+
1939
+ ssize_t ret = gatheredWrite(client->session->fd(), NULL, 0, client->appOutputBuffer);
1940
+ if (ret == -1) {
1941
+ if (errno != EAGAIN && errno != EPIPE) {
1942
+ disconnectWithAppSocketWriteError(client, errno);
1943
+ }
1944
+ } else if (client->appOutputBuffer.empty()) {
1945
+ client->appOutputWatcher.stop();
1946
+ sendBodyToApp(client);
1947
+ }
1948
+ }
1949
+
1950
+
1951
+ /******* State: FORWARDING_BODY_TO_APP *******/
1952
+
1953
+ void state_forwardingBodyToApp_verifyInvariants(const ClientPtr &client) {
1954
+ assert(client->state == Client::FORWARDING_BODY_TO_APP);
1955
+ }
1956
+
1957
+ void sendBodyToApp(const ClientPtr &client) {
1958
+ assert(client->appOutputBuffer.empty());
1959
+ assert(!client->clientBodyBuffer->isStarted());
1960
+ assert(!client->clientInput->isStarted());
1961
+ assert(!client->appOutputWatcher.is_active());
1962
+
1963
+ RH_TRACE(client, 2, "Begin sending body to application");
1964
+
1965
+ client->state = Client::FORWARDING_BODY_TO_APP;
1966
+ if (client->requestBodyIsBuffered) {
1967
+ client->clientBodyBuffer->start();
1968
+ } else if (client->contentLength == 0) {
1969
+ state_forwardingBodyToApp_onClientEof(client);
1970
+ } else {
1971
+ client->clientInput->start();
1972
+ }
1973
+ }
1974
+
1975
+
1976
+ size_t state_forwardingBodyToApp_onClientData(const ClientPtr &client,
1977
+ const char *data, size_t size)
1978
+ {
1979
+ state_forwardingBodyToApp_verifyInvariants(client);
1980
+ assert(!client->requestBodyIsBuffered);
1981
+
1982
+ if (client->contentLength >= 0) {
1983
+ size = std::min<unsigned long long>(
1984
+ size,
1985
+ (unsigned long long) client->contentLength - client->clientBodyAlreadyRead
1986
+ );
1987
+ }
1988
+
1989
+ RH_TRACE(client, 3, "Forwarding " << size << " bytes of client body data to application.");
1990
+ ssize_t ret = syscalls::write(client->session->fd(), data, size);
1991
+ int e = errno;
1992
+ if (ret == -1) {
1993
+ RH_TRACE(client, 3, "Could not write to application socket: " << strerror(e) << " (errno=" << e << ")");
1994
+ if (e == EAGAIN) {
1995
+ RH_TRACE(client, 3, "Waiting until the application socket is writable again.");
1996
+ client->clientInput->stop();
1997
+ client->appOutputWatcher.start();
1998
+ } else if (e == EPIPE) {
1999
+ // Client will be disconnected after response forwarding is done.
2000
+ client->clientInput->stop();
2001
+ syscalls::shutdown(client->fd, SHUT_RD);
2002
+ } else {
2003
+ disconnectWithAppSocketWriteError(client, e);
2004
+ }
2005
+ return 0;
2006
+ } else {
2007
+ client->clientBodyAlreadyRead += ret;
2008
+
2009
+ RH_TRACE(client, 3, "Managed to forward " << ret << " bytes; total=" <<
2010
+ client->clientBodyAlreadyRead << ", content-length=" << client->contentLength);
2011
+ assert(client->contentLength == -1 || client->clientBodyAlreadyRead <= (unsigned long long) client->contentLength);
2012
+ if (client->contentLength >= 0 && client->clientBodyAlreadyRead == (unsigned long long) client->contentLength) {
2013
+ client->clientInput->stop();
2014
+ state_forwardingBodyToApp_onClientEof(client);
2015
+ }
2016
+
2017
+ return ret;
2018
+ }
2019
+ }
2020
+
2021
+ void state_forwardingBodyToApp_onClientEof(const ClientPtr &client) {
2022
+ state_forwardingBodyToApp_verifyInvariants(client);
2023
+ assert(!client->requestBodyIsBuffered);
2024
+
2025
+ RH_TRACE(client, 2, "End of (unbuffered) client body reached; done sending data to application");
2026
+ client->clientInput->stop();
2027
+ if (client->shouldHalfCloseWrite()) {
2028
+ syscalls::shutdown(client->session->fd(), SHUT_WR);
2029
+ }
2030
+ }
2031
+
2032
+ void state_forwardingBodyToApp_onAppOutputWritable(const ClientPtr &client) {
2033
+ state_forwardingBodyToApp_verifyInvariants(client);
2034
+
2035
+ RH_TRACE(client, 3, "Application socket became writable again.");
2036
+ client->appOutputWatcher.stop();
2037
+ if (client->requestBodyIsBuffered) {
2038
+ assert(!client->clientBodyBuffer->isStarted());
2039
+ client->clientBodyBuffer->start();
2040
+ } else {
2041
+ assert(!client->clientInput->isStarted());
2042
+ client->clientInput->start();
2043
+ }
2044
+ }
2045
+
2046
+
2047
+ void state_forwardingBodyToApp_onClientBodyBufferData(const ClientPtr &client,
2048
+ const char *data, size_t size, const FileBackedPipe::ConsumeCallback &consumed)
2049
+ {
2050
+ state_forwardingBodyToApp_verifyInvariants(client);
2051
+ assert(client->requestBodyIsBuffered);
2052
+
2053
+ RH_TRACE(client, 3, "Forwarding " << size << " bytes of buffered client body data to application.");
2054
+ ssize_t ret = syscalls::write(client->session->fd(), data, size);
2055
+ if (ret == -1) {
2056
+ int e = errno;
2057
+ RH_TRACE(client, 3, "Could not write to application socket: " << strerror(e) << " (errno=" << e << ")");
2058
+ if (e == EAGAIN) {
2059
+ RH_TRACE(client, 3, "Waiting until the application socket is writable again.");
2060
+ client->appOutputWatcher.start();
2061
+ consumed(0, true);
2062
+ } else if (e == EPIPE) {
2063
+ // Client will be disconnected after response forwarding is done.
2064
+ syscalls::shutdown(client->fd, SHUT_RD);
2065
+ consumed(0, true);
2066
+ } else {
2067
+ disconnectWithAppSocketWriteError(client, e);
2068
+ }
2069
+ } else {
2070
+ RH_TRACE(client, 3, "Managed to forward " << ret << " bytes.");
2071
+ consumed(ret, false);
2072
+ }
2073
+ }
2074
+
2075
+ void state_forwardingBodyToApp_onClientBodyBufferEnd(const ClientPtr &client) {
2076
+ state_forwardingBodyToApp_verifyInvariants(client);
2077
+ assert(client->requestBodyIsBuffered);
2078
+
2079
+ RH_TRACE(client, 2, "End of (buffered) client body reached; done sending data to application");
2080
+ if (client->shouldHalfCloseWrite()) {
2081
+ syscalls::shutdown(client->session->fd(), SHUT_WR);
2082
+ }
2083
+ }
2084
+
2085
+
2086
+ public:
2087
+ // For unit testing purposes.
2088
+ unsigned int connectPasswordTimeout; // milliseconds
2089
+
2090
+ RequestHandler(const SafeLibevPtr &_libev,
2091
+ const FileDescriptor &_requestSocket,
2092
+ const PoolPtr &_pool,
2093
+ const AgentOptions &_options)
2094
+ : libev(_libev),
2095
+ requestSocket(_requestSocket),
2096
+ pool(_pool),
2097
+ options(_options),
2098
+ resourceLocator(_options.passengerRoot)
2099
+ {
2100
+ accept4Available = true;
2101
+ connectPasswordTimeout = 15000;
2102
+ loggerFactory = pool->loggerFactory;
2103
+
2104
+ requestSocketWatcher.set(_requestSocket, ev::READ);
2105
+ requestSocketWatcher.set(_libev->getLoop());
2106
+ requestSocketWatcher.set<RequestHandler, &RequestHandler::onAcceptable>(this);
2107
+ requestSocketWatcher.start();
2108
+
2109
+ resumeSocketWatcherTimer.set<RequestHandler, &RequestHandler::onResumeSocketWatcher>(this);
2110
+ resumeSocketWatcherTimer.set(_libev->getLoop());
2111
+ resumeSocketWatcherTimer.set(3, 3);
2112
+ }
2113
+
2114
+ template<typename Stream>
2115
+ void inspect(Stream &stream) const {
2116
+ stream << clients.size() << " clients:\n";
2117
+ HashMap<int, ClientPtr>::const_iterator it;
2118
+ for (it = clients.begin(); it != clients.end(); it++) {
2119
+ const ClientPtr &client = it->second;
2120
+ stream << " Client " << client->fd << ":\n";
2121
+ client->inspect(stream);
2122
+ }
2123
+ }
2124
+
2125
+ void resetInactivityTimer() {
2126
+ libev->run(boost::bind(&Timer::start, &inactivityTimer));
2127
+ }
2128
+
2129
+ unsigned long long inactivityTime() const {
2130
+ unsigned long long result;
2131
+ libev->run(boost::bind(&RequestHandler::getInactivityTime, this, &result));
2132
+ return result;
2133
+ }
2134
+ };
2135
+
2136
+
2137
+ } // namespace Passenger
2138
+
2139
+ #endif /* _PASSENGER_REQUEST_HANDLER_H_ */