puma 2.0.0.b5 → 5.0.0.beta1

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.

Files changed (106) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +1598 -0
  3. data/LICENSE +23 -20
  4. data/README.md +222 -62
  5. data/bin/puma-wild +31 -0
  6. data/bin/pumactl +1 -1
  7. data/docs/architecture.md +37 -0
  8. data/docs/deployment.md +113 -0
  9. data/docs/fork_worker.md +31 -0
  10. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  11. data/docs/images/puma-connection-flow.png +0 -0
  12. data/docs/images/puma-general-arch.png +0 -0
  13. data/docs/jungle/README.md +13 -0
  14. data/docs/jungle/rc.d/README.md +74 -0
  15. data/docs/jungle/rc.d/puma +61 -0
  16. data/docs/jungle/rc.d/puma.conf +10 -0
  17. data/docs/jungle/upstart/README.md +61 -0
  18. data/docs/jungle/upstart/puma-manager.conf +31 -0
  19. data/docs/jungle/upstart/puma.conf +69 -0
  20. data/docs/nginx.md +5 -10
  21. data/docs/plugins.md +38 -0
  22. data/docs/restart.md +41 -0
  23. data/docs/signals.md +97 -0
  24. data/docs/systemd.md +228 -0
  25. data/ext/puma_http11/PumaHttp11Service.java +2 -2
  26. data/ext/puma_http11/extconf.rb +23 -2
  27. data/ext/puma_http11/http11_parser.c +301 -482
  28. data/ext/puma_http11/http11_parser.h +13 -11
  29. data/ext/puma_http11/http11_parser.java.rl +26 -42
  30. data/ext/puma_http11/http11_parser.rl +22 -21
  31. data/ext/puma_http11/http11_parser_common.rl +5 -5
  32. data/ext/puma_http11/mini_ssl.c +377 -18
  33. data/ext/puma_http11/org/jruby/puma/Http11.java +108 -107
  34. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +137 -170
  35. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +265 -191
  36. data/ext/puma_http11/puma_http11.c +57 -81
  37. data/lib/puma.rb +25 -4
  38. data/lib/puma/accept_nonblock.rb +7 -1
  39. data/lib/puma/app/status.rb +61 -24
  40. data/lib/puma/binder.rb +212 -78
  41. data/lib/puma/cli.rb +149 -644
  42. data/lib/puma/client.rb +316 -65
  43. data/lib/puma/cluster.rb +659 -0
  44. data/lib/puma/commonlogger.rb +108 -0
  45. data/lib/puma/configuration.rb +279 -180
  46. data/lib/puma/const.rb +126 -39
  47. data/lib/puma/control_cli.rb +183 -96
  48. data/lib/puma/detect.rb +20 -1
  49. data/lib/puma/dsl.rb +776 -0
  50. data/lib/puma/events.rb +91 -23
  51. data/lib/puma/io_buffer.rb +9 -5
  52. data/lib/puma/jruby_restart.rb +9 -5
  53. data/lib/puma/launcher.rb +487 -0
  54. data/lib/puma/minissl.rb +239 -93
  55. data/lib/puma/minissl/context_builder.rb +76 -0
  56. data/lib/puma/null_io.rb +22 -12
  57. data/lib/puma/plugin.rb +111 -0
  58. data/lib/puma/plugin/tmp_restart.rb +36 -0
  59. data/lib/puma/rack/builder.rb +297 -0
  60. data/lib/puma/rack/urlmap.rb +93 -0
  61. data/lib/puma/rack_default.rb +9 -0
  62. data/lib/puma/reactor.rb +290 -43
  63. data/lib/puma/runner.rb +163 -0
  64. data/lib/puma/server.rb +493 -126
  65. data/lib/puma/single.rb +66 -0
  66. data/lib/puma/state_file.rb +34 -0
  67. data/lib/puma/thread_pool.rb +228 -47
  68. data/lib/puma/util.rb +115 -0
  69. data/lib/rack/handler/puma.rb +78 -31
  70. data/tools/Dockerfile +16 -0
  71. data/tools/trickletest.rb +44 -0
  72. metadata +60 -155
  73. data/COPYING +0 -55
  74. data/Gemfile +0 -8
  75. data/History.txt +0 -196
  76. data/Manifest.txt +0 -56
  77. data/Rakefile +0 -121
  78. data/TODO +0 -5
  79. data/docs/config.md +0 -0
  80. data/ext/puma_http11/io_buffer.c +0 -154
  81. data/lib/puma/capistrano.rb +0 -26
  82. data/lib/puma/compat.rb +0 -11
  83. data/lib/puma/daemon_ext.rb +0 -20
  84. data/lib/puma/delegation.rb +0 -11
  85. data/lib/puma/java_io_buffer.rb +0 -45
  86. data/lib/puma/rack_patch.rb +0 -25
  87. data/puma.gemspec +0 -45
  88. data/test/test_app_status.rb +0 -88
  89. data/test/test_cli.rb +0 -171
  90. data/test/test_config.rb +0 -16
  91. data/test/test_http10.rb +0 -27
  92. data/test/test_http11.rb +0 -126
  93. data/test/test_integration.rb +0 -150
  94. data/test/test_iobuffer.rb +0 -38
  95. data/test/test_minissl.rb +0 -22
  96. data/test/test_null_io.rb +0 -31
  97. data/test/test_persistent.rb +0 -238
  98. data/test/test_puma_server.rb +0 -128
  99. data/test/test_rack_handler.rb +0 -10
  100. data/test/test_rack_server.rb +0 -141
  101. data/test/test_thread_pool.rb +0 -146
  102. data/test/test_unix_socket.rb +0 -39
  103. data/test/test_ws.rb +0 -89
  104. data/tools/jungle/README.md +0 -54
  105. data/tools/jungle/puma +0 -332
  106. data/tools/jungle/run-puma +0 -3
