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
@@ -24,320 +24,320 @@
24
24
 
25
25
  module PhusionPassenger
26
26
 
27
- # This class allows reading and writing structured messages over
28
- # I/O channels. This is the Ruby implementation of ext/common/Utils/MessageIO.h;
29
- # see that file for more information.
30
- class MessageChannel
31
- HEADER_SIZE = 2 # :nodoc:
32
- DELIMITER = "\0" # :nodoc:
33
- DELIMITER_NAME = "null byte" # :nodoc:
34
- UINT16_PACK_FORMAT = "n" # :nodoc:
35
- UINT32_PACK_FORMAT = "N" # :nodoc:
36
-
37
- class InvalidHashError < StandardError
38
- end
39
-
40
- # The wrapped IO object.
41
- attr_accessor :io
27
+ # This class allows reading and writing structured messages over
28
+ # I/O channels. This is the Ruby implementation of ext/common/Utils/MessageIO.h;
29
+ # see that file for more information.
30
+ class MessageChannel
31
+ HEADER_SIZE = 2 # :nodoc:
32
+ DELIMITER = "\0" # :nodoc:
33
+ DELIMITER_NAME = "null byte" # :nodoc:
34
+ UINT16_PACK_FORMAT = "n" # :nodoc:
35
+ UINT32_PACK_FORMAT = "N" # :nodoc:
42
36
 
43
- # Create a new MessageChannel by wrapping the given IO object.
44
- def initialize(io = nil)
45
- @io = io
46
- # Make it binary just in case.
47
- @io.binmode if @io
48
- end
49
-
50
- # Read an array message from the underlying file descriptor.
51
- # Returns the array message as an array, or nil when end-of-stream has
52
- # been reached.
53
- #
54
- # Might raise SystemCallError, IOError or SocketError when something
55
- # goes wrong.
56
- def read
57
- buffer = new_buffer
58
- if !@io.read(HEADER_SIZE, buffer)
59
- return nil
60
- end
61
- while buffer.size < HEADER_SIZE
62
- tmp = @io.read(HEADER_SIZE - buffer.size)
63
- if tmp.empty?
64
- return nil
65
- else
66
- buffer << tmp
67
- end
68
- end
69
-
70
- chunk_size = buffer.unpack(UINT16_PACK_FORMAT)[0]
71
- if !@io.read(chunk_size, buffer)
72
- return nil
73
- end
74
- while buffer.size < chunk_size
75
- tmp = @io.read(chunk_size - buffer.size)
76
- if tmp.empty?
77
- return nil
78
- else
79
- buffer << tmp
80
- end
81
- end
82
-
83
- message = []
84
- offset = 0
85
- delimiter_pos = buffer.index(DELIMITER, offset)
86
- while !delimiter_pos.nil?
87
- if delimiter_pos == 0
88
- message << ""
89
- else
90
- message << buffer[offset .. delimiter_pos - 1]
91
- end
92
- offset = delimiter_pos + 1
93
- delimiter_pos = buffer.index(DELIMITER, offset)
94
- end
95
- return message
96
- rescue Errno::ECONNRESET
97
- return nil
98
- end
99
-
100
- # Read an array message from the underlying file descriptor and return the
101
- # result as a hash instead of an array. This assumes that the array message
102
- # has an even number of elements.
103
- # Returns nil when end-of-stream has been reached.
104
- #
105
- # Might raise SystemCallError, IOError or SocketError when something
106
- # goes wrong.
107
- def read_hash
108
- buffer = new_buffer
109
- if !@io.read(HEADER_SIZE, buffer)
110
- return nil
111
- end
112
- while buffer.size < HEADER_SIZE
113
- tmp = @io.read(HEADER_SIZE - buffer.size)
114
- if tmp.empty?
115
- return nil
116
- else
117
- buffer << tmp
118
- end
119
- end
120
-
121
- chunk_size = buffer.unpack(UINT16_PACK_FORMAT)[0]
122
- if !@io.read(chunk_size, buffer)
123
- return nil
124
- end
125
- while buffer.size < chunk_size
126
- tmp = @io.read(chunk_size - buffer.size)
127
- if tmp.empty?
128
- return nil
129
- else
130
- buffer << tmp
131
- end
132
- end
133
-
134
- result = {}
135
- offset = 0
136
- delimiter_pos = buffer.index(DELIMITER, offset)
137
- while !delimiter_pos.nil?
138
- if delimiter_pos == 0
139
- name = ""
140
- else
141
- name = buffer[offset .. delimiter_pos - 1]
142
- end
143
-
144
- offset = delimiter_pos + 1
145
- delimiter_pos = buffer.index(DELIMITER, offset)
146
- if delimiter_pos.nil?
147
- raise InvalidHashError
148
- elsif delimiter_pos == 0
149
- value = ""
150
- else
151
- value = buffer[offset .. delimiter_pos - 1]
152
- end
153
-
154
- result[name] = value
155
- offset = delimiter_pos + 1
156
- delimiter_pos = buffer.index(DELIMITER, offset)
157
- end
158
- return result
159
- rescue Errno::ECONNRESET
160
- return nil
161
- end
37
+ class InvalidHashError < StandardError
38
+ end
162
39
 
