wendell-puma 2.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +55 -0
  3. data/DEPLOYMENT.md +92 -0
  4. data/Gemfile +17 -0
  5. data/History.txt +588 -0
  6. data/LICENSE +26 -0
  7. data/Manifest.txt +68 -0
  8. data/README.md +251 -0
  9. data/Rakefile +158 -0
  10. data/bin/puma +10 -0
  11. data/bin/puma-wild +31 -0
  12. data/bin/pumactl +12 -0
  13. data/docs/config.md +0 -0
  14. data/docs/nginx.md +80 -0
  15. data/docs/signals.md +43 -0
  16. data/ext/puma_http11/PumaHttp11Service.java +17 -0
  17. data/ext/puma_http11/ext_help.h +15 -0
  18. data/ext/puma_http11/extconf.rb +9 -0
  19. data/ext/puma_http11/http11_parser.c +1225 -0
  20. data/ext/puma_http11/http11_parser.h +64 -0
  21. data/ext/puma_http11/http11_parser.java.rl +161 -0
  22. data/ext/puma_http11/http11_parser.rl +146 -0
  23. data/ext/puma_http11/http11_parser_common.rl +54 -0
  24. data/ext/puma_http11/io_buffer.c +155 -0
  25. data/ext/puma_http11/mini_ssl.c +198 -0
  26. data/ext/puma_http11/org/jruby/puma/Http11.java +225 -0
  27. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +488 -0
  28. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +391 -0
  29. data/ext/puma_http11/puma_http11.c +491 -0
  30. data/lib/puma.rb +14 -0
  31. data/lib/puma/accept_nonblock.rb +23 -0
  32. data/lib/puma/app/status.rb +59 -0
  33. data/lib/puma/binder.rb +298 -0
  34. data/lib/puma/capistrano.rb +86 -0
  35. data/lib/puma/cli.rb +606 -0
  36. data/lib/puma/client.rb +289 -0
  37. data/lib/puma/cluster.rb +404 -0
  38. data/lib/puma/compat.rb +18 -0
  39. data/lib/puma/configuration.rb +377 -0
  40. data/lib/puma/const.rb +165 -0
  41. data/lib/puma/control_cli.rb +251 -0
  42. data/lib/puma/daemon_ext.rb +25 -0
  43. data/lib/puma/delegation.rb +11 -0
  44. data/lib/puma/detect.rb +4 -0
  45. data/lib/puma/events.rb +130 -0
  46. data/lib/puma/io_buffer.rb +7 -0
  47. data/lib/puma/java_io_buffer.rb +45 -0
  48. data/lib/puma/jruby_restart.rb +83 -0
  49. data/lib/puma/minissl.rb +187 -0
  50. data/lib/puma/null_io.rb +34 -0
  51. data/lib/puma/rack_default.rb +7 -0
  52. data/lib/puma/rack_patch.rb +45 -0
  53. data/lib/puma/reactor.rb +183 -0
  54. data/lib/puma/runner.rb +146 -0
  55. data/lib/puma/server.rb +801 -0
  56. data/lib/puma/single.rb +102 -0
  57. data/lib/puma/tcp_logger.rb +32 -0
  58. data/lib/puma/thread_pool.rb +185 -0
  59. data/lib/puma/util.rb +9 -0
  60. data/lib/rack/handler/puma.rb +66 -0
  61. data/test/test_app_status.rb +92 -0
  62. data/test/test_cli.rb +173 -0
  63. data/test/test_config.rb +26 -0
  64. data/test/test_http10.rb +27 -0
  65. data/test/test_http11.rb +144 -0
  66. data/test/test_integration.rb +165 -0
  67. data/test/test_iobuffer.rb +38 -0
  68. data/test/test_minissl.rb +29 -0
  69. data/test/test_null_io.rb +31 -0
  70. data/test/test_persistent.rb +238 -0
  71. data/test/test_puma_server.rb +288 -0
  72. data/test/test_puma_server_ssl.rb +137 -0
  73. data/test/test_rack_handler.rb +10 -0
  74. data/test/test_rack_server.rb +141 -0
  75. data/test/test_tcp_rack.rb +42 -0
  76. data/test/test_thread_pool.rb +156 -0
  77. data/test/test_unix_socket.rb +39 -0
  78. data/test/test_ws.rb +89 -0
  79. data/tools/jungle/README.md +9 -0
  80. data/tools/jungle/init.d/README.md +54 -0
  81. data/tools/jungle/init.d/puma +332 -0
  82. data/tools/jungle/init.d/run-puma +3 -0
  83. data/tools/jungle/upstart/README.md +61 -0
  84. data/tools/jungle/upstart/puma-manager.conf +31 -0
  85. data/tools/jungle/upstart/puma.conf +63 -0
  86. data/tools/trickletest.rb +45 -0
  87. data/wendell-puma.gemspec +55 -0
  88. metadata +225 -0