@@ -1,137 +1,283 @@
1
- module Puma::MiniSSL
2
- class Socket
3
- def initialize(socket, engine)
4
- @socket = socket
5
- @engine = engine
6
- end
1
+ # frozen_string_literal: true
7
2
 
8
- def to_io
9
- @socket
10
- end
3
+ begin
4
+ require 'io/wait'
5
+ rescue LoadError
6
+ end
11
7
 
12
- def readpartial(size)
13
- while true
14
- output = @engine.read
15
- return output if output
8
+ module Puma
9
+ module MiniSSL
10
+ class Socket
11
+ def initialize(socket, engine)
12
+ @socket = socket
13
+ @engine = engine
14
+ @peercert = nil
15
+ end
16
16
 
17
- data = @socket.readpartial(size)
18
- @engine.inject(data)
19
- output = @engine.read
17
+ def to_io
18
+ @socket
19
+ end
20
+
21
+ def closed?
22
+ @socket.closed?
23
+ end
20
24
 
21
- return output if output
25
+ def readpartial(size)
26
+ while true
27
+ output = @engine.read
28
+ return output if output
22
29
 
23
- while neg_data = @engine.extract
24
- @socket.write neg_data
30
+ data = @socket.readpartial(size)
31
+ @engine.inject(data)
32
+ output = @engine.read
33
+
34
+ return output if output
35
+
36
+ while neg_data = @engine.extract
37
+ @socket.write neg_data
38
+ end
25
39
  end
26
40
  end
27
- end
28
41
 
29
- def read_nonblock(size)
30
- while true
42
+ def engine_read_all
31
43
  output = @engine.read
32
- return output if output
44
+ while output and additional_output = @engine.read
45
+ output << additional_output
46
+ end
47
+ output
48
+ end
33
49
 