163
- # Read a scalar message from the underlying IO object. Returns the
164
- # read message, or nil on end-of-stream.
165
- #
166
- # Might raise SystemCallError, IOError or SocketError when something
167
- # goes wrong.
168
- #
169
- # The +buffer+ argument specifies a buffer in which #read_scalar
170
- # stores the read data. It is good practice to reuse existing buffers
171
- # in order to minimize stress on the garbage collector.
172
- #
173
- # The +max_size+ argument allows one to specify the maximum allowed
174
- # size for the scalar message. If the received scalar message's size
175
- # is larger than +max_size+, then a SecurityError will be raised.
176
- def read_scalar(buffer = new_buffer, max_size = nil)
177
- if !@io.read(4, buffer)
178
- return nil
179
- end
180
- while buffer.size < 4
181
- tmp = @io.read(4 - buffer.size)
182
- if tmp.empty?
183
- return nil
184
- else
185
- buffer << tmp
186
- end
187
- end
188
-
189
- size = buffer.unpack(UINT32_PACK_FORMAT)[0]
190
- if size == 0
191
- buffer.replace('')
192
- return buffer
193
- else
194
- if !max_size.nil? && size > max_size
195
- raise SecurityError, "Scalar message size (#{size}) " <<
196
- "exceeds maximum allowed size (#{max_size})."
197
- end
198
- if !@io.read(size, buffer)
199
- return nil
200
- end
201
- if buffer.size < size
202
- tmp = ''
203
- while buffer.size < size
204
- if !@io.read(size - buffer.size, tmp)
205
- return nil
206
- else
207
- buffer << tmp
208
- end
209
- end
210
- end
211
- return buffer
212
- end
213
- rescue Errno::ECONNRESET
214
- return nil
215
- end
216
-
217
- # Send an array message, which consists of the given elements, over the underlying
218
- # file descriptor. _name_ is the first element in the message, and _args_ are the
219
- # other elements. These arguments will internally be converted to strings by calling
220
- # to_s().
221
- #
222
- # Might raise SystemCallError, IOError or SocketError when something
223
- # goes wrong.
224
- def write(name, *args)
225
- check_argument(name)
226
- args.each do |arg|
227
- check_argument(arg)
228
- end
229
-
230
- message = "#{name}#{DELIMITER}"
231
- args.each do |arg|
232
- message << arg.to_s << DELIMITER
233
- end
234
- @io.write([message.size].pack('n') << message)
235
- @io.flush
236
- end
237
-
238
- # Send a scalar message over the underlying IO object.
239
- #
240
- # Might raise SystemCallError, IOError or SocketError when something
241
- # goes wrong.
242
- def write_scalar(data)
243
- @io.write([data.size].pack('N') << data)
244
- @io.flush
245
- end
246
-
247
- # Receive an IO object (a file descriptor) from the channel. The other
248
- # side must have sent an IO object by calling send_io(). Note that
249
- # this only works on Unix sockets.
250
- #
251
- # Might raise SystemCallError, IOError or SocketError when something
252
- # goes wrong.
253
- def recv_io(klass = IO, negotiate = true)
254
- write("pass IO") if negotiate
255
- io = @io.recv_io(klass)
256
- write("got IO") if negotiate
257
- return io
258
- end
259
-
260
- # Send an IO object (a file descriptor) over the channel. The other
261
- # side must receive the IO object by calling recv_io(). Note that
262
- # this only works on Unix sockets.
263
- #
264
- # Might raise SystemCallError, IOError or SocketError when something
265
- # goes wrong.
266
- def send_io(io)
267
- # We read a message before actually calling #send_io
268
- # in order to prevent the other side from accidentally
269
- # read()ing past the normal data and reading our file
270
- # descriptor too.
271
- #
272
- # For example suppose that side A looks like this:
273
- #
274
- # read(fd, buf, 1024)
275
- # read_io(fd)
276
- #
277
- # and side B:
278
- #
279
- # write(fd, buf, 100)
280
- # send_io(fd_to_pass)
281
- #
282
- # If B completes both write() and send_io(), then A's read() call
283
- # reads past the 100 bytes that B sent. On some platforms, like
284
- # Linux, this will cause read_io() to fail. And it just so happens
285
- # that Ruby's IO#read method slurps more than just the given amount
286
- # of bytes.
287
- result = read
288
- if !result
289
- raise EOFError, "End of stream"
290
- elsif result != ["pass IO"]
291
- raise IOError, "IO passing pre-negotiation header expected"
292
- else
293
- @io.send_io(io)
294
- # Once you've sent the IO you expect to be able to close it on the
295
- # sender's side, even if the other side hasn't read the IO yet.
296
- # Not so: on some operating systems (I'm looking at you OS X) this
297
- # can cause the receiving side to receive a bad file descriptor.
298
- # The post negotiation protocol ensures that we block until the
299
- # other side has really received the IO.
300
- result = read
301
- if !result
302
- raise EOFError, "End of stream"
303
- elsif result != ["got IO"]
304
- raise IOError, "IO passing post-negotiation header expected"
305
- end
306
- end
307
- end
308
-
309
- # Return the file descriptor of the underlying IO object.
310
- def fileno
311
- return @io.fileno
312
- end
313
-
314
- # Close the underlying IO stream. Might raise SystemCallError or
315
- # IOError when something goes wrong.
316
- def close
317
- @io.close
318
- end
319
-
320
- # Checks whether the underlying IO stream is closed.
321
- def closed?
322
- return @io.closed?
323
- end
40
+ # The wrapped IO object.
41
+ attr_accessor :io
324
42
 
