passenger 5.0.0.beta3 → 5.0.0.rc1
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.
- checksums.yaml +8 -8
- checksums.yaml.gz.asc +7 -7
- data.tar.gz.asc +7 -7
- data/.editorconfig +11 -5
- data/CHANGELOG +38 -0
- data/CONTRIBUTING.md +1 -4
- data/Gemfile +0 -1
- data/Gemfile.lock +0 -2
- data/Rakefile +33 -33
- data/bin/passenger +1 -1
- data/bin/passenger-config +1 -1
- data/bin/passenger-install-apache2-module +800 -800
- data/bin/passenger-install-nginx-module +592 -592
- data/bin/passenger-memory-stats +127 -127
- data/bin/passenger-status +216 -216
- data/build/agents.rb +127 -127
- data/build/apache2.rb +87 -87
- data/build/basics.rb +60 -60
- data/build/common_library.rb +165 -165
- data/build/cplusplus_support.rb +51 -51
- data/build/cxx_tests.rb +268 -268
- data/build/debian.rb +143 -143
- data/build/documentation.rb +58 -58
- data/build/integration_tests.rb +81 -81
- data/build/misc.rb +132 -132
- data/build/nginx.rb +20 -20
- data/build/node_tests.rb +7 -7
- data/build/oxt_tests.rb +14 -14
- data/build/packaging.rb +570 -570
- data/build/preprocessor.rb +260 -260
- data/build/rake_extensions.rb +71 -71
- data/build/ruby_extension.rb +29 -29
- data/build/ruby_tests.rb +6 -6
- data/build/test_basics.rb +37 -37
- data/debian.template/control.template +3 -5
- data/dev/copy_boost_headers +134 -134
- data/dev/install_scripts_bootstrap_code.rb +25 -25
- data/dev/list_tests +20 -20
- data/dev/ruby_server.rb +223 -223
- data/dev/runner +18 -18
- data/doc/ServerOptimizationGuide.txt.md +55 -2
- data/doc/Users guide Nginx.txt +0 -26
- data/doc/Users guide Standalone.txt +5 -1
- data/doc/users_guide_snippets/tips.txt +9 -0
- data/ext/common/ApplicationPool2/Group.h +23 -11
- data/ext/common/ApplicationPool2/Implementation.cpp +32 -7
- data/ext/common/ApplicationPool2/Pool.h +22 -17
- data/ext/common/ApplicationPool2/SmartSpawner.h +4 -1
- data/ext/common/ApplicationPool2/Spawner.h +1 -1
- data/ext/common/Constants.h +1 -1
- data/ext/common/agents/Base.cpp +35 -20
- data/ext/common/agents/HelperAgent/Main.cpp +8 -1
- data/ext/common/agents/HelperAgent/OptionParser.h +18 -4
- data/ext/common/agents/HelperAgent/RequestHandler.h +2 -83
- data/ext/common/agents/HelperAgent/RequestHandler/ForwardResponse.cpp +54 -1
- data/ext/common/agents/HelperAgent/RequestHandler/InitRequest.cpp +7 -4
- data/ext/common/agents/Main.cpp +1 -1
- data/ext/common/agents/Watchdog/Main.cpp +54 -19
- data/ext/nginx/Configuration.c +7 -0
- data/ext/nginx/ContentHandler.c +9 -1
- data/helper-scripts/backtrace-sanitizer.rb +106 -87
- data/helper-scripts/crash-watch.rb +32 -0
- data/helper-scripts/download_binaries/extconf.rb +38 -38
- data/helper-scripts/meteor-loader.rb +107 -107
- data/helper-scripts/prespawn +101 -101
- data/helper-scripts/rack-loader.rb +96 -96
- data/helper-scripts/rack-preloader.rb +137 -137
- data/lib/phusion_passenger.rb +292 -292
- data/lib/phusion_passenger/abstract_installer.rb +438 -438
- data/lib/phusion_passenger/active_support3_extensions/init.rb +168 -170
- data/lib/phusion_passenger/admin_tools.rb +20 -20
- data/lib/phusion_passenger/admin_tools/instance.rb +178 -178
- data/lib/phusion_passenger/admin_tools/instance_registry.rb +61 -61
- data/lib/phusion_passenger/admin_tools/memory_stats.rb +267 -267
- data/lib/phusion_passenger/apache2/config_options.rb +182 -182
- data/lib/phusion_passenger/common_library.rb +479 -485
- data/lib/phusion_passenger/config/about_command.rb +161 -161
- data/lib/phusion_passenger/config/admin_command_command.rb +129 -129
- data/lib/phusion_passenger/config/agent_compiler.rb +121 -121
- data/lib/phusion_passenger/config/build_native_support_command.rb +43 -43
- data/lib/phusion_passenger/config/command.rb +25 -25
- data/lib/phusion_passenger/config/compile_agent_command.rb +62 -62
- data/lib/phusion_passenger/config/compile_nginx_engine_command.rb +88 -73
- data/lib/phusion_passenger/config/detach_process_command.rb +72 -72
- data/lib/phusion_passenger/config/download_agent_command.rb +246 -227
- data/lib/phusion_passenger/config/download_nginx_engine_command.rb +245 -224
- data/lib/phusion_passenger/config/install_agent_command.rb +144 -132
- data/lib/phusion_passenger/config/install_standalone_runtime_command.rb +205 -185
- data/lib/phusion_passenger/config/installation_utils.rb +204 -204
- data/lib/phusion_passenger/config/list_instances_command.rb +64 -64
- data/lib/phusion_passenger/config/main.rb +152 -152
- data/lib/phusion_passenger/config/nginx_engine_compiler.rb +319 -300
- data/lib/phusion_passenger/config/reopen_logs_command.rb +67 -67
- data/lib/phusion_passenger/config/restart_app_command.rb +155 -155
- data/lib/phusion_passenger/config/system_metrics_command.rb +13 -13
- data/lib/phusion_passenger/config/utils.rb +95 -95
- data/lib/phusion_passenger/config/validate_install_command.rb +198 -198
- data/lib/phusion_passenger/console_text_template.rb +25 -25
- data/lib/phusion_passenger/constants.rb +90 -90
- data/lib/phusion_passenger/debug_logging.rb +106 -106
- data/lib/phusion_passenger/loader_shared_helpers.rb +447 -432
- data/lib/phusion_passenger/message_channel.rb +312 -312
- data/lib/phusion_passenger/message_client.rb +176 -176
- data/lib/phusion_passenger/native_support.rb +369 -369
- data/lib/phusion_passenger/nginx/config_options.rb +297 -297
- data/lib/phusion_passenger/packaging.rb +131 -131
- data/lib/phusion_passenger/platform_info.rb +360 -360
- data/lib/phusion_passenger/platform_info/apache.rb +767 -767
- data/lib/phusion_passenger/platform_info/apache_detector.rb +199 -199
- data/lib/phusion_passenger/platform_info/binary_compatibility.rb +107 -107
- data/lib/phusion_passenger/platform_info/compiler.rb +570 -570
- data/lib/phusion_passenger/platform_info/curl.rb +32 -32
- data/lib/phusion_passenger/platform_info/cxx_portability.rb +188 -188
- data/lib/phusion_passenger/platform_info/depcheck.rb +372 -372
- data/lib/phusion_passenger/platform_info/depcheck_specs/apache2.rb +109 -109
- data/lib/phusion_passenger/platform_info/depcheck_specs/compiler_toolchain.rb +4 -4
- data/lib/phusion_passenger/platform_info/depcheck_specs/gems.rb +10 -34
- data/lib/phusion_passenger/platform_info/depcheck_specs/libs.rb +101 -101
- data/lib/phusion_passenger/platform_info/depcheck_specs/ruby.rb +5 -5
- data/lib/phusion_passenger/platform_info/depcheck_specs/utilities.rb +13 -13
- data/lib/phusion_passenger/platform_info/linux.rb +55 -55
- data/lib/phusion_passenger/platform_info/operating_system.rb +149 -149
- data/lib/phusion_passenger/platform_info/ruby.rb +468 -448
- data/lib/phusion_passenger/platform_info/zlib.rb +9 -9
- data/lib/phusion_passenger/plugin.rb +66 -66
- data/lib/phusion_passenger/preloader_shared_helpers.rb +126 -126
- data/lib/phusion_passenger/public_api.rb +191 -191
- data/lib/phusion_passenger/rack/out_of_band_gc.rb +93 -94
- data/lib/phusion_passenger/rack/thread_handler_extension.rb +231 -227
- data/lib/phusion_passenger/request_handler.rb +567 -577
- data/lib/phusion_passenger/request_handler/thread_handler.rb +379 -381
- data/lib/phusion_passenger/ruby_core_enhancements.rb +86 -86
- data/lib/phusion_passenger/ruby_core_io_enhancements.rb +74 -74
- data/lib/phusion_passenger/simple_benchmarking.rb +25 -25
- data/lib/phusion_passenger/standalone/app_finder.rb +153 -150
- data/lib/phusion_passenger/standalone/command.rb +44 -40
- data/lib/phusion_passenger/standalone/config_utils.rb +53 -53
- data/lib/phusion_passenger/standalone/control_utils.rb +38 -59
- data/lib/phusion_passenger/standalone/main.rb +73 -73
- data/lib/phusion_passenger/standalone/start_command.rb +697 -685
- data/lib/phusion_passenger/standalone/start_command/builtin_engine.rb +193 -155
- data/lib/phusion_passenger/standalone/start_command/nginx_engine.rb +162 -133
- data/lib/phusion_passenger/standalone/status_command.rb +64 -64
- data/lib/phusion_passenger/standalone/stop_command.rb +72 -72
- data/lib/phusion_passenger/standalone/version_command.rb +9 -9
- data/lib/phusion_passenger/union_station/connection.rb +32 -32
- data/lib/phusion_passenger/union_station/core.rb +251 -251
- data/lib/phusion_passenger/union_station/transaction.rb +126 -126
- data/lib/phusion_passenger/utils.rb +199 -167
- data/lib/phusion_passenger/utils/ansi_colors.rb +128 -128
- data/lib/phusion_passenger/utils/download.rb +196 -196
- data/lib/phusion_passenger/utils/file_system_watcher.rb +158 -158
- data/lib/phusion_passenger/utils/hosts_file_parser.rb +101 -101
- data/lib/phusion_passenger/utils/lock.rb +31 -31
- data/lib/phusion_passenger/utils/native_support_utils.rb +31 -31
- data/lib/phusion_passenger/utils/progress_bar.rb +26 -26
- data/lib/phusion_passenger/utils/shellwords.rb +20 -20
- data/lib/phusion_passenger/utils/terminal_choice_menu.rb +206 -206
- data/lib/phusion_passenger/utils/unseekable_socket.rb +272 -272
- data/lib/phusion_passenger/vendor/crash_watch/app.rb +129 -0
- data/lib/phusion_passenger/vendor/crash_watch/gdb_controller.rb +341 -0
- data/lib/phusion_passenger/vendor/crash_watch/version.rb +24 -0
- data/lib/phusion_passenger/vendor/daemon_controller.rb +877 -0
- data/lib/phusion_passenger/vendor/daemon_controller/lock_file.rb +127 -0
- data/lib/phusion_passenger/vendor/daemon_controller/spawn.rb +26 -0
- data/lib/phusion_passenger/vendor/daemon_controller/version.rb +29 -0
- data/packaging/rpm/passenger_spec/passenger.spec.template +0 -1
- data/passenger.gemspec +0 -1
- data/resources/templates/config/nginx_engine_compiler/possible_solutions_for_download_and_extraction_problems.txt.erb +27 -0
- data/resources/templates/standalone/config.erb +19 -15
- data/test/integration_tests/apache2_tests.rb +566 -566
- data/test/integration_tests/downloaded_binaries_tests.rb +126 -125
- data/test/integration_tests/native_packaging_spec.rb +296 -296
- data/test/integration_tests/nginx_tests.rb +393 -393
- data/test/integration_tests/shared/example_webapp_tests.rb +282 -280
- data/test/integration_tests/source_packaging_test.rb +138 -138
- data/test/integration_tests/spec_helper.rb +5 -5
- data/test/integration_tests/standalone_tests.rb +367 -367
- data/test/ruby/debug_logging_spec.rb +133 -133
- data/test/ruby/message_channel_spec.rb +186 -186
- data/test/ruby/rack/loader_spec.rb +28 -28
- data/test/ruby/rack/preloader_spec.rb +34 -34
- data/test/ruby/rails3.0/loader_spec.rb +12 -12
- data/test/ruby/rails3.0/preloader_spec.rb +18 -18
- data/test/ruby/rails3.1/loader_spec.rb +12 -12
- data/test/ruby/rails3.1/preloader_spec.rb +18 -18
- data/test/ruby/rails3.2/loader_spec.rb +12 -12
- data/test/ruby/rails3.2/preloader_spec.rb +18 -18
- data/test/ruby/rails4.0/loader_spec.rb +12 -12
- data/test/ruby/rails4.0/preloader_spec.rb +18 -18
- data/test/ruby/rails4.1/loader_spec.rb +12 -12
- data/test/ruby/rails4.1/preloader_spec.rb +18 -18
- data/test/ruby/request_handler_spec.rb +730 -730
- data/test/ruby/shared/loader_sharedspec.rb +224 -224
- data/test/ruby/shared/rails/union_station_extensions_sharedspec.rb +327 -327
- data/test/ruby/shared/ruby_loader_sharedspec.rb +47 -47
- data/test/ruby/spec_helper.rb +65 -65
- data/test/ruby/standalone/runtime_installer_spec.rb +384 -384
- data/test/ruby/union_station_spec.rb +276 -276
- data/test/ruby/utils/file_system_watcher_spec.rb +220 -220
- data/test/ruby/utils/hosts_file_parser.rb +248 -248
- data/test/ruby/utils/tee_input_spec.rb +215 -215
- data/test/ruby/utils/unseekable_socket_spec.rb +57 -57
- data/test/ruby/utils_spec.rb +21 -21
- data/test/stub/rack/config.ru +87 -87
- data/test/stub/rack/library.rb +8 -8
- data/test/stub/rack/start.rb +30 -30
- data/test/support/apache2_controller.rb +191 -191
- data/test/support/nginx_controller.rb +90 -99
- data/test/support/placebo-preloader.rb +57 -57
- data/test/support/test_helper.rb +435 -435
- metadata +11 -21
- metadata.gz.asc +7 -7
- data/lib/phusion_passenger/standalone/command2.rb +0 -292
- data/lib/phusion_passenger/standalone/start2_command.rb +0 -799
- data/resources/templates/standalone/download_tool_missing.txt.erb +0 -18
- data/resources/templates/standalone/possible_solutions_for_download_and_extraction_problems.txt.erb +0 -17
- data/resources/templates/standalone/run_installer_as_root.txt.erb +0 -8
@@ -23,146 +23,146 @@
|
|
23
23
|
|
24
24
|
module PhusionPassenger
|
25
25
|
|
26
|
-
module Packaging
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
26
|
+
module Packaging
|
27
|
+
# A list of HTML files that are generated with Asciidoc.
|
28
|
+
ASCII_DOCS = [
|
29
|
+
'doc/Users guide.html',
|
30
|
+
'doc/Users guide Apache.html',
|
31
|
+
'doc/Users guide Nginx.html',
|
32
|
+
'doc/Users guide Standalone.html',
|
33
|
+
'doc/Security of user switching support.html',
|
34
|
+
'doc/Design and Architecture.html'
|
35
|
+
]
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
37
|
+
# Files that must be generated before packaging.
|
38
|
+
PREGENERATED_FILES = [
|
39
|
+
'ext/common/Constants.h',
|
40
|
+
'doc/Packaging.html',
|
41
|
+
'doc/CloudLicensingConfiguration.html',
|
42
|
+
'doc/ServerOptimizationGuide.html'
|
43
|
+
] + ASCII_DOCS
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
45
|
+
USER_EXECUTABLES = [
|
46
|
+
'passenger',
|
47
|
+
'passenger-install-apache2-module',
|
48
|
+
'passenger-install-nginx-module',
|
49
|
+
'passenger-config'
|
50
|
+
]
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
SUPER_USER_EXECUTABLES = [
|
53
|
+
'passenger-status',
|
54
|
+
'passenger-memory-stats'
|
55
|
+
]
|
56
56
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
57
|
+
# Used during native packaging. Specifies executables for
|
58
|
+
# which the shebang should NOT be set to #!/usr/bin/ruby,
|
59
|
+
# so that these executables can be run with any Ruby interpreter
|
60
|
+
# the user desires.
|
61
|
+
EXECUTABLES_WITH_FREE_RUBY = [
|
62
|
+
'passenger',
|
63
|
+
'passenger-config',
|
64
|
+
'passenger-install-apache2-module',
|
65
|
+
'passenger-install-nginx-module'
|
66
|
+
]
|
67
67
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
68
|
+
# A list of globs which match all files that should be packaged
|
69
|
+
# in the Phusion Passenger gem or tarball.
|
70
|
+
GLOB = [
|
71
|
+
'.gitignore',
|
72
|
+
'.travis.yml',
|
73
|
+
'.editorconfig',
|
74
|
+
'configure',
|
75
|
+
'Rakefile',
|
76
|
+
'Gemfile',
|
77
|
+
'Gemfile.lock',
|
78
|
+
'Vagrantfile',
|
79
|
+
'README.md',
|
80
|
+
'CONTRIBUTORS',
|
81
|
+
'CONTRIBUTING.md',
|
82
|
+
'LICENSE',
|
83
|
+
'CHANGELOG',
|
84
|
+
'INSTALL.md',
|
85
|
+
'NEWS',
|
86
|
+
'package.json',
|
87
|
+
'npm-shrinkwrap.json',
|
88
|
+
'passenger.gemspec',
|
89
|
+
'build/*.rb',
|
90
|
+
'lib/*.rb',
|
91
|
+
'lib/**/*.rb',
|
92
|
+
'lib/**/*.py',
|
93
|
+
'node_lib/**/*.js',
|
94
|
+
'bin/*',
|
95
|
+
'doc/**/*',
|
96
|
+
'man/*',
|
97
|
+
'debian.template/**/*',
|
98
|
+
'packaging/**/*',
|
99
|
+
'helper-scripts/**/*',
|
100
|
+
'ext/common/**/*.{cpp,c,h,hpp,md,erb}',
|
101
|
+
'ext/apache2/*.{cpp,h,hpp,c,erb}',
|
102
|
+
'ext/nginx/*.{c,cpp,h,erb}',
|
103
|
+
'ext/nginx/config',
|
104
|
+
'ext/boost/**/*',
|
105
|
+
'ext/libev/{LICENSE,Changes,README,Makefile.am,Makefile.in}',
|
106
|
+
'ext/libev/{*.m4,autogen.sh,config.guess,config.h.in,config.sub}',
|
107
|
+
'ext/libev/{configure,configure.ac,depcomp,install-sh,ltmain.sh,missing,mkinstalldirs}',
|
108
|
+
'ext/libev/{*.h,*.c}',
|
109
|
+
'ext/libeio/{LICENSE,Changes,README,Makefile.am,Makefile.in}',
|
110
|
+
'ext/libeio/{*.m4,autogen.sh,config.guess,config.h.in,config.sub}',
|
111
|
+
'ext/libeio/{configure,configure.ac,install-sh,ltmain.sh,missing,mkinstalldirs}',
|
112
|
+
'ext/libeio/{*.h,*.c}',
|
113
|
+
'ext/oxt/*.hpp',
|
114
|
+
'ext/oxt/*.cpp',
|
115
|
+
'ext/oxt/*.txt',
|
116
|
+
'ext/oxt/detail/*.hpp',
|
117
|
+
'ext/ruby/*.{c,rb}',
|
118
|
+
'dev/**/*',
|
119
|
+
'resources/**/*',
|
120
|
+
'test/.rspec',
|
121
|
+
'test/*.example',
|
122
|
+
'test/*.travis',
|
123
|
+
'test/*.rpm-automation',
|
124
|
+
'test/*.vagrant',
|
125
|
+
'test/*.supp',
|
126
|
+
'test/support/*.{c,cpp,h,rb}',
|
127
|
+
'test/tut/*',
|
128
|
+
'test/cxx/**/*.{cpp,h}',
|
129
|
+
'test/oxt/*.{cpp,hpp}',
|
130
|
+
'test/ruby/**/*',
|
131
|
+
'test/node/**/*',
|
132
|
+
'test/integration_tests/**/*',
|
133
|
+
'test/stub/**/*',
|
134
|
+
'test/stub/**/.*'
|
135
|
+
]
|
136
136
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
137
|
+
EXCLUDE_GLOB = [
|
138
|
+
'**/.DS_Store',
|
139
|
+
'packaging/*/.git',
|
140
|
+
'test/stub/rails_apps/3.0/empty/help/**/*',
|
141
|
+
'test/stub/*.dSYM'
|
142
|
+
]
|
143
143
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
144
|
+
# Files that should be excluded from the Debian tarball.
|
145
|
+
DEBIAN_EXCLUDE_GLOB = [
|
146
|
+
"debian.template/**/*",
|
147
|
+
"packaging/**/*",
|
148
|
+
]
|
149
149
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
150
|
+
# Files and directories that should be excluded from the Homebrew installation.
|
151
|
+
HOMEBREW_EXCLUDE = [
|
152
|
+
".gitignore", ".gitmodules", ".travis.yml", "package.json", "Vagrantfile",
|
153
|
+
"npm-shrinkwrap.json", "Gemfile", "Gemfile.lock", "debian.template",
|
154
|
+
"packaging", "dev", "test"
|
155
|
+
]
|
156
156
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
157
|
+
def self.files
|
158
|
+
result = Dir[*GLOB] - Dir[*EXCLUDE_GLOB]
|
159
|
+
result.reject! { |path| path =~ %r{/\.\.?$} }
|
160
|
+
return result
|
161
|
+
end
|
162
162
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
end
|
163
|
+
def self.debian_orig_tarball_files
|
164
|
+
return files - Dir[*DEBIAN_EXCLUDE_GLOB]
|
165
|
+
end
|
166
|
+
end
|
167
167
|
|
168
168
|
end # module PhusionPassenger
|
@@ -26,365 +26,365 @@ PhusionPassenger.require_passenger_lib 'utils/tmpio'
|
|
26
26
|
|
27
27
|
module PhusionPassenger
|
28
28
|
|
29
|
-
# This module autodetects various platform-specific information, and
|
30
|
-
# provides that information through constants.
|
31
|
-
module PlatformInfo
|
32
|
-
private
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
public
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
end
|
29
|
+
# This module autodetects various platform-specific information, and
|
30
|
+
# provides that information through constants.
|
31
|
+
module PlatformInfo
|
32
|
+
private
|
33
|
+
@@cache_dir = nil
|
34
|
+
@@verbose = ['1', 'true', 'on', 'yes'].include?(ENV['VERBOSE'])
|
35
|
+
@@log_implementation = lambda do |message|
|
36
|
+
message = reindent(message, 3)
|
37
|
+
message.sub!(/^ /, '')
|
38
|
+
STDERR.puts " * #{message}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.private_class_method(name)
|
42
|
+
metaclass = class << self; self; end
|
43
|
+
metaclass.send(:private, name)
|
44
|
+
end
|
45
|
+
private_class_method :private_class_method
|
46
|
+
|
47
|
+
# Turn the specified class method into a memoized one. If the given
|
48
|
+
# class method is called without arguments, then its result will be
|
49
|
+
# memoized, frozen, and returned upon subsequent calls without arguments.
|
50
|
+
# Calls with arguments are never memoized.
|
51
|
+
#
|
52
|
+
# If +cache_to_disk+ is true and a cache directory has been set with
|
53
|
+
# <tt>PlatformInfo.cache_dir=</tt> then result is cached to a file on disk,
|
54
|
+
# so that memoized results persist over multiple process runs. This
|
55
|
+
# cache file expires in +cache_time+ seconds (1 hour by default) after
|
56
|
+
# it has been written.
|
57
|
+
#
|
58
|
+
# def self.foo(max = 10)
|
59
|
+
# rand(max)
|
60
|
+
# end
|
61
|
+
# memoize :foo
|
62
|
+
#
|
63
|
+
# foo # => 3
|
64
|
+
# foo # => 3
|
65
|
+
# foo(100) # => 49
|
66
|
+
# foo(100) # => 26
|
67
|
+
# foo # => 3
|
68
|
+
def self.memoize(method, cache_to_disk = false, cache_time = 3600)
|
69
|
+
# We use class_eval here because Ruby 1.8.5 doesn't support class_variable_get/set.
|
70
|
+
metaclass = class << self; self; end
|
71
|
+
metaclass.send(:alias_method, "_unmemoized_#{method}", method)
|
72
|
+
variable_name = "@@memoized_#{method}".sub(/\?/, '')
|
73
|
+
check_variable_name = "@@has_memoized_#{method}".sub(/\?/, '')
|
74
|
+
eval(%Q{
|
75
|
+
#{variable_name} = nil
|
76
|
+
#{check_variable_name} = false
|
77
|
+
})
|
78
|
+
line = __LINE__ + 1
|
79
|
+
source = %Q{
|
80
|
+
def self.#{method}(*args) # def self.httpd(*args)
|
81
|
+
if args.empty? # if args.empty?
|
82
|
+
if !#{check_variable_name} # if !@@has_memoized_httpd
|
83
|
+
if @@cache_dir # if @@cache_dir
|
84
|
+
cache_file = File.join(@@cache_dir, "#{method}") # cache_file = File.join(@@cache_dir, "httpd")
|
85
|
+
end # end
|
86
|
+
read_from_cache_file = false # read_from_cache_file = false
|
87
|
+
if #{cache_to_disk} && cache_file && File.exist?(cache_file) # if #{cache_to_disk} && File.exist?(cache_file)
|
88
|
+
cache_file_stat = File.stat(cache_file) # cache_file_stat = File.stat(cache_file)
|
89
|
+
read_from_cache_file = # read_from_cache_file =
|
90
|
+
Time.now - cache_file_stat.mtime < #{cache_time} # Time.now - cache_file_stat.mtime < #{cache_time}
|
91
|
+
end # end
|
92
|
+
if read_from_cache_file # if read_from_cache_file
|
93
|
+
data = File.read(cache_file) # data = File.read(cache_file)
|
94
|
+
#{variable_name} = Marshal.load(data).freeze # @@memoized_httpd = Marshal.load(data).freeze
|
95
|
+
#{check_variable_name} = true # @@has_memoized_httpd = true
|
96
|
+
else # else
|
97
|
+
#{variable_name} = _unmemoized_#{method}.freeze # @@memoized_httpd = _unmemoized_httpd.freeze
|
98
|
+
#{check_variable_name} = true # @@has_memoized_httpd = true
|
99
|
+
if cache_file && #{cache_to_disk} # if cache_file && #{cache_to_disk}
|
100
|
+
begin # begin
|
101
|
+
if !File.directory?(@@cache_dir) # if !File.directory?(@@cache_dir)
|
102
|
+
Dir.mkdir(@@cache_dir) # Dir.mkdir(@@cache_dir)
|
103
|
+
end # end
|
104
|
+
File.open(cache_file, "wb") do |f| # File.open(cache_file, "wb") do |f|
|
105
|
+
f.write(Marshal.dump(#{variable_name})) # f.write(Marshal.dump(@@memoized_httpd))
|
106
|
+
end # end
|
107
|
+
rescue Errno::EACCES # rescue Errno::EACCES
|
108
|
+
# Ignore permission error. # # Ignore permission error.
|
109
|
+
end # end
|
110
|
+
end # end
|
111
|
+
end # end
|
112
|
+
end # end
|
113
|
+
#{variable_name} # @@memoized_httpd
|
114
|
+
else # else
|
115
|
+
_unmemoized_#{method}(*args) # _unmemoized_httpd(*args)
|
116
|
+
end # end
|
117
|
+
end # end
|
118
|
+
}
|
119
|
+
class_eval(source, __FILE__, line)
|
120
|
+
end
|
121
|
+
private_class_method :memoize
|
122
|
+
|
123
|
+
# Look in the directory +dir+ and check whether there's an executable
|
124
|
+
# whose base name is equal to one of the elements in +possible_names+.
|
125
|
+
# If so, returns the full filename. If not, returns nil.
|
126
|
+
def self.select_executable(dir, *possible_names)
|
127
|
+
possible_names.each do |name|
|
128
|
+
filename = "#{dir}/#{name}"
|
129
|
+
if File.file?(filename) && File.executable?(filename)
|
130
|
+
return filename
|
131
|
+
end
|
132
|
+
end
|
133
|
+
return nil
|
134
|
+
end
|
135
|
+
private_class_method :select_executable
|
136
|
+
|
137
|
+
def self.unindent(str)
|
138
|
+
str = str.dup
|
139
|
+
str.gsub!(/\A([\s\t]*\n)+/, '')
|
140
|
+
str.gsub!(/[\s\t\n]+\Z/, '')
|
141
|
+
indent = str.split("\n").select{ |line| !line.strip.empty? }.map{ |line| line.index(/[^\s]/) }.compact.min || 0
|
142
|
+
str.gsub!(/^[[:blank:]]{#{indent}}/, '')
|
143
|
+
return str
|
144
|
+
end
|
145
|
+
private_class_method :unindent
|
146
|
+
|
147
|
+
def self.reindent(str, level)
|
148
|
+
str = unindent(str)
|
149
|
+
str.gsub!(/^/, ' ' * level)
|
150
|
+
return str
|
151
|
+
end
|
152
|
+
private_class_method :reindent
|
153
|
+
|
154
|
+
def self.create_temp_file(name, dir = tmpdir)
|
155
|
+
# This function is mostly used for compiling C programs to autodetect
|
156
|
+
# system properties. We create a secure temp subdirectory to prevent
|
157
|
+
# TOCTU attacks, especially because we don't know how the compiler
|
158
|
+
# handles this.
|
159
|
+
PhusionPassenger::Utils.mktmpdir("passenger.", dir) do |subdir|
|
160
|
+
filename = "#{subdir}/#{name}"
|
161
|
+
f = File.open(filename, "w")
|
162
|
+
begin
|
163
|
+
yield(filename, f)
|
164
|
+
ensure
|
165
|
+
f.close if !f.closed?
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
private_class_method :create_temp_file
|
170
|
+
|
171
|
+
def self.log(message)
|
172
|
+
if verbose?
|
173
|
+
@@log_implementation.call(message)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
private_class_method :log
|
177
|
+
|
178
|
+
public
|
179
|
+
class RuntimeError < ::RuntimeError
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
def self.cache_dir=(value)
|
184
|
+
@@cache_dir = value
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.cache_dir
|
188
|
+
return @@cache_dir
|
189
|
+
end
|
190
|
+
|
191
|
+
def self.verbose=(val)
|
192
|
+
@@verbose = val
|
193
|
+
end
|
194
|
+
|
195
|
+
def self.verbose?
|
196
|
+
return @@verbose
|
197
|
+
end
|
198
|
+
|
199
|
+
def self.log_implementation=(impl)
|
200
|
+
@@log_implementation = impl
|
201
|
+
end
|
202
|
+
|
203
|
+
def self.log_implementation
|
204
|
+
return @@log_implementation
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
def self.env_defined?(name)
|
209
|
+
return !ENV[name].nil? && !ENV[name].empty?
|
210
|
+
end
|
211
|
+
|
212
|
+
def self.string_env(name, default_value = nil)
|
213
|
+
value = ENV[name]
|
214
|
+
if value.nil? || value.empty?
|
215
|
+
return default_value
|
216
|
+
else
|
217
|
+
return value
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def self.read_file(filename)
|
222
|
+
return File.open(filename, "rb") do |f|
|
223
|
+
f.read
|
224
|
+
end
|
225
|
+
rescue
|
226
|
+
return ""
|
227
|
+
end
|
228
|
+
|
229
|
+
def self.tmpdir
|
230
|
+
result = ENV['TMPDIR']
|
231
|
+
if result && !result.empty?
|
232
|
+
return result.sub(/\/+\Z/, '')
|
233
|
+
else
|
234
|
+
return '/tmp'
|
235
|
+
end
|
236
|
+
end
|
237
|
+
memoize :tmpdir
|
238
|
+
|
239
|
+
# Returns the directory in which test executables should be placed. The
|
240
|
+
# returned directory is guaranteed to be writable and guaranteed to
|
241
|
+
# not be mounted with the 'noexec' option.
|
242
|
+
# If no such directory can be found then it will raise a PlatformInfo::RuntimeError
|
243
|
+
# with an appropriate error message.
|
244
|
+
def self.tmpexedir
|
245
|
+
basename = "test-exe.#{Process.pid}.#{Thread.current.object_id}"
|
246
|
+
attempts = []
|
247
|
+
|
248
|
+
dir = tmpdir
|
249
|
+
filename = "#{dir}/#{basename}"
|
250
|
+
begin
|
251
|
+
File.open(filename, 'w') do |f|
|
252
|
+
f.puts("#!/bin/sh")
|
253
|
+
end
|
254
|
+
File.chmod(0700, filename)
|
255
|
+
if system(filename)
|
256
|
+
return dir
|
257
|
+
else
|
258
|
+
attempts << { :dir => dir,
|
259
|
+
:error => "This directory's filesystem is mounted with the 'noexec' option." }
|
260
|
+
end
|
261
|
+
rescue Errno::ENOENT
|
262
|
+
attempts << { :dir => dir, :error => "This directory doesn't exist." }
|
263
|
+
rescue Errno::EACCES
|
264
|
+
attempts << { :dir => dir, :error => "This program doesn't have permission to write to this directory." }
|
265
|
+
rescue SystemCallError => e
|
266
|
+
attempts << { :dir => dir, :error => e.message }
|
267
|
+
ensure
|
268
|
+
File.unlink(filename) rescue nil
|
269
|
+
end
|
270
|
+
|
271
|
+
dir = Dir.pwd
|
272
|
+
filename = "#{dir}/#{basename}"
|
273
|
+
begin
|
274
|
+
File.open(filename, 'w') do |f|
|
275
|
+
f.puts("#!/bin/sh")
|
276
|
+
end
|
277
|
+
File.chmod(0700, filename)
|
278
|
+
if system(filename)
|
279
|
+
return dir
|
280
|
+
else
|
281
|
+
attempts << { :dir => dir,
|
282
|
+
:error => "This directory's filesystem is mounted with the 'noexec' option." }
|
283
|
+
end
|
284
|
+
rescue Errno::ENOENT
|
285
|
+
attempts << { :dir => dir, :error => "This directory doesn't exist." }
|
286
|
+
rescue Errno::EACCES
|
287
|
+
attempts << { :dir => dir, :error => "This program doesn't have permission to write to this directory." }
|
288
|
+
rescue SystemCallError => e
|
289
|
+
attempts << { :dir => dir, :error => e.message }
|
290
|
+
ensure
|
291
|
+
File.unlink(filename) rescue nil
|
292
|
+
end
|
293
|
+
|
294
|
+
message = "ERROR: Cannot find suitable temporary directory\n" +
|
295
|
+
"In order to run certain tests, this program " +
|
296
|
+
"must be able to write temporary\n" +
|
297
|
+
"executable files to some directory. However no such " +
|
298
|
+
"directory can be found. \n" +
|
299
|
+
"The following directories have been tried:\n\n"
|
300
|
+
attempts.each do |attempt|
|
301
|
+
message << " * #{attempt[:dir]}\n"
|
302
|
+
message << " #{attempt[:error]}\n"
|
303
|
+
end
|
304
|
+
message << "\nYou can solve this problem by telling this program what directory to write\n" <<
|
305
|
+
"temporary executable files to, as follows:\n" <<
|
306
|
+
"\n" <<
|
307
|
+
" Set the $TMPDIR environment variable to the desired directory's filename and\n" <<
|
308
|
+
" re-run this program.\n" <<
|
309
|
+
"\n" <<
|
310
|
+
"Notes:\n" <<
|
311
|
+
"\n" <<
|
312
|
+
" * If you're using 'sudo'/'rvmsudo', remember that 'sudo'/'rvmsudo' unsets all\n" <<
|
313
|
+
" environment variables, so you must set the environment variable *after*\n" <<
|
314
|
+
" having gained root privileges.\n" <<
|
315
|
+
" * The directory you choose must writeable and must not be mounted with the\n" <<
|
316
|
+
" 'noexec' option."
|
317
|
+
raise RuntimeError, message
|
318
|
+
end
|
319
|
+
memoize :tmpexedir
|
320
|
+
|
321
|
+
def self.rb_config
|
322
|
+
if defined?(::RbConfig)
|
323
|
+
return ::RbConfig::CONFIG
|
324
|
+
else
|
325
|
+
return ::Config::CONFIG
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
# Check whether the specified command is in $PATH, and return its
|
330
|
+
# absolute filename. Returns nil if the command is not found.
|
331
|
+
#
|
332
|
+
# This function exists because system('which') doesn't always behave
|
333
|
+
# correctly, for some weird reason.
|
334
|
+
#
|
335
|
+
# When `is_executable` is true, this function checks whether
|
336
|
+
# there is an executable named `name` in $PATH. When false, it
|
337
|
+
# assumes that `name` is not an executable name but a command string
|
338
|
+
# (e.g. "ccache gcc"). It then infers the executable name ("ccache")
|
339
|
+
# from the command string, and checks for that instead.
|
340
|
+
def self.find_command(name, is_executable = true)
|
341
|
+
name = name.to_s
|
342
|
+
if !is_executable && name =~ / /
|
343
|
+
name = name.sub(/ .*/, '')
|
344
|
+
end
|
345
|
+
if name =~ /^\//
|
346
|
+
if File.executable?(name)
|
347
|
+
return name
|
348
|
+
else
|
349
|
+
return nil
|
350
|
+
end
|
351
|
+
else
|
352
|
+
ENV['PATH'].to_s.split(File::PATH_SEPARATOR).each do |directory|
|
353
|
+
next if directory.empty?
|
354
|
+
path = File.join(directory, name)
|
355
|
+
if File.file?(path) && File.executable?(path)
|
356
|
+
return path
|
357
|
+
end
|
358
|
+
end
|
359
|
+
return nil
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
def self.find_all_commands(name)
|
364
|
+
search_dirs = ENV['PATH'].to_s.split(File::PATH_SEPARATOR)
|
365
|
+
search_dirs.concat(%w(/bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin))
|
366
|
+
["/opt/*/bin", "/opt/*/sbin", "/usr/local/*/bin", "/usr/local/*/sbin"].each do |glob|
|
367
|
+
search_dirs.concat(Dir[glob])
|
368
|
+
end
|
369
|
+
search_dirs.delete("")
|
370
|
+
search_dirs.uniq!
|
371
|
+
|
372
|
+
result = []
|
373
|
+
search_dirs.each do |directory|
|
374
|
+
path = File.join(directory, name)
|
375
|
+
if !File.exist?(path)
|
376
|
+
log "Looking for #{path}: not found"
|
377
|
+
elsif !File.file?(path)
|
378
|
+
log "Looking for #{path}: found, but is not a file"
|
379
|
+
elsif !File.executable?(path)
|
380
|
+
log "Looking for #{path}: found, but is not executable"
|
381
|
+
else
|
382
|
+
log "Looking for #{path}: found"
|
383
|
+
result << path
|
384
|
+
end
|
385
|
+
end
|
386
|
+
return result
|
387
|
+
end
|
388
|
+
end
|
389
389
|
|
390
390
|
end # module PhusionPassenger
|