@@ -0,0 +1,83 @@
1
+ require 'ffi'
2
+
3
+ module Puma
4
+ module JRubyRestart
5
+ extend FFI::Library
6
+ ffi_lib 'c'
7
+
8
+ attach_function :execlp, [:string, :varargs], :int
9
+ attach_function :chdir, [:string], :int
10
+ attach_function :fork, [], :int
11
+ attach_function :exit, [:int], :void
12
+ attach_function :setsid, [], :int
13
+
14
+ def self.chdir_exec(dir, argv)
15
+ chdir(dir)
16
+ cmd = argv.first
17
+ argv = ([:string] * argv.size).zip(argv).flatten
18
+ argv << :string
19
+ argv << nil
20
+ execlp(cmd, *argv)
21
+ raise SystemCallError.new(FFI.errno)
22
+ end
23
+
24
+ PermKey = 'PUMA_DAEMON_PERM'
25
+ RestartKey = 'PUMA_DAEMON_RESTART'
26
+
27
+ # Called to tell things "Your now always in daemon mode,
28
+ # don't try to reenter it."
29
+ #
30
+ def self.perm_daemonize
31
+ ENV[PermKey] = "1"
32
+ end
33
+
34
+ def self.daemon?
35
+ ENV.key?(PermKey) || ENV.key?(RestartKey)
36
+ end
37
+
38
+ def self.daemon_init
39
+ return true if ENV.key?(PermKey)
40
+
41
+ return false unless ENV.key? RestartKey
42
+
43
+ master = ENV[RestartKey]
44
+
45
+ # In case the master disappears early
46
+ begin
47
+ Process.kill "SIGUSR2", master.to_i
48
+ rescue SystemCallError => e
49
+ end
50
+
51
+ ENV[RestartKey] = ""
52
+
53
+ setsid
54
+
55
+ null = File.open "/dev/null", "w+"
56
+ STDIN.reopen null
57
+ STDOUT.reopen null
58
+ STDERR.reopen null
59
+
60
+ true
61
+ end
62
+
63
+ def self.daemon_start(dir, argv)
64
+ ENV['PUMA_DAEMON_RESTART'] = Process.pid.to_s
65
+
66
+ if k = ENV['PUMA_JRUBY_DAEMON_OPTS']
67
+ ENV['JRUBY_OPTS'] = k
68
+ end
69
+
70
+ cmd = argv.first
71
+ argv = ([:string] * argv.size).zip(argv).flatten
72
+ argv << :string
73
+ argv << nil
74
+
75
+ chdir(dir)
76
+ ret = fork
77
+ return ret if ret != 0
78
+ execlp(cmd, *argv)
79
+ raise SystemCallError.new(FFI.errno)
80
+ end
81
+ end
82
+ end
83
+
@@ -0,0 +1,187 @@
1
+ if Puma::IS_JRUBY
2
+ require 'java'
3
+ java_import 'java.lang.RuntimeException'
4
+ require 'puma/delegation'
5
+ end
6
+
7
+
8
+ module Puma
9
+ module MiniSSL
10
+ class EngineWrapper
11
+ extend Puma::Delegation
12
+
13
+ %w(inject extract write).each do |action|
14
+ forward action, :@engine
15
+ end
16
+
17
+ def initialize(engine)
18
+ @engine=engine
19
+ end
20
+
21
+ if Puma::IS_JRUBY
22
+ def read
23
+ begin
24
+ @engine.read
25
+ rescue RuntimeException=>e
26
+ raise IOError.new("Unable to read from engine, #{e.message}")
27
+ end
28
+ end
29
+ else
30
+ forward :read, :@engine
31
+ end
32
+ end
33
+ class Socket
34
+ def initialize(socket, engine)
35
+ @socket = socket
36
+ @engine = EngineWrapper.new(engine)
37
+ end
38
+
39
+ def to_io
40
+ @socket
41
+ end
42
+
43
+ def readpartial(size)
44
+ while true
45
+ output = @engine.read
46
+ return output if output
47
+
48
+ data = @socket.readpartial(size)
49
+ @engine.inject(data)
50
+ output = @engine.read
51
+
52
+ return output if output
53
+
54
+ while neg_data = @engine.extract
55
+ @socket.write neg_data
56
+ end
57
+ end
58
+ end
59
+
60
+ def engine_read_all
61
+ output = @engine.read
62
+ while output and additional_output = @engine.read
63
+ output << additional_output
64
+ end
65
+ output
66
+ end
67
+
68
+ def read_nonblock(size)
69
+ while true
70
+ output = engine_read_all
71
+ return output if output
72
+
73
+ data = @socket.read_nonblock(size)
74
+
75
+ @engine.inject(data)
76
+ output = engine_read_all
77
+
78
+ return output if output
79
+
80
+ while neg_data = @engine.extract
81
+ @socket.write neg_data
82
+ end
83
+ end
84
+ end
85
+
86
+ def write(data)
87
+ need = data.bytesize
88
+
89
+ while true
90
+ wrote = @engine.write data
91
+ enc = @engine.extract
92
+
93
+ while enc
94
+ @socket.write enc
95
+ enc = @engine.extract
96
+ end
97
+
98
+ need -= wrote
99
+
100
+ return data.bytesize if need == 0
101
+
102
+ data = data[wrote..-1]
103
+ end
104
+ end
105
+
106
+ alias_method :syswrite, :write
107
+
108
+ def flush
109
+ @socket.flush
110
+ end
111
+
112
+ def close
113
+ @socket.close
114
+ end
115
+
116
+ def peeraddr
117
+ @socket.peeraddr
118
+ end
119
+ end
120
+
121
+ class Context
122
+ attr_accessor :verify_mode
123
+
124
+ if defined?(JRUBY_VERSION)
125
+ # jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair
126
+ attr_reader :keystore
127
+ attr_accessor :keystore_pass
128
+ attr_accessor :enable_SSLv3
129
+
130
+ def initialize
131
+ @enable_SSLv3 = false
132
+ end
133
+
134
+ def keystore=(keystore)
135
+ raise ArgumentError, "No such keystore file '#{keystore}'" unless File.exist? keystore
136
+ @keystore = keystore
137
+ end
138
+ else
139
+ # non-jruby Context properties
140
+ attr_reader :key
141
+ attr_reader :cert
142
+
143
+ def key=(key)
144
+ raise ArgumentError, "No such key file '#{key}'" unless File.exist? key
145
+ @key = key
146
+ end
147
+
148
+ def cert=(cert)
149
+ raise ArgumentError, "No such cert file '#{cert}'" unless File.exist? cert
150
+ @cert = cert
151
+ end
152
+ end
153
+ end
154
+
155
+ VERIFY_NONE = 0
156
+ VERIFY_PEER = 1
157
+
158
+ class Server
159
+ def initialize(socket, ctx)
160
+ @socket = socket
161
+ @ctx = ctx
162
+ end
163
+
164
+ def to_io
165
+ @socket
166
+ end
167
+
168
+ def accept
169
+ io = @socket.accept
170
+ engine = Engine.server @ctx
171
+
172
+ Socket.new io, engine
173
+ end
174
+
175
+ def accept_nonblock
176
+ io = @socket.accept_nonblock
177
+ engine = Engine.server @ctx
178
+
179
+ Socket.new io, engine
180
+ end
181
+
182
+ def close
183
+ @socket.close
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,34 @@
1
+ module Puma
2
+
3
+ # Provides an IO-like object that always appears to contain no data.
4
+ # Used as the value for rack.input when the request has no body.
5
+ #
6
+ class NullIO
7
+ # Always returns nil
8
+ #
9
+ def gets
10
+ nil
11
+ end
12
+
13
+ # Never yields
14
+ #
15
+ def each
16
+ end
17
+
18
+ # Mimics IO#read with no data
19
+ #
20
+ def read(count=nil,buffer=nil)
21
+ (count && count > 0) ? nil : ""
22
+ end
23
+
24
+ # Does nothing
25
+ #
26
+ def rewind
27
+ end
28
+
29
+ # Does nothing
30
+ #
31
+ def close
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,7 @@
1
+ require 'rack/handler/puma'
2
+
3
+ module Rack::Handler
4
+ def self.default(options = {})
5
+ Rack::Handler::Puma
6
+ end
7
+ end
@@ -0,0 +1,45 @@
1
+ require 'rack/commonlogger'
2
+
3
+ module Rack
4
+ # Patch CommonLogger to use after_reply.
5
+ #
6
+ # Simply request this file and CommonLogger will be a bit more
7
+ # efficient.
8
+ class CommonLogger
9
+ remove_method :call
10
+
11
+ def call(env)
12
+ began_at = Time.now
13
+ status, header, body = @app.call(env)
14
+ header = Utils::HeaderHash.new(header)
15
+
16
+ # If we've been hijacked, then output a special line
17
+ if env['rack.hijack_io']
18
+ log_hijacking(env, 'HIJACK', header, began_at)
19
+ elsif ary = env['rack.after_reply']
20
+ ary << lambda { log(env, status, header, began_at) }
21
+ else
22
+ body = BodyProxy.new(body) { log(env, status, header, began_at) }
23
+ end
24
+
25
+ [status, header, body]
26
+ end
27
+
28
+ HIJACK_FORMAT = %{%s - %s [%s] "%s %s%s %s" HIJACKED -1 %0.4f\n}
29
+
30
+ def log_hijacking(env, status, header, began_at)
31
+ now = Time.now
32
+
33
+ logger = @logger || env['rack.errors']
34
+ logger.write HIJACK_FORMAT % [
35
+ env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
36
+ env["REMOTE_USER"] || "-",
37
+ now.strftime("%d/%b/%Y %H:%M:%S"),
38
+ env["REQUEST_METHOD"],
39
+ env["PATH_INFO"],
40
+ env["QUERY_STRING"].empty? ? "" : "?"+env["QUERY_STRING"],
41
+ env["HTTP_VERSION"],
42
+ now - began_at ]
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,183 @@
1
+ require 'puma/util'
2
+
3
+ module Puma
4
+ class Reactor
5
+ DefaultSleepFor = 5
6
+
7
+ def initialize(server, app_pool)
8
+ @server = server
9
+ @events = server.events
10
+ @app_pool = app_pool
11
+
12
+ @mutex = Mutex.new
13
+ @ready, @trigger = Puma::Util.pipe
14
+ @input = []
15
+ @sleep_for = DefaultSleepFor
16
+ @timeouts = []
17
+
18
+ @sockets = [@ready]
19
+ end
20
+
21
+ private
22
+
23
+ def run_internal
24
+ sockets = @sockets
25
+
26
+ while true
27
+ begin
28
+ ready = IO.select sockets, nil, nil, @sleep_for
29
+ rescue IOError => e
30
+ if sockets.any? { |socket| socket.closed? }
31
+ STDERR.puts "Error in select: #{e.message} (#{e.class})"
32
+ STDERR.puts e.backtrace
33
+ sockets = sockets.reject { |socket| socket.closed? }
34
+ retry
35
+ else
36
+ raise
37
+ end
38
+ end
39
+
40
+ if ready and reads = ready[0]
41
+ reads.each do |c|
42
+ if c == @ready
43
+ @mutex.synchronize do
44
+ case @ready.read(1)
45
+ when "*"
46
+ sockets += @input
47
+ @input.clear
48
+ when "c"
49
+ sockets.delete_if do |s|
50
+ if s == @ready
51
+ false
52
+ else
53
+ s.close
54
+ true
55
+ end
56
+ end
57
+ when "!"
58
+ return
59
+ end
60
+ end
61
+ else
62
+ # We have to be sure to remove it from the timeout
63
+ # list or we'll accidentally close the socket when
64
+ # it's in use!
65
+ if c.timeout_at
66
+ @mutex.synchronize do
67
+ @timeouts.delete c
68
+ end
69
+ end
70
+
71
+ begin
72
+ if c.try_to_finish
73
+ @app_pool << c
74
+ sockets.delete c
75
+ end
76
+
77
+ # The client doesn't know HTTP well
78
+ rescue HttpParserError => e
79
+ c.write_400
80
+ c.close
81
+
82
+ sockets.delete c
83
+
84
+ @events.parse_error @server, c.env, e
85
+ rescue StandardError => e
86
+ c.write_500
87
+ c.close
88
+
89
+ sockets.delete c
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ unless @timeouts.empty?
96
+ @mutex.synchronize do
97
+ now = Time.now
98
+
99
+ while @timeouts.first.timeout_at < now
100
+ c = @timeouts.shift
101
+ c.write_408 if c.in_data_phase
102
+ c.close
103
+ sockets.delete c
104
+
105
+ break if @timeouts.empty?
106
+ end
107
+
108
+ calculate_sleep
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+ public
115
+
116
+ def run
117
+ run_internal
118
+ ensure
119
+ @trigger.close
120
+ @ready.close
121
+ end
122
+
123
+ def run_in_thread
124
+ @thread = Thread.new do
125
+ begin
126
+ run_internal
127
+ rescue StandardError => e
128
+ STDERR.puts "Error in reactor loop escaped: #{e.message} (#{e.class})"
129
+ STDERR.puts e.backtrace
130
+ retry
131
+ ensure
132
+ @trigger.close
133
+ @ready.close
134
+ end
135
+ end
136
+ end
137
+
138
+ def calculate_sleep
139
+ if @timeouts.empty?
140
+ @sleep_for = DefaultSleepFor
141
+ else
142
+ diff = @timeouts.first.timeout_at.to_f - Time.now.to_f
143
+
144
+ if diff < 0.0
145
+ @sleep_for = 0
146
+ else
147
+ @sleep_for = diff
148
+ end
149
+ end
150
+ end
151
+
152
+ def add(c)
153
+ @mutex.synchronize do
154
+ @input << c
155
+ @trigger << "*"
156
+
157
+ if c.timeout_at
158
+ @timeouts << c
159
+ @timeouts.sort! { |a,b| a.timeout_at <=> b.timeout_at }
160
+
161
+ calculate_sleep
162
+ end
163
+ end
164
+ end
165
+
166
+ # Close all watched sockets and clear them from being watched
167
+ def clear!
168
+ begin
169
+ @trigger << "c"
170
+ rescue IOError
171
+ end
172
+ end
173
+
174
+ def shutdown
175
+ begin
176
+ @trigger << "!"
177
+ rescue IOError
178
+ end
179
+
180
+ @thread.join
181
+ end
182
+ end
183
+ end