puma 2.4.0 → 2.4.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

@@ -1,3 +1,8 @@
1
+ === 2.4.1 / 2013-08-07
2
+
3
+ * 1 experimental feature:
4
+ * Support raw tcp servers (aka Lopez Express mode)
5
+
1
6
  === 2.4.0 / 2013-07-22
2
7
 
3
8
  * 5 minor features:
@@ -51,6 +51,7 @@ lib/puma/reactor.rb
51
51
  lib/puma/runner.rb
52
52
  lib/puma/server.rb
53
53
  lib/puma/single.rb
54
+ lib/puma/tcp_logger.rb
54
55
  lib/puma/thread_pool.rb
55
56
  lib/puma/util.rb
56
57
  lib/rack/handler/puma.rb
@@ -186,6 +186,10 @@ module Puma
186
186
  end
187
187
  end
188
188
 
189
+ o.on "--tcp-mode", "Run the app in raw TCP mode instead of HTTP mode" do
190
+ @options[:mode] = :tcp
191
+ end
192
+
189
193
  o.on "-V", "--version", "Print the version information" do
190
194
  puts "puma version #{Puma::Const::VERSION}"
191
195
  exit 1
@@ -22,11 +22,15 @@ module Puma
22
22
  class Client
23
23
  include Puma::Const
24
24
 
25
- def initialize(io, env)
25
+ def initialize(io, env=nil)
26
26
  @io = io
27
27
  @to_io = io.to_io
28
28
  @proto_env = env
29
- @env = env.dup
29
+ if !env
30
+ @env = nil
31
+ else
32
+ @env = env.dup
33
+ end
30
34
 
31
35
  @parser = HttpParser.new
32
36
  @parsed_bytes = 0
@@ -17,6 +17,7 @@ module Puma
17
17
 
18
18
  def initialize(options)
19
19
  @options = options
20
+ @options[:mode] ||= :http
20
21
  @options[:binds] ||= []
21
22
  @options[:on_restart] ||= []
22
23
  @options[:worker_boot] ||= []
@@ -99,6 +100,13 @@ module Puma
99
100
  end
100
101
  end
101
102
 
103
+ if @options[:mode] == :tcp
104
+ require 'puma/tcp_logger'
105
+
106
+ logger = @options[:logger] || STDOUT
107
+ return TCPLogger.new(logger, app, @options[:quiet])
108
+ end
109
+
102
110
  if !@options[:quiet] and @options[:environment] == "development"
103
111
  logger = @options[:logger] || STDOUT
104
112
  app = Rack::CommonLogger.new(app, logger)
@@ -303,6 +311,11 @@ module Puma
303
311
  @options[:worker_directory] = dir.to_s
304
312
  end
305
313
 
314
+ # Run the app as a raw TCP app instead of an HTTP rack app
315
+ def tcp_mode
316
+ @options[:mode] = :tcp
317
+ end
318
+
306
319
  # *Cluster mode only* Preload the application before starting
307
320
  # the workers and setting up the listen ports. This conflicts
308
321
  # with using the phased restart feature, you can't use both.
@@ -28,7 +28,7 @@ module Puma
28
28
  # too taxing on performance.
29
29
  module Const
30
30
 
31
- PUMA_VERSION = VERSION = "2.4.0".freeze
31
+ PUMA_VERSION = VERSION = "2.4.1".freeze
32
32
  CODE_NAME = "Crunchy Munchy Lunchy"
33
33
 
34
34
  FAST_TRACK_KA_TIMEOUT = 0.2
@@ -70,6 +70,10 @@ module Puma
70
70
  log "* Version #{Puma::Const::PUMA_VERSION}, codename: #{Puma::Const::CODE_NAME}"
71
71
  log "* Min threads: #{min_t}, max threads: #{max_t}"
72
72
  log "* Environment: #{ENV['RACK_ENV']}"
73
+
74
+ if @options[:mode] == :tcp
75
+ log "* Mode: Lopez Express (tcp)"
76
+ end
73
77
  end
