passenger 4.0.38 → 4.0.39

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 (39) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.asc +7 -7
  3. data.tar.gz.asc +7 -7
  4. data/CHANGELOG +24 -0
  5. data/bin/passenger +5 -0
  6. data/bin/passenger-install-apache2-module +1 -1
  7. data/bin/passenger-install-nginx-module +1 -1
  8. data/build/apache2.rb +2 -6
  9. data/build/cxx_tests.rb +5 -0
  10. data/build/packaging.rb +1 -0
  11. data/dev/list_tests.rb +36 -0
  12. data/doc/Users guide Standalone.txt +20 -5
  13. data/doc/users_guide_snippets/analysis_and_system_maintenance.txt +6 -13
  14. data/ext/common/Constants.h +1 -1
  15. data/ext/common/MessageClient.h +7 -7
  16. data/ext/common/Utils.cpp +6 -2
  17. data/ext/common/Utils/MessageIO.h +6 -11
  18. data/ext/common/agents/HelperAgent/RequestHandler.cpp +6 -3
  19. data/ext/common/agents/HelperAgent/RequestHandler.h +146 -24
  20. data/ext/ruby/extconf.rb +12 -0
  21. data/ext/ruby/passenger_native_support.c +18 -13
  22. data/helper-scripts/wsgi-loader.py +25 -2
  23. data/lib/phusion_passenger.rb +1 -1
  24. data/lib/phusion_passenger/config/about_command.rb +3 -0
  25. data/lib/phusion_passenger/rack/thread_handler_extension.rb +1 -4
  26. data/lib/phusion_passenger/request_handler/thread_handler.rb +0 -21
  27. data/lib/phusion_passenger/standalone/command.rb +10 -3
  28. data/lib/phusion_passenger/standalone/start_command.rb +4 -0
  29. data/lib/phusion_passenger/utils/tee_input.rb +82 -14
  30. data/lib/phusion_passenger/utils/terminal_choice_menu.rb +17 -2
  31. data/lib/phusion_passenger/utils/unseekable_socket.rb +0 -30
  32. data/resources/templates/error_layout.html.template +1 -1
  33. data/resources/templates/standalone/config.erb +18 -5
  34. data/test/cxx/RequestHandlerTest.cpp +410 -194
  35. data/test/integration_tests/native_packaging_spec.rb +5 -1
  36. data/test/ruby/request_handler_spec.rb +4 -71
  37. data/test/ruby/utils/tee_input_spec.rb +235 -0
  38. metadata +5 -3
  39. metadata.gz.asc +7 -7
@@ -35,7 +35,9 @@ if RUBY_PLATFORM =~ /solaris/
35
35
  end
36
36
 
37
37
  have_header('alloca.h')
38
+ have_header('ruby/version.h')
38
39
  have_header('ruby/io.h')
40
+ have_var('ruby_version')
39
41
  have_func('rb_thread_io_blocking_region')
40
42
 
41
43
  with_cflags($CFLAGS) do
@@ -48,5 +50,15 @@ with_cflags($CFLAGS) do
48
50
  File.open("Makefile", "w") do |f|
49
51
  f.write(makefile)
50
52
  end
53
+ elsif RUBY_PLATFORM =~ /darwin/
54
+ # The OS X Clang 503.0.38 update (circa March 15 2014) broke
55
+ # /usr/bin/ruby's mkmf. mkmf inserts -multiply_definedsuppress
56
+ # into the Makefile, but that flag is no longer supported by
57
+ # Clang. We remove this manually.
58
+ makefile = File.read("Makefile")
59
+ makefile.sub!(/-multiply_definedsuppress/, "")
60
+ File.open("Makefile", "w") do |f|
61
+ f.write(makefile)
62
+ end
51
63
  end
52
64
  end
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2010-2013 Phusion
3
+ * Copyright (c) 2010-2014 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -27,12 +27,15 @@
27
27
  /* Ruby 1.9 */
28
28
  #include "ruby/intern.h"
29
29
  #include "ruby/io.h"
30
- #include "ruby/version.h"
31
30
  #else
