puma 2.11.3 → 2.12.0

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.

@@ -22,7 +22,7 @@ Capistrano::Configuration.instance.load do
22
22
  namespace :puma do
23
23
  desc 'Start puma'
24
24
  task :start, :roles => lambda { puma_role }, :on_no_matching_servers => :continue do
25
- run "cd #{current_path} && #{puma_cmd} #{start_options}", :pty => false
25
+ run "cd #{current_path} && #{puma_rails_additional_env} #{puma_cmd} #{start_options}", :pty => false
26
26
  end
27
27
 
28
28
  desc 'Stop puma'
@@ -33,7 +33,7 @@ Capistrano::Configuration.instance.load do
33
33
  desc 'Restart puma'
34
34
  task :restart, :roles => lambda { puma_role }, :on_no_matching_servers => :continue do
35
35
  begin
36
- run "cd #{current_path} && #{pumactl_cmd} -S #{state_path} restart"
36
+ run "cd #{current_path} && #{puma_rails_additional_env} #{pumactl_cmd} -S #{state_path} restart"
37
37
  rescue Capistrano::CommandError => ex
38
38
  puts "Failed to restart puma: #{ex}\nAssuming not started."
39
39
  start
@@ -43,7 +43,7 @@ Capistrano::Configuration.instance.load do
43
43
  desc 'Restart puma (phased restart)'
44
44
  task :phased_restart, :roles => lambda { puma_role }, :on_no_matching_servers => :continue do
45
45
  begin
46
- run "cd #{current_path} && #{pumactl_cmd} -S #{state_path} phased-restart"
46
+ run "cd #{current_path} && #{puma_rails_additional_env} #{pumactl_cmd} -S #{state_path} phased-restart"
47
47
  rescue Capistrano::CommandError => ex
48
48
  puts "Failed to restart puma: #{ex}\nAssuming not started."
49
49
  start
@@ -72,6 +72,11 @@ Capistrano::Configuration.instance.load do
72
72
  fetch(:rack_env, fetch(:rails_env, 'production'))
73
73
  end
74
74
 
75
+ #add additional env when start rails, such as : secret key, db username, db pwd or other what you want.
76
+ def puma_rails_additional_env
77
+ fetch(:puma_rails_additional_env, '')
78
+ end
79
+
75
80
  def state_path
76
81
  (config_file ? configuration.options[:state] : nil) || puma_state
77
82
  end
@@ -11,8 +11,7 @@ require 'puma/util'
11
11
  require 'puma/single'
12
12
  require 'puma/cluster'
13
13
 
14
- require 'rack/commonlogger'
15
- require 'rack/utils'
14
+ require 'puma/commonlogger'
16
15
 
17
16
  module Puma
18
17
  class << self
@@ -408,7 +407,7 @@ module Puma
408
407
 
409
408
  o.on "-V", "--version", "Print the version information" do
410
409
  puts "puma version #{Puma::Const::VERSION}"
411
- exit 1
410
+ exit 0
412
411
  end
413
412
 
414
413
  o.on "-w", "--workers COUNT",
@@ -424,7 +423,7 @@ module Puma
424
423
 
425
424
  o.on_tail "-h", "--help", "Show help" do
426
425
  log o
427
- exit 1
426
+ exit 0
428
427
  end
429
428
  end
430
429
  end
@@ -445,32 +444,28 @@ module Puma
445
444
 
446
445
  @restart_dir ||= Dir.pwd
447
446
 
448
- @original_argv = ARGV.dup
447
+ @original_argv = @argv.dup
449
448
 
450
- if defined? Rubinius::OS_ARGV
451
- @restart_argv = Rubinius::OS_ARGV
449
+ require 'rubygems'
450
+
451
+ # if $0 is a file in the current directory, then restart
452
+ # it the same, otherwise add -S on there because it was
453
+ # picked up in PATH.
454
+ #
455
+ if File.exist?($0)
456
+ arg0 = [Gem.ruby, $0]
452
457
  else