74
78
 
75
79
  def redirect_io
@@ -120,6 +124,10 @@ module Puma
120
124
  server.max_threads = max_t
121
125
  server.inherit_binder @cli.binder
122
126
 
127
+ if @options[:mode] == :tcp
128
+ server.tcp_mode!
129
+ end
130
+
123
131
  unless development?
124
132
  server.leak_stack_on_error = false
125
133
  end
@@ -75,6 +75,8 @@ module Puma
75
75
  @options = options
76
76
 
77
77
  ENV['RACK_ENV'] ||= "development"
78
+
79
+ @mode = :http
78
80
  end
79
81
 
80
82
  attr_accessor :binder, :leak_stack_on_error
@@ -88,6 +90,10 @@ module Puma
88
90
  @own_binder = false
89
91
  end
90
92
 
93
+ def tcp_mode!
94
+ @mode = :tcp
95
+ end
96
+
91
97
  # On Linux, use TCP_CORK to better control how the TCP stack
92
98
  # packetizes our stream. This improves both latency and throughput.
93
99
  #
@@ -121,6 +127,87 @@ module Puma
121
127
  @thread_pool and @thread_pool.spawned
122
128
  end
123
129
 
130
+ # Lopez Mode == raw tcp apps
131
+
132
+ def run_lopez_mode(background=true)
133
+ @thread_pool = ThreadPool.new(@min_threads,
134
+ @max_threads,
135
+ Hash) do |client, tl|
136
+
137
+ io = client.to_io
138
+ addr = io.peeraddr.last
139
+
140
+ if addr.empty?
141
+ # Set unix socket addrs to localhost
142
+ addr = "127.0.0.1:0"
143
+ else
144
+ addr = "#{addr}:#{io.peeraddr[1]}"
145
+ end
146
+
147
+ env = { 'thread' => tl, REMOTE_ADDR => addr }
148
+
149
+ begin
150
+ @app.call env, client.to_io
151
+ rescue Object => e
152
+ STDERR.puts "! Detected exception at toplevel: #{e.message} (#{e.class})"
153
+ STDERR.puts e.backtrace
154
+ end
155
+
156
+ client.close unless env['detach']
157
+ end
158
+
159
+ if background
160
+ @thread = Thread.new { handle_servers_lopez_mode }
161
+ return @thread
162
+ else
163
+ handle_lopez_servers
164
+ end
165
+ end
166
+
167
+ def handle_servers_lopez_mode
168
+ begin
169
+ check = @check
170
+ sockets = [check] + @binder.ios
171
+ pool = @thread_pool
172
+
173
+ while @status == :run
174
+ begin
175
+ ios = IO.select sockets
176
+ ios.first.each do |sock|
177
+ if sock == check
178
+ break if handle_check
179
+ else
180
+ begin
181
+ if io = sock.accept_nonblock
182
+ c = Client.new io, nil
183
+ pool << c
184
+ end
185
+ rescue SystemCallError
186
+ end
187
+ end
188
+ end
189
+ rescue Errno::ECONNABORTED
190
+ # client closed the socket even before accept
191
+ client.close rescue nil
192
+ rescue Object => e
193
+ @events.unknown_error self, e, "Listen loop"
194
+ end
195
+ end
196
+
197
+ graceful_shutdown if @status == :stop || @status == :restart
198
+
199
+ rescue Exception => e
200
+ STDERR.puts "Exception handling servers: #{e.message} (#{e.class})"
201
+ STDERR.puts e.backtrace
202
+ ensure
203
+ @check.close
204
+ @notify.close
205
+
206
+ if @status != :restart and @own_binder
207
+ @binder.close
208
+ end
209
+ end
210
+ end
124
211
  # Runs the server.
125
212
  #
126
213
  # If +background+ is true (the default) then a thread is spun
@@ -132,6 +219,10 @@ module Puma
132
219
 
133
220
  @status = :run
134
221
 
