gitlab-puma 4.3.1.gitlab.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +1537 -0
  3. data/LICENSE +26 -0
  4. data/README.md +291 -0
  5. data/bin/puma +10 -0
  6. data/bin/puma-wild +31 -0
  7. data/bin/pumactl +12 -0
  8. data/docs/architecture.md +37 -0
  9. data/docs/deployment.md +111 -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/nginx.md +80 -0
  14. data/docs/plugins.md +38 -0
  15. data/docs/restart.md +41 -0
  16. data/docs/signals.md +96 -0
  17. data/docs/systemd.md +290 -0
  18. data/docs/tcp_mode.md +96 -0
  19. data/ext/puma_http11/PumaHttp11Service.java +19 -0
  20. data/ext/puma_http11/ext_help.h +15 -0
  21. data/ext/puma_http11/extconf.rb +28 -0
  22. data/ext/puma_http11/http11_parser.c +1044 -0
  23. data/ext/puma_http11/http11_parser.h +65 -0
  24. data/ext/puma_http11/http11_parser.java.rl +145 -0
  25. data/ext/puma_http11/http11_parser.rl +147 -0
  26. data/ext/puma_http11/http11_parser_common.rl +54 -0
  27. data/ext/puma_http11/io_buffer.c +155 -0
  28. data/ext/puma_http11/mini_ssl.c +553 -0
  29. data/ext/puma_http11/org/jruby/puma/Http11.java +226 -0
  30. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +455 -0
  31. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
  32. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +363 -0
  33. data/ext/puma_http11/puma_http11.c +502 -0
  34. data/lib/puma.rb +31 -0
  35. data/lib/puma/accept_nonblock.rb +29 -0
  36. data/lib/puma/app/status.rb +80 -0
  37. data/lib/puma/binder.rb +385 -0
  38. data/lib/puma/cli.rb +239 -0
  39. data/lib/puma/client.rb +494 -0
  40. data/lib/puma/cluster.rb +554 -0
  41. data/lib/puma/commonlogger.rb +108 -0
  42. data/lib/puma/configuration.rb +362 -0
  43. data/lib/puma/const.rb +242 -0
  44. data/lib/puma/control_cli.rb +289 -0
  45. data/lib/puma/detect.rb +15 -0
  46. data/lib/puma/dsl.rb +740 -0
  47. data/lib/puma/events.rb +156 -0
  48. data/lib/puma/io_buffer.rb +4 -0
  49. data/lib/puma/jruby_restart.rb +84 -0
  50. data/lib/puma/launcher.rb +475 -0
  51. data/lib/puma/minissl.rb +278 -0
  52. data/lib/puma/minissl/context_builder.rb +76 -0
  53. data/lib/puma/null_io.rb +44 -0
  54. data/lib/puma/plugin.rb +120 -0
  55. data/lib/puma/plugin/tmp_restart.rb +36 -0
  56. data/lib/puma/rack/builder.rb +301 -0
  57. data/lib/puma/rack/urlmap.rb +93 -0
  58. data/lib/puma/rack_default.rb +9 -0
  59. data/lib/puma/reactor.rb +400 -0
  60. data/lib/puma/runner.rb +192 -0
  61. data/lib/puma/server.rb +1053 -0
  62. data/lib/puma/single.rb +123 -0
  63. data/lib/puma/state_file.rb +31 -0
  64. data/lib/puma/tcp_logger.rb +41 -0
  65. data/lib/puma/thread_pool.rb +348 -0
  66. data/lib/puma/util.rb +124 -0
  67. data/lib/rack/handler/puma.rb +115 -0
  68. data/tools/docker/Dockerfile +16 -0
  69. data/tools/jungle/README.md +19 -0
  70. data/tools/jungle/init.d/README.md +61 -0
  71. data/tools/jungle/init.d/puma +421 -0
  72. data/tools/jungle/init.d/run-puma +18 -0
  73. data/tools/jungle/rc.d/README.md +74 -0
  74. data/tools/jungle/rc.d/puma +61 -0
  75. data/tools/jungle/rc.d/puma.conf +10 -0
  76. data/tools/jungle/upstart/README.md +61 -0
  77. data/tools/jungle/upstart/puma-manager.conf +31 -0
  78. data/tools/jungle/upstart/puma.conf +69 -0
  79. data/tools/trickletest.rb +44 -0
  80. metadata +147 -0