34
- data = @socket.read_nonblock(size)
50
+ def read_nonblock(size, *_)
51
+ # *_ is to deal with keyword args that were added
52
+ # at some point (and being used in the wild)
53
+ while true
54
+ output = engine_read_all
55
+ return output if output
56
+
57
+ data = @socket.read_nonblock(size, exception: false)
58
+ if data == :wait_readable || data == :wait_writable
59
+ # It would make more sense to let @socket.read_nonblock raise
60
+ # EAGAIN if necessary but it seems like it'll misbehave on Windows.
61
+ # I don't have a Windows machine to debug this so I can't explain
62
+ # exactly whats happening in that OS. Please let me know if you
63
+ # find out!
64
+ #
65
+ # In the meantime, we can emulate the correct behavior by
66
+ # capturing :wait_readable & :wait_writable and raising EAGAIN
67
+ # ourselves.
68
+ raise IO::EAGAINWaitReadable
69
+ elsif data.nil?
70
+ return nil
71
+ end
72
+
73
+ @engine.inject(data)
74
+ output = engine_read_all
75
+
76
+ return output if output
77
+
78
+ while neg_data = @engine.extract
79
+ @socket.write neg_data
80
+ end
81
+ end
82
+ end
35
83
 
36
- @engine.inject(data)
37
- output = @engine.read
84
+ def write(data)
85
+ return 0 if data.empty?
38
86
 
39
- return output if output
87
+ need = data.bytesize
40
88
 
41
- while neg_data = @engine.extract
42
- @socket.write neg_data
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]
43
103
  end
44
104
  end
45
- end
46
105
 
47
- def write(data)
48
- need = data.size
106
+ alias_method :syswrite, :write
107
+ alias_method :<<, :write
108
+
109
+ # This is a temporary fix to deal with websockets code using
110
+ # write_nonblock. The problem with implementing it properly
111
+ # is that it means we'd have to have the ability to rewind
112
+ # an engine because after we write+extract, the socket
113
+ # write_nonblock call might raise an exception and later
114
+ # code would pass the same data in, but the engine would think
115
+ # it had already written the data in. So for the time being
116
+ # (and since write blocking is quite rare), go ahead and actually
117
+ # block in write_nonblock.
118
+ def write_nonblock(data, *_)
119
+ write data
120
+ end
49
121
 
50
- while true
51
- wrote = @engine.write data
52
- enc = @engine.extract
122
+ def flush
123
+ @socket.flush
124
+ end
53
125
 
54
- if enc
55
- @socket.syswrite enc
126
+ def read_and_drop(timeout = 1)
127
+ return :timeout unless IO.select([@socket], nil, nil, timeout)
128
+ case @socket.read_nonblock(1024, exception: false)
129
+ when nil
130
+ :eof
131
+ when :wait_readable
132
+ :eagain
133
+ else
134
+ :drop
56
135
  end
136
+ end
57
137
 
58
- need -= wrote
138
+ def should_drop_bytes?
139
+ @engine.init? || !@engine.shutdown
140
+ end
59
141
 
60
- return data.size if need == 0
142
+ def close
143
+ begin
144
+ # Read any drop any partially initialized sockets and any received bytes during shutdown.
145
+ # Don't let this socket hold this loop forever.
146
+ # If it can't send more packets within 1s, then give up.
147
+ return if [:timeout, :eof].include?(read_and_drop(1)) while should_drop_bytes?
148
+ rescue IOError, SystemCallError
149
+ Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
150
+ # nothing
151
+ ensure
152
+ @socket.close
153
+ end
154
+ end
61
155
 
62
- data = data[need..-1]
156
+ def peeraddr
157
+ @socket.peeraddr
63
158
  end
64
- end
65
159
 
66
- alias_method :syswrite, :write
160
+ def peercert
161
+ return @peercert if @peercert
67
162
 
68
- def flush
69
- @socket.flush
70
- end
163
+ raw = @engine.peercert
164
+ return nil unless raw
71
165
 