222
+ if @mode == :tcp
223
+ return run_lopez_mode(background)
224
+ end
225
+
135
226
  @thread_pool = ThreadPool.new(@min_threads,
136
227
  @max_threads,
137
228
  IOBuffer) do |client, buffer|
@@ -0,0 +1,32 @@
1
+ module Puma
2
+ class TCPLogger
3
+ def initialize(logger, app, quiet=false)
4
+ @logger = logger
5
+ @app = app
6
+ @quiet = quiet
7
+ end
8
+
9
+ FORMAT = "%s - %s"
10
+
11
+ def log(who, str)
12
+ now = Time.now.strftime("%d/%b/%Y %H:%M:%S")
13
+
14
+ @logger.puts "#{now} - #{who} - #{str}"
15
+ end
16
+
17
+ def call(env, socket)
18
+ who = env[Const::REMOTE_ADDR]
19
+ log who, "connected" unless @quiet
20
+
21
+ env['log'] = lambda { |str| log(who, str) }
22
+
23
+ begin
24
+ @app.call env, socket
25
+ rescue Object => e
26
+ log who, "exception: #{e.message} (#{e.class})"
27
+ else
28
+ log who, "disconnected" unless @quiet
29
+ end
30
+ end
31
+ end
32
+ end
@@ -2,17 +2,17 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "puma"
5
- s.version = "2.4.0"
5
+ s.version = "2.4.1"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Evan Phoenix"]
9
- s.date = "2013-07-22"
9
+ s.date = "2013-08-07"
10
10
  s.description = "Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server for Ruby/Rack applications. Puma is intended for use in both development and production environments. In order to get the best throughput, it is highly recommended that you use a Ruby implementation with real threads like Rubinius or JRuby."
11
11
  s.email = ["evan@phx.io"]
12
12
  s.executables = ["puma", "pumactl"]
13
13
  s.extensions = ["ext/puma_http11/extconf.rb"]
14
14
  s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.md", "docs/config.md", "docs/nginx.md", "tools/jungle/README.md", "tools/jungle/init.d/README.md", "tools/jungle/upstart/README.md"]
