puma-simon 3.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/issue_template.md +20 -0
- data/.gitignore +18 -0
- data/.hoeignore +12 -0
- data/.travis.yml +29 -0
- data/DEPLOYMENT.md +91 -0
- data/Gemfile +12 -0
- data/History.md +1254 -0
- data/LICENSE +26 -0
- data/Manifest.txt +78 -0
- data/README.md +353 -0
- data/Rakefile +158 -0
- data/Release.md +9 -0
- data/bin/puma +10 -0
- data/bin/puma-wild +31 -0
- data/bin/pumactl +12 -0
- data/docs/nginx.md +80 -0
- data/docs/signals.md +43 -0
- data/docs/systemd.md +197 -0
- data/examples/CA/cacert.pem +23 -0
- data/examples/CA/newcerts/cert_1.pem +19 -0
- data/examples/CA/newcerts/cert_2.pem +19 -0
- data/examples/CA/private/cakeypair.pem +30 -0
- data/examples/CA/serial +1 -0
- data/examples/config.rb +200 -0
- data/examples/plugins/redis_stop_puma.rb +46 -0
- data/examples/puma/cert_puma.pem +19 -0
- data/examples/puma/client-certs/ca.crt +19 -0
- data/examples/puma/client-certs/ca.key +27 -0
- data/examples/puma/client-certs/client.crt +19 -0
- data/examples/puma/client-certs/client.key +27 -0
- data/examples/puma/client-certs/client_expired.crt +19 -0
- data/examples/puma/client-certs/client_expired.key +27 -0
- data/examples/puma/client-certs/client_unknown.crt +19 -0
- data/examples/puma/client-certs/client_unknown.key +27 -0
- data/examples/puma/client-certs/generate.rb +78 -0
- data/examples/puma/client-certs/keystore.jks +0 -0
- data/examples/puma/client-certs/server.crt +19 -0
- data/examples/puma/client-certs/server.key +27 -0
- data/examples/puma/client-certs/server.p12 +0 -0
- data/examples/puma/client-certs/unknown_ca.crt +19 -0
- data/examples/puma/client-certs/unknown_ca.key +27 -0
- data/examples/puma/csr_puma.pem +11 -0
- data/examples/puma/keystore.jks +0 -0
- data/examples/puma/puma_keypair.pem +15 -0
- data/examples/qc_config.rb +13 -0
- data/ext/puma_http11/PumaHttp11Service.java +17 -0
- data/ext/puma_http11/ext_help.h +15 -0
- data/ext/puma_http11/extconf.rb +15 -0
- data/ext/puma_http11/http11_parser.c +1069 -0
- data/ext/puma_http11/http11_parser.h +65 -0
- data/ext/puma_http11/http11_parser.java.rl +161 -0
- data/ext/puma_http11/http11_parser.rl +147 -0
- data/ext/puma_http11/http11_parser_common.rl +54 -0
- data/ext/puma_http11/io_buffer.c +155 -0
- data/ext/puma_http11/mini_ssl.c +457 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +234 -0
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +473 -0
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +339 -0
- data/ext/puma_http11/puma_http11.c +500 -0
- data/gemfiles/2.1-Gemfile +12 -0
- data/lib/puma.rb +15 -0
- data/lib/puma/accept_nonblock.rb +23 -0
- data/lib/puma/app/status.rb +66 -0
- data/lib/puma/binder.rb +402 -0
- data/lib/puma/cli.rb +220 -0
- data/lib/puma/client.rb +434 -0
- data/lib/puma/cluster.rb +510 -0
- data/lib/puma/commonlogger.rb +106 -0
- data/lib/puma/compat.rb +14 -0
- data/lib/puma/configuration.rb +364 -0
- data/lib/puma/const.rb +224 -0
- data/lib/puma/control_cli.rb +259 -0
- data/lib/puma/convenient.rb +23 -0
- data/lib/puma/daemon_ext.rb +31 -0
- data/lib/puma/delegation.rb +11 -0
- data/lib/puma/detect.rb +13 -0
- data/lib/puma/dsl.rb +486 -0
- data/lib/puma/events.rb +152 -0
- data/lib/puma/io_buffer.rb +7 -0
- data/lib/puma/java_io_buffer.rb +45 -0
- data/lib/puma/jruby_restart.rb +83 -0
- data/lib/puma/launcher.rb +410 -0
- data/lib/puma/minissl.rb +221 -0
- data/lib/puma/null_io.rb +42 -0
- data/lib/puma/plugin.rb +115 -0
- data/lib/puma/plugin/tmp_restart.rb +35 -0
- data/lib/puma/rack/backports/uri/common_193.rb +33 -0
- data/lib/puma/rack/builder.rb +298 -0
- data/lib/puma/rack/urlmap.rb +91 -0
- data/lib/puma/rack_default.rb +7 -0
- data/lib/puma/reactor.rb +210 -0
- data/lib/puma/runner.rb +171 -0
- data/lib/puma/server.rb +949 -0
- data/lib/puma/single.rb +112 -0
- data/lib/puma/state_file.rb +29 -0
- data/lib/puma/tcp_logger.rb +39 -0
- data/lib/puma/thread_pool.rb +297 -0
- data/lib/puma/util.rb +128 -0
- data/lib/rack/handler/puma.rb +78 -0
- data/puma.gemspec +52 -0
- data/test/ab_rs.rb +22 -0
- data/test/config.rb +2 -0
- data/test/config/app.rb +9 -0
- data/test/config/plugin.rb +1 -0
- data/test/config/settings.rb +2 -0
- data/test/config/state_file_testing_config.rb +14 -0
- data/test/hello-bind.ru +2 -0
- data/test/hello-delay.ru +3 -0
- data/test/hello-map.ru +3 -0
- data/test/hello-post.ru +4 -0
- data/test/hello-stuck.ru +1 -0
- data/test/hello-tcp.ru +5 -0
- data/test/hello.ru +1 -0
- data/test/hijack.ru +6 -0
- data/test/hijack2.ru +5 -0
- data/test/lobster.ru +4 -0
- data/test/shell/run.sh +24 -0
- data/test/shell/t1.rb +19 -0
- data/test/shell/t1_conf.rb +3 -0
- data/test/shell/t2.rb +17 -0
- data/test/shell/t2_conf.rb +6 -0
- data/test/shell/t3.rb +25 -0
- data/test/shell/t3_conf.rb +5 -0
- data/test/slow.ru +4 -0
- data/test/ssl_config.rb +4 -0
- data/test/test_app_status.rb +93 -0
- data/test/test_binder.rb +31 -0
- data/test/test_cli.rb +209 -0
- data/test/test_config.rb +95 -0
- data/test/test_events.rb +161 -0
- data/test/test_helper.rb +50 -0
- data/test/test_http10.rb +27 -0
- data/test/test_http11.rb +186 -0
- data/test/test_integration.rb +247 -0
- data/test/test_iobuffer.rb +39 -0
- data/test/test_minissl.rb +29 -0
- data/test/test_null_io.rb +49 -0
- data/test/test_persistent.rb +245 -0
- data/test/test_puma_server.rb +626 -0
- data/test/test_puma_server_ssl.rb +222 -0
- data/test/test_rack_handler.rb +57 -0
- data/test/test_rack_server.rb +138 -0
- data/test/test_tcp_logger.rb +39 -0
- data/test/test_tcp_rack.rb +36 -0
- data/test/test_thread_pool.rb +250 -0
- data/test/test_unix_socket.rb +35 -0
- data/test/test_web_server.rb +88 -0
- data/tools/jungle/README.md +9 -0
- data/tools/jungle/init.d/README.md +59 -0
- data/tools/jungle/init.d/puma +421 -0
- data/tools/jungle/init.d/run-puma +18 -0
- data/tools/jungle/upstart/README.md +61 -0
- data/tools/jungle/upstart/puma-manager.conf +31 -0
- data/tools/jungle/upstart/puma.conf +69 -0
- data/tools/trickletest.rb +45 -0
- metadata +297 -0
data/lib/puma/cli.rb
ADDED
@@ -0,0 +1,220 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
require 'puma/configuration'
|
5
|
+
require 'puma/launcher'
|
6
|
+
require 'puma/const'
|
7
|
+
require 'puma/events'
|
8
|
+
|
9
|
+
module Puma
|
10
|
+
class << self
|
11
|
+
# The CLI exports its Puma::Configuration object here to allow
|
12
|
+
# apps to pick it up. An app needs to use it conditionally though
|
13
|
+
# since it is not set if the app is launched via another
|
14
|
+
# mechanism than the CLI class.
|
15
|
+
attr_accessor :cli_config
|
16
|
+
end
|
17
|
+
|
18
|
+
# Handles invoke a Puma::Server in a command line style.
|
19
|
+
#
|
20
|
+
class CLI
|
21
|
+
KEYS_NOT_TO_PERSIST_IN_STATE = Launcher::KEYS_NOT_TO_PERSIST_IN_STATE
|
22
|
+
|
23
|
+
# Create a new CLI object using +argv+ as the command line
|
24
|
+
# arguments.
|
25
|
+
#
|
26
|
+
# +stdout+ and +stderr+ can be set to IO-like objects which
|
27
|
+
# this object will report status on.
|
28
|
+
#
|
29
|
+
def initialize(argv, events=Events.stdio)
|
30
|
+
@debug = false
|
31
|
+
@argv = argv.dup
|
32
|
+
|
33
|
+
@events = events
|
34
|
+
|
35
|
+
@conf = nil
|
36
|
+
|
37
|
+
@stdout = nil
|
38
|
+
@stderr = nil
|
39
|
+
@append = false
|
40
|
+
|
41
|
+
@control_url = nil
|
42
|
+
@control_options = {}
|
43
|
+
|
44
|
+
setup_options
|
45
|
+
|
46
|
+
begin
|
47
|
+
@parser.parse! @argv
|
48
|
+
|
49
|
+
if file = @argv.shift
|
50
|
+
@conf.configure do |c|
|
51
|
+
c.rackup file
|
52
|
+
end
|
53
|
+
end
|
54
|
+
rescue UnsupportedOption
|
55
|
+
exit 1
|
56
|
+
end
|
57
|
+
|
58
|
+
@conf.configure do |c|
|
59
|
+
if @stdout || @stderr
|
60
|
+
c.stdout_redirect @stdout, @stderr, @append
|
61
|
+
end
|
62
|
+
|
63
|
+
if @control_url
|
64
|
+
c.activate_control_app @control_url, @control_options
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
@launcher = Puma::Launcher.new(@conf, :events => @events, :argv => argv)
|
69
|
+
end
|
70
|
+
|
71
|
+
attr_reader :launcher
|
72
|
+
|
73
|
+
# Parse the options, load the rackup, start the server and wait
|
74
|
+
# for it to finish.
|
75
|
+
#
|
76
|
+
def run
|
77
|
+
@launcher.run
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
def unsupported(str)
|
82
|
+
@events.error(str)
|
83
|
+
raise UnsupportedOption
|
84
|
+
end
|
85
|
+
|
86
|
+
# Build the OptionParser object to handle the available options.
|
87
|
+
#
|
88
|
+
|
89
|
+
def setup_options
|
90
|
+
@conf = Configuration.new do |c|
|
91
|
+
@parser = OptionParser.new do |o|
|
92
|
+
o.on "-b", "--bind URI", "URI to bind to (tcp://, unix://, ssl://)" do |arg|
|
93
|
+
c.bind arg
|
94
|
+
end
|
95
|
+
|
96
|
+
o.on "-C", "--config PATH", "Load PATH as a config file" do |arg|
|
97
|
+
c.load arg
|
98
|
+
end
|
99
|
+
|
100
|
+
o.on "--control URL", "The bind url to use for the control server",
|
101
|
+
"Use 'auto' to use temp unix server" do |arg|
|
102
|
+
if arg
|
103
|
+
@control_url = arg
|
104
|
+
elsif Puma.jruby?
|
105
|
+
unsupported "No default url available on JRuby"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
o.on "--control-token TOKEN",
|
110
|
+
"The token to use as authentication for the control server" do |arg|
|
111
|
+
@control_options[:auth_token] = arg
|
112
|
+
end
|
113
|
+
|
114
|
+
o.on "-d", "--daemon", "Daemonize the server into the background" do
|
115
|
+
c.daemonize
|
116
|
+
c.quiet
|
117
|
+
end
|
118
|
+
|
119
|
+
o.on "--debug", "Log lowlevel debugging information" do
|
120
|
+
c.debug
|
121
|
+
end
|
122
|
+
|
123
|
+
o.on "--dir DIR", "Change to DIR before starting" do |d|
|
124
|
+
c.directory d
|
125
|
+
end
|
126
|
+
|
127
|
+
o.on "-e", "--environment ENVIRONMENT",
|
128
|
+
"The environment to run the Rack app on (default development)" do |arg|
|
129
|
+
c.environment arg
|
130
|
+
end
|
131
|
+
|
132
|
+
o.on "-I", "--include PATH", "Specify $LOAD_PATH directories" do |arg|
|
133
|
+
$LOAD_PATH.unshift(*arg.split(':'))
|
134
|
+
end
|
135
|
+
|
136
|
+
o.on "-p", "--port PORT", "Define the TCP port to bind to",
|
137
|
+
"Use -b for more advanced options" do |arg|
|
138
|
+
c.bind "tcp://#{Configuration::DefaultTCPHost}:#{arg}"
|
139
|
+
end
|
140
|
+
|
141
|
+
o.on "--pidfile PATH", "Use PATH as a pidfile" do |arg|
|
142
|
+
c.pidfile arg
|
143
|
+
end
|
144
|
+
|
145
|
+
o.on "--preload", "Preload the app. Cluster mode only" do
|
146
|
+
c.preload_app!
|
147
|
+
end
|
148
|
+
|
149
|
+
o.on "--prune-bundler", "Prune out the bundler env if possible" do
|
150
|
+
c.prune_bundler
|
151
|
+
end
|
152
|
+
|
153
|
+
o.on "-q", "--quiet", "Do not log requests internally (default true)" do
|
154
|
+
c.quiet
|
155
|
+
end
|
156
|
+
|
157
|
+
o.on "-v", "--log-requests", "Log requests as they occur" do
|
158
|
+
c.log_requests
|
159
|
+
end
|
160
|
+
|
161
|
+
o.on "-R", "--restart-cmd CMD",
|
162
|
+
"The puma command to run during a hot restart",
|
163
|
+
"Default: inferred" do |cmd|
|
164
|
+
c.restart_command cmd
|
165
|
+
end
|
166
|
+
|
167
|
+
o.on "-S", "--state PATH", "Where to store the state details" do |arg|
|
168
|
+
c.state_path arg
|
169
|
+
end
|
170
|
+
|
171
|
+
o.on '-t', '--threads INT', "min:max threads to use (default 0:16)" do |arg|
|
172
|
+
min, max = arg.split(":")
|
173
|
+
if max
|
174
|
+
c.threads min, max
|
175
|
+
else
|
176
|
+
c.threads min, min
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
o.on "--tcp-mode", "Run the app in raw TCP mode instead of HTTP mode" do
|
181
|
+
c.tcp_mode!
|
182
|
+
end
|
183
|
+
|
184
|
+
o.on "-V", "--version", "Print the version information" do
|
185
|
+
puts "puma version #{Puma::Const::VERSION}"
|
186
|
+
exit 0
|
187
|
+
end
|
188
|
+
|
189
|
+
o.on "-w", "--workers COUNT",
|
190
|
+
"Activate cluster mode: How many worker processes to create" do |arg|
|
191
|
+
c.workers arg
|
192
|
+
end
|
193
|
+
|
194
|
+
o.on "--tag NAME", "Additional text to display in process listing" do |arg|
|
195
|
+
c.tag arg
|
196
|
+
end
|
197
|
+
|
198
|
+
o.on "--redirect-stdout FILE", "Redirect STDOUT to a specific file" do |arg|
|
199
|
+
@stdout = arg.to_s
|
200
|
+
end
|
201
|
+
|
202
|
+
o.on "--redirect-stderr FILE", "Redirect STDERR to a specific file" do |arg|
|
203
|
+
@stderr = arg.to_s
|
204
|
+
end
|
205
|
+
|
206
|
+
o.on "--[no-]redirect-append", "Append to redirected files" do |val|
|
207
|
+
@append = val
|
208
|
+
end
|
209
|
+
|
210
|
+
o.banner = "puma <options> <rackup file>"
|
211
|
+
|
212
|
+
o.on_tail "-h", "--help", "Show help" do
|
213
|
+
$stdout.puts o
|
214
|
+
exit 0
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
data/lib/puma/client.rb
ADDED
@@ -0,0 +1,434 @@
|
|
1
|
+
class IO
|
2
|
+
# We need to use this for a jruby work around on both 1.8 and 1.9.
|
3
|
+
# So this either creates the constant (on 1.8), or harmlessly
|
4
|
+
# reopens it (on 1.9).
|
5
|
+
module WaitReadable
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'puma/detect'
|
10
|
+
require 'puma/delegation'
|
11
|
+
require 'tempfile'
|
12
|
+
|
13
|
+
if Puma::IS_JRUBY
|
14
|
+
# We have to work around some OpenSSL buffer/io-readiness bugs
|
15
|
+
# so we pull it in regardless of if the user is binding
|
16
|
+
# to an SSL socket
|
17
|
+
require 'openssl'
|
18
|
+
end
|
19
|
+
|
20
|
+
module Puma
|
21
|
+
|
22
|
+
class ConnectionError < RuntimeError; end
|
23
|
+
|
24
|
+
class Client
|
25
|
+
include Puma::Const
|
26
|
+
extend Puma::Delegation
|
27
|
+
|
28
|
+
def initialize(io, env=nil)
|
29
|
+
@io = io
|
30
|
+
@to_io = io.to_io
|
31
|
+
@proto_env = env
|
32
|
+
if !env
|
33
|
+
@env = nil
|
34
|
+
else
|
35
|
+
@env = env.dup
|
36
|
+
end
|
37
|
+
|
38
|
+
@parser = HttpParser.new
|
39
|
+
@parsed_bytes = 0
|
40
|
+
@read_header = true
|
41
|
+
@ready = false
|
42
|
+
|
43
|
+
@body = nil
|
44
|
+
@buffer = nil
|
45
|
+
@tempfile = nil
|
46
|
+
|
47
|
+
@timeout_at = nil
|
48
|
+
|
49
|
+
@requests_served = 0
|
50
|
+
@hijacked = false
|
51
|
+
|
52
|
+
@peerip = nil
|
53
|
+
@remote_addr_header = nil
|
54
|
+
end
|
55
|
+
|
56
|
+
attr_reader :env, :to_io, :body, :io, :timeout_at, :ready, :hijacked,
|
57
|
+
:tempfile
|
58
|
+
|
59
|
+
attr_writer :peerip
|
60
|
+
|
61
|
+
attr_accessor :remote_addr_header
|
62
|
+
|
63
|
+
forward :closed?, :@io
|
64
|
+
|
65
|
+
def inspect
|
66
|
+
"#<Puma::Client:0x#{object_id.to_s(16)} @ready=#{@ready.inspect}>"
|
67
|
+
end
|
68
|
+
|
69
|
+
# For the hijack protocol (allows us to just put the Client object
|
70
|
+
# into the env)
|
71
|
+
def call
|
72
|
+
@hijacked = true
|
73
|
+
env[HIJACK_IO] ||= @io
|
74
|
+
end
|
75
|
+
|
76
|
+
def in_data_phase
|
77
|
+
!@read_header
|
78
|
+
end
|
79
|
+
|
80
|
+
def set_timeout(val)
|
81
|
+
@timeout_at = Time.now + val
|
82
|
+
end
|
83
|
+
|
84
|
+
def reset(fast_check=true)
|
85
|
+
@parser.reset
|
86
|
+
@read_header = true
|
87
|
+
@env = @proto_env.dup
|
88
|
+
@body = nil
|
89
|
+
@tempfile = nil
|
90
|
+
@parsed_bytes = 0
|
91
|
+
@ready = false
|
92
|
+
|
93
|
+
if @buffer
|
94
|
+
@parsed_bytes = @parser.execute(@env, @buffer, @parsed_bytes)
|
95
|
+
|
96
|
+
if @parser.finished?
|
97
|
+
return setup_body
|
98
|
+
elsif @parsed_bytes >= MAX_HEADER
|
99
|
+
raise HttpParserError,
|
100
|
+
"HEADER is longer than allowed, aborting client early."
|
101
|
+
end
|
102
|
+
|
103
|
+
return false
|
104
|
+
elsif fast_check &&
|
105
|
+
IO.select([@to_io], nil, nil, FAST_TRACK_KA_TIMEOUT)
|
106
|
+
return try_to_finish
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def close
|
111
|
+
begin
|
112
|
+
@io.close
|
113
|
+
rescue IOError
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# The object used for a request with no body. All requests with
|
118
|
+
# no body share this one object since it has no state.
|
119
|
+
EmptyBody = NullIO.new
|
120
|
+
|
121
|
+
def setup_chunked_body(body)
|
122
|
+
@chunked_body = true
|
123
|
+
@partial_part_left = 0
|
124
|
+
@prev_chunk = ""
|
125
|
+
|
126
|
+
@body = Tempfile.new(Const::PUMA_TMP_BASE)
|
127
|
+
@body.binmode
|
128
|
+
@tempfile = @body
|
129
|
+
|
130
|
+
return decode_chunk(body)
|
131
|
+
end
|
132
|
+
|
133
|
+
def decode_chunk(chunk)
|
134
|
+
if @partial_part_left > 0
|
135
|
+
if @partial_part_left <= chunk.size
|
136
|
+
@body << chunk[0..(@partial_part_left-3)] # skip the \r\n
|
137
|
+
chunk = chunk[@partial_part_left..-1]
|
138
|
+
else
|
139
|
+
@body << chunk
|
140
|
+
@partial_part_left -= chunk.size
|
141
|
+
return false
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
if @prev_chunk.empty?
|
146
|
+
io = StringIO.new(chunk)
|
147
|
+
else
|
148
|
+
io = StringIO.new(@prev_chunk+chunk)
|
149
|
+
@prev_chunk = ""
|
150
|
+
end
|
151
|
+
|
152
|
+
while !io.eof?
|
153
|
+
line = io.gets
|
154
|
+
if line.end_with?("\r\n")
|
155
|
+
len = line.strip.to_i(16)
|
156
|
+
if len == 0
|
157
|
+
@body.rewind
|
158
|
+
rest = io.read
|
159
|
+
@buffer = rest.empty? ? nil : rest
|
160
|
+
@requests_served += 1
|
161
|
+
@ready = true
|
162
|
+
return true
|
163
|
+
end
|
164
|
+
|
165
|
+
len += 2
|
166
|
+
|
167
|
+
part = io.read(len)
|
168
|
+
|
169
|
+
unless part
|
170
|
+
@partial_part_left = len
|
171
|
+
next
|
172
|
+
end
|
173
|
+
|
174
|
+
got = part.size
|
175
|
+
|
176
|
+
case
|
177
|
+
when got == len
|
178
|
+
@body << part[0..-3] # to skip the ending \r\n
|
179
|
+
when got <= len - 2
|
180
|
+
@body << part
|
181
|
+
@partial_part_left = len - part.size
|
182
|
+
when got == len - 1 # edge where we get just \r but not \n
|
183
|
+
@body << part[0..-2]
|
184
|
+
@partial_part_left = len - part.size
|
185
|
+
end
|
186
|
+
else
|
187
|
+
@prev_chunk = line
|
188
|
+
return false
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
return false
|
193
|
+
end
|
194
|
+
|
195
|
+
def read_chunked_body
|
196
|
+
while true
|
197
|
+
begin
|
198
|
+
chunk = @io.read_nonblock(4096)
|
199
|
+
rescue Errno::EAGAIN
|
200
|
+
return false
|
201
|
+
rescue SystemCallError, IOError
|
202
|
+
raise ConnectionError, "Connection error detected during read"
|
203
|
+
end
|
204
|
+
|
205
|
+
# No chunk means a closed socket
|
206
|
+
unless chunk
|
207
|
+
@body.close
|
208
|
+
@buffer = nil
|
209
|
+
@requests_served += 1
|
210
|
+
@ready = true
|
211
|
+
raise EOFError
|
212
|
+
end
|
213
|
+
|
214
|
+
return true if decode_chunk(chunk)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def setup_body
|
219
|
+
@read_header = false
|
220
|
+
|
221
|
+
body = @parser.body
|
222
|
+
|
223
|
+
te = @env[TRANSFER_ENCODING2]
|
224
|
+
|
225
|
+
if te && CHUNKED.casecmp(te) == 0
|
226
|
+
return setup_chunked_body(body)
|
227
|
+
end
|
228
|
+
|
229
|
+
@chunked_body = false
|
230
|
+
|
231
|
+
cl = @env[CONTENT_LENGTH]
|
232
|
+
|
233
|
+
unless cl
|
234
|
+
@buffer = body.empty? ? nil : body
|
235
|
+
@body = EmptyBody
|
236
|
+
@requests_served += 1
|
237
|
+
@ready = true
|
238
|
+
return true
|
239
|
+
end
|
240
|
+
|
241
|
+
remain = cl.to_i - body.bytesize
|
242
|
+
|
243
|
+
if remain <= 0
|
244
|
+
@body = StringIO.new(body)
|
245
|
+
@buffer = nil
|
246
|
+
@requests_served += 1
|
247
|
+
@ready = true
|
248
|
+
return true
|
249
|
+
end
|
250
|
+
|
251
|
+
if remain > MAX_BODY
|
252
|
+
@body = Tempfile.new(Const::PUMA_TMP_BASE)
|
253
|
+
@body.binmode
|
254
|
+
@tempfile = @body
|
255
|
+
else
|
256
|
+
# The body[0,0] trick is to get an empty string in the same
|
257
|
+
# encoding as body.
|
258
|
+
@body = StringIO.new body[0,0]
|
259
|
+
end
|
260
|
+
|
261
|
+
@body.write body
|
262
|
+
|
263
|
+
@body_remain = remain
|
264
|
+
|
265
|
+
return false
|
266
|
+
end
|
267
|
+
|
268
|
+
def try_to_finish
|
269
|
+
return read_body unless @read_header
|
270
|
+
|
271
|
+
begin
|
272
|
+
data = @io.read_nonblock(CHUNK_SIZE)
|
273
|
+
rescue Errno::EAGAIN
|
274
|
+
return false
|
275
|
+
rescue SystemCallError, IOError
|
276
|
+
raise ConnectionError, "Connection error detected during read"
|
277
|
+
end
|
278
|
+
|
279
|
+
if @buffer
|
280
|
+
@buffer << data
|
281
|
+
else
|
282
|
+
@buffer = data
|
283
|
+
end
|
284
|
+
|
285
|
+
@parsed_bytes = @parser.execute(@env, @buffer, @parsed_bytes)
|
286
|
+
|
287
|
+
if @parser.finished?
|
288
|
+
return setup_body
|
289
|
+
elsif @parsed_bytes >= MAX_HEADER
|
290
|
+
raise HttpParserError,
|
291
|
+
"HEADER is longer than allowed, aborting client early."
|
292
|
+
end
|
293
|
+
|
294
|
+
false
|
295
|
+
end
|
296
|
+
|
297
|
+
if IS_JRUBY
|
298
|
+
def jruby_start_try_to_finish
|
299
|
+
return read_body unless @read_header
|
300
|
+
|
301
|
+
begin
|
302
|
+
data = @io.sysread_nonblock(CHUNK_SIZE)
|
303
|
+
rescue OpenSSL::SSL::SSLError => e
|
304
|
+
return false if e.kind_of? IO::WaitReadable
|
305
|
+
raise e
|
306
|
+
end
|
307
|
+
|
308
|
+
if @buffer
|
309
|
+
@buffer << data
|
310
|
+
else
|
311
|
+
@buffer = data
|
312
|
+
end
|
313
|
+
|
314
|
+
@parsed_bytes = @parser.execute(@env, @buffer, @parsed_bytes)
|
315
|
+
|
316
|
+
if @parser.finished?
|
317
|
+
return setup_body
|
318
|
+
elsif @parsed_bytes >= MAX_HEADER
|
319
|
+
raise HttpParserError,
|
320
|
+
"HEADER is longer than allowed, aborting client early."
|
321
|
+
end
|
322
|
+
|
323
|
+
false
|
324
|
+
end
|
325
|
+
|
326
|
+
def eagerly_finish
|
327
|
+
return true if @ready
|
328
|
+
|
329
|
+
if @io.kind_of? OpenSSL::SSL::SSLSocket
|
330
|
+
return true if jruby_start_try_to_finish
|
331
|
+
end
|
332
|
+
|
333
|
+
return false unless IO.select([@to_io], nil, nil, 0)
|
334
|
+
try_to_finish
|
335
|
+
end
|
336
|
+
|
337
|
+
else
|
338
|
+
|
339
|
+
def eagerly_finish
|
340
|
+
return true if @ready
|
341
|
+
return false unless IO.select([@to_io], nil, nil, 0)
|
342
|
+
try_to_finish
|
343
|
+
end
|
344
|
+
end # IS_JRUBY
|
345
|
+
|
346
|
+
def finish
|
347
|
+
return true if @ready
|
348
|
+
until try_to_finish
|
349
|
+
IO.select([@to_io], nil, nil)
|
350
|
+
end
|
351
|
+
true
|
352
|
+
end
|
353
|
+
|
354
|
+
def read_body
|
355
|
+
if @chunked_body
|
356
|
+
return read_chunked_body
|
357
|
+
end
|
358
|
+
|
359
|
+
# Read an odd sized chunk so we can read even sized ones
|
360
|
+
# after this
|
361
|
+
remain = @body_remain
|
362
|
+
|
363
|
+
if remain > CHUNK_SIZE
|
364
|
+
want = CHUNK_SIZE
|
365
|
+
else
|
366
|
+
want = remain
|
367
|
+
end
|
368
|
+
|
369
|
+
begin
|
370
|
+
chunk = @io.read_nonblock(want)
|
371
|
+
rescue Errno::EAGAIN
|
372
|
+
return false
|
373
|
+
rescue SystemCallError, IOError
|
374
|
+
raise ConnectionError, "Connection error detected during read"
|
375
|
+
end
|
376
|
+
|
377
|
+
# No chunk means a closed socket
|
378
|
+
unless chunk
|
379
|
+
@body.close
|
380
|
+
@buffer = nil
|
381
|
+
@requests_served += 1
|
382
|
+
@ready = true
|
383
|
+
raise EOFError
|
384
|
+
end
|
385
|
+
|
386
|
+
remain -= @body.write(chunk)
|
387
|
+
|
388
|
+
if remain <= 0
|
389
|
+
@body.rewind
|
390
|
+
@buffer = nil
|
391
|
+
@requests_served += 1
|
392
|
+
@ready = true
|
393
|
+
return true
|
394
|
+
end
|
395
|
+
|
396
|
+
@body_remain = remain
|
397
|
+
|
398
|
+
false
|
399
|
+
end
|
400
|
+
|
401
|
+
def write_400
|
402
|
+
begin
|
403
|
+
@io << ERROR_400_RESPONSE
|
404
|
+
rescue StandardError
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
def write_408
|
409
|
+
begin
|
410
|
+
@io << ERROR_408_RESPONSE
|
411
|
+
rescue StandardError
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
def write_500
|
416
|
+
begin
|
417
|
+
@io << ERROR_500_RESPONSE
|
418
|
+
rescue StandardError
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
def peerip
|
423
|
+
return @peerip if @peerip
|
424
|
+
|
425
|
+
if @remote_addr_header
|
426
|
+
hdr = (@env[@remote_addr_header] || LOCALHOST_ADDR).split(/[\s,]/).first
|
427
|
+
@peerip = hdr
|
428
|
+
return hdr
|
429
|
+
end
|
430
|
+
|
431
|
+
@peerip ||= @io.peeraddr.last
|
432
|
+
end
|
433
|
+
end
|
434
|
+
end
|