453
- require 'rubygems'
454
-
455
- # if $0 is a file in the current directory, then restart
456
- # it the same, otherwise add -S on there because it was
457
- # picked up in PATH.
458
- #
459
- if File.exist?($0)
460
- arg0 = [Gem.ruby, $0]
461
- else
462
- arg0 = [Gem.ruby, "-S", $0]
463
- end
458
+ arg0 = [Gem.ruby, "-S", $0]
459
+ end
464
460
 
465
- # Detect and reinject -Ilib from the command line
466
- lib = File.expand_path "lib"
467
- arg0[1,0] = ["-I", lib] if $:[0] == lib
461
+ # Detect and reinject -Ilib from the command line
462
+ lib = File.expand_path "lib"
463
+ arg0[1,0] = ["-I", lib] if $:[0] == lib
468
464
 
469
- if defined? Puma::WILD_ARGS
470
- @restart_argv = arg0 + Puma::WILD_ARGS + ARGV
471
- else
472
- @restart_argv = arg0 + ARGV
473
- end
465
+ if defined? Puma::WILD_ARGS
466
+ @restart_argv = arg0 + Puma::WILD_ARGS + @original_argv
467
+ else
468
+ @restart_argv = arg0 + @original_argv
474
469
  end
475
470
  end
476
471
 
@@ -176,6 +176,8 @@ module Puma
176
176
  end
177
177
 
178
178
  def wakeup!
179
+ return unless @wakeup
180
+
179
181
  begin
180
182
  @wakeup.write "!" unless @wakeup.closed?
181
183
  rescue SystemCallError, IOError
@@ -0,0 +1,107 @@
1
+ module Puma
2
+ # Rack::CommonLogger forwards every request to the given +app+, and
3
+ # logs a line in the
4
+ # {Apache common log format}[http://httpd.apache.org/docs/1.3/logs.html#common]
5
+ # to the +logger+.
6
+ #
7
+ # If +logger+ is nil, CommonLogger will fall back +rack.errors+, which is
8
+ # an instance of Rack::NullLogger.
9
+ #
10
+ # +logger+ can be any class, including the standard library Logger, and is
11
+ # expected to have either +write+ or +<<+ method, which accepts the CommonLogger::FORMAT.
12
+ # According to the SPEC, the error stream must also respond to +puts+
13
+ # (which takes a single argument that responds to +to_s+), and +flush+
14
+ # (which is called without arguments in order to make the error appear for
15
+ # sure)
16
+ class CommonLogger
17
+ # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
18
+ #
19
+ # lilith.local - - [07/Aug/2006 23:58:02 -0400] "GET / HTTP/1.1" 500 -
20
+ #
21
+ # %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
22
+ FORMAT = %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n}
23
+
24
+ def initialize(app, logger=nil)
25
+ @app = app
26
+ @logger = logger
27
+ end
28
+
29
+ def call(env)
30
+ began_at = Time.now
31
+ status, header, body = @app.call(env)
32
+ header = Util::HeaderHash.new(header)
33
+
34
+ # If we've been hijacked, then output a special line
35
+ if env['rack.hijack_io']
36
+ log_hijacking(env, 'HIJACK', header, began_at)
37
+ else
38
+ ary = env['rack.after_reply']
39
+ ary << lambda { log(env, status, header, began_at) }
40
+ end
41
+
42
+ [status, header, body]
43
+ end
44
+
45
+ HIJACK_FORMAT = %{%s - %s [%s] "%s %s%s %s" HIJACKED -1 %0.4f\n}
46
+
47
+ private
48
+
49
+ def log_hijacking(env, status, header, began_at)
50
+ now = Time.now
51
+
52
+ logger = @logger || env['rack.errors']
53
+ logger.write HIJACK_FORMAT % [
54
+ env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
55
+ env["REMOTE_USER"] || "-",
56
+ now.strftime("%d/%b/%Y %H:%M:%S"),
57
+ env["REQUEST_METHOD"],
58
+ env["PATH_INFO"],
59
+ env["QUERY_STRING"].empty? ? "" : "?"+env["QUERY_STRING"],
60
+ env["HTTP_VERSION"],
61
+ now - began_at ]
62
+ end
63
+
64
+ PATH_INFO = 'PATH_INFO'.freeze
65
+ REQUEST_METHOD = 'REQUEST_METHOD'.freeze
66
+ SCRIPT_NAME = 'SCRIPT_NAME'.freeze
67
+ QUERY_STRING = 'QUERY_STRING'.freeze
68
+ CACHE_CONTROL = 'Cache-Control'.freeze
69
+ CONTENT_LENGTH = 'Content-Length'.freeze
70
+ CONTENT_TYPE = 'Content-Type'.freeze
71
+
72
+ GET = 'GET'.freeze
73
+ HEAD = 'HEAD'.freeze
74
+
75
+
76
+ def log(env, status, header, began_at)
77
+ now = Time.now
78
+ length = extract_content_length(header)
79
+
80
+ msg = FORMAT % [
81
+ env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
82
+ env["REMOTE_USER"] || "-",
83
+ now.strftime("%d/%b/%Y:%H:%M:%S %z"),
84
+ env[REQUEST_METHOD],
85
+ env[PATH_INFO],
86
+ env[QUERY_STRING].empty? ? "" : "?"+env[QUERY_STRING],
87
+ env["HTTP_VERSION"],
88
+ status.to_s[0..3],
89
+ length,
90
+ now - began_at ]
91
+
92
+ logger = @logger || env['rack.errors']
93
+ # Standard library logger doesn't support write but it supports << which actually
94
+ # calls to write on the log device without formatting
95
+ if logger.respond_to?(:write)
96
+ logger.write(msg)
97
+ else
98
+ logger << msg
99
+ end
100
+ end
101
+
102
+ def extract_content_length(headers)
103
+ value = headers[CONTENT_LENGTH] or return '-'
104
+ value.to_s == '0' ? '-' : value
105
+ end
106
+ end
107
+ end
@@ -1,3 +1,5 @@
1
+ require 'puma/rack/builder'
2
+
1
3
  module Puma