31
+ /* Ruby 1.8 */
32
32
  #include "rubysig.h"
33
33
  #include "rubyio.h"
34
34
  #include "version.h"
35
35
  #endif
36
+ #ifdef HAVE_RUBY_VERSION_H
37
+ #include "ruby/version.h"
38
+ #endif
36
39
  #include <sys/types.h>
37
40
  #include <sys/stat.h>
38
41
  #include <sys/ioctl.h>
@@ -920,17 +923,19 @@ Init_passenger_native_support() {
920
923
  #define ESTIMATED_RUBY_MINOR_VERSION '8'
921
924
  #endif
922
925
 
923
- if (strlen(ruby_version) < sizeof("1.8.7") - 1
924
- || ruby_version[0] != '1'
925
- || ruby_version[1] != '.'
926
- || ruby_version[2] != ESTIMATED_RUBY_MINOR_VERSION)
927
- {
928
- fprintf(stderr, " --> passenger_native_support was compiled for Ruby %s, "
929
- "but you're currently running Ruby %s\n",
930
- ESTIMATED_RUBY_VERSION, ruby_version);
931
- fprintf(stderr, " Refusing to load existing passenger_native_support.\n");
932
- return;
933
- }
926
+ #ifdef HAVE_RUBY_VERSION
927
+ if (strlen(ruby_version) < sizeof("1.8.7") - 1
928
+ || ruby_version[0] != '1'
929
+ || ruby_version[1] != '.'
930
+ || ruby_version[2] != ESTIMATED_RUBY_MINOR_VERSION)
931
+ {
932
+ fprintf(stderr, " --> passenger_native_support was compiled for Ruby %s, "
933
+ "but you're currently running Ruby %s\n",
934
+ ESTIMATED_RUBY_VERSION, ruby_version);
935
+ fprintf(stderr, " Refusing to load existing passenger_native_support.\n");
936
+ return;
937
+ }
938
+ #endif
934
939
  #endif
935
940
 
936
941
  mPassenger = rb_define_module("PhusionPassenger");
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env python
2
2
  # Phusion Passenger - https://www.phusionpassenger.com/
3
- # Copyright (c) 2010-2013 Phusion
3
+ # Copyright (c) 2010-2014 Phusion
4
4
  #
5
5
  # "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  #
@@ -22,7 +22,7 @@
22
22
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
23
  # THE SOFTWARE.
24
24
 
25
- import sys, os, re, imp, traceback, socket, select, struct, logging, errno
25
+ import sys, os, re, imp, threading, signal, traceback, socket, select, struct, logging, errno
26
26
 
27
27
  options = {}
28
28
 
@@ -68,6 +68,28 @@ def create_server_socket():
68
68
  s.listen(1000)
69
69
  return (filename, s)
70
70
 
71
+ def install_signal_handlers():
72
+ def debug(sig, frame):
73
+ id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
74
+ code = []
75
+ for thread_id, stack in sys._current_frames().items():
76
+ code.append("\n# Thread: %s(%d)" % (id2name.get(thread_id,""), thread_id))
77
+ for filename, lineno, name, line in traceback.extract_stack(stack):
78
+ code.append(' File: "%s", line %d, in %s' % (filename, lineno, name))
79
+ if line:
80
+ code.append(" %s" % (line.strip()))
81
+ print("\n".join(code))
82
+
83
+ def debug_and_exit(sig, frame):
84
+ debug(sig, frame)
85
+ sys.exit(1)
86
+
87
+ # Unfortunately, there's no way to install a signal handler that prints
88
+ # the backtrace without interrupting the current system call. os.siginterrupt()
89
+ # doesn't seem to work properly either. That is why we only have a SIGABRT
90
+ # handler and no SIGQUIT handler.
91
+ signal.signal(signal.SIGABRT, debug_and_exit)
92
+
71
93
  def advertise_sockets(socket_filename):
72
94
  print("!> socket: main;unix:%s;session;1" % socket_filename)
73
95
  print("!> ")
@@ -270,6 +292,7 @@ if __name__ == "__main__":
270
292
  handshake_and_read_startup_request()
271
293
  app_module = load_app()
272
294
  socket_filename, server_socket = create_server_socket()
295
+ install_signal_handlers()
273
296
  handler = RequestHandler(server_socket, sys.stdin, app_module.application)
274
297
  print("!> Ready")
275
298
  advertise_sockets(socket_filename)
@@ -30,7 +30,7 @@ module PhusionPassenger
30
30
 
31
31
  PACKAGE_NAME = 'passenger'
32
32
  # Run 'rake ext/common/Constants.h' after changing this number.
33
- VERSION_STRING = '4.0.38'
33
+ VERSION_STRING = '4.0.39'
34
34
 
35
35
  PREFERRED_NGINX_VERSION = '1.4.6'
36
36
  NGINX_SHA256_CHECKSUM = '7a8b5b15d708b5b9c61e723bd93faa247b06c8b90babb76f612c128edb5812c6'
@@ -42,6 +42,7 @@ class AboutCommand < Command
42
42
  puts " includedir Show the Nginx runtime library headers directory."
43
43
  puts " nginx-addon-dir Show #{PROGRAM_NAME}'s Nginx addon directory."
44
44
  puts " nginx-libs Show Nginx runtime library flags."
45
+ puts " resourcesdir Show #{PROGRAM_NAME}'s resources directory."
45
46
  puts " compiled Check whether runtime libraries are compiled."
46
47
  puts " natively-packaged Check whether Phusion Passenger is natively"
47
48
  puts " packaged."
@@ -77,6 +78,8 @@ class AboutCommand < Command
77
78
  puts PhusionPassenger.nginx_module_source_dir
78
79
  when "--nginx-libs"
79
80
  puts "#{common_library.link_objects_as_string} #{PhusionPassenger.lib_dir}/common/libboost_oxt.a"
81
+ when "--resourcesdir"
82
+ puts PhusionPassenger.resources_dir
80
83
  when "--compiled"
81
84
  common_library.link_objects.each do |filename|
82
85
  if !File.exist?(filename)
@@ -67,10 +67,7 @@ module ThreadHandlerExtension
67
67
  end
68
68
  env[RACK_HIJACK_P] = true
69
69
  env[RACK_HIJACK] = lambda do
70
- env[RACK_HIJACK_IO] ||= begin
71
- connection.stop_simulating_eof!
72
- connection
73
- end
70
+ env[RACK_HIJACK_IO] ||= connection
74
71
  end
75
72
 
76
73
  begin
@@ -46,7 +46,6 @@ class ThreadHandler
46
46
  OOBW = 'OOBW'.freeze
47
47
  PASSENGER_CONNECT_PASSWORD = 'PASSENGER_CONNECT_PASSWORD'.freeze
48
48
  CONTENT_LENGTH = 'CONTENT_LENGTH'.freeze
49
- HTTP_TRANSFER_ENCODING = 'HTTP_TRANSFER_ENCODING'.freeze
50
49
 
51
50
  MAX_HEADER_SIZE = 128 * 1024
52
51
 
@@ -277,10 +276,6 @@ private
277
276
  # end
278
277
 
279
278
  def prepare_request(connection, headers)
280
- if !may_have_request_body?(headers)
281
- connection.simulate_eof!
282
- end
283
-
284
279
  if @analytics_logger && headers[PASSENGER_TXN_ID]
285
280
  txn_id = headers[PASSENGER_TXN_ID]
286
281
  union_station_key = headers[PASSENGER_UNION_STATION_KEY]
@@ -310,10 +305,6 @@ private
310
305
  end
311
306
 
312
307
  def finalize_request(connection, headers, has_error)
313
- if connection
314
- connection.stop_simulating_eof!
315
- end
316
-
317
308
  log = headers[PASSENGER_ANALYTICS_WEB_LOG]
318
309
  if log && !log.closed?
319
310
  exception_occurred = false
@@ -385,18 +376,6 @@ private
385
376
  end
386
377
  end
387
378
 
388
- def may_have_request_body?(headers)
389
- if headers[REQUEST_METHOD] == GET
390
- if content_length = headers[CONTENT_LENGTH]
391
- return content_length != 0
392
- else
393
- return headers.has_key?(HTTP_TRANSFER_ENCODING)
394
- end
395
- else
396
- return true
397
- end
398
- end
399
-
400
379
  def should_reraise_error?(e)
401
380
  # Stubable by unit tests.
402
381
  return true
@@ -217,10 +217,8 @@ private
217
217
 
218
218
  File.open(@config_filename, 'w') do |f|
219
219
  f.chmod(0644)
220
- template_filename = File.join(PhusionPassenger.resources_dir,
221
- "templates", "standalone", "config.erb")
222
220
  require_erb
223
- erb = ERB.new(File.read(template_filename))
221
+ erb = ERB.new(File.read(nginx_config_template_filename))
224
222
  current_user = Etc.getpwuid(Process.uid).name
225
223
 
226
224
  # The template requires some helper methods which are defined in start_command.rb.
@@ -230,6 +228,15 @@ private
230
228
  end
231
229
  end
232
230
 
231
+ def nginx_config_template_filename
232
+ if @options[:nginx_config_template]
233
+ return @options[:nginx_config_template]
234
+ else
235
+ return File.join(PhusionPassenger.resources_dir,
236
+ "templates", "standalone", "config.erb")
237
+ end
238
+ end
239
+
233
240
  def serialize_strset(*items)
234
241
  if "".respond_to?(:force_encoding)
235
242
  items = items.map { |x| x.force_encoding('binary') }
@@ -270,6 +270,10 @@ private
270
270
  "be used instead of downloading from the Internet")) do |value|
