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.

Files changed (218) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.asc +7 -7
  3. data.tar.gz.asc +7 -7
  4. data/.editorconfig +11 -5
  5. data/CHANGELOG +38 -0
  6. data/CONTRIBUTING.md +1 -4
  7. data/Gemfile +0 -1
  8. data/Gemfile.lock +0 -2
  9. data/Rakefile +33 -33
  10. data/bin/passenger +1 -1
  11. data/bin/passenger-config +1 -1
  12. data/bin/passenger-install-apache2-module +800 -800
  13. data/bin/passenger-install-nginx-module +592 -592
  14. data/bin/passenger-memory-stats +127 -127
  15. data/bin/passenger-status +216 -216
  16. data/build/agents.rb +127 -127
  17. data/build/apache2.rb +87 -87
  18. data/build/basics.rb +60 -60
  19. data/build/common_library.rb +165 -165
  20. data/build/cplusplus_support.rb +51 -51
  21. data/build/cxx_tests.rb +268 -268
  22. data/build/debian.rb +143 -143
  23. data/build/documentation.rb +58 -58
  24. data/build/integration_tests.rb +81 -81
  25. data/build/misc.rb +132 -132
  26. data/build/nginx.rb +20 -20
  27. data/build/node_tests.rb +7 -7
  28. data/build/oxt_tests.rb +14 -14
  29. data/build/packaging.rb +570 -570
  30. data/build/preprocessor.rb +260 -260
  31. data/build/rake_extensions.rb +71 -71
  32. data/build/ruby_extension.rb +29 -29
  33. data/build/ruby_tests.rb +6 -6
  34. data/build/test_basics.rb +37 -37
  35. data/debian.template/control.template +3 -5
  36. data/dev/copy_boost_headers +134 -134
  37. data/dev/install_scripts_bootstrap_code.rb +25 -25
  38. data/dev/list_tests +20 -20
  39. data/dev/ruby_server.rb +223 -223
  40. data/dev/runner +18 -18
  41. data/doc/ServerOptimizationGuide.txt.md +55 -2
  42. data/doc/Users guide Nginx.txt +0 -26
  43. data/doc/Users guide Standalone.txt +5 -1
  44. data/doc/users_guide_snippets/tips.txt +9 -0
  45. data/ext/common/ApplicationPool2/Group.h +23 -11
  46. data/ext/common/ApplicationPool2/Implementation.cpp +32 -7
  47. data/ext/common/ApplicationPool2/Pool.h +22 -17
  48. data/ext/common/ApplicationPool2/SmartSpawner.h +4 -1
  49. data/ext/common/ApplicationPool2/Spawner.h +1 -1
  50. data/ext/common/Constants.h +1 -1
  51. data/ext/common/agents/Base.cpp +35 -20
  52. data/ext/common/agents/HelperAgent/Main.cpp +8 -1
  53. data/ext/common/agents/HelperAgent/OptionParser.h +18 -4
  54. data/ext/common/agents/HelperAgent/RequestHandler.h +2 -83
  55. data/ext/common/agents/HelperAgent/RequestHandler/ForwardResponse.cpp +54 -1
  56. data/ext/common/agents/HelperAgent/RequestHandler/InitRequest.cpp +7 -4
  57. data/ext/common/agents/Main.cpp +1 -1
  58. data/ext/common/agents/Watchdog/Main.cpp +54 -19
  59. data/ext/nginx/Configuration.c +7 -0
  60. data/ext/nginx/ContentHandler.c +9 -1
  61. data/helper-scripts/backtrace-sanitizer.rb +106 -87
  62. data/helper-scripts/crash-watch.rb +32 -0
  63. data/helper-scripts/download_binaries/extconf.rb +38 -38
  64. data/helper-scripts/meteor-loader.rb +107 -107
  65. data/helper-scripts/prespawn +101 -101
  66. data/helper-scripts/rack-loader.rb +96 -96
  67. data/helper-scripts/rack-preloader.rb +137 -137
  68. data/lib/phusion_passenger.rb +292 -292
  69. data/lib/phusion_passenger/abstract_installer.rb +438 -438
  70. data/lib/phusion_passenger/active_support3_extensions/init.rb +168 -170
  71. data/lib/phusion_passenger/admin_tools.rb +20 -20
  72. data/lib/phusion_passenger/admin_tools/instance.rb +178 -178
  73. data/lib/phusion_passenger/admin_tools/instance_registry.rb +61 -61
  74. data/lib/phusion_passenger/admin_tools/memory_stats.rb +267 -267
  75. data/lib/phusion_passenger/apache2/config_options.rb +182 -182
  76. data/lib/phusion_passenger/common_library.rb +479 -485
  77. data/lib/phusion_passenger/config/about_command.rb +161 -161
  78. data/lib/phusion_passenger/config/admin_command_command.rb +129 -129
  79. data/lib/phusion_passenger/config/agent_compiler.rb +121 -121
  80. data/lib/phusion_passenger/config/build_native_support_command.rb +43 -43
  81. data/lib/phusion_passenger/config/command.rb +25 -25
  82. data/lib/phusion_passenger/config/compile_agent_command.rb +62 -62
  83. data/lib/phusion_passenger/config/compile_nginx_engine_command.rb +88 -73
  84. data/lib/phusion_passenger/config/detach_process_command.rb +72 -72
  85. data/lib/phusion_passenger/config/download_agent_command.rb +246 -227
  86. data/lib/phusion_passenger/config/download_nginx_engine_command.rb +245 -224
  87. data/lib/phusion_passenger/config/install_agent_command.rb +144 -132
  88. data/lib/phusion_passenger/config/install_standalone_runtime_command.rb +205 -185
  89. data/lib/phusion_passenger/config/installation_utils.rb +204 -204
  90. data/lib/phusion_passenger/config/list_instances_command.rb +64 -64
  91. data/lib/phusion_passenger/config/main.rb +152 -152
  92. data/lib/phusion_passenger/config/nginx_engine_compiler.rb +319 -300
  93. data/lib/phusion_passenger/config/reopen_logs_command.rb +67 -67
  94. data/lib/phusion_passenger/config/restart_app_command.rb +155 -155
  95. data/lib/phusion_passenger/config/system_metrics_command.rb +13 -13
  96. data/lib/phusion_passenger/config/utils.rb +95 -95
  97. data/lib/phusion_passenger/config/validate_install_command.rb +198 -198
  98. data/lib/phusion_passenger/console_text_template.rb +25 -25
  99. data/lib/phusion_passenger/constants.rb +90 -90
  100. data/lib/phusion_passenger/debug_logging.rb +106 -106
  101. data/lib/phusion_passenger/loader_shared_helpers.rb +447 -432
  102. data/lib/phusion_passenger/message_channel.rb +312 -312
  103. data/lib/phusion_passenger/message_client.rb +176 -176
  104. data/lib/phusion_passenger/native_support.rb +369 -369
  105. data/lib/phusion_passenger/nginx/config_options.rb +297 -297
  106. data/lib/phusion_passenger/packaging.rb +131 -131
  107. data/lib/phusion_passenger/platform_info.rb +360 -360
  108. data/lib/phusion_passenger/platform_info/apache.rb +767 -767
  109. data/lib/phusion_passenger/platform_info/apache_detector.rb +199 -199
  110. data/lib/phusion_passenger/platform_info/binary_compatibility.rb +107 -107
  111. data/lib/phusion_passenger/platform_info/compiler.rb +570 -570
  112. data/lib/phusion_passenger/platform_info/curl.rb +32 -32
  113. data/lib/phusion_passenger/platform_info/cxx_portability.rb +188 -188
  114. data/lib/phusion_passenger/platform_info/depcheck.rb +372 -372
  115. data/lib/phusion_passenger/platform_info/depcheck_specs/apache2.rb +109 -109
  116. data/lib/phusion_passenger/platform_info/depcheck_specs/compiler_toolchain.rb +4 -4
  117. data/lib/phusion_passenger/platform_info/depcheck_specs/gems.rb +10 -34
  118. data/lib/phusion_passenger/platform_info/depcheck_specs/libs.rb +101 -101
  119. data/lib/phusion_passenger/platform_info/depcheck_specs/ruby.rb +5 -5
  120. data/lib/phusion_passenger/platform_info/depcheck_specs/utilities.rb +13 -13
  121. data/lib/phusion_passenger/platform_info/linux.rb +55 -55
  122. data/lib/phusion_passenger/platform_info/operating_system.rb +149 -149
  123. data/lib/phusion_passenger/platform_info/ruby.rb +468 -448
  124. data/lib/phusion_passenger/platform_info/zlib.rb +9 -9
  125. data/lib/phusion_passenger/plugin.rb +66 -66
  126. data/lib/phusion_passenger/preloader_shared_helpers.rb +126 -126
  127. data/lib/phusion_passenger/public_api.rb +191 -191
  128. data/lib/phusion_passenger/rack/out_of_band_gc.rb +93 -94
  129. data/lib/phusion_passenger/rack/thread_handler_extension.rb +231 -227
  130. data/lib/phusion_passenger/request_handler.rb +567 -577
  131. data/lib/phusion_passenger/request_handler/thread_handler.rb +379 -381
  132. data/lib/phusion_passenger/ruby_core_enhancements.rb +86 -86
  133. data/lib/phusion_passenger/ruby_core_io_enhancements.rb +74 -74
  134. data/lib/phusion_passenger/simple_benchmarking.rb +25 -25
  135. data/lib/phusion_passenger/standalone/app_finder.rb +153 -150
  136. data/lib/phusion_passenger/standalone/command.rb +44 -40
  137. data/lib/phusion_passenger/standalone/config_utils.rb +53 -53
  138. data/lib/phusion_passenger/standalone/control_utils.rb +38 -59
  139. data/lib/phusion_passenger/standalone/main.rb +73 -73
  140. data/lib/phusion_passenger/standalone/start_command.rb +697 -685
  141. data/lib/phusion_passenger/standalone/start_command/builtin_engine.rb +193 -155
  142. data/lib/phusion_passenger/standalone/start_command/nginx_engine.rb +162 -133
  143. data/lib/phusion_passenger/standalone/status_command.rb +64 -64
  144. data/lib/phusion_passenger/standalone/stop_command.rb +72 -72
  145. data/lib/phusion_passenger/standalone/version_command.rb +9 -9
  146. data/lib/phusion_passenger/union_station/connection.rb +32 -32
  147. data/lib/phusion_passenger/union_station/core.rb +251 -251
  148. data/lib/phusion_passenger/union_station/transaction.rb +126 -126
  149. data/lib/phusion_passenger/utils.rb +199 -167
  150. data/lib/phusion_passenger/utils/ansi_colors.rb +128 -128
  151. data/lib/phusion_passenger/utils/download.rb +196 -196
  152. data/lib/phusion_passenger/utils/file_system_watcher.rb +158 -158
  153. data/lib/phusion_passenger/utils/hosts_file_parser.rb +101 -101
  154. data/lib/phusion_passenger/utils/lock.rb +31 -31
  155. data/lib/phusion_passenger/utils/native_support_utils.rb +31 -31
  156. data/lib/phusion_passenger/utils/progress_bar.rb +26 -26
  157. data/lib/phusion_passenger/utils/shellwords.rb +20 -20
  158. data/lib/phusion_passenger/utils/terminal_choice_menu.rb +206 -206
  159. data/lib/phusion_passenger/utils/unseekable_socket.rb +272 -272
  160. data/lib/phusion_passenger/vendor/crash_watch/app.rb +129 -0
  161. data/lib/phusion_passenger/vendor/crash_watch/gdb_controller.rb +341 -0
  162. data/lib/phusion_passenger/vendor/crash_watch/version.rb +24 -0
  163. data/lib/phusion_passenger/vendor/daemon_controller.rb +877 -0
  164. data/lib/phusion_passenger/vendor/daemon_controller/lock_file.rb +127 -0
  165. data/lib/phusion_passenger/vendor/daemon_controller/spawn.rb +26 -0
  166. data/lib/phusion_passenger/vendor/daemon_controller/version.rb +29 -0
  167. data/packaging/rpm/passenger_spec/passenger.spec.template +0 -1
  168. data/passenger.gemspec +0 -1
  169. data/resources/templates/config/nginx_engine_compiler/possible_solutions_for_download_and_extraction_problems.txt.erb +27 -0
  170. data/resources/templates/standalone/config.erb +19 -15
  171. data/test/integration_tests/apache2_tests.rb +566 -566
  172. data/test/integration_tests/downloaded_binaries_tests.rb +126 -125
  173. data/test/integration_tests/native_packaging_spec.rb +296 -296
  174. data/test/integration_tests/nginx_tests.rb +393 -393
  175. data/test/integration_tests/shared/example_webapp_tests.rb +282 -280
  176. data/test/integration_tests/source_packaging_test.rb +138 -138
  177. data/test/integration_tests/spec_helper.rb +5 -5
  178. data/test/integration_tests/standalone_tests.rb +367 -367
  179. data/test/ruby/debug_logging_spec.rb +133 -133
  180. data/test/ruby/message_channel_spec.rb +186 -186
  181. data/test/ruby/rack/loader_spec.rb +28 -28
  182. data/test/ruby/rack/preloader_spec.rb +34 -34
  183. data/test/ruby/rails3.0/loader_spec.rb +12 -12
  184. data/test/ruby/rails3.0/preloader_spec.rb +18 -18
  185. data/test/ruby/rails3.1/loader_spec.rb +12 -12
  186. data/test/ruby/rails3.1/preloader_spec.rb +18 -18
  187. data/test/ruby/rails3.2/loader_spec.rb +12 -12
  188. data/test/ruby/rails3.2/preloader_spec.rb +18 -18
  189. data/test/ruby/rails4.0/loader_spec.rb +12 -12
  190. data/test/ruby/rails4.0/preloader_spec.rb +18 -18
  191. data/test/ruby/rails4.1/loader_spec.rb +12 -12
  192. data/test/ruby/rails4.1/preloader_spec.rb +18 -18
  193. data/test/ruby/request_handler_spec.rb +730 -730
  194. data/test/ruby/shared/loader_sharedspec.rb +224 -224
  195. data/test/ruby/shared/rails/union_station_extensions_sharedspec.rb +327 -327
  196. data/test/ruby/shared/ruby_loader_sharedspec.rb +47 -47
  197. data/test/ruby/spec_helper.rb +65 -65
  198. data/test/ruby/standalone/runtime_installer_spec.rb +384 -384
  199. data/test/ruby/union_station_spec.rb +276 -276
  200. data/test/ruby/utils/file_system_watcher_spec.rb +220 -220
  201. data/test/ruby/utils/hosts_file_parser.rb +248 -248
  202. data/test/ruby/utils/tee_input_spec.rb +215 -215
  203. data/test/ruby/utils/unseekable_socket_spec.rb +57 -57
  204. data/test/ruby/utils_spec.rb +21 -21
  205. data/test/stub/rack/config.ru +87 -87
  206. data/test/stub/rack/library.rb +8 -8
  207. data/test/stub/rack/start.rb +30 -30
  208. data/test/support/apache2_controller.rb +191 -191
  209. data/test/support/nginx_controller.rb +90 -99
  210. data/test/support/placebo-preloader.rb +57 -57
  211. data/test/support/test_helper.rb +435 -435
  212. metadata +11 -21
  213. metadata.gz.asc +7 -7
  214. data/lib/phusion_passenger/standalone/command2.rb +0 -292
  215. data/lib/phusion_passenger/standalone/start2_command.rb +0 -799
  216. data/resources/templates/standalone/download_tool_missing.txt.erb +0 -18
  217. data/resources/templates/standalone/possible_solutions_for_download_and_extraction_problems.txt.erb +0 -17
  218. data/resources/templates/standalone/run_installer_as_root.txt.erb +0 -8