15
- s.files = ["COPYING", "Gemfile", "History.txt", "LICENSE", "Manifest.txt", "README.md", "Rakefile", "TODO", "bin/puma", "bin/pumactl", "docs/config.md", "docs/nginx.md", "ext/puma_http11/PumaHttp11Service.java", "ext/puma_http11/ext_help.h", "ext/puma_http11/extconf.rb", "ext/puma_http11/http11_parser.c", "ext/puma_http11/http11_parser.h", "ext/puma_http11/http11_parser.java.rl", "ext/puma_http11/http11_parser.rl", "ext/puma_http11/http11_parser_common.rl", "ext/puma_http11/io_buffer.c", "ext/puma_http11/mini_ssl.c", "ext/puma_http11/org/jruby/puma/Http11.java", "ext/puma_http11/org/jruby/puma/Http11Parser.java", "ext/puma_http11/org/jruby/puma/MiniSSL.java", "ext/puma_http11/puma_http11.c", "lib/puma.rb", "lib/puma/accept_nonblock.rb", "lib/puma/app/status.rb", "lib/puma/binder.rb", "lib/puma/capistrano.rb", "lib/puma/cli.rb", "lib/puma/client.rb", "lib/puma/cluster.rb", "lib/puma/compat.rb", "lib/puma/configuration.rb", "lib/puma/const.rb", "lib/puma/control_cli.rb", "lib/puma/daemon_ext.rb", "lib/puma/delegation.rb", "lib/puma/detect.rb", "lib/puma/events.rb", "lib/puma/io_buffer.rb", "lib/puma/java_io_buffer.rb", "lib/puma/jruby_restart.rb", "lib/puma/minissl.rb", "lib/puma/null_io.rb", "lib/puma/rack_default.rb", "lib/puma/rack_patch.rb", "lib/puma/reactor.rb", "lib/puma/runner.rb", "lib/puma/server.rb", "lib/puma/single.rb", "lib/puma/thread_pool.rb", "lib/puma/util.rb", "lib/rack/handler/puma.rb", "puma.gemspec", "tools/jungle/README.md", "tools/jungle/init.d/README.md", "tools/jungle/init.d/puma", "tools/jungle/init.d/run-puma", "tools/jungle/upstart/README.md", "tools/jungle/upstart/puma-manager.conf", "tools/jungle/upstart/puma.conf", "tools/trickletest.rb", "test/test_app_status.rb", "test/test_cli.rb", "test/test_config.rb", "test/test_http10.rb", "test/test_http11.rb", "test/test_integration.rb", "test/test_iobuffer.rb", "test/test_minissl.rb", "test/test_null_io.rb", "test/test_persistent.rb", "test/test_puma_server.rb", "test/test_rack_handler.rb", "test/test_rack_server.rb", "test/test_thread_pool.rb", "test/test_unix_socket.rb", "test/test_ws.rb"]
15
+ s.files = ["COPYING", "Gemfile", "History.txt", "LICENSE", "Manifest.txt", "README.md", "Rakefile", "TODO", "bin/puma", "bin/pumactl", "docs/config.md", "docs/nginx.md", "ext/puma_http11/PumaHttp11Service.java", "ext/puma_http11/ext_help.h", "ext/puma_http11/extconf.rb", "ext/puma_http11/http11_parser.c", "ext/puma_http11/http11_parser.h", "ext/puma_http11/http11_parser.java.rl", "ext/puma_http11/http11_parser.rl", "ext/puma_http11/http11_parser_common.rl", "ext/puma_http11/io_buffer.c", "ext/puma_http11/mini_ssl.c", "ext/puma_http11/org/jruby/puma/Http11.java", "ext/puma_http11/org/jruby/puma/Http11Parser.java", "ext/puma_http11/org/jruby/puma/MiniSSL.java", "ext/puma_http11/puma_http11.c", "lib/puma.rb", "lib/puma/accept_nonblock.rb", "lib/puma/app/status.rb", "lib/puma/binder.rb", "lib/puma/capistrano.rb", "lib/puma/cli.rb", "lib/puma/client.rb", "lib/puma/cluster.rb", "lib/puma/compat.rb", "lib/puma/configuration.rb", "lib/puma/const.rb", "lib/puma/control_cli.rb", "lib/puma/daemon_ext.rb", "lib/puma/delegation.rb", "lib/puma/detect.rb", "lib/puma/events.rb", "lib/puma/io_buffer.rb", "lib/puma/java_io_buffer.rb", "lib/puma/jruby_restart.rb", "lib/puma/minissl.rb", "lib/puma/null_io.rb", "lib/puma/rack_default.rb", "lib/puma/rack_patch.rb", "lib/puma/reactor.rb", "lib/puma/runner.rb", "lib/puma/server.rb", "lib/puma/single.rb", "lib/puma/tcp_logger.rb", "lib/puma/thread_pool.rb", "lib/puma/util.rb", "lib/rack/handler/puma.rb", "puma.gemspec", "tools/jungle/README.md", "tools/jungle/init.d/README.md", "tools/jungle/init.d/puma", "tools/jungle/init.d/run-puma", "tools/jungle/upstart/README.md", "tools/jungle/upstart/puma-manager.conf", "tools/jungle/upstart/puma.conf", "tools/trickletest.rb", "test/test_app_status.rb", "test/test_cli.rb", "test/test_config.rb", "test/test_http10.rb", "test/test_http11.rb", "test/test_integration.rb", "test/test_iobuffer.rb", "test/test_minissl.rb", "test/test_null_io.rb", "test/test_persistent.rb", "test/test_puma_server.rb", "test/test_rack_handler.rb", "test/test_rack_server.rb", "test/test_tcp_rack.rb", "test/test_thread_pool.rb", "test/test_unix_socket.rb", "test/test_ws.rb"]
16
16
  s.homepage = "http://puma.io"