72
- def close
73
- @socket.close
166
+ @peercert = OpenSSL::X509::Certificate.new raw
167
+ end
74
168
  end
75
169
 
76
- def peeraddr
77
- @socket.peeraddr
170
+ if defined?(JRUBY_VERSION)
171
+ class SSLError < StandardError
172
+ # Define this for jruby even though it isn't used.
173
+ end
174
+
175
+ def self.check; end
78
176
  end
79
- end
80
177
 
81
- class Context
82
- attr_accessor :verify_mode
178
+ class Context
179
+ attr_accessor :verify_mode
180
+ attr_reader :no_tlsv1, :no_tlsv1_1
83
181
 
84
- attr_reader :key
85
- attr_reader :cert
182
+ def initialize
183
+ @no_tlsv1 = false
184
+ @no_tlsv1_1 = false
185
+ end
86
186
 
87
- def key=(key)
88
- raise ArgumentError, "No such key file '#{key}'" unless File.exist? key
89
- @key = key
90
- end
187
+ if defined?(JRUBY_VERSION)
188
+ # jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair
189
+ attr_reader :keystore
190
+ attr_accessor :keystore_pass
191
+ attr_accessor :ssl_cipher_list
91
192
 
92
- def cert=(cert)
93
- raise ArgumentError, "No such cert file '#{cert}'" unless File.exist? cert
94
- @cert = cert
95
- end
96
- end
193
+ def keystore=(keystore)
194
+ raise ArgumentError, "No such keystore file '#{keystore}'" unless File.exist? keystore
195
+ @keystore = keystore
196
+ end
97
197
 
98
- VERIFY_NONE = 0
99
- VERIFY_PEER = 1
100
-
101
- #if defined?(JRUBY_VERSION)
102
- #class Engine
103
- #def self.server(key, cert)
104
- #new(key, cert)
105
- #end
106
- #end
107
- #end
108
-
109
- class Server
110
- def initialize(socket, ctx)
111
- @socket = socket
112
- @ctx = ctx
113
- end
198
+ def check
199
+ raise "Keystore not configured" unless @keystore
200
+ end
114
201
 
115
- def to_io
116
- @socket
117
- end
202
+ else
203
+ # non-jruby Context properties
204
+ attr_reader :key
205
+ attr_reader :cert
206
+ attr_reader :ca
207
+ attr_accessor :ssl_cipher_filter
118
208
 
119
- def accept
120
- io = @socket.accept
121
- engine = Engine.server @ctx.key, @ctx.cert
209
+ def key=(key)
210
+ raise ArgumentError, "No such key file '#{key}'" unless File.exist? key
211
+ @key = key
212
+ end
122
213
 
123
- Socket.new io, engine
124
- end
214
+ def cert=(cert)
215
+ raise ArgumentError, "No such cert file '#{cert}'" unless File.exist? cert
216
+ @cert = cert
217
+ end
218
+
219
+ def ca=(ca)
220
+ raise ArgumentError, "No such ca file '#{ca}'" unless File.exist? ca
221
+ @ca = ca
222
+ end
125
223
 
126
- def accept_nonblock
127
- io = @socket.accept_nonblock
128
- engine = Engine.server @ctx.key, @ctx.cert
224
+ def check
225
+ raise "Key not configured" unless @key
226
+ raise "Cert not configured" unless @cert
227
+ end
228
+ end
229
+
230
+ # disables TLSv1
231
+ def no_tlsv1=(tlsv1)
232
+ raise ArgumentError, "Invalid value of no_tlsv1" unless ['true', 'false', true, false].include?(tlsv1)
233
+ @no_tlsv1 = tlsv1
234
+ end
235
+
236
+ # disables TLSv1 and TLSv1.1. Overrides `#no_tlsv1=`
237
+ def no_tlsv1_1=(tlsv1_1)
238
+ raise ArgumentError, "Invalid value of no_tlsv1" unless ['true', 'false', true, false].include?(tlsv1_1)
239
+ @no_tlsv1_1 = tlsv1_1
240
+ end
129
241
 