271
271
  @options[:nginx_tarball] = File.expand_path(value)
272
272
  end
273
+ opts.on("--nginx-config-template FILENAME", String,
274
+ wrap_desc("The template to use for generating the Nginx config file")) do |value|
275
+ @options[:nginx_config_template] = File.expand_path(value)
276
+ end
273
277
  opts.on("--binaries-url-root URL", String,
274
278
  wrap_desc("If Nginx needs to be installed, then the specified URL will be " +
275
279
  "checked for binaries prior to a local build.")) do |value|
@@ -72,6 +72,8 @@ module Utils
72
72
  # "rack.input" of the Rack environment.
73
73
  class TeeInput
74
74
  CONTENT_LENGTH = "CONTENT_LENGTH".freeze
75
+ HTTP_TRANSFER_ENCODING = "HTTP_TRANSFER_ENCODING".freeze
76
+ CHUNKED = "chunked".freeze
75
77
 
76
78
  # The maximum size (in +bytes+) to buffer in memory before
77
79
  # resorting to a temporary file. Default is 112 kilobytes.
@@ -92,11 +94,18 @@ class TeeInput
92
94
  # Initializes a new TeeInput object. You normally do not have to call
93
95
  # this unless you are writing an HTTP server.
94
96
  def initialize(socket, env)
