puma-simon 3.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|