130
- Socket.new io, engine
131
242
  end
132
243
 
133
- def close
134
- @socket.close
244
+ VERIFY_NONE = 0
245
+ VERIFY_PEER = 1
246
+ VERIFY_FAIL_IF_NO_PEER_CERT = 2
247
+
248
+ class Server
249
+ def initialize(socket, ctx)
250
+ @socket = socket
251
+ @ctx = ctx
252
+ end
253
+
254
+ def to_io
255
+ @socket
256
+ end
257
+
258
+ def accept
259
+ @ctx.check
260
+ io = @socket.accept
261
+ engine = Engine.server @ctx
262
+
263
+ Socket.new io, engine
264
+ end
265
+
266
+ def accept_nonblock
267
+ @ctx.check
268
+ io = @socket.accept_nonblock
269
+ engine = Engine.server @ctx
270
+
271
+ Socket.new io, engine
272
+ end
273
+
274
+ def addr
275
+ @socket.addr
276
+ end
277
+
278
+ def close
279
+ @socket.close unless @socket.closed? # closed? call is for Windows
280
+ end
135
281
  end
136
282
  end
137
283
  end
@@ -0,0 +1,76 @@
1
+ module Puma
2
+ module MiniSSL
3
+ class ContextBuilder
4
+ def initialize(params, events)
5
+ require 'puma/minissl'
6
+ MiniSSL.check
7
+
8
+ @params = params
9
+ @events = events
10
+ end
11
+
12
+ def context
13
+ ctx = MiniSSL::Context.new
14
+
15
+ if defined?(JRUBY_VERSION)
16
+ unless params['keystore']
17
+ events.error "Please specify the Java keystore via 'keystore='"
18
+ end
19
+
20
+ ctx.keystore = params['keystore']
21
+
22
+ unless params['keystore-pass']
23
+ events.error "Please specify the Java keystore password via 'keystore-pass='"
24
+ end
25
+
26
+ ctx.keystore_pass = params['keystore-pass']
27
+ ctx.ssl_cipher_list = params['ssl_cipher_list'] if params['ssl_cipher_list']
28
+ else
29
+ unless params['key']
30
+ events.error "Please specify the SSL key via 'key='"
31
+ end
32
+
33
+ ctx.key = params['key']
34
+
35
+ unless params['cert']
36
+ events.error "Please specify the SSL cert via 'cert='"
37
+ end
38
+
39
+ ctx.cert = params['cert']
40
+
41
+ if ['peer', 'force_peer'].include?(params['verify_mode'])
42
+ unless params['ca']
43
+ events.error "Please specify the SSL ca via 'ca='"
44
+ end
45
+ end
46
+
47
+ ctx.ca = params['ca'] if params['ca']
48
+ ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter']
49
+ end
50
+
51
+ ctx.no_tlsv1 = true if params['no_tlsv1'] == 'true'
52
+ ctx.no_tlsv1_1 = true if params['no_tlsv1_1'] == 'true'
53
+
54
+ if params['verify_mode']
55
+ ctx.verify_mode = case params['verify_mode']
56
+ when "peer"
57
+ MiniSSL::VERIFY_PEER
58
+ when "force_peer"
59
+ MiniSSL::VERIFY_PEER | MiniSSL::VERIFY_FAIL_IF_NO_PEER_CERT
60
+ when "none"
61
+ MiniSSL::VERIFY_NONE
62
+ else
63
+ events.error "Please specify a valid verify_mode="
64
+ MiniSSL::VERIFY_NONE
65
+ end
66
+ end
67
+
68
+ ctx
69
+ end
70
+
71
+ private
72
+
73
+ attr_reader :params, :events
74
+ end
75
+ end
76
+ end