2
4
 
3
5
  module ConfigDefault
@@ -77,7 +79,7 @@ module Puma
77
79
 
78
80
  if !@options[:quiet] and @options[:environment] == "development"
79
81
  logger = @options[:logger] || STDOUT
80
- found = Rack::CommonLogger.new(found, logger)
82
+ found = CommonLogger.new(found, logger)
81
83
  end
82
84
 
83
85
  ConfigMiddleware.new(self, found)
@@ -99,11 +101,11 @@ module Puma
99
101
  def load_rackup
100
102
  raise "Missing rackup file '#{rackup}'" unless File.exist?(rackup)
101
103
 
102
- rack_app, rack_options = Rack::Builder.parse_file(rackup)
104
+ rack_app, rack_options = Puma::Rack::Builder.parse_file(rackup)
103
105
  @options.merge!(rack_options)
104
106
 
105
107
  config_ru_binds = rack_options.each_with_object([]) do |(k, v), b|
106
- b << v if k.to_s[0,4] == "bind"
108
+ b << v if k.to_s.start_with?("bind")
107
109
  end
108
110
  @options[:binds] = config_ru_binds unless config_ru_binds.empty?
109
111
 
@@ -1,5 +1,3 @@
1
- require 'rack'
2
-
3
1
  module Puma
4
2
  class UnsupportedOption < RuntimeError
5
3
  end
@@ -8,12 +6,85 @@ module Puma
8
6
  # Every standard HTTP code mapped to the appropriate message. These are
9
7
  # used so frequently that they are placed directly in Puma for easy
10
8
  # access rather than Puma::Const itself.