95
- @len = env[CONTENT_LENGTH]
96
- @len = @len.to_i if @len
97
+ if @len = env[CONTENT_LENGTH]
98
+ @len = @len.to_i
99
+ elsif env[HTTP_TRANSFER_ENCODING] != CHUNKED
100
+ @len = 0
101
+ end
97
102
  @socket = socket
98
- @tmp = @len && @len <= @@client_body_buffer_size ?
99
- StringIO.new("") : TmpIO.new("PassengerTeeInput")
103
+ @bytes_read = 0
104
+ if @len && @len <= @@client_body_buffer_size
105
+ @tmp = StringIO.new("")
106
+ else
107
+ @tmp = TmpIO.new("PassengerTeeInput")
108
+ end
100
109
  end
101
110
 
102
111
  def close
@@ -104,18 +113,37 @@ class TeeInput
104
113
  end
105
114
 
106
115
  def size
107
- @len and return @len
108
- pos = @tmp.pos
109
- consume!
110
- @tmp.pos = pos
111
- @len = @tmp.size
116
+ if @len
117
+ @len
118
+ else
119
+ pos = @tmp.pos
120
+ consume!
121
+ @tmp.pos = pos
122
+ @len = @tmp.size
123
+ end
112
124
  end
113
125
 
114
- def read(*args)
115
- if socket_drained?
116
- @tmp.read(*args)
126
+ def read(len = nil, buf = "")
127
+ buf ||= ""
128
+ if len
129
+ if len < 0
130
+ raise ArgumentError, "negative length #{len} given"
131
+ elsif len == 0
132
+ buf.replace('')
133
+ buf
134
+ else
135
+ if socket_drained?
136
+ @tmp.read(len, buf)
137
+ else
138
+ tee(read_exact(len, buf))
139
+ end
140
+ end
117
141
  else
