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.
Files changed (157) hide show
  1. checksums.yaml +7 -0
  2. data/.github/issue_template.md +20 -0
  3. data/.gitignore +18 -0
  4. data/.hoeignore +12 -0
  5. data/.travis.yml +29 -0
  6. data/DEPLOYMENT.md +91 -0
  7. data/Gemfile +12 -0
  8. data/History.md +1254 -0
  9. data/LICENSE +26 -0
  10. data/Manifest.txt +78 -0
  11. data/README.md +353 -0
  12. data/Rakefile +158 -0
  13. data/Release.md +9 -0
  14. data/bin/puma +10 -0
  15. data/bin/puma-wild +31 -0
  16. data/bin/pumactl +12 -0
  17. data/docs/nginx.md +80 -0
  18. data/docs/signals.md +43 -0
  19. data/docs/systemd.md +197 -0
  20. data/examples/CA/cacert.pem +23 -0
  21. data/examples/CA/newcerts/cert_1.pem +19 -0
  22. data/examples/CA/newcerts/cert_2.pem +19 -0
  23. data/examples/CA/private/cakeypair.pem +30 -0
  24. data/examples/CA/serial +1 -0
  25. data/examples/config.rb +200 -0
  26. data/examples/plugins/redis_stop_puma.rb +46 -0
  27. data/examples/puma/cert_puma.pem +19 -0
  28. data/examples/puma/client-certs/ca.crt +19 -0
  29. data/examples/puma/client-certs/ca.key +27 -0
  30. data/examples/puma/client-certs/client.crt +19 -0
  31. data/examples/puma/client-certs/client.key +27 -0
  32. data/examples/puma/client-certs/client_expired.crt +19 -0
  33. data/examples/puma/client-certs/client_expired.key +27 -0
  34. data/examples/puma/client-certs/client_unknown.crt +19 -0
  35. data/examples/puma/client-certs/client_unknown.key +27 -0
  36. data/examples/puma/client-certs/generate.rb +78 -0
  37. data/examples/puma/client-certs/keystore.jks +0 -0
  38. data/examples/puma/client-certs/server.crt +19 -0
  39. data/examples/puma/client-certs/server.key +27 -0
  40. data/examples/puma/client-certs/server.p12 +0 -0
  41. data/examples/puma/client-certs/unknown_ca.crt +19 -0
  42. data/examples/puma/client-certs/unknown_ca.key +27 -0
  43. data/examples/puma/csr_puma.pem +11 -0
  44. data/examples/puma/keystore.jks +0 -0
  45. data/examples/puma/puma_keypair.pem +15 -0
  46. data/examples/qc_config.rb +13 -0
  47. data/ext/puma_http11/PumaHttp11Service.java +17 -0
  48. data/ext/puma_http11/ext_help.h +15 -0
  49. data/ext/puma_http11/extconf.rb +15 -0
  50. data/ext/puma_http11/http11_parser.c +1069 -0
  51. data/ext/puma_http11/http11_parser.h +65 -0
  52. data/ext/puma_http11/http11_parser.java.rl +161 -0
  53. data/ext/puma_http11/http11_parser.rl +147 -0
  54. data/ext/puma_http11/http11_parser_common.rl +54 -0
  55. data/ext/puma_http11/io_buffer.c +155 -0
  56. data/ext/puma_http11/mini_ssl.c +457 -0
  57. data/ext/puma_http11/org/jruby/puma/Http11.java +234 -0
  58. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +473 -0
  59. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +339 -0
  60. data/ext/puma_http11/puma_http11.c +500 -0
  61. data/gemfiles/2.1-Gemfile +12 -0
  62. data/lib/puma.rb +15 -0
  63. data/lib/puma/accept_nonblock.rb +23 -0
  64. data/lib/puma/app/status.rb +66 -0
  65. data/lib/puma/binder.rb +402 -0
  66. data/lib/puma/cli.rb +220 -0
  67. data/lib/puma/client.rb +434 -0
  68. data/lib/puma/cluster.rb +510 -0
  69. data/lib/puma/commonlogger.rb +106 -0
  70. data/lib/puma/compat.rb +14 -0
  71. data/lib/puma/configuration.rb +364 -0
  72. data/lib/puma/const.rb +224 -0
  73. data/lib/puma/control_cli.rb +259 -0
  74. data/lib/puma/convenient.rb +23 -0
  75. data/lib/puma/daemon_ext.rb +31 -0
  76. data/lib/puma/delegation.rb +11 -0
  77. data/lib/puma/detect.rb +13 -0
  78. data/lib/puma/dsl.rb +486 -0
  79. data/lib/puma/events.rb +152 -0
  80. data/lib/puma/io_buffer.rb +7 -0
  81. data/lib/puma/java_io_buffer.rb +45 -0
  82. data/lib/puma/jruby_restart.rb +83 -0
  83. data/lib/puma/launcher.rb +410 -0
  84. data/lib/puma/minissl.rb +221 -0
  85. data/lib/puma/null_io.rb +42 -0
  86. data/lib/puma/plugin.rb +115 -0
  87. data/lib/puma/plugin/tmp_restart.rb +35 -0
  88. data/lib/puma/rack/backports/uri/common_193.rb +33 -0
  89. data/lib/puma/rack/builder.rb +298 -0
  90. data/lib/puma/rack/urlmap.rb +91 -0
  91. data/lib/puma/rack_default.rb +7 -0
  92. data/lib/puma/reactor.rb +210 -0
  93. data/lib/puma/runner.rb +171 -0
  94. data/lib/puma/server.rb +949 -0
  95. data/lib/puma/single.rb +112 -0
  96. data/lib/puma/state_file.rb +29 -0
  97. data/lib/puma/tcp_logger.rb +39 -0
  98. data/lib/puma/thread_pool.rb +297 -0
  99. data/lib/puma/util.rb +128 -0
  100. data/lib/rack/handler/puma.rb +78 -0
  101. data/puma.gemspec +52 -0
  102. data/test/ab_rs.rb +22 -0
  103. data/test/config.rb +2 -0
  104. data/test/config/app.rb +9 -0
  105. data/test/config/plugin.rb +1 -0
  106. data/test/config/settings.rb +2 -0
  107. data/test/config/state_file_testing_config.rb +14 -0
  108. data/test/hello-bind.ru +2 -0
  109. data/test/hello-delay.ru +3 -0
  110. data/test/hello-map.ru +3 -0
  111. data/test/hello-post.ru +4 -0
  112. data/test/hello-stuck.ru +1 -0
  113. data/test/hello-tcp.ru +5 -0
  114. data/test/hello.ru +1 -0
  115. data/test/hijack.ru +6 -0
  116. data/test/hijack2.ru +5 -0
  117. data/test/lobster.ru +4 -0
  118. data/test/shell/run.sh +24 -0
  119. data/test/shell/t1.rb +19 -0
  120. data/test/shell/t1_conf.rb +3 -0
  121. data/test/shell/t2.rb +17 -0
  122. data/test/shell/t2_conf.rb +6 -0
  123. data/test/shell/t3.rb +25 -0
  124. data/test/shell/t3_conf.rb +5 -0
  125. data/test/slow.ru +4 -0
  126. data/test/ssl_config.rb +4 -0
  127. data/test/test_app_status.rb +93 -0
  128. data/test/test_binder.rb +31 -0
  129. data/test/test_cli.rb +209 -0
  130. data/test/test_config.rb +95 -0
  131. data/test/test_events.rb +161 -0
  132. data/test/test_helper.rb +50 -0
  133. data/test/test_http10.rb +27 -0
  134. data/test/test_http11.rb +186 -0
  135. data/test/test_integration.rb +247 -0
  136. data/test/test_iobuffer.rb +39 -0
  137. data/test/test_minissl.rb +29 -0
  138. data/test/test_null_io.rb +49 -0
  139. data/test/test_persistent.rb +245 -0
  140. data/test/test_puma_server.rb +626 -0
  141. data/test/test_puma_server_ssl.rb +222 -0
  142. data/test/test_rack_handler.rb +57 -0
  143. data/test/test_rack_server.rb +138 -0
  144. data/test/test_tcp_logger.rb +39 -0
  145. data/test/test_tcp_rack.rb +36 -0
  146. data/test/test_thread_pool.rb +250 -0
  147. data/test/test_unix_socket.rb +35 -0
  148. data/test/test_web_server.rb +88 -0
  149. data/tools/jungle/README.md +9 -0
  150. data/tools/jungle/init.d/README.md +59 -0
  151. data/tools/jungle/init.d/puma +421 -0
  152. data/tools/jungle/init.d/run-puma +18 -0
  153. data/tools/jungle/upstart/README.md +61 -0
  154. data/tools/jungle/upstart/puma-manager.conf +31 -0
  155. data/tools/jungle/upstart/puma.conf +69 -0
  156. data/tools/trickletest.rb +45 -0
  157. metadata +297 -0
@@ -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
@@ -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