@@ -0,0 +1,278 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'io/wait'
5
+ rescue LoadError
6
+ end
7
+
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
+
17
+ def to_io
18
+ @socket
19
+ end
20
+
21
+ def closed?
22
+ @socket.closed?
23
+ end
24
+
25
+ def readpartial(size)
26
+ while true
27
+ output = @engine.read
28
+ return output if output
29
+
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
39
+ end
40
+ end
41
+
42
+ def engine_read_all
43
+ output = @engine.read
44
+ while output and additional_output = @engine.read
45
+ output << additional_output
46
+ end
47
+ output
48
+ end
49
+
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
83
+
84
+ def write(data)
85
+ return 0 if data.empty?
86
+
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
+ 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
121
+
122
+ def flush
123
+ @socket.flush
124
+ end
125
+
126
+ def read_and_drop(timeout = 1)
127
+ return :timeout unless IO.select([@socket], nil, nil, timeout)
128
+ return :eof unless read_nonblock(1024)
129
+ :drop
130
+ rescue Errno::EAGAIN
131
+ # do nothing
132
+ :eagain
133
+ end
134
+
135
+ def should_drop_bytes?
136
+ @engine.init? || !@engine.shutdown
137
+ end
138
+
139
+ def close
140
+ begin
141
+ # Read any drop any partially initialized sockets and any received bytes during shutdown.
142
+ # Don't let this socket hold this loop forever.
143
+ # If it can't send more packets within 1s, then give up.
144
+ while should_drop_bytes?
145
+ return if [:timeout, :eof].include?(read_and_drop(1))
146
+ end
147
+ rescue IOError, SystemCallError
148
+ Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
149
+ # nothing
150
+ ensure
151
+ @socket.close
152
+ end
153
+ end
154
+
155
+ def peeraddr
156
+ @socket.peeraddr
157
+ end
158
+
159
+ def peercert
160
+ return @peercert if @peercert
161
+
162
+ raw = @engine.peercert
163
+ return nil unless raw
164
+
165
+ @peercert = OpenSSL::X509::Certificate.new raw
166
+ end
167
+ end
168
+
169
+ if defined?(JRUBY_VERSION)
170
+ class SSLError < StandardError
171
+ # Define this for jruby even though it isn't used.
172
+ end
173
+
174
+ def self.check; end
175
+ end
176
+
177
+ class Context
178
+ attr_accessor :verify_mode
179
+ attr_reader :no_tlsv1, :no_tlsv1_1
180
+
181
+ def initialize
182
+ @no_tlsv1 = false
183
+ @no_tlsv1_1 = false
184
+ end
185
+
186
+ if defined?(JRUBY_VERSION)
187
+ # jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair
188
+ attr_reader :keystore
189
+ attr_accessor :keystore_pass
190
+ attr_accessor :ssl_cipher_list
191
+
192
+ def keystore=(keystore)
193
+ raise ArgumentError, "No such keystore file '#{keystore}'" unless File.exist? keystore
194
+ @keystore = keystore
195
+ end
196
+
197
+ def check
198
+ raise "Keystore not configured" unless @keystore
199
+ end
200
+
201
+ else
202
+ # non-jruby Context properties
203
+ attr_reader :key
204
+ attr_reader :cert
205
+ attr_reader :ca
206
+ attr_accessor :ssl_cipher_filter
207
+
208
+ def key=(key)
209
+ raise ArgumentError, "No such key file '#{key}'" unless File.exist? key
210
+ @key = key
211
+ end
212
+
213
+ def cert=(cert)
214
+ raise ArgumentError, "No such cert file '#{cert}'" unless File.exist? cert
215
+ @cert = cert
216
+ end
217
+
218
+ def ca=(ca)
219
+ raise ArgumentError, "No such ca file '#{ca}'" unless File.exist? ca
220
+ @ca = ca
221
+ end
222
+
223
+ def check
224
+ raise "Key not configured" unless @key
225
+ raise "Cert not configured" unless @cert
226
+ end
227
+ end
228
+
229
+ # disables TLSv1
230
+ def no_tlsv1=(tlsv1)
231
+ raise ArgumentError, "Invalid value of no_tlsv1" unless ['true', 'false', true, false].include?(tlsv1)
232
+ @no_tlsv1 = tlsv1
233
+ end
234
+
235
+ # disables TLSv1 and TLSv1.1. Overrides `#no_tlsv1=`
236
+ def no_tlsv1_1=(tlsv1_1)
237
+ raise ArgumentError, "Invalid value of no_tlsv1" unless ['true', 'false', true, false].include?(tlsv1_1)
238
+ @no_tlsv1_1 = tlsv1_1
239
+ end
240
+
241
+ end
242
+
243
+ VERIFY_NONE = 0
244
+ VERIFY_PEER = 1
245
+ VERIFY_FAIL_IF_NO_PEER_CERT = 2
246
+
247
+ class Server
248
+ def initialize(socket, ctx)
249
+ @socket = socket
250
+ @ctx = ctx
251
+ end
252
+
253
+ def to_io
254
+ @socket
255
+ end
256
+
257
+ def accept
258
+ @ctx.check
259
+ io = @socket.accept
260
+ engine = Engine.server @ctx
261
+
262
+ Socket.new io, engine
263
+ end
264
+
265
+ def accept_nonblock
266
+ @ctx.check
267
+ io = @socket.accept_nonblock
268
+ engine = Engine.server @ctx
269
+
270
+ Socket.new io, engine
271
+ end
272
+
273
+ def close
274
+ @socket.close unless @socket.closed? # closed? call is for Windows
275
+ end
276
+ end
277
+ end
278
+ 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
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Puma
4
+ # Provides an IO-like object that always appears to contain no data.
5
+ # Used as the value for rack.input when the request has no body.
6
+ #
7
+ class NullIO
8
+ def gets
9
+ nil
10
+ end
11
+
12
+ def each
13
+ end
14
+
15
+ # Mimics IO#read with no data.
16
+ #
17
+ def read(count = nil, _buffer = nil)
18
+ (count && count > 0) ? nil : ""
19
+ end
20
+
21
+ def rewind
22
+ end
23
+
24
+ def close
25
+ end
26
+
27
+ def size
28
+ 0
29
+ end
30
+
31
+ def eof?
32
+ true
33
+ end
34
+
35
+ def sync=(v)
36
+ end
37
+
38
+ def puts(*ary)
39
+ end
40
+
41
+ def write(*ary)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Puma
4
+ class UnknownPlugin < RuntimeError; end
5
+
6
+ class PluginLoader
7
+ def initialize
8
+ @instances = []
9
+ end
10
+
11
+ def create(name)
12
+ if cls = Plugins.find(name)
13
+ plugin = cls.new(Plugin)
14
+ @instances << plugin
15
+ return plugin
16
+ end
17
+
18
+ raise UnknownPlugin, "File failed to register properly named plugin"
19
+ end
20
+
21
+ def fire_starts(launcher)
22
+ @instances.each do |i|
23
+ if i.respond_to? :start
24
+ i.start(launcher)
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ class PluginRegistry
31
+ def initialize
32
+ @plugins = {}
33
+ @background = []
34
+ end
35
+
36
+ def register(name, cls)
37
+ @plugins[name] = cls
38
+ end
39
+
40
+ def find(name)
41
+ name = name.to_s
42
+
43
+ if cls = @plugins[name]
44
+ return cls
45
+ end
46
+
47
+ begin
48
+ require "puma/plugin/#{name}"
49
+ rescue LoadError
50
+ raise UnknownPlugin, "Unable to find plugin: #{name}"
51
+ end
52
+
53
+ if cls = @plugins[name]
54
+ return cls
55
+ end
56
+
57
+ raise UnknownPlugin, "file failed to register a plugin"
58
+ end
59
+
60
+ def add_background(blk)
61
+ @background << blk
62
+ end
63
+
64
+ def fire_background
65
+ @background.each_with_index do |b, i|
66
+ Thread.new do
67
+ Puma.set_thread_name "plugin background #{i}"
68
+ b.call
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ Plugins = PluginRegistry.new
75
+
76
+ class Plugin
77
+ # Matches
78
+ # "C:/Ruby22/lib/ruby/gems/2.2.0/gems/puma-3.0.1/lib/puma/plugin/tmp_restart.rb:3:in `<top (required)>'"
79
+ # AS
80
+ # C:/Ruby22/lib/ruby/gems/2.2.0/gems/puma-3.0.1/lib/puma/plugin/tmp_restart.rb
81
+ CALLER_FILE = /
82
+ \A # start of string
83
+ .+ # file path (one or more characters)
84
+ (?= # stop previous match when
85
+ :\d+ # a colon is followed by one or more digits
86
+ :in # followed by a colon followed by in
87
+ )
88
+ /x
89
+
90
+ def self.extract_name(ary)
91
+ path = ary.first[CALLER_FILE]
92
+
93
+ m = %r!puma/plugin/([^/]*)\.rb$!.match(path)
94
+ return m[1]
95
+ end
96
+
97
+ def self.create(&blk)
98
+ name = extract_name(caller)
99
+
100
+ cls = Class.new(self)
101
+
102
+ cls.class_eval(&blk)
103
+
104
+ Plugins.register name, cls
105
+ end
106
+
107
+ def initialize(loader)
108
+ @loader = loader
109
+ end
110
+
111
+ def in_background(&blk)
112
+ Plugins.add_background blk
113
+ end
114
+
115
+ def workers_supported?
116
+ return false if Puma.jruby? || Puma.windows?
117
+ true
118
+ end
119
+ end
120
+ end