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.
- data/DEVELOPERS.TXT +4 -10
- data/NEWS +19 -27
- data/Rakefile +20 -19
- data/bin/passenger +3 -2
- data/bin/passenger-config +35 -5
- data/bin/passenger-install-apache2-module +12 -12
- data/bin/passenger-install-nginx-module +55 -38
- data/bin/passenger-memory-stats +3 -1
- data/bin/passenger-status +7 -35
- data/build/agents.rb +107 -21
- data/build/apache2.rb +11 -46
- data/build/basics.rb +61 -9
- data/build/common_library.rb +59 -142
- data/build/cxx_tests.rb +111 -110
- data/build/documentation.rb +33 -0
- data/build/misc.rb +30 -12
- data/build/nginx.rb +10 -39
- data/build/oxt_tests.rb +1 -0
- data/build/ruby_extension.rb +1 -5
- data/build/test_basics.rb +3 -2
- data/dev/copy_boost_headers.rb +2 -1
- data/doc/Architectural overview.html +49 -90
- data/doc/DebuggingAndStressTesting.txt.md +49 -0
- data/doc/Packaging.txt.md +254 -0
- data/doc/Security of user switching support.html +35 -66
- data/doc/Users guide Apache.html +588 -758
- data/doc/Users guide Apache.idmap.txt +253 -136
- data/doc/Users guide Apache.txt +154 -109
- data/doc/Users guide Nginx.html +544 -660
- data/doc/Users guide Nginx.idmap.txt +179 -91
- data/doc/Users guide Nginx.txt +192 -118
- data/doc/Users guide Standalone.html +65 -48
- data/doc/Users guide Standalone.idmap.txt +10 -2
- data/doc/Users guide Standalone.txt +4 -0
- data/doc/images/glyphicons-halflings-white.png +0 -0
- data/doc/images/glyphicons-halflings.png +0 -0
- data/doc/images/phusion_banner_small.png +0 -0
- data/doc/images/{smart-lv2.png → smart.png} +0 -0
- data/doc/images/{smart-lv2.svg → smart.svg} +0 -0
- data/doc/templates/bootstrap.min.css +397 -0
- data/doc/templates/markdown.html.erb +117 -0
- data/doc/users_guide_snippets/analysis_and_system_maintenance.txt +2 -1
- data/doc/users_guide_snippets/appendix_c_spawning_methods.txt +26 -48
- data/doc/users_guide_snippets/passenger_spawn_method.txt +18 -30
- data/doc/users_guide_snippets/support_information.txt +30 -0
- data/ext/apache2/Bucket.cpp +9 -26
- data/ext/apache2/Bucket.h +13 -10
- data/ext/apache2/Configuration.cpp +70 -58
- data/ext/apache2/Configuration.hpp +19 -47
- data/ext/apache2/DirectoryMapper.h +7 -7
- data/ext/apache2/Hooks.cpp +150 -313
- data/ext/boost/algorithm/string/detail/case_conv.hpp +4 -2
- data/ext/boost/algorithm/string/detail/find_format.hpp +20 -20
- data/ext/boost/algorithm/string/detail/find_format_all.hpp +23 -23
- data/ext/boost/algorithm/string/detail/find_format_store.hpp +2 -2
- data/ext/boost/algorithm/string/detail/formatter.hpp +25 -0
- data/ext/boost/algorithm/string/formatter.hpp +20 -3
- data/ext/boost/assert.hpp +85 -4
- data/ext/boost/bind/bind.hpp +1 -1
- data/ext/boost/concept/detail/backward_compatibility.hpp +1 -1
- data/ext/boost/concept_check.hpp +140 -64
- data/ext/boost/config.hpp +1 -1
- data/ext/boost/config/auto_link.hpp +8 -6
- data/ext/boost/config/compiler/borland.hpp +12 -2
- data/ext/boost/config/compiler/clang.hpp +89 -30
- data/ext/boost/config/compiler/codegear.hpp +3 -2
- data/ext/boost/config/compiler/common_edg.hpp +7 -5
- data/ext/boost/config/compiler/cray.hpp +61 -0
- data/ext/boost/config/compiler/digitalmars.hpp +9 -1
- data/ext/boost/config/compiler/gcc.hpp +33 -24
- data/ext/boost/config/compiler/gcc_xml.hpp +4 -0
- data/ext/boost/config/compiler/hp_acc.hpp +12 -1
- data/ext/boost/config/compiler/intel.hpp +78 -4
- data/ext/boost/config/compiler/metrowerks.hpp +4 -1
- data/ext/boost/config/compiler/mpw.hpp +4 -1
- data/ext/boost/config/compiler/nvcc.hpp +8 -66
- data/ext/boost/config/compiler/pathscale.hpp +80 -0
- data/ext/boost/config/compiler/pgi.hpp +5 -5
- data/ext/boost/config/compiler/sunpro_cc.hpp +4 -1
- data/ext/boost/config/compiler/vacpp.hpp +37 -13
- data/ext/boost/config/compiler/visualc.hpp +24 -11
- data/ext/boost/config/platform/bsd.hpp +1 -1
- data/ext/boost/config/platform/cray.hpp +18 -0
- data/ext/boost/config/platform/cygwin.hpp +10 -0
- data/ext/boost/config/platform/linux.hpp +5 -0
- data/ext/boost/config/platform/macos.hpp +5 -4
- data/ext/boost/config/platform/symbian.hpp +5 -2
- data/ext/boost/config/platform/vms.hpp +25 -0
- data/ext/boost/config/platform/win32.hpp +7 -1
- data/ext/boost/config/select_compiler_config.hpp +8 -25
- data/ext/boost/config/select_platform_config.hpp +8 -1
- data/ext/boost/config/select_stdlib_config.hpp +9 -1
- data/ext/boost/config/stdlib/dinkumware.hpp +6 -9
- data/ext/boost/config/stdlib/libcomo.hpp +1 -4
- data/ext/boost/config/stdlib/libcpp.hpp +36 -0
- data/ext/boost/config/stdlib/libstdcpp3.hpp +37 -11
- data/ext/boost/config/stdlib/modena.hpp +1 -4
- data/ext/boost/config/stdlib/msl.hpp +1 -4
- data/ext/boost/config/stdlib/roguewave.hpp +9 -6
- data/ext/boost/config/stdlib/sgi.hpp +12 -4
- data/ext/boost/config/stdlib/stlport.hpp +11 -4
- data/ext/boost/config/stdlib/vacpp.hpp +11 -4
- data/ext/boost/config/suffix.hpp +71 -6
- data/ext/boost/config/warning_disable.hpp +1 -1
- data/ext/boost/container/container_fwd.hpp +177 -0
- data/ext/boost/cstdint.hpp +17 -12
- data/ext/boost/current_function.hpp +2 -1
- data/ext/boost/date_time/c_time.hpp +17 -1
- data/ext/boost/date_time/compiler_config.hpp +13 -15
- data/ext/boost/date_time/date_formatting.hpp +7 -1
- data/ext/boost/date_time/filetime_functions.hpp +4 -4
- data/ext/boost/date_time/gregorian_calendar.ipp +2 -2
- data/ext/boost/date_time/strings_from_facet.hpp +3 -3
- data/ext/boost/date_time/time_facet.hpp +101 -101
- data/ext/boost/detail/endian.hpp +4 -2
- data/ext/boost/detail/fenv.hpp +74 -0
- data/ext/boost/detail/sp_typeinfo.hpp +6 -0
- data/ext/boost/exception/detail/clone_current_exception.hpp +47 -0
- data/ext/boost/exception/detail/exception_ptr.hpp +194 -122
- data/ext/boost/exception/detail/type_info.hpp +3 -3
- data/ext/boost/exception/diagnostic_information.hpp +37 -21
- data/ext/boost/exception/exception.hpp +21 -1
- data/ext/boost/exception/info.hpp +0 -1
- data/ext/boost/function.hpp +2 -2
- data/ext/boost/function/function_base.hpp +15 -9
- data/ext/boost/function/function_template.hpp +26 -48
- data/ext/boost/integer_fwd.hpp +0 -16
- data/ext/boost/integer_traits.hpp +2 -2
- data/ext/boost/iterator.hpp +1 -1
- data/ext/boost/iterator/iterator_adaptor.hpp +1 -7
- data/ext/boost/iterator/iterator_facade.hpp +13 -13
- data/ext/boost/iterator/transform_iterator.hpp +5 -20
- data/ext/boost/lexical_cast.hpp +1655 -673
- data/ext/boost/math/policies/policy.hpp +982 -0
- data/ext/boost/math/special_functions/detail/fp_traits.hpp +570 -0
- data/ext/boost/math/special_functions/detail/round_fwd.hpp +80 -0
- data/ext/boost/math/special_functions/fpclassify.hpp +533 -0
- data/ext/boost/math/special_functions/math_fwd.hpp +1070 -0
- data/ext/boost/math/special_functions/sign.hpp +145 -0
- data/ext/boost/math/tools/config.hpp +321 -0
- data/ext/boost/math/tools/promotion.hpp +150 -0
- data/ext/boost/math/tools/real_cast.hpp +29 -0
- data/ext/boost/math/tools/user.hpp +97 -0
- data/ext/boost/move/move.hpp +1222 -0
- data/ext/boost/mpl/O1_size.hpp +40 -0
- data/ext/boost/mpl/O1_size_fwd.hpp +24 -0
- data/ext/boost/mpl/advance.hpp +76 -0
- data/ext/boost/mpl/advance_fwd.hpp +28 -0
- data/ext/boost/mpl/at.hpp +52 -0
- data/ext/boost/mpl/at_fwd.hpp +24 -0
- data/ext/boost/mpl/aux_/O1_size_impl.hpp +87 -0
- data/ext/boost/mpl/aux_/advance_backward.hpp +128 -0
- data/ext/boost/mpl/aux_/advance_forward.hpp +127 -0
- data/ext/boost/mpl/aux_/arithmetic_op.hpp +92 -0
- data/ext/boost/mpl/aux_/at_impl.hpp +45 -0
- data/ext/boost/mpl/aux_/begin_end_impl.hpp +101 -0
- data/ext/boost/mpl/aux_/clear_impl.hpp +35 -0
- data/ext/boost/mpl/aux_/comparison_op.hpp +83 -0
- data/ext/boost/mpl/aux_/config/forwarding.hpp +27 -0
- data/ext/boost/mpl/aux_/config/typeof.hpp +38 -0
- data/ext/boost/mpl/aux_/contains_impl.hpp +61 -0
- data/ext/boost/mpl/aux_/find_if_pred.hpp +31 -0
- data/ext/boost/mpl/aux_/fold_impl.hpp +43 -0
- data/ext/boost/mpl/aux_/has_begin.hpp +23 -0
- data/ext/boost/mpl/aux_/has_size.hpp +23 -0
- data/ext/boost/mpl/aux_/has_tag.hpp +23 -0
- data/ext/boost/mpl/aux_/inserter_algorithm.hpp +159 -0
- data/ext/boost/mpl/aux_/is_msvc_eti_arg.hpp +64 -0
- data/ext/boost/mpl/aux_/iter_apply.hpp +47 -0
- data/ext/boost/mpl/aux_/iter_fold_if_impl.hpp +210 -0
- data/ext/boost/mpl/aux_/iter_fold_impl.hpp +42 -0
- data/ext/boost/mpl/aux_/lambda_spec.hpp +49 -0
- data/ext/boost/mpl/aux_/largest_int.hpp +63 -0
- data/ext/boost/mpl/aux_/msvc_eti_base.hpp +77 -0
- data/ext/boost/mpl/aux_/msvc_type.hpp +62 -0
- data/ext/boost/mpl/aux_/numeric_cast_utils.hpp +77 -0
- data/ext/boost/mpl/aux_/numeric_op.hpp +315 -0
- data/ext/boost/mpl/aux_/preprocessed/gcc/advance_backward.hpp +97 -0
- data/ext/boost/mpl/aux_/preprocessed/gcc/advance_forward.hpp +97 -0
- data/ext/boost/mpl/aux_/preprocessed/gcc/equal_to.hpp +94 -0
- data/ext/boost/mpl/aux_/preprocessed/gcc/fold_impl.hpp +180 -0
- data/ext/boost/mpl/aux_/preprocessed/gcc/greater.hpp +94 -0
- data/ext/boost/mpl/aux_/preprocessed/gcc/greater_equal.hpp +94 -0
- data/ext/boost/mpl/aux_/preprocessed/gcc/iter_fold_if_impl.hpp +133 -0
- data/ext/boost/mpl/aux_/preprocessed/gcc/iter_fold_impl.hpp +180 -0
- data/ext/boost/mpl/aux_/preprocessed/gcc/less.hpp +94 -0
- data/ext/boost/mpl/aux_/preprocessed/gcc/less_equal.hpp +94 -0
- data/ext/boost/mpl/aux_/preprocessed/gcc/list.hpp +323 -0
- data/ext/boost/mpl/aux_/preprocessed/gcc/minus.hpp +146 -0
- data/ext/boost/mpl/aux_/preprocessed/gcc/not_equal_to.hpp +94 -0
- data/ext/boost/mpl/aux_/preprocessed/gcc/plus.hpp +146 -0
- data/ext/boost/mpl/aux_/preprocessed/gcc/reverse_fold_impl.hpp +231 -0
- data/ext/boost/mpl/aux_/preprocessed/gcc/times.hpp +146 -0
- data/ext/boost/mpl/aux_/preprocessed/gcc/vector.hpp +323 -0
- data/ext/boost/mpl/aux_/preprocessor/default_params.hpp +67 -0
- data/ext/boost/mpl/aux_/push_back_impl.hpp +70 -0
- data/ext/boost/mpl/aux_/push_front_impl.hpp +71 -0
- data/ext/boost/mpl/aux_/reverse_fold_impl.hpp +44 -0
- data/ext/boost/mpl/aux_/size_impl.hpp +52 -0
- data/ext/boost/mpl/aux_/traits_lambda_spec.hpp +63 -0
- data/ext/boost/mpl/back_fwd.hpp +24 -0
- data/ext/boost/mpl/back_inserter.hpp +34 -0
- data/ext/boost/mpl/begin_end.hpp +57 -0
- data/ext/boost/mpl/begin_end_fwd.hpp +27 -0
- data/ext/boost/mpl/clear.hpp +39 -0
- data/ext/boost/mpl/clear_fwd.hpp +24 -0
- data/ext/boost/mpl/comparison.hpp +24 -0
- data/ext/boost/mpl/contains.hpp +41 -0
- data/ext/boost/mpl/contains_fwd.hpp +25 -0
- data/ext/boost/mpl/deref.hpp +41 -0
- data/ext/boost/mpl/distance.hpp +78 -0
- data/ext/boost/mpl/distance_fwd.hpp +28 -0
- data/ext/boost/mpl/empty_fwd.hpp +24 -0
- data/ext/boost/mpl/equal_to.hpp +21 -0
- data/ext/boost/mpl/find.hpp +38 -0
- data/ext/boost/mpl/find_if.hpp +50 -0
- data/ext/boost/mpl/fold.hpp +48 -0
- data/ext/boost/mpl/front_fwd.hpp +24 -0
- data/ext/boost/mpl/front_inserter.hpp +33 -0
- data/ext/boost/mpl/greater.hpp +21 -0
- data/ext/boost/mpl/greater_equal.hpp +21 -0
- data/ext/boost/mpl/inserter.hpp +32 -0
- data/ext/boost/mpl/iter_fold.hpp +49 -0
- data/ext/boost/mpl/iter_fold_if.hpp +117 -0
- data/ext/boost/mpl/iterator_range.hpp +42 -0
- data/ext/boost/mpl/iterator_tags.hpp +27 -0
- data/ext/boost/mpl/less.hpp +21 -0
- data/ext/boost/mpl/less_equal.hpp +21 -0
- data/ext/boost/mpl/limits/list.hpp +21 -0
- data/ext/boost/mpl/limits/vector.hpp +21 -0
- data/ext/boost/mpl/list.hpp +57 -0
- data/ext/boost/mpl/list/aux_/O1_size.hpp +33 -0
- data/ext/boost/mpl/list/aux_/begin_end.hpp +44 -0
- data/ext/boost/mpl/list/aux_/clear.hpp +34 -0
- data/ext/boost/mpl/list/aux_/empty.hpp +34 -0
- data/ext/boost/mpl/list/aux_/front.hpp +33 -0
- data/ext/boost/mpl/list/aux_/include_preprocessed.hpp +35 -0
- data/ext/boost/mpl/list/aux_/item.hpp +55 -0
- data/ext/boost/mpl/list/aux_/iterator.hpp +76 -0
- data/ext/boost/mpl/list/aux_/pop_front.hpp +34 -0
- data/ext/boost/mpl/list/aux_/preprocessed/plain/list10.hpp +149 -0
- data/ext/boost/mpl/list/aux_/preprocessed/plain/list20.hpp +169 -0
- data/ext/boost/mpl/list/aux_/push_back.hpp +36 -0
- data/ext/boost/mpl/list/aux_/push_front.hpp +39 -0
- data/ext/boost/mpl/list/aux_/size.hpp +33 -0
- data/ext/boost/mpl/list/aux_/tag.hpp +24 -0
- data/ext/boost/mpl/list/list0.hpp +42 -0
- data/ext/boost/mpl/list/list10.hpp +43 -0
- data/ext/boost/mpl/list/list20.hpp +43 -0
- data/ext/boost/mpl/long.hpp +22 -0
- data/ext/boost/mpl/long_fwd.hpp +27 -0
- data/ext/boost/mpl/minus.hpp +21 -0
- data/ext/boost/mpl/multiplies.hpp +53 -0
- data/ext/boost/mpl/negate.hpp +81 -0
- data/ext/boost/mpl/not_equal_to.hpp +21 -0
- data/ext/boost/mpl/numeric_cast.hpp +41 -0
- data/ext/boost/mpl/pair.hpp +70 -0
- data/ext/boost/mpl/plus.hpp +21 -0
- data/ext/boost/mpl/pop_back_fwd.hpp +24 -0
- data/ext/boost/mpl/pop_front_fwd.hpp +24 -0
- data/ext/boost/mpl/prior.hpp +19 -0
- data/ext/boost/mpl/push_back.hpp +53 -0
- data/ext/boost/mpl/push_back_fwd.hpp +24 -0
- data/ext/boost/mpl/push_front.hpp +52 -0
- data/ext/boost/mpl/push_front_fwd.hpp +24 -0
- data/ext/boost/mpl/remove_if.hpp +83 -0
- data/ext/boost/mpl/reverse_fold.hpp +50 -0
- data/ext/boost/mpl/same_as.hpp +55 -0
- data/ext/boost/mpl/sequence_tag.hpp +124 -0
- data/ext/boost/mpl/sequence_tag_fwd.hpp +26 -0
- data/ext/boost/mpl/size.hpp +42 -0
- data/ext/boost/mpl/size_fwd.hpp +24 -0
- data/ext/boost/mpl/tag.hpp +52 -0
- data/ext/boost/mpl/times.hpp +21 -0
- data/ext/boost/mpl/vector.hpp +57 -0
- data/ext/boost/mpl/vector/aux_/O1_size.hpp +56 -0
- data/ext/boost/mpl/vector/aux_/at.hpp +116 -0
- data/ext/boost/mpl/vector/aux_/back.hpp +59 -0
- data/ext/boost/mpl/vector/aux_/begin_end.hpp +49 -0
- data/ext/boost/mpl/vector/aux_/clear.hpp +55 -0
- data/ext/boost/mpl/vector/aux_/empty.hpp +68 -0
- data/ext/boost/mpl/vector/aux_/front.hpp +56 -0
- data/ext/boost/mpl/vector/aux_/include_preprocessed.hpp +55 -0
- data/ext/boost/mpl/vector/aux_/item.hpp +103 -0
- data/ext/boost/mpl/vector/aux_/iterator.hpp +130 -0
- data/ext/boost/mpl/vector/aux_/pop_back.hpp +40 -0
- data/ext/boost/mpl/vector/aux_/pop_front.hpp +40 -0
- data/ext/boost/mpl/vector/aux_/preprocessed/plain/vector10.hpp +829 -0
- data/ext/boost/mpl/vector/aux_/preprocessed/plain/vector20.hpp +1144 -0
- data/ext/boost/mpl/vector/aux_/preprocessed/typeof_based/vector10.hpp +139 -0
- data/ext/boost/mpl/vector/aux_/preprocessed/typeof_based/vector20.hpp +159 -0
- data/ext/boost/mpl/vector/aux_/push_back.hpp +40 -0
- data/ext/boost/mpl/vector/aux_/push_front.hpp +40 -0
- data/ext/boost/mpl/vector/aux_/size.hpp +49 -0
- data/ext/boost/mpl/vector/aux_/tag.hpp +32 -0
- data/ext/boost/mpl/vector/aux_/vector0.hpp +52 -0
- data/ext/boost/mpl/vector/vector0.hpp +34 -0
- data/ext/boost/mpl/vector/vector10.hpp +45 -0
- data/ext/boost/mpl/vector/vector20.hpp +45 -0
- data/ext/boost/none.hpp +1 -1
- data/ext/boost/numeric/conversion/bounds.hpp +24 -0
- data/ext/boost/numeric/conversion/cast.hpp +61 -0
- data/ext/boost/numeric/conversion/conversion_traits.hpp +39 -0
- data/ext/boost/numeric/conversion/converter.hpp +68 -0
- data/ext/boost/numeric/conversion/converter_policies.hpp +186 -0
- data/ext/boost/numeric/conversion/detail/bounds.hpp +58 -0
- data/ext/boost/numeric/conversion/detail/conversion_traits.hpp +97 -0
- data/ext/boost/numeric/conversion/detail/converter.hpp +602 -0
- data/ext/boost/numeric/conversion/detail/int_float_mixture.hpp +72 -0
- data/ext/boost/numeric/conversion/detail/is_subranged.hpp +234 -0
- data/ext/boost/numeric/conversion/detail/meta.hpp +120 -0
- data/ext/boost/numeric/conversion/detail/numeric_cast_traits.hpp +138 -0
- data/ext/boost/numeric/conversion/detail/preprocessed/numeric_cast_traits_common.hpp +1741 -0
- data/ext/boost/numeric/conversion/detail/preprocessed/numeric_cast_traits_long_long.hpp +347 -0
- data/ext/boost/numeric/conversion/detail/sign_mixture.hpp +72 -0
- data/ext/boost/numeric/conversion/detail/udt_builtin_mixture.hpp +69 -0
- data/ext/boost/numeric/conversion/int_float_mixture_enum.hpp +29 -0
- data/ext/boost/numeric/conversion/numeric_cast_traits.hpp +31 -0
- data/ext/boost/numeric/conversion/sign_mixture_enum.hpp +29 -0
- data/ext/boost/numeric/conversion/udt_builtin_mixture_enum.hpp +26 -0
- data/ext/boost/operators.hpp +3 -1
- data/ext/boost/optional/optional.hpp +146 -79
- data/ext/boost/optional/optional_fwd.hpp +8 -1
- data/ext/boost/preprocessor/cat.hpp +2 -2
- data/ext/boost/preprocessor/config/config.hpp +39 -4
- data/ext/boost/preprocessor/facilities/intercept.hpp +277 -0
- data/ext/boost/preprocessor/facilities/overload.hpp +25 -0
- data/ext/boost/preprocessor/iteration/detail/iter/forward1.hpp +3 -3
- data/ext/boost/preprocessor/iteration/iterate.hpp +3 -3
- data/ext/boost/preprocessor/punctuation/paren.hpp +23 -0
- data/ext/boost/preprocessor/repetition/enum_shifted_params.hpp +44 -0
- data/ext/boost/preprocessor/seq/cat.hpp +5 -4
- data/ext/boost/preprocessor/seq/size.hpp +0 -1
- data/ext/boost/preprocessor/tuple/eat.hpp +83 -34
- data/ext/boost/preprocessor/tuple/elem.hpp +161 -355
- data/ext/boost/preprocessor/tuple/rem.hpp +110 -48
- data/ext/boost/preprocessor/tuple/to_list.hpp +90 -36
- data/ext/boost/preprocessor/variadic/elem.hpp +94 -0
- data/ext/boost/preprocessor/variadic/size.hpp +30 -0
- data/ext/boost/range/begin.hpp +17 -6
- data/ext/boost/range/concepts.hpp +37 -2
- data/ext/boost/range/detail/safe_bool.hpp +72 -0
- data/ext/boost/range/end.hpp +14 -9
- data/ext/boost/range/iterator_range_core.hpp +120 -12
- data/ext/boost/range/size.hpp +21 -5
- data/ext/boost/smart_ptr/detail/shared_count.hpp +88 -0
- data/ext/boost/smart_ptr/detail/sp_counted_base.hpp +3 -0
- data/ext/boost/smart_ptr/detail/sp_counted_base_aix.hpp +142 -0
- data/ext/boost/smart_ptr/detail/sp_counted_base_gcc_mips.hpp +9 -0
- data/ext/boost/smart_ptr/detail/sp_counted_impl.hpp +10 -2
- data/ext/boost/smart_ptr/detail/sp_has_sync.hpp +5 -1
- data/ext/boost/smart_ptr/detail/spinlock.hpp +4 -1
- data/ext/boost/smart_ptr/detail/spinlock_gcc_arm.hpp +20 -3
- data/ext/boost/smart_ptr/detail/spinlock_pool.hpp +4 -0
- data/ext/boost/smart_ptr/make_shared.hpp +591 -22
- data/ext/boost/smart_ptr/shared_array.hpp +29 -1
- data/ext/boost/smart_ptr/shared_ptr.hpp +29 -13
- data/ext/boost/smart_ptr/weak_ptr.hpp +24 -12
- data/ext/boost/src/pthread/once.cpp +9 -7
- data/ext/boost/src/pthread/thread.cpp +32 -28
- data/ext/boost/src/pthread/timeconv.inl +4 -5
- data/ext/boost/src/tss_null.cpp +5 -1
- data/ext/boost/static_assert.hpp +8 -2
- data/ext/boost/thread/detail/config.hpp +19 -4
- data/ext/boost/thread/detail/move.hpp +11 -5
- data/ext/boost/thread/detail/thread.hpp +59 -43
- data/ext/boost/thread/exceptions.hpp +9 -9
- data/ext/boost/thread/future.hpp +150 -82
- data/ext/boost/thread/locks.hpp +101 -60
- data/ext/boost/thread/pthread/condition_variable.hpp +79 -32
- data/ext/boost/thread/pthread/condition_variable_fwd.hpp +12 -3
- data/ext/boost/thread/pthread/mutex.hpp +17 -14
- data/ext/boost/thread/pthread/once.hpp +3 -4
- data/ext/boost/thread/pthread/pthread_mutex_scoped_lock.hpp +12 -2
- data/ext/boost/thread/pthread/recursive_mutex.hpp +19 -19
- data/ext/boost/thread/pthread/shared_mutex.hpp +13 -13
- data/ext/boost/thread/pthread/thread_data.hpp +40 -12
- data/ext/boost/thread/thread_time.hpp +5 -0
- data/ext/boost/throw_exception.hpp +1 -1
- data/ext/boost/token_functions.hpp +34 -10
- data/ext/boost/type_traits/add_rvalue_reference.hpp +66 -0
- data/ext/boost/type_traits/alignment_of.hpp +1 -1
- data/ext/boost/type_traits/detail/bool_trait_def.hpp +26 -3
- data/ext/boost/type_traits/detail/bool_trait_undef.hpp +3 -2
- data/ext/boost/type_traits/detail/cv_traits_impl.hpp +1 -1
- data/ext/boost/type_traits/detail/size_t_trait_def.hpp +6 -4
- data/ext/boost/type_traits/detail/type_trait_def.hpp +8 -2
- data/ext/boost/type_traits/function_traits.hpp +1 -1
- data/ext/boost/type_traits/has_nothrow_constructor.hpp +53 -0
- data/ext/boost/type_traits/has_nothrow_copy.hpp +19 -5
- data/ext/boost/type_traits/has_trivial_constructor.hpp +51 -0
- data/ext/boost/type_traits/has_trivial_copy.hpp +20 -5
- data/ext/boost/type_traits/has_trivial_destructor.hpp +12 -5
- data/ext/boost/type_traits/intrinsics.hpp +119 -71
- data/ext/boost/type_traits/is_const.hpp +5 -5
- data/ext/boost/type_traits/is_convertible.hpp +14 -13
- data/ext/boost/type_traits/is_enum.hpp +1 -1
- data/ext/boost/type_traits/is_floating_point.hpp +27 -0
- data/ext/boost/type_traits/is_function.hpp +3 -3
- data/ext/boost/type_traits/is_fundamental.hpp +1 -1
- data/ext/boost/type_traits/is_member_function_pointer.hpp +2 -2
- data/ext/boost/type_traits/is_member_pointer.hpp +2 -2
- data/ext/boost/type_traits/is_pod.hpp +11 -3
- data/ext/boost/type_traits/is_pointer.hpp +2 -2
- data/ext/boost/type_traits/is_signed.hpp +8 -3
- data/ext/boost/type_traits/is_union.hpp +8 -0
- data/ext/boost/type_traits/is_unsigned.hpp +9 -4
- data/ext/boost/type_traits/is_volatile.hpp +5 -5
- data/ext/boost/type_traits/remove_cv.hpp +4 -3
- data/ext/boost/type_traits/remove_pointer.hpp +51 -2
- data/ext/boost/type_traits/remove_reference.hpp +2 -2
- data/ext/boost/type_traits/type_with_alignment.hpp +8 -2
- data/ext/boost/utility/declval.hpp +44 -0
- data/ext/boost/utility/detail/in_place_factory_prefix.hpp +36 -0
- data/ext/boost/utility/detail/in_place_factory_suffix.hpp +23 -0
- data/ext/boost/utility/detail/result_of_iterate.hpp +142 -0
- data/ext/boost/utility/in_place_factory.hpp +88 -0
- data/ext/boost/utility/result_of.hpp +103 -0
- data/ext/boost/utility/swap.hpp +55 -0
- data/ext/common/AnsiColorConstants.h +36 -0
- data/ext/common/ApplicationPool2/Common.h +87 -0
- data/ext/common/ApplicationPool2/ComponentInfo.h +53 -0
- data/ext/common/ApplicationPool2/Group.h +648 -0
- data/ext/common/ApplicationPool2/Implementation.cpp +580 -0
- data/ext/common/ApplicationPool2/Options.h +576 -0
- data/ext/common/ApplicationPool2/PipeWatcher.h +61 -0
- data/ext/common/ApplicationPool2/Pool.h +1181 -0
- data/ext/common/ApplicationPool2/Process.h +425 -0
- data/ext/common/ApplicationPool2/README.md +96 -0
- data/ext/common/ApplicationPool2/Session.h +158 -0
- data/ext/common/ApplicationPool2/Socket.h +246 -0
- data/ext/common/ApplicationPool2/Spawner.h +2212 -0
- data/ext/common/ApplicationPool2/SuperGroup.h +749 -0
- data/ext/common/BackgroundEventLoop.cpp +129 -0
- data/ext/common/BackgroundEventLoop.h +61 -0
- data/ext/common/Constants.h +3 -1
- data/ext/common/EventedBufferedInput.h +331 -0
- data/ext/common/EventedMessageServer.h +17 -34
- data/ext/common/EventedServer.h +2 -2
- data/ext/common/Exceptions.h +71 -19
- data/ext/common/FileDescriptor.h +8 -6
- data/ext/common/HttpConstants.h +167 -0
- data/ext/common/IniFile.h +24 -0
- data/ext/common/Logging.h +62 -849
- data/ext/common/MessageReadersWriters.h +19 -0
- data/ext/common/MessageServer.h +11 -14
- data/ext/common/MultiLibeio.cpp +198 -0
- data/ext/common/MultiLibeio.h +67 -0
- data/ext/common/ResourceLocator.h +24 -41
- data/ext/common/SafeLibev.h +186 -14
- data/ext/common/StaticString.h +23 -3
- data/ext/common/UnionStation.h +972 -0
- data/ext/common/Utils.cpp +168 -24
- data/ext/common/Utils.h +25 -3
- data/ext/common/Utils/CachedFileStat.hpp +4 -3
- data/ext/common/Utils/FileChangeChecker.h +2 -2
- data/ext/common/Utils/HashMap.h +50 -0
- data/ext/common/Utils/IOUtils.cpp +229 -68
- data/ext/common/Utils/IOUtils.h +134 -3
- data/ext/common/Utils/Lock.h +28 -0
- data/ext/common/Utils/MemoryBarrier.h +52 -0
- data/ext/common/Utils/PriorityQueue.h +54 -0
- data/ext/common/Utils/ProcessMetricsCollector.h +9 -11
- data/ext/common/Utils/ScopeGuard.h +50 -1
- data/ext/common/Utils/SmallVector.h +653 -0
- data/ext/common/Utils/StrIntUtils.cpp +26 -2
- data/ext/common/Utils/StrIntUtils.h +18 -2
- data/ext/common/Utils/StringMap.h +125 -8
- data/ext/common/Utils/Template.h +212 -0
- data/ext/common/Utils/fib.c +699 -0
- data/ext/common/Utils/fib.h +101 -0
- data/ext/common/Utils/fibpriv.h +67 -0
- data/ext/common/Utils/json-forwards.h +249 -0
- data/ext/common/Utils/json.h +1855 -0
- data/ext/common/Utils/jsoncpp.cpp +4230 -0
- data/ext/common/agents/Base.cpp +1126 -0
- data/ext/common/{AgentBase.h → agents/Base.h} +5 -1
- data/ext/common/agents/EnvPrinter.c +16 -0
- data/ext/common/agents/HelperAgent/AgentOptions.h +81 -0
- data/ext/common/{HelperAgent → agents/HelperAgent}/BacktracesServer.h +3 -2
- data/ext/common/agents/HelperAgent/FileBackedPipe.h +732 -0
- data/ext/common/agents/HelperAgent/Main.cpp +497 -0
- data/ext/common/agents/HelperAgent/RequestHandler.cpp +283 -0
- data/ext/common/agents/HelperAgent/RequestHandler.h +2139 -0
- data/ext/common/agents/HelperAgent/ScgiRequestParser.h +451 -0
- data/ext/common/{LoggingAgent → agents/LoggingAgent}/DataStoreId.h +1 -1
- data/ext/common/{LoggingAgent → agents/LoggingAgent}/FilterSupport.cpp +1 -1
- data/ext/common/{LoggingAgent → agents/LoggingAgent}/FilterSupport.h +0 -0
- data/ext/common/{LoggingAgent → agents/LoggingAgent}/LoggingServer.h +18 -16
- data/ext/common/{LoggingAgent → agents/LoggingAgent}/Main.cpp +15 -13
- data/ext/common/{LoggingAgent → agents/LoggingAgent}/RemoteSender.h +6 -6
- data/ext/common/agents/SpawnPreparer.cpp +127 -0
- data/ext/common/{Watchdog.cpp → agents/Watchdog/Main.cpp} +63 -25
- data/ext/libeio/Changes +72 -0
- data/ext/{google/COPYING → libeio/LICENSE} +17 -9
- data/ext/libeio/Makefile.am +15 -0
- data/ext/libeio/Makefile.in +694 -0
- data/ext/libeio/aclocal.m4 +9418 -0
- data/ext/libeio/autogen.sh +3 -0
- data/ext/libeio/config.guess +1501 -0
- data/ext/libeio/config.h.in +136 -0
- data/ext/libeio/config.sub +1705 -0
- data/ext/libeio/configure +14822 -0
- data/ext/libeio/configure.ac +22 -0
- data/ext/libeio/demo.c +194 -0
- data/ext/libeio/ecb.h +457 -0
- data/ext/libeio/eio.c +2816 -0
- data/ext/libeio/eio.h +411 -0
- data/ext/libeio/install-sh +520 -0
- data/ext/libeio/libeio.m4 +211 -0
- data/ext/libeio/ltmain.sh +9636 -0
- data/ext/libeio/missing +376 -0
- data/ext/libeio/xthread.h +166 -0
- data/ext/libev/Changes +125 -7
- data/ext/libev/Makefile.am +5 -3
- data/ext/libev/Makefile.in +209 -120
- data/ext/libev/aclocal.m4 +6027 -4619
- data/ext/libev/autogen.sh +1 -4
- data/ext/libev/config.h.in +11 -7
- data/ext/libev/configure +7312 -14993
- data/ext/libev/configure.ac +12 -5
- data/ext/libev/depcomp +630 -0
- data/ext/libev/ev++.h +48 -32
- data/ext/libev/ev.c +1173 -391
- data/ext/libev/ev.h +315 -181
- data/ext/libev/ev_epoll.c +66 -15
- data/ext/libev/ev_kqueue.c +20 -18
- data/ext/libev/ev_poll.c +27 -23
- data/ext/libev/ev_port.c +39 -19
- data/ext/libev/ev_select.c +23 -17
- data/ext/libev/ev_vars.h +25 -8
- data/ext/libev/ev_win32.c +6 -6
- data/ext/libev/ev_wrap.h +22 -2
- data/ext/libev/event.c +18 -17
- data/ext/libev/event.h +16 -4
- data/ext/libev/libev.m4 +10 -6
- data/ext/libev/ltmain.sh +7353 -5811
- data/ext/nginx/Configuration.c +74 -42
- data/ext/nginx/Configuration.h +3 -5
- data/ext/nginx/ContentHandler.c +26 -83
- data/ext/nginx/ContentHandler.h +1 -1
- data/ext/nginx/config +13 -9
- data/ext/nginx/ngx_http_passenger_module.c +3 -7
- data/ext/oxt/detail/backtrace_enabled.hpp +5 -102
- data/ext/oxt/detail/context.hpp +90 -0
- data/ext/oxt/detail/spin_lock_darwin.hpp +4 -0
- data/ext/oxt/detail/spin_lock_gcc_x86.hpp +4 -0
- data/ext/oxt/detail/spin_lock_pthreads.hpp +14 -0
- data/ext/oxt/detail/tracable_exception_enabled.hpp +2 -2
- data/ext/oxt/dynamic_thread_group.hpp +27 -1
- data/ext/oxt/implementation.cpp +415 -0
- data/ext/oxt/{thread.cpp → initialize.hpp} +13 -6
- data/ext/oxt/macros.hpp +32 -1
- data/ext/oxt/spin_lock.hpp +6 -11
- data/ext/oxt/system_calls.cpp +204 -16
- data/ext/oxt/system_calls.hpp +85 -45
- data/ext/oxt/thread.hpp +13 -117
- data/ext/ruby/passenger_native_support.c +82 -237
- data/helper-scripts/backtrace-sanitizer.rb +114 -0
- data/helper-scripts/classic-rails-loader.rb +135 -0
- data/helper-scripts/classic-rails-preloader.rb +161 -0
- data/helper-scripts/node-loader.js +314 -0
- data/helper-scripts/rack-loader.rb +104 -0
- data/helper-scripts/rack-preloader.rb +132 -0
- data/helper-scripts/wsgi-loader.py +231 -0
- data/helper-scripts/wsgi-preloader.py +1 -0
- data/lib/phusion_passenger.rb +159 -61
- data/lib/phusion_passenger/abstract_installer.rb +182 -87
- data/lib/phusion_passenger/admin_tools/server_instance.rb +25 -19
- data/lib/phusion_passenger/analytics_logger.rb +5 -4
- data/lib/phusion_passenger/classic_rails/{request_handler.rb → thread_handler_extension.rb} +4 -40
- data/lib/phusion_passenger/classic_rails_extensions/init.rb +5 -3
- data/lib/phusion_passenger/common_library.rb +441 -0
- data/lib/phusion_passenger/console_text_template.rb +4 -16
- data/lib/phusion_passenger/constants.rb +1 -8
- data/lib/phusion_passenger/debug_logging.rb +5 -2
- data/lib/phusion_passenger/dependencies.rb +51 -13
- data/lib/phusion_passenger/loader_shared_helpers.rb +318 -0
- data/lib/phusion_passenger/message_channel.rb +3 -47
- data/lib/phusion_passenger/message_client.rb +2 -2
- data/lib/phusion_passenger/native_support.rb +36 -15
- data/lib/phusion_passenger/packaging.rb +8 -11
- data/lib/phusion_passenger/platform_info.rb +25 -17
- data/lib/phusion_passenger/platform_info/apache.rb +10 -7
- data/lib/phusion_passenger/platform_info/binary_compatibility.rb +10 -30
- data/lib/phusion_passenger/platform_info/compiler.rb +93 -34
- data/lib/phusion_passenger/platform_info/ruby.rb +37 -97
- data/lib/phusion_passenger/preloader_shared_helpers.rb +121 -0
- data/lib/phusion_passenger/public_api.rb +1 -4
- data/lib/phusion_passenger/rack/{request_handler.rb → thread_handler_extension.rb} +14 -63
- data/lib/phusion_passenger/rails3_extensions/init.rb +9 -8
- data/lib/phusion_passenger/request_handler.rb +500 -0
- data/lib/phusion_passenger/request_handler/thread_handler.rb +360 -0
- data/lib/phusion_passenger/ruby_core_enhancements.rb +142 -0
- data/lib/phusion_passenger/standalone/command.rb +36 -15
- data/lib/phusion_passenger/standalone/package_runtime_command.rb +16 -8
- data/lib/phusion_passenger/standalone/runtime_installer.rb +169 -72
- data/lib/phusion_passenger/standalone/start_command.rb +44 -39
- data/lib/phusion_passenger/standalone/utils.rb +5 -5
- data/lib/phusion_passenger/utils.rb +35 -914
- data/lib/phusion_passenger/utils/ansi_colors.rb +59 -0
- data/lib/phusion_passenger/utils/file_system_watcher.rb +1 -1
- data/lib/phusion_passenger/utils/robust_interruption.rb +134 -0
- data/lib/phusion_passenger/utils/tee_input.rb +174 -0
- data/lib/phusion_passenger/utils/tmpio.rb +33 -0
- data/lib/phusion_passenger/utils/unseekable_socket.rb +6 -0
- data/resources/mime.types +5 -1
- data/{lib/phusion_passenger/templates → resources}/standalone_default_root/index.html +0 -0
- data/{lib/phusion_passenger → resources}/templates/apache2/apache_must_be_compiled_with_compatible_mpm.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/apache2/config_snippets.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/apache2/deployment_example.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/apache2/no_write_permission_to_passenger_root.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/apache2/possible_solutions_for_compilation_and_installation_problems.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/apache2/run_installer_as_root.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/apache2/welcome.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/error_layout.css +6 -0
- data/resources/templates/error_layout.html.template +89 -0
- data/resources/templates/general_error.html.template +1 -0
- data/resources/templates/general_error_with_html.html.template +1 -0
- data/{lib/phusion_passenger → resources}/templates/nginx/ask_for_extra_configure_flags.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/nginx/cannot_write_to_dir.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/nginx/config_snippets.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/nginx/config_snippets_inserted.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/nginx/confirm_extra_configure_flags.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/nginx/deployment_example.txt.erb +0 -0
- data/resources/templates/nginx/not_available_when_natively_packaged.txt.erb +8 -0
- data/{lib/phusion_passenger → resources}/templates/nginx/pcre_could_not_be_downloaded.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/nginx/pcre_could_not_be_extracted.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/nginx/possible_solutions_for_compilation_and_installation_problems.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/nginx/possible_solutions_for_download_and_extraction_problems.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/nginx/query_download_and_install.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/nginx/run_installer_as_root.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/nginx/welcome.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/standalone/cannot_write_to_dir.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/standalone/config.erb +26 -5
- data/{lib/phusion_passenger → resources}/templates/standalone/possible_solutions_for_download_and_extraction_problems.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/standalone/run_installer_as_root.txt.erb +0 -0
- data/{lib/phusion_passenger → resources}/templates/standalone/welcome.txt.erb +0 -0
- data/resources/templates/undisclosed_error.html.template +25 -0
- data/test/config.json.example +42 -0
- data/test/cxx/ApplicationPool2/DirectSpawnerTest.cpp +86 -0
- data/test/cxx/ApplicationPool2/OptionsTest.cpp +44 -0
- data/test/cxx/ApplicationPool2/PoolTest.cpp +1234 -0
- data/test/cxx/ApplicationPool2/ProcessTest.cpp +131 -0
- data/test/cxx/ApplicationPool2/SmartSpawnerTest.cpp +229 -0
- data/test/cxx/ApplicationPool2/SpawnerTestCases.cpp +744 -0
- data/test/cxx/BufferedIOTest.cpp +7 -7
- data/test/cxx/CxxTestMain.cpp +65 -2
- data/test/cxx/FileBackedPipeTest.cpp +626 -0
- data/test/cxx/FileChangeCheckerTest.cpp +20 -18
- data/test/cxx/FilterSupportTest.cpp +5 -5
- data/test/cxx/IOUtilsTest.cpp +11 -4
- data/test/cxx/MessageReadersWritersTest.cpp +1 -1
- data/test/cxx/MessageServerTest.cpp +31 -30
- data/test/cxx/RequestHandlerTest.cpp +777 -0
- data/test/cxx/ScgiRequestParserTest.cpp +36 -16
- data/test/cxx/ServerInstanceDirTest.cpp +1 -1
- data/test/cxx/StringMapTest.cpp +61 -0
- data/test/cxx/TemplateTest.cpp +118 -0
- data/test/cxx/TestSupport.cpp +25 -68
- data/test/cxx/TestSupport.h +81 -41
- data/test/cxx/{LoggingTest.cpp → UnionStationTest.cpp} +79 -74
- data/test/cxx/UtilsTest.cpp +59 -5
- data/test/integration_tests/apache2_tests.rb +2 -2
- data/test/integration_tests/nginx_tests.rb +1 -1
- data/test/integration_tests/spec_helper.rb +7 -5
- data/test/oxt/oxt_test_main.cpp +2 -0
- data/test/oxt/syscall_interruption_test.cpp +1 -0
- data/test/ruby/classic_rails/loader_spec.rb +48 -0
- data/test/ruby/classic_rails/preloader_spec.rb +54 -0
- data/test/ruby/rack/loader_spec.rb +62 -0
- data/test/ruby/rack/preloader_spec.rb +74 -0
- data/test/ruby/{abstract_request_handler_spec.rb → request_handler_spec.rb} +31 -68
- data/test/ruby/shared/loader_spec.rb +241 -0
- data/test/ruby/shared/rails/analytics_logging_extensions_spec.rb +141 -182
- data/test/ruby/shared/ruby_loader_spec.rb +55 -0
- data/test/ruby/spec_helper.rb +8 -53
- data/test/ruby/utils/file_system_watcher_spec.rb +9 -1
- data/test/ruby/utils_spec.rb +10 -683
- data/test/stub/rack/config.ru +28 -3
- data/test/stub/rack/start.rb +47 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/Rakefile +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/app/controllers/application_controller.rb +0 -2
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/app/controllers/bar_controller_1.rb +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/app/controllers/bar_controller_2.rb +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/app/controllers/foo_controller.rb +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/app/helpers/application_helper.rb +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/boot.rb +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/database.yml +3 -3
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/environment.rb +5 -2
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/environments/development.rb +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/environments/production.rb +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/environments/staging.rb +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/initializers/inflections.rb +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/initializers/mime_types.rb +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/routes.rb +1 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/about +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/console +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/dbconsole +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/destroy +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/generate +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/performance/benchmarker +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/performance/profiler +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/performance/request +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/plugin +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/process/inspector +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/process/reaper +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/process/spawner +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/runner +0 -0
- data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/server +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/Gemfile +0 -0
- data/test/stub/rails3.0/Gemfile.lock +80 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/Rakefile +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/app/controllers/application_controller.rb +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/app/helpers/application_helper.rb +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/app/views/layouts/application.html.erb +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/config.ru +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/application.rb +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/boot.rb +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/database.yml +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/environment.rb +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/environments/development.rb +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/environments/production.rb +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/environments/test.rb +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/initializers/backtrace_silencers.rb +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/initializers/inflections.rb +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/initializers/mime_types.rb +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/initializers/passenger.rb +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/initializers/secret_token.rb +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/initializers/session_store.rb +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/locales/en.yml +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/routes.rb +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/db/seeds.rb +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/doc/README_FOR_APP +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/public/404.html +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/public/422.html +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/public/500.html +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/public/favicon.ico +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/public/index.html +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/public/robots.txt +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/script/rails +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/test/performance/browsing_test.rb +0 -0
- data/test/stub/{rails_apps/3.0/empty → rails3.0}/test/test_helper.rb +0 -0
- data/test/stub/start_error.pl +24 -0
- data/test/stub/wsgi/passenger_wsgi.py +71 -3
- data/test/support/apache2_controller.rb +2 -2
- data/test/support/placebo-preloader.rb +88 -0
- data/test/support/test_helper.rb +1 -14
- data/test/tut/tut.h +11 -4
- metadata +590 -326
- data.tar.gz.asc +0 -12
- data/PACKAGING.TXT +0 -25
- data/build/config.rb +0 -46
- data/ext/apache2/HelperAgent.cpp +0 -364
- data/ext/boost/call_traits.hpp +0 -24
- data/ext/boost/detail/call_traits.hpp +0 -164
- data/ext/common/AbstractSpawnManager.h +0 -110
- data/ext/common/AgentBase.cpp +0 -432
- data/ext/common/ApplicationPool/Client.h +0 -788
- data/ext/common/ApplicationPool/Interface.h +0 -295
- data/ext/common/ApplicationPool/Pool.h +0 -1327
- data/ext/common/ApplicationPool/Server.h +0 -479
- data/ext/common/MessageChannel.h +0 -494
- data/ext/common/PoolOptions.h +0 -518
- data/ext/common/Process.h +0 -253
- data/ext/common/Session.h +0 -436
- data/ext/common/SpawnManager.h +0 -611
- data/ext/google/ChangeLog +0 -167
- data/ext/google/dense_hash_map +0 -310
- data/ext/google/dense_hash_set +0 -287
- data/ext/google/sparse_hash_map +0 -294
- data/ext/google/sparse_hash_set +0 -275
- data/ext/google/sparsehash/densehashtable.h +0 -1062
- data/ext/google/sparsehash/sparseconfig.h +0 -55
- data/ext/google/sparsehash/sparsehashtable.h +0 -1015
- data/ext/google/sparsetable +0 -1468
- data/ext/google/type_traits.h +0 -250
- data/ext/nginx/HelperAgent.cpp +0 -1355
- data/ext/nginx/ScgiRequestParser.h +0 -375
- data/ext/oxt/backtrace.cpp +0 -185
- data/ext/oxt/tracable_exception.cpp +0 -89
- data/helper-scripts/passenger-spawn-server +0 -106
- data/lib/phusion_passenger/abstract_request_handler.rb +0 -766
- data/lib/phusion_passenger/abstract_server.rb +0 -372
- data/lib/phusion_passenger/abstract_server_collection.rb +0 -335
- data/lib/phusion_passenger/app_process.rb +0 -174
- data/lib/phusion_passenger/classic_rails/application_spawner.rb +0 -344
- data/lib/phusion_passenger/classic_rails/framework_spawner.rb +0 -311
- data/lib/phusion_passenger/exceptions.rb +0 -103
- data/lib/phusion_passenger/html_template.rb +0 -107
- data/lib/phusion_passenger/rack/application_spawner.rb +0 -231
- data/lib/phusion_passenger/spawn_manager.rb +0 -359
- data/lib/phusion_passenger/templates/app_exited_during_initialization.html.erb +0 -38
- data/lib/phusion_passenger/templates/app_init_error.html.erb +0 -64
- data/lib/phusion_passenger/templates/database_error.html.erb +0 -66
- data/lib/phusion_passenger/templates/error_layout.html.erb +0 -39
- data/lib/phusion_passenger/templates/framework_init_error.html.erb +0 -39
- data/lib/phusion_passenger/templates/general_error.html.erb +0 -22
- data/lib/phusion_passenger/templates/load_error.html.erb +0 -46
- data/lib/phusion_passenger/templates/version_not_found.html.erb +0 -34
- data/lib/phusion_passenger/utils/rewindable_input.rb +0 -125
- data/lib/phusion_passenger/wsgi/application_spawner.rb +0 -108
- data/test/config.yml.example +0 -41
- data/test/cxx/ApplicationPool_PoolTest.cpp +0 -33
- data/test/cxx/ApplicationPool_PoolTestCases.cpp +0 -1029
- data/test/cxx/ApplicationPool_ServerTest.cpp +0 -308
- data/test/cxx/ApplicationPool_Server_PoolTest.cpp +0 -80
- data/test/cxx/MessageChannelTest.cpp +0 -557
- data/test/cxx/PoolOptionsTest.cpp +0 -116
- data/test/cxx/SpawnManagerTest.cpp +0 -161
- data/test/ruby/abstract_server_collection_spec.rb +0 -247
- data/test/ruby/abstract_server_spec.rb +0 -61
- data/test/ruby/app_process_spec.rb +0 -43
- data/test/ruby/classic_rails/application_spawner_spec.rb +0 -89
- data/test/ruby/classic_rails/framework_spawner_spec.rb +0 -92
- data/test/ruby/rack/application_spawner_spec.rb +0 -116
- data/test/ruby/shared/abstract_server_spec.rb +0 -23
- data/test/ruby/shared/spawners/classic_rails/framework_spawner_spec.rb +0 -38
- data/test/ruby/shared/spawners/classic_rails/lack_of_rails_gem_version_spec.rb +0 -19
- data/test/ruby/shared/spawners/classic_rails/spawner_spec.rb +0 -15
- data/test/ruby/shared/spawners/non_preloading_spawner_spec.rb +0 -27
- data/test/ruby/shared/spawners/preloading_spawner_spec.rb +0 -29
- data/test/ruby/shared/spawners/reload_all_spec.rb +0 -36
- data/test/ruby/shared/spawners/reload_single_spec.rb +0 -52
- data/test/ruby/shared/spawners/spawn_server_spec.rb +0 -28
- data/test/ruby/shared/spawners/spawner_spec.rb +0 -273
- data/test/ruby/shared/utils/pseudo_io_spec.rb +0 -60
- data/test/ruby/spawn_manager_spec.rb +0 -134
- data/test/ruby/wsgi/application_spawner_spec.rb +0 -50
- data/test/stub/message_channel.rb +0 -11
- data/test/stub/message_channel_2.rb +0 -12
- data/test/stub/message_channel_3.rb +0 -19
- data/test/stub/rails_apps/3.0/empty/Gemfile.lock +0 -73
- data/test/stub/spawn_server.rb +0 -22
- 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_ */
|