118
- tee(@socket.read(*args))
142
+ if socket_drained?
143
+ @tmp.read(nil, buf)
144
+ else
145
+ tee(read_all(buf))
146
+ end
119
147
  end
120
148
  end
121
149
 
@@ -123,7 +151,18 @@ class TeeInput
123
151
  if socket_drained?
124
152
  @tmp.gets
125
153
  else
126
- tee(@socket.gets)
154
+ if @bytes_read == @len
155
+ nil
156
+ elsif line = @socket.gets
157
+ if @len
158
+ max_len = @len - @bytes_read
159
+ line.slice!(max_len, line.size - max_len)
160
+ end
161
+ @bytes_read += line.size
162
+ tee(line)
163
+ else
164
+ nil
165
+ end
127
166
  end
128
167
  end
129
168
 
@@ -160,6 +199,7 @@ private
160
199
  def consume!
161
200
  junk = ""
162
201
  nil while read(16 * 1024, junk)
202
+ @socket = nil
163
203
  end
164
204
 
165
205
  def tee(buffer)
@@ -168,6 +208,34 @@ private
168
208
  end
169
209
  buffer
170
210
  end
211
+
212
+ def read_exact(len, buf)
213
+ if @len
214
+ max_len = @len - @bytes_read
215
+ len = max_len if len > max_len
216
+ return nil if len == 0
217
+ end
218
+ ret = @socket.read(len, buf)
219
+ @bytes_read += ret.size if ret
220
+ ret
221
+ end
222
+
223
+ def read_all(buf)
224
+ if @len
225
+ ret = @socket.read(@len - @bytes_read, buf)
226
+ if ret
227
+ @bytes_read += ret.size
228
+ ret
229
+ else
230
+ buf.replace("")
231
+ buf
232
+ end
233
+ else
234
+ ret = @socket.read(nil, buf)
235
+ @bytes_read += ret.size
236
+ ret
237
+ end
238
+ end
171
239
  end
172
240
 
173
241
  end # module Utils
@@ -109,6 +109,9 @@ private
109
109
  when " "
110
110
  process_toggle
111
111
  return false
112
+ when "!"
113
+ process_disable_utf8
114
+ return false
112
115
  when "\r"
113
116
  return true
114
117
  else
@@ -131,6 +134,10 @@ private
131
134
  @choices[@pointer].toggle!
132
135
  end
133
136
 
137
+ def process_disable_utf8
138
+ ENV['UTF8_MENUS'] = '0'
139
+ end
140
+
134
141
  def render_to_string
135
142
  str = ""
136
143
  @choices.each_with_index do |choice, i|
@@ -143,11 +150,11 @@ private
143
150
  end
144
151
 
145
152
  def render_pointer(index)
146
- return @pointer == index ? "‣" : " "
153
+ return @pointer == index ? maybe_utf8("‣", ">") : " "
147
154
  end
148
155
 
149
156
  def render_checkbox(checked)
150
- return checked ? "⬢" : "⬡"
157
+ return checked ? maybe_utf8("⬢", "(*)") : maybe_utf8("⬡", "( )")
151
158
  end
152
159
 
153
160
  def display(str)
@@ -163,6 +170,14 @@ private
163
170
  end
164
171
  end
165
172
 
173
+ def maybe_utf8(utf8, plain)
174
+ if ENV['UTF8_MENUS'] == '0'
175
+ return plain
176
+ else
177
+ return utf8
178
+ end
179
+ end
180
+
166
181
  def hide_cursor
167
182
  display("\x1b[?25l")
168
183
  end