11
- HTTP_STATUS_CODES = Rack::Utils::HTTP_STATUS_CODES
9
+
10
+ # Every standard HTTP code mapped to the appropriate message.
11
+ # Generated with:
12
+ # curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv | \
13
+ # ruby -ne 'm = /^(\d{3}),(?!Unassigned|\(Unused\))([^,]+)/.match($_) and \
14
+ # puts "#{m[1]} => \x27#{m[2].strip}\x27,"'
15
+ HTTP_STATUS_CODES = {
16
+ 100 => 'Continue',
17
+ 101 => 'Switching Protocols',
18
+ 102 => 'Processing',
19
+ 200 => 'OK',
20
+ 201 => 'Created',
21
+ 202 => 'Accepted',
22
+ 203 => 'Non-Authoritative Information',
23
+ 204 => 'No Content',
24
+ 205 => 'Reset Content',
25
+ 206 => 'Partial Content',
26
+ 207 => 'Multi-Status',
27
+ 208 => 'Already Reported',
28
+ 226 => 'IM Used',
29
+ 300 => 'Multiple Choices',
30
+ 301 => 'Moved Permanently',
31
+ 302 => 'Found',
32
+ 303 => 'See Other',
33
+ 304 => 'Not Modified',
34
+ 305 => 'Use Proxy',
35
+ 307 => 'Temporary Redirect',
36
+ 308 => 'Permanent Redirect',
37
+ 400 => 'Bad Request',
38
+ 401 => 'Unauthorized',
39
+ 402 => 'Payment Required',
40
+ 403 => 'Forbidden',
41
+ 404 => 'Not Found',
42
+ 405 => 'Method Not Allowed',
43
+ 406 => 'Not Acceptable',
44
+ 407 => 'Proxy Authentication Required',
45
+ 408 => 'Request Timeout',
46
+ 409 => 'Conflict',
47
+ 410 => 'Gone',
48
+ 411 => 'Length Required',
49
+ 412 => 'Precondition Failed',
50
+ 413 => 'Payload Too Large',
51
+ 414 => 'URI Too Long',
52
+ 415 => 'Unsupported Media Type',
53
+ 416 => 'Range Not Satisfiable',
54
+ 417 => 'Expectation Failed',
55
+ 422 => 'Unprocessable Entity',
56
+ 423 => 'Locked',
57
+ 424 => 'Failed Dependency',
58
+ 426 => 'Upgrade Required',
59
+ 428 => 'Precondition Required',
60
+ 429 => 'Too Many Requests',
61
+ 431 => 'Request Header Fields Too Large',
62
+ 500 => 'Internal Server Error',
63
+ 501 => 'Not Implemented',
64
+ 502 => 'Bad Gateway',
65
+ 503 => 'Service Unavailable',
66
+ 504 => 'Gateway Timeout',
67
+ 505 => 'HTTP Version Not Supported',
68
+ 506 => 'Variant Also Negotiates',
69
+ 507 => 'Insufficient Storage',
70
+ 508 => 'Loop Detected',
71
+ 510 => 'Not Extended',
72
+ 511 => 'Network Authentication Required'
73
+ }
74
+
75
+ SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
76
+ [message.downcase.gsub(/\s|-|'/, '_').to_sym, code]
77
+ }.flatten]
12
78
 
13
79
  # For some HTTP status codes the client only expects headers.
14
- STATUS_WITH_NO_ENTITY_BODY = Hash[Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.map { |s|
15
- [s, true]
16
- }]
80
+ #
81
+
82
+ no_body = {}
83
+ ((100..199).to_a << 204 << 205 << 304).each do |code|
84
+ no_body[code] = true
85
+ end
86
+
87
+ STATUS_WITH_NO_ENTITY_BODY = no_body
17
88
 
18
89
  # Frequently used constants when constructing requests or responses. Many times
19
90
  # the constant just refers to a string with the same contents. Using these constants
@@ -28,8 +99,8 @@ module Puma
28
99
  # too taxing on performance.
29
100
  module Const
30
101
 
31
- PUMA_VERSION = VERSION = "2.11.3".freeze
32
- CODE_NAME = "Intrepid Squirrel".freeze
102
+ PUMA_VERSION = VERSION = "2.12.0".freeze
103
+ CODE_NAME = "Plutonian Photo Shoot".freeze
33
104
 
34
105
  FAST_TRACK_KA_TIMEOUT = 0.2
35
106
 
@@ -111,6 +182,7 @@ module Puma
111
182
  PORT_80 = "80".freeze
112
183
  PORT_443 = "443".freeze