17
17
  s.rdoc_options = ["--main", "README.md"]
18
18
  s.require_paths = ["lib"]
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
20
20
  s.rubyforge_project = "puma"
21
21
  s.rubygems_version = "1.8.25"
22
22
  s.summary = "Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server for Ruby/Rack applications"
23
- s.test_files = ["test/test_app_status.rb", "test/test_cli.rb", "test/test_config.rb", "test/test_http10.rb", "test/test_http11.rb", "test/test_integration.rb", "test/test_iobuffer.rb", "test/test_minissl.rb", "test/test_null_io.rb", "test/test_persistent.rb", "test/test_puma_server.rb", "test/test_rack_handler.rb", "test/test_rack_server.rb", "test/test_thread_pool.rb", "test/test_unix_socket.rb", "test/test_ws.rb"]
23
+ s.test_files = ["test/test_app_status.rb", "test/test_cli.rb", "test/test_config.rb", "test/test_http10.rb", "test/test_http11.rb", "test/test_integration.rb", "test/test_iobuffer.rb", "test/test_minissl.rb", "test/test_null_io.rb", "test/test_persistent.rb", "test/test_puma_server.rb", "test/test_rack_handler.rb", "test/test_rack_server.rb", "test/test_tcp_rack.rb", "test/test_thread_pool.rb", "test/test_unix_socket.rb", "test/test_ws.rb"]
24
24
 
25
25
  if s.respond_to? :specification_version then
26
26
  s.specification_version = 3
@@ -0,0 +1,42 @@
1
+ require "rbconfig"
2
+ require 'test/unit'
3
+ require 'socket'
4
+ require 'openssl'
5
+
6
+ require 'puma/minissl'
7
+ require 'puma/server'
8
+
9
+ require 'net/https'
10
+
11
+ class TestTCPRack < Test::Unit::TestCase
12
+
13
+ def setup
14
+ @port = 3212
15
+ @host = "127.0.0.1"
16
+
17
+ @events = Puma::Events.new STDOUT, STDERR
18
+ @server = Puma::Server.new nil, @events
19
+ end
20
+
21
+ def teardown
22
+ @server.stop(true)
23
+ end
24
+
25
+ def test_passes_the_socket
26
+ @server.tcp_mode!
27
+
28
+ body = "We sell hats for a discount!\n"
29
+
30
+ @server.app = proc do |env, socket|
31
+ socket << body
32
+ socket.close
33
+ end
34
+
35
+ @server.add_tcp_listener @host, @port
36
+ @server.run
37
+
38
+ sock = TCPSocket.new @host, @port
39
+
40
+ assert_equal body, sock.read
41
+ end
42
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.0
4
+ version: 2.4.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-07-22 00:00:00.000000000 Z
12
+ date: 2013-08-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
@@ -155,6 +155,7 @@ files:
155
155
  - lib/puma/runner.rb
156
156
  - lib/puma/server.rb
157
157
  - lib/puma/single.rb
158
+ - lib/puma/tcp_logger.rb
158
159
  - lib/puma/thread_pool.rb
159
160
  - lib/puma/util.rb
160
161
  - lib/rack/handler/puma.rb
@@ -180,6 +181,7 @@ files:
180
181
  - test/test_puma_server.rb
181
182
  - test/test_rack_handler.rb
182
183
  - test/test_rack_server.rb
184
+ - test/test_tcp_rack.rb
183
185
  - test/test_thread_pool.rb
184
186
  - test/test_unix_socket.rb
185
187
  - test/test_ws.rb
@@ -224,6 +226,7 @@ test_files:
224
226
  - test/test_puma_server.rb
225
227
  - test/test_rack_handler.rb
226
228
  - test/test_rack_server.rb
229
+ - test/test_tcp_rack.rb
227
230
  - test/test_thread_pool.rb
228
231
  - test/test_unix_socket.rb
229
232
  - test/test_ws.rb