325
- private
326
- def check_argument(arg)
327
- if arg.to_s.index(DELIMITER)
328
- raise ArgumentError, "Message name and arguments may not contain #{DELIMITER_NAME}."
329
- end
330
- end
331
-
332
- if defined?(ByteString)
333
- def new_buffer
334
- return ByteString.new
335
- end
336
- else
337
- def new_buffer
338
- return ""
339
- end
340
- end
341
- end
43
+ # Create a new MessageChannel by wrapping the given IO object.
44
+ def initialize(io = nil)
45
+ @io = io
46
+ # Make it binary just in case.
47
+ @io.binmode if @io
48
+ end
49
+
50
+ # Read an array message from the underlying file descriptor.
51
+ # Returns the array message as an array, or nil when end-of-stream has
52
+ # been reached.
53
+ #
54
+ # Might raise SystemCallError, IOError or SocketError when something
55
+ # goes wrong.
56
+ def read
57
+ buffer = new_buffer
58
+ if !@io.read(HEADER_SIZE, buffer)
59
+ return nil
60
+ end
61
+ while buffer.size < HEADER_SIZE
62
+ tmp = @io.read(HEADER_SIZE - buffer.size)
63
+ if tmp.empty?
64
+ return nil
65
+ else
66
+ buffer << tmp
67
+ end
68
+ end
69
+
70
+ chunk_size = buffer.unpack(UINT16_PACK_FORMAT)[0]
71
+ if !@io.read(chunk_size, buffer)
72
+ return nil
73
+ end
74
+ while buffer.size < chunk_size
75
+ tmp = @io.read(chunk_size - buffer.size)
76
+ if tmp.empty?
77
+ return nil
78
+ else
79
+ buffer << tmp
80
+ end
81
+ end
82
+
83
+ message = []
84
+ offset = 0
85
+ delimiter_pos = buffer.index(DELIMITER, offset)
86
+ while !delimiter_pos.nil?
87
+ if delimiter_pos == 0
88
+ message << ""
89
+ else
90
+ message << buffer[offset .. delimiter_pos - 1]
91
+ end
92
+ offset = delimiter_pos + 1
93
+ delimiter_pos = buffer.index(DELIMITER, offset)
94
+ end
95
+ return message
96
+ rescue Errno::ECONNRESET
97
+ return nil
98
+ end
99
+
100
+ # Read an array message from the underlying file descriptor and return the
101
+ # result as a hash instead of an array. This assumes that the array message
102
+ # has an even number of elements.
103
+ # Returns nil when end-of-stream has been reached.
104
+ #
105
+ # Might raise SystemCallError, IOError or SocketError when something
106
+ # goes wrong.
107
+ def read_hash
108
+ buffer = new_buffer
109
+ if !@io.read(HEADER_SIZE, buffer)
110
+ return nil
111
+ end
112
+ while buffer.size < HEADER_SIZE
113
+ tmp = @io.read(HEADER_SIZE - buffer.size)
114
+ if tmp.empty?
115
+ return nil
116
+ else
117
+ buffer << tmp
118
+ end
119
+ end
120
+
121
+ chunk_size = buffer.unpack(UINT16_PACK_FORMAT)[0]
122
+ if !@io.read(chunk_size, buffer)
123
+ return nil
124
+ end
125
+ while buffer.size < chunk_size
126
+ tmp = @io.read(chunk_size - buffer.size)
127
+ if tmp.empty?
128
+ return nil
129
+ else
130
+ buffer << tmp
131
+ end
132
+ end
133
+
134
+ result = {}
135
+ offset = 0
136
+ delimiter_pos = buffer.index(DELIMITER, offset)
137
+ while !delimiter_pos.nil?
138
+ if delimiter_pos == 0
139
+ name = ""
140
+ else
141
+ name = buffer[offset .. delimiter_pos - 1]
142
+ end
143
+
144
+ offset = delimiter_pos + 1
145
+ delimiter_pos = buffer.index(DELIMITER, offset)
146
+ if delimiter_pos.nil?
147
+ raise InvalidHashError
148
+ elsif delimiter_pos == 0
149
+ value = ""
150
+ else
151
+ value = buffer[offset .. delimiter_pos - 1]
152
+ end
153
+
154
+ result[name] = value
155
+ offset = delimiter_pos + 1
156
+ delimiter_pos = buffer.index(DELIMITER, offset)
157
+ end
158
+ return result
159
+ rescue Errno::ECONNRESET
160
+ return nil
161
+ end
162
+
163
+ # Read a scalar message from the underlying IO object. Returns the
164
+ # read message, or nil on end-of-stream.
165
+ #
166
+ # Might raise SystemCallError, IOError or SocketError when something
167
+ # goes wrong.
168
+ #
169
+ # The +buffer+ argument specifies a buffer in which #read_scalar
170
+ # stores the read data. It is good practice to reuse existing buffers
171
+ # in order to minimize stress on the garbage collector.
172
+ #
173
+ # The +max_size+ argument allows one to specify the maximum allowed
174
+ # size for the scalar message. If the received scalar message's size
175
+ # is larger than +max_size+, then a SecurityError will be raised.
176
+ def read_scalar(buffer = new_buffer, max_size = nil)
177
+ if !@io.read(4, buffer)
178
+ return nil
179
+ end
180
+ while buffer.size < 4
181
+ tmp = @io.read(4 - buffer.size)
182
+ if tmp.empty?
183
+ return nil
184
+ else
185
+ buffer << tmp
186
+ end
187
+ end
188
+
189
+ size = buffer.unpack(UINT32_PACK_FORMAT)[0]
190
+ if size == 0
191
+ buffer.replace('')
192
+ return buffer
193
+ else
194
+ if !max_size.nil? && size > max_size
195
+ raise SecurityError, "Scalar message size (#{size}) " <<
196
+ "exceeds maximum allowed size (#{max_size})."
197
+ end
198
+ if !@io.read(size, buffer)
199
+ return nil
200
+ end
201
+ if buffer.size < size
202
+ tmp = ''
203
+ while buffer.size < size
204
+ if !@io.read(size - buffer.size, tmp)
205
+ return nil
206
+ else
207
+ buffer << tmp
208
+ end
209
+ end
210
+ end
211
+ return buffer
212
+ end
213
+ rescue Errno::ECONNRESET
214
+ return nil
215
+ end
216
+
217
+ # Send an array message, which consists of the given elements, over the underlying
218
+ # file descriptor. _name_ is the first element in the message, and _args_ are the
219
+ # other elements. These arguments will internally be converted to strings by calling
220
+ # to_s().
221
+ #
222
+ # Might raise SystemCallError, IOError or SocketError when something
223
+ # goes wrong.
224
+ def write(name, *args)
225
+ check_argument(name)
226
+ args.each do |arg|
227
+ check_argument(arg)
228
+ end
229
+
230
+ message = "#{name}#{DELIMITER}"
231
+ args.each do |arg|
232
+ message << arg.to_s << DELIMITER
233
+ end
234
+ @io.write([message.size].pack('n') << message)
235
+ @io.flush
236
+ end
237
+
238
+ # Send a scalar message over the underlying IO object.
239
+ #
240
+ # Might raise SystemCallError, IOError or SocketError when something
241
+ # goes wrong.
242
+ def write_scalar(data)
243
+ @io.write([data.size].pack('N') << data)
244
+ @io.flush
245
+ end
246
+
247
+ # Receive an IO object (a file descriptor) from the channel. The other
248
+ # side must have sent an IO object by calling send_io(). Note that
249
+ # this only works on Unix sockets.
250
+ #
251
+ # Might raise SystemCallError, IOError or SocketError when something
252
+ # goes wrong.
253
+ def recv_io(klass = IO, negotiate = true)
254
+ write("pass IO") if negotiate
255
+ io = @io.recv_io(klass)
256
+ write("got IO") if negotiate
257
+ return io
258
+ end
259
+
260
+ # Send an IO object (a file descriptor) over the channel. The other
261
+ # side must receive the IO object by calling recv_io(). Note that
262
+ # this only works on Unix sockets.
263
+ #
264
+ # Might raise SystemCallError, IOError or SocketError when something
265
+ # goes wrong.
266
+ def send_io(io)
267
+ # We read a message before actually calling #send_io
268
+ # in order to prevent the other side from accidentally
269
+ # read()ing past the normal data and reading our file
270
+ # descriptor too.
271
+ #
272
+ # For example suppose that side A looks like this:
273
+ #
274
+ # read(fd, buf, 1024)
275
+ # read_io(fd)
276
+ #
277
+ # and side B:
278
+ #
279
+ # write(fd, buf, 100)
280
+ # send_io(fd_to_pass)
281
+ #
282
+ # If B completes both write() and send_io(), then A's read() call
283
+ # reads past the 100 bytes that B sent. On some platforms, like
284
+ # Linux, this will cause read_io() to fail. And it just so happens
285
+ # that Ruby's IO#read method slurps more than just the given amount
286
+ # of bytes.
287
+ result = read
288
+ if !result
289
+ raise EOFError, "End of stream"
290
+ elsif result != ["pass IO"]
291
+ raise IOError, "IO passing pre-negotiation header expected"
292
+ else
293
+ @io.send_io(io)
294
+ # Once you've sent the IO you expect to be able to close it on the
295
+ # sender's side, even if the other side hasn't read the IO yet.
296
+ # Not so: on some operating systems (I'm looking at you OS X) this
297
+ # can cause the receiving side to receive a bad file descriptor.
298
+ # The post negotiation protocol ensures that we block until the
299
+ # other side has really received the IO.
300
+ result = read
301
+ if !result
302
+ raise EOFError, "End of stream"
303
+ elsif result != ["got IO"]
304
+ raise IOError, "IO passing post-negotiation header expected"
305
+ end
306
+ end
307
+ end
308
+
309
+ # Return the file descriptor of the underlying IO object.
310
+ def fileno
311
+ return @io.fileno
312
+ end
313
+
314
+ # Close the underlying IO stream. Might raise SystemCallError or
315
+ # IOError when something goes wrong.
316
+ def close
317
+ @io.close
318
+ end
319
+
320
+ # Checks whether the underlying IO stream is closed.
321
+ def closed?
322
+ return @io.closed?
323
+ end
324
+
325
+ private
326
+ def check_argument(arg)
327
+ if arg.to_s.index(DELIMITER)
328
+ raise ArgumentError, "Message name and arguments may not contain #{DELIMITER_NAME}."
329
+ end
330
+ end
331
+
332
+ if defined?(ByteString)
333
+ def new_buffer
334
+ return ByteString.new
335
+ end
336
+ else
337
+ def new_buffer
338
+ return ""
339
+ end
340
+ end
341
+ end
342
342
 
343
343
  end # module PhusionPassenger