puma 2.16.0-java → 3.0.0-java
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.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/History.txt +50 -0
- data/Manifest.txt +5 -1
- data/README.md +25 -13
- data/ext/puma_http11/http11_parser.java.rl +5 -5
- data/ext/puma_http11/io_buffer.c +1 -1
- data/ext/puma_http11/mini_ssl.c +7 -1
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +39 -39
- data/lib/puma.rb +1 -0
- data/lib/puma/app/status.rb +1 -1
- data/lib/puma/binder.rb +14 -3
- data/lib/puma/cli.rb +128 -493
- data/lib/puma/client.rb +4 -0
- data/lib/puma/cluster.rb +68 -52
- data/lib/puma/configuration.rb +192 -61
- data/lib/puma/const.rb +2 -2
- data/lib/puma/control_cli.rb +58 -73
- data/lib/puma/convenient.rb +23 -0
- data/lib/puma/daemon_ext.rb +6 -0
- data/lib/puma/detect.rb +8 -1
- data/lib/puma/dsl.rb +113 -28
- data/lib/puma/events.rb +5 -0
- data/lib/puma/launcher.rb +397 -0
- data/lib/puma/null_io.rb +15 -0
- data/lib/puma/plugin.rb +91 -0
- data/lib/puma/plugin/tmp_restart.rb +23 -0
- data/lib/puma/puma_http11.jar +0 -0
- data/lib/puma/runner.rb +19 -14
- data/lib/puma/server.rb +82 -48
- data/lib/puma/single.rb +9 -4
- data/lib/puma/state_file.rb +29 -0
- data/lib/puma/thread_pool.rb +8 -3
- data/lib/rack/handler/puma.rb +34 -27
- metadata +7 -3
- data/COPYING +0 -55
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'puma/plugin'
|
2
|
+
|
3
|
+
Puma::Plugin.create do
|
4
|
+
def start(launcher)
|
5
|
+
path = File.join("tmp", "restart.txt")
|
6
|
+
|
7
|
+
File.write path, ""
|
8
|
+
|
9
|
+
orig = File.stat(path).mtime
|
10
|
+
|
11
|
+
in_background do
|
12
|
+
while true
|
13
|
+
sleep 2
|
14
|
+
|
15
|
+
if File.stat(path).mtime > orig
|
16
|
+
launcher.restart
|
17
|
+
break
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
data/lib/puma/puma_http11.jar
CHANGED
Binary file
|
data/lib/puma/runner.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
module Puma
|
2
2
|
class Runner
|
3
|
-
def initialize(cli)
|
4
|
-
@
|
3
|
+
def initialize(cli, events)
|
4
|
+
@launcher = cli
|
5
|
+
@events = events
|
5
6
|
@options = cli.options
|
6
7
|
@app = nil
|
7
8
|
@control = nil
|
@@ -16,15 +17,19 @@ module Puma
|
|
16
17
|
end
|
17
18
|
|
18
19
|
def log(str)
|
19
|
-
@
|
20
|
+
@events.log str
|
21
|
+
end
|
22
|
+
|
23
|
+
def before_restart
|
24
|
+
@control.stop(true) if @control
|
20
25
|
end
|
21
26
|
|
22
27
|
def error(str)
|
23
|
-
@
|
28
|
+
@events.error str
|
24
29
|
end
|
25
30
|
|
26
|
-
def
|
27
|
-
@
|
31
|
+
def debug(str)
|
32
|
+
@events.log "- #{str}" if @options[:debug]
|
28
33
|
end
|
29
34
|
|
30
35
|
def start_control
|
@@ -35,13 +40,13 @@ module Puma
|
|
35
40
|
|
36
41
|
uri = URI.parse str
|
37
42
|
|
38
|
-
app = Puma::App::Status.new @
|
43
|
+
app = Puma::App::Status.new @launcher
|
39
44
|
|
40
45
|
if token = @options[:control_auth_token]
|
41
46
|
app.auth_token = token unless token.empty? or token == :none
|
42
47
|
end
|
43
48
|
|
44
|
-
control = Puma::Server.new app, @
|
49
|
+
control = Puma::Server.new app, @launcher.events
|
45
50
|
control.min_threads = 0
|
46
51
|
control.max_threads = 1
|
47
52
|
|
@@ -104,34 +109,34 @@ module Puma
|
|
104
109
|
end
|
105
110
|
|
106
111
|
def load_and_bind
|
107
|
-
unless @
|
112
|
+
unless @launcher.config.app_configured?
|
108
113
|
error "No application configured, nothing to run"
|
109
114
|
exit 1
|
110
115
|
end
|
111
116
|
|
112
117
|
# Load the app before we daemonize.
|
113
118
|
begin
|
114
|
-
@app = @
|
119
|
+
@app = @launcher.config.app
|
115
120
|
rescue Exception => e
|
116
121
|
log "! Unable to load application: #{e.class}: #{e.message}"
|
117
122
|
raise e
|
118
123
|
end
|
119
124
|
|
120
|
-
@
|
125
|
+
@launcher.binder.parse @options[:binds], self
|
121
126
|
end
|
122
127
|
|
123
128
|
def app
|
124
|
-
@app ||= @
|
129
|
+
@app ||= @launcher.config.app
|
125
130
|
end
|
126
131
|
|
127
132
|
def start_server
|
128
133
|
min_t = @options[:min_threads]
|
129
134
|
max_t = @options[:max_threads]
|
130
135
|
|
131
|
-
server = Puma::Server.new app, @
|
136
|
+
server = Puma::Server.new app, @launcher.events, @options
|
132
137
|
server.min_threads = min_t
|
133
138
|
server.max_threads = max_t
|
134
|
-
server.inherit_binder @
|
139
|
+
server.inherit_binder @launcher.binder
|
135
140
|
|
136
141
|
if @options[:mode] == :tcp
|
137
142
|
server.tcp_mode!
|
data/lib/puma/server.rb
CHANGED
@@ -85,6 +85,7 @@ module Puma
|
|
85
85
|
forward :add_tcp_listener, :@binder
|
86
86
|
forward :add_ssl_listener, :@binder
|
87
87
|
forward :add_unix_listener, :@binder
|
88
|
+
forward :connected_port, :@binder
|
88
89
|
|
89
90
|
def inherit_binder(bind)
|
90
91
|
@binder = bind
|
@@ -398,6 +399,7 @@ module Puma
|
|
398
399
|
#
|
399
400
|
def process_client(client, buffer)
|
400
401
|
begin
|
402
|
+
clean_thread_locals = @options[:clean_thread_locals]
|
401
403
|
close_socket = true
|
402
404
|
|
403
405
|
while true
|
@@ -411,6 +413,8 @@ module Puma
|
|
411
413
|
return unless @queue_requests
|
412
414
|
buffer.reset
|
413
415
|
|
416
|
+
ThreadPool.clean_thread_locals if clean_thread_locals
|
417
|
+
|
414
418
|
unless client.reset(@status == :run)
|
415
419
|
close_socket = false
|
416
420
|
client.set_timeout @persistent_timeout
|
@@ -516,63 +520,20 @@ module Puma
|
|
516
520
|
env['HTTP_X_FORWARDED_PROTO'] == 'https' ? PORT_443 : PORT_80
|
517
521
|
end
|
518
522
|
|
519
|
-
|
520
|
-
# in +body+, finish reading the body if there is one and invoke
|
521
|
-
# the rack app. Then construct the response and write it back to
|
522
|
-
# +client+
|
523
|
-
#
|
524
|
-
# +cl+ is the previously fetched Content-Length header if there
|
525
|
-
# was one. This is an optimization to keep from having to look
|
526
|
-
# it up again.
|
527
|
-
#
|
528
|
-
def handle_request(req, lines)
|
529
|
-
env = req.env
|
523
|
+
def handle_app_response(req, lines, env, status, headers, res_body)
|
530
524
|
client = req.io
|
531
525
|
|
532
|
-
normalize_env env, req
|
533
|
-
|
534
|
-
env[PUMA_SOCKET] = client
|
535
|
-
|
536
|
-
if env[HTTPS_KEY] && client.peercert
|
537
|
-
env[PUMA_PEERCERT] = client.peercert
|
538
|
-
end
|
539
|
-
|
540
|
-
env[HIJACK_P] = true
|
541
|
-
env[HIJACK] = req
|
542
|
-
|
543
526
|
body = req.body
|
544
527
|
|
545
528
|
head = env[REQUEST_METHOD] == HEAD
|
546
529
|
|
547
|
-
env[RACK_INPUT] = body
|
548
|
-
env[RACK_URL_SCHEME] = env[HTTPS_KEY] ? HTTPS : HTTP
|
549
|
-
|
550
530
|
# A rack extension. If the app writes #call'ables to this
|
551
531
|
# array, we will invoke them when the request is done.
|
552
532
|
#
|
553
|
-
|
533
|
+
# (This is already initialized in `handle_request`, just getting here)
|
534
|
+
after_reply = env[RACK_AFTER_REPLY]
|
554
535
|
|
555
536
|
begin
|
556
|
-
begin
|
557
|
-
status, headers, res_body = @app.call(env)
|
558
|
-
|
559
|
-
return :async if req.hijacked
|
560
|
-
|
561
|
-
status = status.to_i
|
562
|
-
|
563
|
-
if status == -1
|
564
|
-
unless headers.empty? and res_body == []
|
565
|
-
raise "async response must have empty headers and body"
|
566
|
-
end
|
567
|
-
|
568
|
-
return :async
|
569
|
-
end
|
570
|
-
rescue StandardError => e
|
571
|
-
@events.unknown_error self, e, "Rack app"
|
572
|
-
|
573
|
-
status, headers, res_body = lowlevel_error(e)
|
574
|
-
end
|
575
|
-
|
576
537
|
content_length = nil
|
577
538
|
no_body = head
|
578
539
|
|
@@ -714,6 +675,75 @@ module Puma
|
|
714
675
|
return keep_alive
|
715
676
|
end
|
716
677
|
|
678
|
+
# Given the request +env+ from +client+ and a partial request body
|
679
|
+
# in +body+, finish reading the body if there is one and invoke
|
680
|
+
# the rack app. Then construct the response and write it back to
|
681
|
+
# +client+
|
682
|
+
#
|
683
|
+
# +cl+ is the previously fetched Content-Length header if there
|
684
|
+
# was one. This is an optimization to keep from having to look
|
685
|
+
# it up again.
|
686
|
+
#
|
687
|
+
def handle_request(req, lines)
|
688
|
+
env = req.env
|
689
|
+
client = req.io
|
690
|
+
|
691
|
+
normalize_env env, req
|
692
|
+
|
693
|
+
env[PUMA_SOCKET] = client
|
694
|
+
|
695
|
+
if env[HTTPS_KEY] && client.peercert
|
696
|
+
env[PUMA_PEERCERT] = client.peercert
|
697
|
+
end
|
698
|
+
|
699
|
+
env[HIJACK_P] = true
|
700
|
+
env[HIJACK] = req
|
701
|
+
|
702
|
+
env['async.callback'] = lambda { |__response|
|
703
|
+
_status, _headers, _res_body = __response
|
704
|
+
# Faye websocket gem also calls this (when available)
|
705
|
+
# with status=101, and res_body being a stream
|
706
|
+
# but calling here just finishes the response
|
707
|
+
# that's why we only response if the request is not
|
708
|
+
# hijacked. not sure if this is a correct solution
|
709
|
+
# but working. for now.
|
710
|
+
unless req.hijacked
|
711
|
+
handle_app_response(req, lines, env, _status, _headers, _res_body)
|
712
|
+
end
|
713
|
+
}
|
714
|
+
|
715
|
+
body = req.body
|
716
|
+
|
717
|
+
env[RACK_INPUT] = body
|
718
|
+
env[RACK_URL_SCHEME] = env[HTTPS_KEY] ? HTTPS : HTTP
|
719
|
+
|
720
|
+
env[RACK_AFTER_REPLY] = []
|
721
|
+
|
722
|
+
begin
|
723
|
+
status, headers, res_body = @app.call(env)
|
724
|
+
|
725
|
+
return :async if req.hijacked
|
726
|
+
|
727
|
+
status = status.to_i
|
728
|
+
|
729
|
+
if status == -1
|
730
|
+
# unless headers.empty? and res_body == []
|
731
|
+
# puts "HEADERS: ".white.on_green + headers.inspect
|
732
|
+
# puts "BODY: ".white.on_green + res_body.inspect
|
733
|
+
# raise "async response must have empty headers and body"
|
734
|
+
# end
|
735
|
+
|
736
|
+
return :async
|
737
|
+
end
|
738
|
+
rescue StandardError => e
|
739
|
+
@events.unknown_error self, e, "Rack app"
|
740
|
+
|
741
|
+
status, headers, res_body = lowlevel_error(e, env)
|
742
|
+
end
|
743
|
+
|
744
|
+
return handle_app_response(req, lines, env, status, headers, res_body)
|
745
|
+
end
|
746
|
+
|
717
747
|
def fetch_status_code(status)
|
718
748
|
HTTP_STATUS_CODES.fetch(status) { 'CUSTOM' }
|
719
749
|
end
|
@@ -775,9 +805,13 @@ module Puma
|
|
775
805
|
|
776
806
|
# A fallback rack response if +@app+ raises as exception.
|
777
807
|
#
|
778
|
-
def lowlevel_error(e)
|
808
|
+
def lowlevel_error(e, env)
|
779
809
|
if handler = @options[:lowlevel_error_handler]
|
780
|
-
|
810
|
+
if handler.arity == 1
|
811
|
+
return handler.call(e)
|
812
|
+
else
|
813
|
+
return handler.call(e, env)
|
814
|
+
end
|
781
815
|
end
|
782
816
|
|
783
817
|
if @leak_stack_on_error
|
data/lib/puma/single.rb
CHANGED
@@ -27,7 +27,12 @@ module Puma
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def jruby_daemon?
|
30
|
-
daemon? and
|
30
|
+
daemon? and Puma.jruby?
|
31
|
+
end
|
32
|
+
|
33
|
+
def jruby_daemon_start
|
34
|
+
require 'puma/jruby_restart'
|
35
|
+
JRubyRestart.daemon_start(@restart_dir, restart_args)
|
31
36
|
end
|
32
37
|
|
33
38
|
def run
|
@@ -66,7 +71,7 @@ module Puma
|
|
66
71
|
exit 1
|
67
72
|
end
|
68
73
|
|
69
|
-
pid =
|
74
|
+
pid = jruby_daemon_start
|
70
75
|
sleep
|
71
76
|
end
|
72
77
|
else
|
@@ -79,7 +84,7 @@ module Puma
|
|
79
84
|
load_and_bind
|
80
85
|
end
|
81
86
|
|
82
|
-
@
|
87
|
+
@launcher.write_state
|
83
88
|
|
84
89
|
start_control
|
85
90
|
|
@@ -90,7 +95,7 @@ module Puma
|
|
90
95
|
redirect_io
|
91
96
|
end
|
92
97
|
|
93
|
-
@
|
98
|
+
@launcher.events.fire_on_booted!
|
94
99
|
|
95
100
|
begin
|
96
101
|
server.run.join
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Puma
|
4
|
+
class StateFile
|
5
|
+
def initialize
|
6
|
+
@options = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def save(path)
|
10
|
+
File.write path, YAML.dump(@options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def load(path)
|
14
|
+
@options = YAML.load File.read(path)
|
15
|
+
end
|
16
|
+
|
17
|
+
FIELDS = %w!control_url control_auth_token pid!
|
18
|
+
|
19
|
+
FIELDS.each do |f|
|
20
|
+
define_method f do
|
21
|
+
@options[f]
|
22
|
+
end
|
23
|
+
|
24
|
+
define_method "#{f}=" do |v|
|
25
|
+
@options[f] = v
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/puma/thread_pool.rb
CHANGED
@@ -45,6 +45,12 @@ module Puma
|
|
45
45
|
attr_reader :spawned, :trim_requested
|
46
46
|
attr_accessor :clean_thread_locals
|
47
47
|
|
48
|
+
def self.clean_thread_locals
|
49
|
+
Thread.current.keys.each do |key|
|
50
|
+
Thread.current[key] = nil unless key == :__recursive_key__
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
48
54
|
# How many objects have yet to be processed by the pool?
|
49
55
|
#
|
50
56
|
def backlog
|
@@ -77,6 +83,7 @@ module Puma
|
|
77
83
|
if @trim_requested > 0
|
78
84
|
@trim_requested -= 1
|
79
85
|
continue = false
|
86
|
+
not_full.signal
|
80
87
|
break
|
81
88
|
end
|
82
89
|
|
@@ -97,9 +104,7 @@ module Puma
|
|
97
104
|
break unless continue
|
98
105
|
|
99
106
|
if @clean_thread_locals
|
100
|
-
|
101
|
-
Thread.current[key] = nil unless key == :__recursive_key__
|
102
|
-
end
|
107
|
+
ThreadPool.clean_thread_locals
|
103
108
|
end
|
104
109
|
|
105
110
|
begin
|
data/lib/rack/handler/puma.rb
CHANGED
@@ -5,48 +5,55 @@ module Rack
|
|
5
5
|
module Handler
|
6
6
|
module Puma
|
7
7
|
DEFAULT_OPTIONS = {
|
8
|
-
:Host => '0.0.0.0',
|
9
|
-
:Port => 8080,
|
10
|
-
:Threads => '0:16',
|
11
8
|
:Verbose => false,
|
12
|
-
:Silent
|
9
|
+
:Silent => false
|
13
10
|
}
|
14
11
|
|
15
12
|
def self.run(app, options = {})
|
16
13
|
options = DEFAULT_OPTIONS.merge(options)
|
17
14
|
|
18
|
-
|
19
|
-
|
20
|
-
end
|
15
|
+
conf = ::Puma::Configuration.new do |c|
|
16
|
+
c.quiet
|
21
17
|
|
22
|
-
|
23
|
-
|
24
|
-
|
18
|
+
if options.delete(:Verbose)
|
19
|
+
app = Rack::CommonLogger.new(app, STDOUT)
|
20
|
+
end
|
21
|
+
|
22
|
+
if options[:environment]
|
23
|
+
c.environment options[:environment]
|
24
|
+
end
|
25
|
+
|
26
|
+
if options[:Threads]
|
27
|
+
min, max = options.delete(:Threads).split(':', 2)
|
28
|
+
c.threads min, max
|
29
|
+
end
|
25
30
|
|
26
|
-
|
27
|
-
server = ::Puma::Server.new(app, events_hander)
|
28
|
-
min, max = options[:Threads].split(':', 2)
|
31
|
+
host = options[:Host]
|
29
32
|
|
30
|
-
|
33
|
+
if host && (host[0,1] == '.' || host[0,1] == '/')
|
34
|
+
c.bind "unix://#{host}"
|
35
|
+
else
|
36
|
+
host ||= ::Puma::Configuration::DefaultTCPHost
|
37
|
+
port = options[:Port] || ::Puma::Configuration::DefaultTCPPort
|
31
38
|
|
32
|
-
|
33
|
-
|
34
|
-
log.puts "* Environment: #{ENV['RACK_ENV']}"
|
35
|
-
log.puts "* Listening on tcp://#{options[:Host]}:#{options[:Port]}"
|
39
|
+
c.port port, host
|
40
|
+
end
|
36
41
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
42
|
+
c.app app
|
43
|
+
end
|
44
|
+
|
45
|
+
events = options.delete(:Silent) ? ::Puma::Events.strings : ::Puma::Events.stdio
|
41
46
|
|
47
|
+
launcher = ::Puma::Launcher.new(conf, :events => events)
|
48
|
+
|
49
|
+
yield launcher if block_given?
|
42
50
|
begin
|
43
|
-
|
51
|
+
launcher.run
|
44
52
|
rescue Interrupt
|
45
|
-
|
46
|
-
|
47
|
-
|
53
|
+
puts "* Gracefully stopping, waiting for requests to finish"
|
54
|
+
launcher.stop
|
55
|
+
puts "* Goodbye!"
|
48
56
|
end
|
49
|
-
|
50
57
|
end
|
51
58
|
|
52
59
|
def self.valid_options
|