@@ -2,286 +2,288 @@ require 'socket'
2
2
  require 'fileutils'
3
3
 
4
4
  shared_examples_for "an example web app" do
5
- it "responds to GET requests for static asset" do
6
- FileUtils.cp('stub/garbage1.dat', @stub.full_app_root + "/public/garbage1.dat")
7
- get('/garbage1.dat').should == @stub.public_file('garbage1.dat')
8
- end
9
-
10
- it "supports page caching on file URIs" do
11
- File.write(@stub.full_app_root + "/public/cached.html", "This is the cached version of /cached")
12
- get('/cached').should == "This is the cached version of /cached"
13
- end
14
-
15
- it "supports page caching on directory URIs" do
16
- File.write(@stub.full_app_root + "/public/cached.html", "This is the cached version of /cached")
17
- Dir.mkdir(@stub.full_app_root + "/public/cached")
18
- get('/cached').should == "This is the cached version of /cached"
19
- end
20
-
21
- it "supports page caching on root/base URIs" do
22
- File.write(@stub.full_app_root + "/public/index.html", "This is index.html")
23
- get('/').should == "This is index.html"
24
- end
25
-
26
- it "doesn't use page caching if the HTTP request is not GET" do
27
- File.write(@stub.full_app_root + "/public/cached.html", "This is the cached version of /cached")
28
- post('/cached').should == "This is the uncached version of /cached"
29
- end
30
-
31
- it "responds to GET requests on dynamic pages" do
32
- get('/').should == "front page"
33
- end
34
-
35
- it "properly receives GET parameters" do
36
- result = get('/parameters?first=one&second=two')
37
- result.should == "Method: GET\n" +
38
- "First: one\n" +
39
- "Second: two\n"
40
- end
41
-
42
- it "responds to POST requests on dynamic pages" do
43
- result = post('/parameters',
44
- "first" => "one",
45
- "second" => "two"
46
- )
47
- result.should == "Method: POST\n" +
48
- "First: one\n" +
49
- "Second: two\n"
50
- end
51
-
52
- it "properly handles file uploads" do
53
- static_file = File.open('stub/garbage1.dat', 'rb')
54
- params = {
55
- 'name1' => 'Kotonoha',
56
- 'name2' => 'Sekai',
57
- 'data' => static_file
58
- }
59
- begin
60
- # For some reason the WSGI stub app does not accept the multipart data generated by
61
- # post(), so we use curl instead.
62
- command = "curl --silent --fail -F name1=Kotonoha -F name2=Sekai -F data=@stub/garbage1.dat " +
63
- "#{@server}/upload_with_params"
64
- response = IO.popen(command, "rb") do |io|
65
- io.read
66
- end
67
- response.should ==
68
- binary_string("name 1 = Kotonoha\n") <<
69
- binary_string("name 2 = Sekai\n") <<
70
- binary_string("data = ") << static_file.read
71
- ensure
72
- static_file.close
73
- end
74
- end
75
-
76
- describe "when handling POST requests with 'chunked' transfer encoding" do
77
- before :each do
78
- @uri = URI.parse(@server)
79
- end
80
-
81
- it "correctly forwards the request body to the app" do
82
- socket = TCPSocket.new(@uri.host, @uri.port)
83
- begin
84
- socket.write("POST #{base_uri}/raw_upload_to_file HTTP/1.1\r\n")
85
- socket.write("Host: #{@uri.host}:#{@uri.port}\r\n")
86
- socket.write("Transfer-Encoding: chunked\r\n")
87
- socket.write("Content-Type: text/plain\r\n")
88
- socket.write("Connection: close\r\n")
89
- socket.write("X-Output: output.txt\r\n")
90
- socket.write("\r\n")
91
-
92
- chunk = "foo=bar!"
93
- socket.write("%X\r\n%s\r\n" % [chunk.size, chunk])
94
- socket.write("0\r\n\r\n")
95
- socket.flush
96
-
97
- socket.read.should =~ /\r\nok\Z/
98
- ensure
99
- socket.close
100
- end
101
-
102
- File.read(@stub.full_app_root + "/output.txt").should == "foo=bar!"
103
- end
104
-
105
- if WEB_SERVER_DECHUNKS_REQUESTS
106
- it "sets Content-Length and removes Transfer-Encoding in the request" do
107
- socket = TCPSocket.new(@uri.host, @uri.port)
108
- begin
109
- socket.write("POST #{base_uri}/env HTTP/1.1\r\n")
110
- socket.write("Host: #{@uri.host}:#{@uri.port}\r\n")
111
- socket.write("Transfer-Encoding: chunked\r\n")
112
- socket.write("Content-Type: text/plain\r\n")
113
- socket.write("Connection: close\r\n")
114
- socket.write("\r\n")
115
-
116
- chunk = "foo=bar!"
117
- socket.write("%X\r\n%s\r\n" % [chunk.size, chunk])
118
- socket.write("0\r\n\r\n")
119
- socket.flush
120
-
121
- response = socket.read
122
- response.should include("CONTENT_LENGTH = 8\n")
123
- response.should_not include("HTTP_TRANSFER_ENCODING = ")
124
- ensure
125
- socket.close
126
- end
127
- end
128
- end
129
- end
130
-
131
- it "supports responses with the 'chunked' transfer encoding" do
132
- get('/chunked').should ==
133
- "chunk1\n" +
134
- "chunk2\n" +
135
- "chunk3\n"
136
- end
137
-
138
- it "supports custom headers in responses" do
139
- response = get_response('/extra_header')
140
- response["X-Foo"].should == "Bar"
141
- end
142
-
143
- it "sets the 'Status' header in responses" do
144
- response = get_response('/nonexistant')
145
- response["Status"].should == "404 Not Found"
146
- end
147
-
148
- specify "REQUEST_URI contains the request URI including query string" do
149
- cgi_envs = get('/env?foo=escaped%20string')
150
- cgi_envs.should include("REQUEST_URI = #{base_uri}/env?foo=escaped%20string\n")
151
- end
152
-
153
- specify "REQUEST_URI contains the original escaped URI" do
154
- cgi_envs = get('/env/%C3%BC?foo=escaped%20string')
155
- cgi_envs.downcase.should include("request_uri = #{base_uri}/env/%c3%bc?foo=escaped%20string\n")
156
- end
157
-
158
- specify "PATH_INFO contains the request URI without the base URI and without the query string" do
159
- cgi_envs = get('/env?foo=escaped%20string')
160
- cgi_envs.should include("PATH_INFO = /env\n")
161
- end
162
-
163
- specify "PATH_INFO contains the original escaped URI" do
164
- cgi_envs = get('/env/%C3%BC')
165
- cgi_envs.downcase.should include("path_info = /env/%c3%bc\n")
166
- end
167
-
168
- specify "QUERY_STRING contains the query string" do
169
- cgi_envs = get('/env?foo=escaped%20string')
170
- cgi_envs.should include("QUERY_STRING = foo=escaped%20string\n")
171
- end
172
-
173
- specify "QUERY_STRING must be present even when there's no query string" do
174
- cgi_envs = get('/env')
175
- cgi_envs.should include("QUERY_STRING = \n")
176
- end
177
-
178
- specify "SCRIPT_NAME contains the base URI, or the empty string if the app is deployed on the root URI" do
179
- cgi_envs = get('/env')
180
- cgi_envs.should include("SCRIPT_NAME = #{base_uri}\n")
181
- end
182
-
183
- it "appends an X-Powered-By header containing the Phusion Passenger version number" do
184
- response = get_response('/')
185
- response["X-Powered-By"].should include("Phusion Passenger")
186
- response["X-Powered-By"].should include(PhusionPassenger::VERSION_STRING)
187
- end
188
-
189
- it "buffers uploads" do
190
- get('/') # Force spawning so that the timeout below is enough.
191
-
192
- uri = URI.parse(@server)
193
- socket = TCPSocket.new(uri.host, uri.port)
194
- begin
195
- upload_data = File.read("stub/upload_data.txt")
196
- size_of_first_half = upload_data.size / 2
197
-
198
- socket.write("POST #{base_uri}/raw_upload_to_file HTTP/1.1\r\n")
199
- socket.write("Host: #{uri.host}:#{uri.port}\r\n")
200
- socket.write("Content-Type: multipart/form-data\r\n")
201
- socket.write("Content-Length: #{upload_data.size}\r\n")
202
- socket.write("Connection: close\r\n")
203
- socket.write("X-Output: /dev/null\r\n")
204
- socket.write("\r\n")
205
-
206
- socket.write(upload_data[0 .. size_of_first_half - 1])
207
- socket.flush
208
-
209
- Timeout.timeout(10) do
210
- get('/').should == "front page"
211
- end
212
-
213
- socket.write(upload_data[0 .. size_of_first_half])
214
- socket.flush
215
- socket.read.should =~ /ok\Z/
216
- ensure
217
- socket.close rescue nil
218
- end
219
- end
220
-
221
- it "buffers any number of concurrent uploads" do
222
- get('/') # Force spawning so that the timeout below is enough.
223
- sockets = []
224
-
225
- uri = URI.parse(@server)
226
- upload_data = File.read("stub/upload_data.txt")
227
- size_of_first_half = upload_data.size / 2
228
-
229
- begin
230
- 5.times do |i|
231
- log "Begin sending request #{i}"
232
- socket = TCPSocket.new(uri.host, uri.port)
233
- sockets << socket
234
- socket.write("POST #{base_uri}/raw_upload_to_file HTTP/1.1\r\n")
235
- socket.write("Host: #{uri.host}:#{uri.port}\r\n")
236
- socket.write("Content-Type: multipart/form-data\r\n")
237
- socket.write("Content-Length: #{upload_data.size}\r\n")
238
- socket.write("Connection: close\r\n")
239
- socket.write("X-Index: #{i}\r\n")
240
- socket.write("X-Output: /dev/null\r\n")
241
- socket.write("\r\n")
242
- socket.write(upload_data[0 .. size_of_first_half - 1])
243
- socket.flush
244
- end
245
- log "Reading front page"
246
- Timeout.timeout(10) do
247
- get('/').should == "front page"
248
- end
249
- sockets.each_with_index do |socket, i|
250
- log "Resuming request #{i}"
251
- socket.write(upload_data[size_of_first_half .. -1])
252
- socket.flush
253
- log "Completely sent request #{i}; reading response"
254
- content = socket.read
255
- if content !~ /ok\Z/
256
- raise "Connection #{i} did not send a correct response:\n#{content}"
257
- end
258
- end
259
- ensure
260
- sockets.each do |socket|
261
- socket.close rescue nil
262
- end
263
- end
264
- end
265
-
266
- it "supports restarting via restart.txt" do
267
- get('/').should == "front page"
268
- File.write(@stub.full_app_root + "/front_page.txt", "new front page")
269
- File.touch(@stub.full_app_root + "/tmp/restart.txt", 2)
270
- get('/').should == "new front page"
271
- end
272
-
273
- it "runs as an unprivileged user" do
274
- get('/touch_file?file=file.txt').should == "ok"
275
- stat = File.stat(@stub.full_app_root + "/file.txt")
276
- stat.uid.should_not == 0
277
- stat.gid.should_not == 0
278
- end
279
-
280
- ############
5
+ it "responds to GET requests for static asset" do
6
+ FileUtils.cp('stub/garbage1.dat', @stub.full_app_root + "/public/garbage1.dat")
7
+ get('/garbage1.dat').should == @stub.public_file('garbage1.dat')
8
+ end
9
+
10
+ it "supports page caching on file URIs" do
11
+ File.write(@stub.full_app_root + "/public/cached.html", "This is the cached version of /cached")
12
+ get('/cached').should == "This is the cached version of /cached"
13
+ end
14
+
15
+ it "supports page caching on directory URIs" do
16
+ File.write(@stub.full_app_root + "/public/cached.html", "This is the cached version of /cached")
17
+ Dir.mkdir(@stub.full_app_root + "/public/cached")
18
+ get('/cached').should == "This is the cached version of /cached"
19
+ end
20
+
21
+ it "supports page caching on root/base URIs" do
22
+ File.write(@stub.full_app_root + "/public/index.html", "This is index.html")
23
+ get('/').should == "This is index.html"
24
+ end
25
+
26
+ it "doesn't use page caching if the HTTP request is not GET" do
27
+ File.write(@stub.full_app_root + "/public/cached.html", "This is the cached version of /cached")
28
+ post('/cached').should == "This is the uncached version of /cached"
29
+ end
30
+
31
+ it "responds to GET requests on dynamic pages" do
32
+ get('/').should == "front page"
33
+ end
34
+
35
+ it "properly receives GET parameters" do
36
+ result = get('/parameters?first=one&second=two')
37
+ result.should == "Method: GET\n" +
38
+ "First: one\n" +
39
+ "Second: two\n"
40
+ end
41
+
42
+ it "responds to POST requests on dynamic pages" do
43
+ result = post('/parameters',
44
+ "first" => "one",
45
+ "second" => "two"
46
+ )
47
+ result.should == "Method: POST\n" +
48
+ "First: one\n" +
49
+ "Second: two\n"
50
+ end
51
+
52
+ it "properly handles file uploads" do
53
+ static_file = File.open('stub/garbage1.dat', 'rb')
54
+ params = {
55
+ 'name1' => 'Kotonoha',
56
+ 'name2' => 'Sekai',
57
+ 'data' => static_file
58
+ }
59
+ begin
60
+ # For some reason the WSGI stub app does not accept the multipart data generated by
61
+ # post(), so we use curl instead.
62
+ command = "curl --silent --fail -F name1=Kotonoha -F name2=Sekai -F data=@stub/garbage1.dat " +
63
+ "#{@server}/upload_with_params"
64
+ response = IO.popen(command, "rb") do |io|
65
+ io.read
66
+ end
67
+ response.should ==
68
+ binary_string("name 1 = Kotonoha\n") <<
69
+ binary_string("name 2 = Sekai\n") <<
70
+ binary_string("data = ") << static_file.read
71
+ ensure
72
+ static_file.close
73
+ end
74
+ end
75
+
76
+ describe "when handling POST requests with 'chunked' transfer encoding" do
77
+ before :each do
78
+ @uri = URI.parse(@server)
79
+ end
80
+
81
+ it "correctly forwards the request body to the app" do
82
+ socket = TCPSocket.new(@uri.host, @uri.port)
83
+ begin
84
+ socket.write("POST #{base_uri}/raw_upload_to_file HTTP/1.1\r\n")
85
+ socket.write("Host: #{@uri.host}:#{@uri.port}\r\n")
86
+ socket.write("Transfer-Encoding: chunked\r\n")
87
+ socket.write("Content-Type: text/plain\r\n")
88
+ socket.write("Connection: close\r\n")
89
+ socket.write("X-Output: output.txt\r\n")
90
+ socket.write("\r\n")
91
+
92
+ chunk = "foo=bar!"
93
+ socket.write("%X\r\n%s\r\n" % [chunk.size, chunk])
94
+ socket.write("0\r\n\r\n")
95
+ socket.flush
96
+
97
+ socket.read.should =~ /\r\nok\Z/
98
+ ensure
99
+ socket.close
100
+ end
101
+
102
+ File.read(@stub.full_app_root + "/output.txt").should == "foo=bar!"
103
+ end
104
+
105
+ if WEB_SERVER_DECHUNKS_REQUESTS
106
+ it "sets Content-Length and removes Transfer-Encoding in the request" do
107
+ socket = TCPSocket.new(@uri.host, @uri.port)
108
+ begin
109
+ socket.write("POST #{base_uri}/env HTTP/1.1\r\n")
110
+ socket.write("Host: #{@uri.host}:#{@uri.port}\r\n")
111
+ socket.write("Transfer-Encoding: chunked\r\n")
112
+ socket.write("Content-Type: text/plain\r\n")
113
+ socket.write("Connection: close\r\n")
114
+ socket.write("\r\n")
115
+
116
+ chunk = "foo=bar!"
117
+ socket.write("%X\r\n%s\r\n" % [chunk.size, chunk])
118
+ socket.write("0\r\n\r\n")
119
+ socket.flush
120
+
121
+ response = socket.read
122
+ response.should include("CONTENT_LENGTH = 8\n")
123
+ response.should_not include("HTTP_TRANSFER_ENCODING = ")
124
+ ensure
125
+ socket.close
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ it "supports responses with the 'chunked' transfer encoding" do
132
+ response = get_response('/chunked')
133
+ response["Transfer-Encoding"].should == "chunked"
134
+ response.body.should ==
135
+ "chunk1\n" +
136
+ "chunk2\n" +
137
+ "chunk3\n"
138
+ end
139
+
140
+ it "supports custom headers in responses" do
141
+ response = get_response('/extra_header')
142
+ response["X-Foo"].should == "Bar"
143
+ end
144
+
145
+ it "sets the 'Status' header in responses" do
146
+ response = get_response('/nonexistant')
147
+ response["Status"].should == "404 Not Found"
148
+ end
149
+
150
+ specify "REQUEST_URI contains the request URI including query string" do
151
+ cgi_envs = get('/env?foo=escaped%20string')
152
+ cgi_envs.should include("REQUEST_URI = #{base_uri}/env?foo=escaped%20string\n")
153
+ end
154
+
155
+ specify "REQUEST_URI contains the original escaped URI" do
156
+ cgi_envs = get('/env/%C3%BC?foo=escaped%20string')
157
+ cgi_envs.downcase.should include("request_uri = #{base_uri}/env/%c3%bc?foo=escaped%20string\n")
158
+ end
159
+
160
+ specify "PATH_INFO contains the request URI without the base URI and without the query string" do
161
+ cgi_envs = get('/env?foo=escaped%20string')
162
+ cgi_envs.should include("PATH_INFO = /env\n")
163
+ end
164
+
165
+ specify "PATH_INFO contains the original escaped URI" do
166
+ cgi_envs = get('/env/%C3%BC')
167
+ cgi_envs.downcase.should include("path_info = /env/%c3%bc\n")
168
+ end
169
+
170
+ specify "QUERY_STRING contains the query string" do
171
+ cgi_envs = get('/env?foo=escaped%20string')
172
+ cgi_envs.should include("QUERY_STRING = foo=escaped%20string\n")
173
+ end
174
+
175
+ specify "QUERY_STRING must be present even when there's no query string" do
176
+ cgi_envs = get('/env')
177
+ cgi_envs.should include("QUERY_STRING = \n")
178
+ end
179
+
180
+ specify "SCRIPT_NAME contains the base URI, or the empty string if the app is deployed on the root URI" do
181
+ cgi_envs = get('/env')
182
+ cgi_envs.should include("SCRIPT_NAME = #{base_uri}\n")
183
+ end
184
+
185
+ it "appends an X-Powered-By header containing the Phusion Passenger version number" do
186
+ response = get_response('/')
187
+ response["X-Powered-By"].should include("Phusion Passenger")
188
+ response["X-Powered-By"].should include(PhusionPassenger::VERSION_STRING)
189
+ end
190
+
191
+ it "buffers uploads" do
192
+ get('/') # Force spawning so that the timeout below is enough.
193
+
194
+ uri = URI.parse(@server)
195
+ socket = TCPSocket.new(uri.host, uri.port)
196
+ begin
197
+ upload_data = File.read("stub/upload_data.txt")
198
+ size_of_first_half = upload_data.size / 2
199
+
200
+ socket.write("POST #{base_uri}/raw_upload_to_file HTTP/1.1\r\n")
201
+ socket.write("Host: #{uri.host}:#{uri.port}\r\n")
202
+ socket.write("Content-Type: multipart/form-data\r\n")
203
+ socket.write("Content-Length: #{upload_data.size}\r\n")
204
+ socket.write("Connection: close\r\n")
205
+ socket.write("X-Output: /dev/null\r\n")
206
+ socket.write("\r\n")
207
+
208
+ socket.write(upload_data[0 .. size_of_first_half - 1])
209
+ socket.flush
210
+
211
+ Timeout.timeout(10) do
212
+ get('/').should == "front page"
213
+ end
214
+
215
+ socket.write(upload_data[0 .. size_of_first_half])
216
+ socket.flush
217
+ socket.read.should =~ /ok\Z/
218
+ ensure
219
+ socket.close rescue nil
220
+ end
221
+ end
222
+
223
+ it "buffers any number of concurrent uploads" do
224
+ get('/') # Force spawning so that the timeout below is enough.
225
+ sockets = []
226
+
227
+ uri = URI.parse(@server)
228
+ upload_data = File.read("stub/upload_data.txt")
229
+ size_of_first_half = upload_data.size / 2
230
+
231
+ begin
232
+ 5.times do |i|
233
+ log "Begin sending request #{i}"
234
+ socket = TCPSocket.new(uri.host, uri.port)
235
+ sockets << socket
236
+ socket.write("POST #{base_uri}/raw_upload_to_file HTTP/1.1\r\n")
237
+ socket.write("Host: #{uri.host}:#{uri.port}\r\n")
238
+ socket.write("Content-Type: multipart/form-data\r\n")
239
+ socket.write("Content-Length: #{upload_data.size}\r\n")
240
+ socket.write("Connection: close\r\n")
241
+ socket.write("X-Index: #{i}\r\n")
242
+ socket.write("X-Output: /dev/null\r\n")
243
+ socket.write("\r\n")
244
+ socket.write(upload_data[0 .. size_of_first_half - 1])
245
+ socket.flush
246
+ end
247
+ log "Reading front page"
248
+ Timeout.timeout(10) do
249
+ get('/').should == "front page"
250
+ end
251
+ sockets.each_with_index do |socket, i|
252
+ log "Resuming request #{i}"
253
+ socket.write(upload_data[size_of_first_half .. -1])
254
+ socket.flush
255
+ log "Completely sent request #{i}; reading response"
256
+ content = socket.read
257
+ if content !~ /ok\Z/
258
+ raise "Connection #{i} did not send a correct response:\n#{content}"
259
+ end
260
+ end
261
+ ensure
262
+ sockets.each do |socket|
263
+ socket.close rescue nil
264
+ end
265
+ end
266
+ end
267
+
268
+ it "supports restarting via restart.txt" do
269
+ get('/').should == "front page"
270
+ File.write(@stub.full_app_root + "/front_page.txt", "new front page")
271
+ File.touch(@stub.full_app_root + "/tmp/restart.txt", 2)
272
+ get('/').should == "new front page"
273
+ end
274
+
275
+ it "runs as an unprivileged user" do
276
+ get('/touch_file?file=file.txt').should == "ok"
277
+ stat = File.stat(@stub.full_app_root + "/file.txt")
278
+ stat.uid.should_not == 0
279
+ stat.gid.should_not == 0
280
+ end
281
+
282
+ ############
281
283
 
282
284
  private
283
- def base_uri
284
- uri = URI.parse(@server)
285
- return uri.path.sub(%r(/$), '')
286
- end
285
+ def base_uri
286
+ uri = URI.parse(@server)
287
+ return uri.path.sub(%r(/$), '')
288
+ end
287
289
  end