113
184
  LOCALHOST = "localhost".freeze
185
+ LOCALHOST_IP = "127.0.0.1".freeze
114
186
 
115
187
  SERVER_PROTOCOL = "SERVER_PROTOCOL".freeze
116
188
  HTTP_11 = "HTTP/1.1".freeze
@@ -129,6 +201,7 @@ module Puma
129
201
  RACK_AFTER_REPLY = "rack.after_reply".freeze
130
202
  PUMA_SOCKET = "puma.socket".freeze
131
203
  PUMA_CONFIG = "puma.config".freeze
204
+ PUMA_PEERCERT = "puma.peercert".freeze
132
205
 
133
206
  HTTP = "http".freeze
134
207
  HTTPS = "https".freeze
@@ -66,7 +66,15 @@ module Puma
66
66
  command = argv.shift
67
67
  @options[:command] = command if command
68
68
 
69
- Puma::Configuration.new(@options).load if @options[:config_file]
69
+ unless @options[:config_file] == '-'
70
+ if @options[:config_file].nil? and File.exist?('config/puma.rb')
71
+ @options[:config_file] = 'config/puma.rb'
72
+ end
73
+
74
+ if @options[:config_file]
75
+ Puma::Configuration.new(@options).load
76
+ end
77
+ end
70
78
 
71
79
  # check present of command
72
80
  unless @options[:command]
@@ -96,6 +96,15 @@ module Puma
96
96
  @stderr.puts "#{Time.now}: ENV: #{env.inspect}\n---\n"
97
97
  end
98
98
 
99
+ # An SSL error has occured.
100
+ # +server+ is the Server object, +peeraddr+ peer address, +peercert+
101
+ # any peer certificate (if present), and +error+ an exception object.
102
+ #
103
+ def ssl_error(server, peeraddr, peercert, error)
104
+ subject = peercert ? peercert.subject : nil
105
+ @stderr.puts "#{Time.now}: SSL error, peer: #{peeraddr}, peer cert: #{subject}, #{error.inspect}"
106
+ end
107
+
99
108
  # An unknown error has occured.
100
109
  # +server+ is the Server object, +env+ the request, +error+ an exception
101
110
  # object, and +kind+ some additional info.
@@ -4,6 +4,7 @@ module Puma
4
4
  def initialize(socket, engine)
5
5
  @socket = socket
6
6
  @engine = engine
7
+ @peercert = nil
7
8
  end
8
9
 
9
10
  def to_io
@@ -86,6 +87,21 @@ module Puma
86
87
  def peeraddr
87
88
  @socket.peeraddr
88
89
  end
90
+
91
+ def peercert
92
+ return @peercert if @peercert
93
+
94
+ raw = @engine.peercert
95
+ return nil unless raw
96
+
97
+ @peercert = OpenSSL::X509::Certificate.new raw
98
+ end
99
+ end
100
+
101
+ if defined?(JRUBY_VERSION)
102
+ class SSLError < StandardError
103
+ # Define this for jruby even though it isn't used.
104
+ end
89
105
  end
90
106
 
91
107
  class Context
@@ -104,6 +120,7 @@ module Puma
104
120
  # non-jruby Context properties
105
121
  attr_reader :key
106
122
  attr_reader :cert
123
+ attr_reader :ca
107
124
 
108
125
  def key=(key)
109
126
  raise ArgumentError, "No such key file '#{key}'" unless File.exist? key
@@ -114,11 +131,17 @@ module Puma
114
131
  raise ArgumentError, "No such cert file '#{cert}'" unless File.exist? cert
115
132
  @cert = cert
116
133
  end
134
+
135
+ def ca=(ca)
136
+ raise ArgumentError, "No such ca file '#{ca}'" unless File.exist? ca
137
+ @ca = ca
138
+ end
117
139
  end
118
140
  end
119
141
 
120
142
  VERIFY_NONE = 0
121
143
  VERIFY_PEER = 1
144
+ VERIFY_FAIL_IF_NO_PEER_CERT = 2
122
145
 
123
146
  class Server
124
147
  def initialize(socket, ctx)