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,251 @@
1
+ require 'optparse'
2
+ require 'puma/const'
3
+ require 'puma/configuration'
4
+ require 'yaml'
5
+ require 'uri'
6
+ require 'socket'
7
+ module Puma
8
+ class ControlCLI
9
+
10
+ COMMANDS = %w{halt restart phased-restart start stats status stop}
11
+
12
+ def is_windows?
13
+ RUBY_PLATFORM =~ /(win|w)32$/ ? true : false
14
+ end
15
+
16
+ def initialize(argv, stdout=STDOUT, stderr=STDERR)
17
+ @argv = argv
18
+ @stdout = stdout
19
+ @stderr = stderr
20
+ @options = {}
21
+
22
+ opts = OptionParser.new do |o|
23
+ o.banner = "Usage: pumactl (-p PID | -P pidfile | -S status_file | -C url -T token | -F config.rb) (#{COMMANDS.join("|")})"
24
+
25
+ o.on "-S", "--state PATH", "Where the state file to use is" do |arg|
26
+ @options[:state] = arg
27
+ end
28
+
29
+ o.on "-Q", "--quiet", "Not display messages" do |arg|
30
+ @options[:quiet_flag] = true
31
+ end
32
+
33
+ o.on "-P", "--pidfile PATH", "Pid file" do |arg|
34
+ @options[:pidfile] = arg
35
+ end
36
+
37
+ o.on "-p", "--pid PID", "Pid" do |arg|
38
+ @options[:pid] = arg.to_i
39
+ end
40
+
41
+ o.on "-C", "--control-url URL", "The bind url to use for the control server" do |arg|
42
+ @options[:control_url] = arg
43
+ end
44
+
45
+ o.on "-T", "--control-token TOKEN", "The token to use as authentication for the control server" do |arg|
46
+ @options[:control_auth_token] = arg
47
+ end
48
+
49
+ o.on "-F", "--config-file PATH", "Puma config script" do |arg|
50
+ @options[:config_file] = arg
51
+ end
52
+
53
+ o.on_tail("-H", "--help", "Show this message") do
54
+ @stdout.puts o
55
+ exit
56
+ end
57
+
58
+ o.on_tail("-V", "--version", "Show version") do
59
+ puts Const::PUMA_VERSION
60
+ exit
61
+ end
62
+ end
63
+
64
+ opts.order!(argv) { |a| opts.terminate a }
65
+
66
+ command = argv.shift
67
+ @options[:command] = command if command
68
+
69
+ Puma::Configuration.new(@options).load if @options[:config_file]
70
+
71
+ # check present of command
72
+ unless @options[:command]
73
+ raise "Available commands: #{COMMANDS.join(", ")}"
74
+ end
75
+
76
+ unless COMMANDS.include? @options[:command]
77
+ raise "Invalid command: #{@options[:command]}"
78
+ end
79
+
80
+ rescue => e
81
+ @stdout.puts e.message
82
+ exit 1
83
+ end
84
+
85
+ def message(msg)
86
+ @stdout.puts msg unless @options[:quiet_flag]
87
+ end
88
+
89
+ def prepare_configuration
90
+ if @options.has_key? :state
91
+ unless File.exist? @options[:state]
92
+ raise "Status file not found: #{@options[:state]}"
93
+ end
94
+
95
+ status = YAML.load File.read(@options[:state])
96
+
97
+ if status.kind_of?(Hash) && status.has_key?("config")
98
+
99
+ conf = status["config"]
100
+
101
+ # get control_url
102
+ if url = conf.options[:control_url]
103
+ @options[:control_url] = url
104
+ end
105
+
106
+ # get control_auth_token
107
+ if token = conf.options[:control_auth_token]
108
+ @options[:control_auth_token] = token
109
+ end
110
+
111
+ # get pid
112
+ @options[:pid] = status["pid"].to_i
113
+ else
114
+ raise "Invalid status file: #{@options[:state]}"
115
+ end
116
+
117
+ elsif @options.has_key? :pidfile
118
+ # get pid from pid_file
119
+ @options[:pid] = File.open(@options[:pidfile]).gets.to_i
120
+ end
121
+ end
122
+
123
+ def send_request
124
+ uri = URI.parse @options[:control_url]
125
+
126
+ # create server object by scheme
127
+ @server = case uri.scheme
128
+ when "tcp"
129
+ TCPSocket.new uri.host, uri.port
130
+ when "unix"
131
+ UNIXSocket.new "#{uri.host}#{uri.path}"
132
+ else
133
+ raise "Invalid scheme: #{uri.scheme}"
134
+ end
135
+
136
+ if @options[:command] == "status"
137
+ message "Puma is started"
138
+ else
139
+ url = "/#{@options[:command]}"
140
+
141
+ if @options.has_key?(:control_auth_token)
142
+ url = url + "?token=#{@options[:control_auth_token]}"
143
+ end
144
+
145
+ @server << "GET #{url} HTTP/1.0\r\n\r\n"
146
+
147
+ unless data = @server.read
148
+ raise "Server closed connection before responding"
149
+ end
150
+
151
+ response = data.split("\r\n")
152
+
153
+ if response.empty?
154
+ raise "Server sent empty response"
155
+ end
156
+
157
+ (@http,@code,@message) = response.first.split(" ",3)
158
+
159
+ if @code == "403"
160
+ raise "Unauthorized access to server (wrong auth token)"
161
+ elsif @code == "404"
162
+ raise "Command error: #{response.last}"
163
+ elsif @code != "200"
164
+ raise "Bad response from server: #{@code}"
165
+ end
166
+
167
+ message "Command #{@options[:command]} sent success"
168
+ message response.last if @options[:command] == "stats"
169
+ end
170
+
171
+ @server.close
172
+ end
173
+
174
+ def send_signal
175
+ unless pid = @options[:pid]
176
+ raise "Neither pid nor control url available"
177
+ end
178
+
179
+ begin
180
+ Process.getpgid pid
181
+ rescue SystemCallError
182
+ if @options[:command] == "restart"
183
+ @options.delete(:command)
184
+ start
185
+ else
186
+ raise "No pid '#{pid}' found"
187
+ end
188
+ end
189
+
190
+ case @options[:command]
191
+ when "restart"
192
+ Process.kill "SIGUSR2", pid
193
+
194
+ when "halt"
195
+ Process.kill "QUIT", pid
196
+
197
+ when "stop"
198
+ Process.kill "SIGTERM", pid
199
+
200
+ when "stats"
201
+ puts "Stats not available via pid only"
202
+ return
203
+
204
+ when "phased-restart"
205
+ Process.kill "SIGUSR1", pid
206
+
207
+ else
208
+ message "Puma is started"
209
+ return
210
+ end
211
+
212
+ message "Command #{@options[:command]} sent success"
213
+ end
214
+
215
+ def run
216
+ start if @options[:command] == "start"
217
+
218
+ prepare_configuration
219
+
220
+ if is_windows?
221
+ send_request
222
+ else
223
+ @options.has_key?(:control_url) ? send_request : send_signal
224
+ end
225
+
226
+ rescue => e
227
+ message e.message
228
+ exit 1
229
+ end
230
+
231
+ private
232
+ def start
233
+ require 'puma/cli'
234
+
235
+ run_args = @argv
236
+
237
+ if path = @options[:state]
238
+ run_args = ["-S", path] + run_args
239
+ end
240
+
241
+ if path = @options[:config_file]
242
+ run_args = ["-C", path] + run_args
243
+ end
244
+
245
+ events = Puma::Events.new @stdout, @stderr
246
+
247
+ cli = Puma::CLI.new run_args, events
248
+ cli.run
249
+ end
250
+ end
251
+ end
@@ -0,0 +1,25 @@
1
+ module Process
2
+
3
+ # This overrides the default version because it is broken if it
4
+ # exists.
5
+
6
+ def self.daemon(nochdir=false, noclose=false)
7
+ exit if fork # Parent exits, child continues.
8
+
9
+ Process.setsid # Become session leader.
10
+
11
+ exit if fork # Zap session leader. See [1].
12
+
13
+ Dir.chdir "/" unless nochdir # Release old working directory.
14
+
15
+ if !noclose
16
+ STDIN.reopen File.open("/dev/null", "r")
17
+
18
+ null_out = File.open "/dev/null", "w"
19
+ STDOUT.reopen null_out
20
+ STDERR.reopen null_out
21
+ end
22
+
23
+ 0
24
+ end
25
+ end
@@ -0,0 +1,11 @@
1
+ module Puma
2
+ module Delegation
3
+ def forward(what, who)
4
+ module_eval <<-CODE
5
+ def #{what}(*args, &block)
6
+ #{who}.#{what}(*args, &block)
7
+ end
8
+ CODE
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,4 @@
1
+ module Puma
2
+ IS_JRUBY = defined?(JRUBY_VERSION)
3
+ end
4
+
@@ -0,0 +1,130 @@
1
+ require 'puma/const'
2
+ require 'stringio'
3
+
4
+ module Puma
5
+ # The default implement of an event sink object used by Server
6
+ # for when certain kinds of events occur in the life of the server.
7
+ #
8
+ # The methods available are the events that the Server fires.
9
+ #
10
+ class Events
11
+
12
+ include Const
13
+
14
+ # Create an Events object that prints to +stdout+ and +stderr+.
15
+ #
16
+ def initialize(stdout, stderr)
17
+ @stdout = stdout
18
+ @stderr = stderr
19
+
20
+ @stdout.sync = true
21
+ @stderr.sync = true
22
+
23
+ @debug = ENV.key? 'PUMA_DEBUG'
24
+
25
+ @on_booted = []
26
+
27
+ @hooks = Hash.new { |h,k| h[k] = [] }
28
+ end
29
+
30
+ attr_reader :stdout, :stderr
31
+
32
+ # Fire callbacks for the named hook
33
+ #
34
+ def fire(hook, *args)
35
+ @hooks[hook].each { |t| t.call(*args) }
36
+ end
37
+
38
+ # Register a callbock for a given hook
39
+ #
40
+ def register(hook, obj=nil, &blk)
41
+ if obj and blk
42
+ raise "Specify either an object or a block, not both"
43
+ end
44
+
45
+ h = obj || blk
46
+
47
+ @hooks[hook] << h
48
+
49
+ h
50
+ end
51
+
52
+ # Write +str+ to +@stdout+
53
+ #
54
+ def log(str)
55
+ @stdout.puts str
56
+ end
57
+
58
+ def write(str)
59
+ @stdout.write str
60
+ end
61
+
62
+ def debug(str)
63
+ log("% #{str}") if @debug
64
+ end
65
+
66
+ # Write +str+ to +@stderr+
67
+ #
68
+ def error(str)
69
+ @stderr.puts "ERROR: #{str}"
70
+ exit 1
71
+ end
72
+
73
+ # An HTTP parse error has occured.
74
+ # +server+ is the Server object, +env+ the request, and +error+ a
75
+ # parsing exception.
76
+ #
77
+ def parse_error(server, env, error)
78
+ @stderr.puts "#{Time.now}: HTTP parse error, malformed request (#{env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR]}): #{error.inspect}"
79
+ @stderr.puts "#{Time.now}: ENV: #{env.inspect}\n---\n"
80
+ end
81
+
82
+ # An unknown error has occured.
83
+ # +server+ is the Server object, +env+ the request, +error+ an exception
84
+ # object, and +kind+ some additional info.
85
+ #
86
+ def unknown_error(server, error, kind="Unknown")
87
+ if error.respond_to? :render
88
+ error.render "#{Time.now}: #{kind} error", @stderr
89
+ else
90
+ @stderr.puts "#{Time.now}: #{kind} error: #{error.inspect}"
91
+ @stderr.puts error.backtrace.join("\n")
92
+ end
93
+ end
94
+
95
+ def on_booted(&b)
96
+ @on_booted << b
97
+ end
98
+
99
+ def fire_on_booted!
100
+ @on_booted.each { |b| b.call }
101
+ end
102
+
103
+ DEFAULT = new(STDOUT, STDERR)
104
+
105
+ # Returns an Events object which writes it's status to 2 StringIO
106
+ # objects.
107
+ #
108
+ def self.strings
109
+ Events.new StringIO.new, StringIO.new
110
+ end
111
+
112
+ def self.stdio
113
+ Events.new $stdout, $stderr
114
+ end
115
+ end
116
+
117
+ class PidEvents < Events
118
+ def log(str)
119
+ super "[#{$$}] #{str}"
120
+ end
121
+
122
+ def write(str)
123
+ super "[#{$$}] #{str}"
124
+ end
125
+
126
+ def error(str)
127
+ super "[#{$$}] #{str}"
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,7 @@
1
+ require 'puma/detect'
2
+
3
+ if Puma::IS_JRUBY
4
+ require 'puma/java_io_buffer'
5
+ else
6
+ require 'puma/puma_http11'
7
+ end
@@ -0,0 +1,45 @@
1
+ require 'java'
2
+
3
+ # Conservative native JRuby/Java implementation of IOBuffer
4
+ # backed by a ByteArrayOutputStream and conversion between
5
+ # Ruby String and Java bytes
6
+ module Puma
7
+ class JavaIOBuffer < java.io.ByteArrayOutputStream
8
+ field_reader :buf
9
+ end
10
+
11
+ class IOBuffer
12
+ BUF_DEFAULT_SIZE = 4096
13
+
14
+ def initialize
15
+ @buf = JavaIOBuffer.new(BUF_DEFAULT_SIZE)
16
+ end
17
+
18
+ def reset
19
+ @buf.reset
20
+ end
21
+
22
+ def <<(str)
23
+ bytes = str.to_java_bytes
24
+ @buf.write(bytes, 0, bytes.length)
25
+ end
26
+
27
+ def append(*strs)
28
+ strs.each { |s| self << s; }
29
+ end
30
+
31
+ def to_s
32
+ String.from_java_bytes @buf.to_byte_array
33
+ end
34
+
35
+ alias_method :to_str, :to_s
36
+
37
+ def used
38
+ @buf.size
39
+ end
40
+
41
+ def capacity
42
+ @buf.buf.length
43
+ end
44
+ end
45
+ end