rainbows 4.7.0 → 5.0.0
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 +4 -4
- data/.gitattributes +5 -0
- data/GIT-VERSION-GEN +1 -1
- data/README +5 -9
- data/bin/rainbows +3 -3
- data/lib/rainbows.rb +18 -6
- data/lib/rainbows/configurator.rb +8 -8
- data/lib/rainbows/const.rb +0 -3
- data/lib/rainbows/coolio.rb +2 -7
- data/lib/rainbows/coolio/client.rb +6 -6
- data/lib/rainbows/coolio/heartbeat.rb +2 -2
- data/lib/rainbows/coolio/thread_client.rb +3 -3
- data/lib/rainbows/dev_fd_response.rb +8 -14
- data/lib/rainbows/epoll/client.rb +9 -10
- data/lib/rainbows/error.rb +2 -2
- data/lib/rainbows/ev_core.rb +11 -17
- data/lib/rainbows/event_machine/client.rb +7 -7
- data/lib/rainbows/event_machine/try_defer.rb +1 -4
- data/lib/rainbows/fiber.rb +1 -1
- data/lib/rainbows/fiber/base.rb +3 -3
- data/lib/rainbows/fiber/coolio/heartbeat.rb +1 -1
- data/lib/rainbows/fiber/io.rb +1 -1
- data/lib/rainbows/http_parser.rb +24 -0
- data/lib/rainbows/http_server.rb +5 -4
- data/lib/rainbows/join_threads.rb +2 -2
- data/lib/rainbows/max_body.rb +4 -9
- data/lib/rainbows/process_client.rb +11 -12
- data/lib/rainbows/response.rb +20 -37
- data/lib/rainbows/revactor.rb +0 -1
- data/lib/rainbows/revactor/client.rb +2 -3
- data/lib/rainbows/revactor/proxy.rb +1 -1
- data/lib/rainbows/reverse_proxy.rb +9 -19
- data/lib/rainbows/reverse_proxy/coolio.rb +3 -3
- data/lib/rainbows/reverse_proxy/ev_client.rb +2 -5
- data/lib/rainbows/reverse_proxy/event_machine.rb +1 -1
- data/lib/rainbows/sendfile.rb +3 -9
- data/lib/rainbows/server_token.rb +1 -6
- data/lib/rainbows/stream_response_epoll.rb +8 -9
- data/lib/rainbows/thread_timeout.rb +4 -4
- data/lib/rainbows/writer_thread_pool.rb +2 -2
- data/lib/rainbows/xepoll_thread_pool/client.rb +3 -4
- data/lib/rainbows/xepoll_thread_spawn/client.rb +3 -4
- data/rainbows.gemspec +1 -1
- data/t/t0105-rack-input-limit-bigger.sh +10 -2
- data/t/test_isolate.rb +1 -1
- metadata +5 -4
@@ -10,9 +10,6 @@
|
|
10
10
|
# See http://brainspl.at/articles/2008/04/18/deferred-requests-with-merb-ebb-and-thin
|
11
11
|
# for more information.
|
12
12
|
class Rainbows::EventMachine::TryDefer
|
13
|
-
# shortcuts
|
14
|
-
ASYNC_CALLBACK = Rainbows::EvCore::ASYNC_CALLBACK # :nodoc:
|
15
|
-
|
16
13
|
def initialize(app) # :nodoc:
|
17
14
|
# the entire app becomes multithreaded, even the root (non-deferred)
|
18
15
|
# thread since any thread can share processes with others
|
@@ -22,7 +19,7 @@ def initialize(app) # :nodoc:
|
|
22
19
|
|
23
20
|
def call(env) # :nodoc:
|
24
21
|
if @app.deferred?(env)
|
25
|
-
EM.defer(proc { catch(:async) { @app.call(env) } }, env[
|
22
|
+
EM.defer(proc { catch(:async) { @app.call(env) } }, env['async.callback'])
|
26
23
|
# all of the async/deferred stuff breaks Rack::Lint :<
|
27
24
|
nil
|
28
25
|
else
|
data/lib/rainbows/fiber.rb
CHANGED
data/lib/rainbows/fiber/base.rb
CHANGED
@@ -40,7 +40,7 @@ def schedule
|
|
40
40
|
# woken and returns an interval to IO.select on
|
41
41
|
def schedule_sleepers
|
42
42
|
max = nil
|
43
|
-
now =
|
43
|
+
now = Rainbows.now
|
44
44
|
fibs = []
|
45
45
|
ZZ.delete_if { |fib, time|
|
46
46
|
if now >= time
|
@@ -50,11 +50,11 @@ def schedule_sleepers
|
|
50
50
|
false
|
51
51
|
end
|
52
52
|
}
|
53
|
-
fibs.each
|
53
|
+
fibs.each(&:resume)
|
54
54
|
|
55
55
|
max_sleep = 1.0 # wake up semi-frequently to prevent SIGKILL from master
|
56
56
|
if max
|
57
|
-
max -=
|
57
|
+
max -= Rainbows.now
|
58
58
|
return 0 if max < 0.0
|
59
59
|
return max_sleep if max > max_sleep
|
60
60
|
max
|
@@ -5,7 +5,7 @@ class Rainbows::Fiber::Coolio::Heartbeat < Coolio::TimerWatcher
|
|
5
5
|
ZZ = Rainbows::Fiber::ZZ
|
6
6
|
def on_timer
|
7
7
|
exit if (! Rainbows.tick && Rainbows.cur <= 0)
|
8
|
-
now =
|
8
|
+
now = Rainbows.now
|
9
9
|
fibs = []
|
10
10
|
ZZ.delete_if { |fib, time| now >= time ? fibs << fib : ! fib.alive? }
|
11
11
|
fibs.each { |fib| fib.resume if fib.alive? }
|
data/lib/rainbows/fiber/io.rb
CHANGED
data/lib/rainbows/http_parser.rb
CHANGED
@@ -2,6 +2,30 @@
|
|
2
2
|
# :enddoc:
|
3
3
|
# avoid modifying Unicorn::HttpParser
|
4
4
|
class Rainbows::HttpParser < Unicorn::HttpParser
|
5
|
+
@keepalive_requests = 100
|
6
|
+
class << self
|
7
|
+
attr_accessor :keepalive_requests
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(*args)
|
11
|
+
@keepalive_requests = self.class.keepalive_requests
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
def next?
|
16
|
+
return false if (@keepalive_requests -= 1) <= 0
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def hijack_setup(io)
|
21
|
+
@hijack_io = io
|
22
|
+
env['rack.hijack'] = self # avoid allocating a new proc this way
|
23
|
+
end
|
24
|
+
|
25
|
+
def call # for rack.hijack
|
26
|
+
env['rack.hijack_io'] = @hijack_io
|
27
|
+
end
|
28
|
+
|
5
29
|
def self.quit
|
6
30
|
alias_method :next?, :never!
|
7
31
|
end
|
data/lib/rainbows/http_server.rb
CHANGED
@@ -8,6 +8,7 @@ class Rainbows::HttpServer < Unicorn::HttpServer
|
|
8
8
|
attr_accessor :client_header_buffer_size
|
9
9
|
attr_accessor :client_max_body_size
|
10
10
|
attr_reader :use
|
11
|
+
attr_reader :master_pid
|
11
12
|
|
12
13
|
def self.setup(block)
|
13
14
|
Rainbows.server.instance_eval(&block)
|
@@ -81,10 +82,10 @@ def svc
|
|
81
82
|
end
|
82
83
|
|
83
84
|
def use=(mod)
|
84
|
-
@use = mod.to_s.split(
|
85
|
+
@use = mod.to_s.split('::')[-1].to_sym
|
85
86
|
new_defaults = {
|
86
87
|
'rainbows.model' => @use,
|
87
|
-
'rack.multithread' =>
|
88
|
+
'rack.multithread' => mod.to_s.include?('Thread'),
|
88
89
|
'rainbows.autochunk' => [:Coolio,:Rev,:Epoll,:XEpoll,
|
89
90
|
:EventMachine,:NeverBlock].include?(@use),
|
90
91
|
}
|
@@ -92,11 +93,11 @@ def use=(mod)
|
|
92
93
|
end
|
93
94
|
|
94
95
|
def keepalive_requests=(nr)
|
95
|
-
|
96
|
+
Rainbows::HttpParser.keepalive_requests = nr
|
96
97
|
end
|
97
98
|
|
98
99
|
def keepalive_requests
|
99
|
-
|
100
|
+
Rainbows::HttpParser.keepalive_requests
|
100
101
|
end
|
101
102
|
|
102
103
|
def client_max_header_size=(bytes)
|
@@ -5,12 +5,12 @@ module Rainbows::JoinThreads
|
|
5
5
|
|
6
6
|
# blocking acceptor threads must be forced to run
|
7
7
|
def self.acceptors(threads)
|
8
|
-
expire =
|
8
|
+
expire = Rainbows.now + Rainbows.server.timeout
|
9
9
|
threads.delete_if do |thr|
|
10
10
|
Rainbows.tick
|
11
11
|
begin
|
12
12
|
# blocking accept() may not wake up properly
|
13
|
-
thr.raise(Errno::EINTR) if
|
13
|
+
thr.raise(Errno::EINTR) if Rainbows.now > expire && thr.stop?
|
14
14
|
|
15
15
|
thr.run
|
16
16
|
thr.join(0.01)
|
data/lib/rainbows/max_body.rb
CHANGED
@@ -48,19 +48,14 @@ def initialize(app, limit = nil)
|
|
48
48
|
@app, @limit = app, limit
|
49
49
|
end
|
50
50
|
|
51
|
-
# :stopdoc:
|
52
|
-
RACK_INPUT = "rack.input".freeze
|
53
|
-
CONTENT_LENGTH = "CONTENT_LENGTH"
|
54
|
-
HTTP_TRANSFER_ENCODING = "HTTP_TRANSFER_ENCODING"
|
55
|
-
|
56
51
|
# our main Rack middleware endpoint
|
57
52
|
def call(env)
|
58
53
|
@limit = Rainbows.server.client_max_body_size if nil == @limit
|
59
54
|
catch(:rainbows_EFBIG) do
|
60
|
-
len = env[CONTENT_LENGTH]
|
55
|
+
len = env['CONTENT_LENGTH']
|
61
56
|
if len && len.to_i > @limit
|
62
57
|
return err
|
63
|
-
elsif /\Achunked\z/i =~ env[HTTP_TRANSFER_ENCODING]
|
58
|
+
elsif /\Achunked\z/i =~ env['HTTP_TRANSFER_ENCODING']
|
64
59
|
limit_input!(env)
|
65
60
|
end
|
66
61
|
@app.call(env)
|
@@ -89,9 +84,9 @@ def err # :nodoc:
|
|
89
84
|
end
|
90
85
|
|
91
86
|
def limit_input!(env)
|
92
|
-
input = env[
|
87
|
+
input = env['rack.input']
|
93
88
|
klass = input.respond_to?(:rewind) ? RewindableWrapper : Wrapper
|
94
|
-
env[
|
89
|
+
env['rack.input'] = klass.new(input, @limit)
|
95
90
|
end
|
96
91
|
|
97
92
|
# :startdoc:
|
@@ -5,12 +5,11 @@ module Rainbows::ProcessClient
|
|
5
5
|
include Rainbows::Const
|
6
6
|
|
7
7
|
NULL_IO = Unicorn::HttpRequest::NULL_IO
|
8
|
-
RACK_INPUT = Unicorn::HttpRequest::RACK_INPUT
|
9
8
|
IC = Unicorn::HttpRequest.input_class
|
10
9
|
Rainbows.config!(self, :client_header_buffer_size, :keepalive_timeout)
|
11
10
|
|
12
11
|
def read_expire
|
13
|
-
|
12
|
+
Rainbows.now + KEEPALIVE_TIMEOUT
|
14
13
|
end
|
15
14
|
|
16
15
|
# used for reading headers (respecting keepalive_timeout)
|
@@ -19,7 +18,7 @@ def timed_read(buf)
|
|
19
18
|
begin
|
20
19
|
case rv = kgio_tryread(CLIENT_HEADER_BUFFER_SIZE, buf)
|
21
20
|
when :wait_readable
|
22
|
-
return if expire && expire <
|
21
|
+
return if expire && expire < Rainbows.now
|
23
22
|
expire ||= read_expire
|
24
23
|
kgio_wait_readable(KEEPALIVE_TIMEOUT)
|
25
24
|
else
|
@@ -39,13 +38,13 @@ def process_loop
|
|
39
38
|
end
|
40
39
|
|
41
40
|
set_input(env, hp)
|
42
|
-
env[REMOTE_ADDR] = kgio_addr
|
43
|
-
hp.hijack_setup(
|
41
|
+
env['REMOTE_ADDR'] = kgio_addr
|
42
|
+
hp.hijack_setup(to_io)
|
44
43
|
status, headers, body = APP.call(env.merge!(RACK_DEFAULTS))
|
45
44
|
|
46
45
|
if 100 == status.to_i
|
47
|
-
write(
|
48
|
-
env.delete(HTTP_EXPECT)
|
46
|
+
write("HTTP/1.1 100 Continue\r\n\r\n".freeze)
|
47
|
+
env.delete('HTTP_EXPECT'.freeze)
|
49
48
|
status, headers, body = APP.call(env)
|
50
49
|
end
|
51
50
|
return if hp.hijacked?
|
@@ -66,18 +65,18 @@ def handle_error(e)
|
|
66
65
|
end
|
67
66
|
|
68
67
|
def set_input(env, hp)
|
69
|
-
env[
|
68
|
+
env['rack.input'] = 0 == hp.content_length ? NULL_IO : IC.new(self, hp)
|
70
69
|
end
|
71
70
|
|
72
71
|
def process_pipeline(env, hp)
|
73
72
|
begin
|
74
73
|
set_input(env, hp)
|
75
|
-
env[REMOTE_ADDR] = kgio_addr
|
76
|
-
hp.hijack_setup(
|
74
|
+
env['REMOTE_ADDR'] = kgio_addr
|
75
|
+
hp.hijack_setup(to_io)
|
77
76
|
status, headers, body = APP.call(env.merge!(RACK_DEFAULTS))
|
78
77
|
if 100 == status.to_i
|
79
|
-
write(
|
80
|
-
env.delete(HTTP_EXPECT)
|
78
|
+
write("HTTP/1.1 100 Continue\r\n\r\n".freeze)
|
79
|
+
env.delete('HTTP_EXPECT'.freeze)
|
81
80
|
status, headers, body = APP.call(env)
|
82
81
|
end
|
83
82
|
return if hp.hijacked?
|
data/lib/rainbows/response.rb
CHANGED
@@ -2,10 +2,6 @@
|
|
2
2
|
# :enddoc:
|
3
3
|
module Rainbows::Response
|
4
4
|
include Unicorn::HttpResponse
|
5
|
-
Close = "close"
|
6
|
-
KeepAlive = "keep-alive"
|
7
|
-
Content_Length = "Content-Length".freeze
|
8
|
-
Transfer_Encoding = "Transfer-Encoding".freeze
|
9
5
|
Rainbows.config!(self, :copy_stream)
|
10
6
|
|
11
7
|
# private file class for IO objects opened by Rainbows! itself (and not
|
@@ -19,20 +15,8 @@ def self.setup
|
|
19
15
|
Rainbows::HttpParser.keepalive_requests = 0
|
20
16
|
end
|
21
17
|
|
22
|
-
|
23
|
-
|
24
|
-
RACK_HIJACK = "rack.hijack"
|
25
|
-
|
26
|
-
def hijack_prepare(value)
|
27
|
-
value
|
28
|
-
end
|
29
|
-
|
30
|
-
def hijack_socket
|
31
|
-
@hp.env[RACK_HIJACK].call
|
32
|
-
end
|
33
|
-
else
|
34
|
-
def hijack_prepare(_)
|
35
|
-
end
|
18
|
+
def hijack_socket
|
19
|
+
@hp.env['rack.hijack'].call
|
36
20
|
end
|
37
21
|
|
38
22
|
# returns the original body on success
|
@@ -40,29 +24,30 @@ def hijack_prepare(_)
|
|
40
24
|
def write_headers(status, headers, alive, body)
|
41
25
|
@hp.headers? or return body
|
42
26
|
hijack = nil
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
"
|
27
|
+
code = status.to_i
|
28
|
+
msg = Rack::Utils::HTTP_STATUS_CODES[code]
|
29
|
+
buf = "HTTP/1.1 #{msg ? %Q(#{code} #{msg}) : status}\r\n" \
|
30
|
+
"Date: #{httpdate}\r\n"
|
47
31
|
headers.each do |key, value|
|
48
32
|
case key
|
49
|
-
when %r{\A(?:Date
|
33
|
+
when %r{\A(?:Date|Connection)\z}i
|
50
34
|
next
|
51
35
|
when "rack.hijack"
|
52
36
|
# this was an illegal key in Rack < 1.5, so it should be
|
53
37
|
# OK to silently discard it for those older versions
|
54
|
-
hijack =
|
38
|
+
hijack = value
|
55
39
|
alive = false # No persistent connections for hijacking
|
56
40
|
else
|
57
41
|
if /\n/ =~ value
|
58
42
|
# avoiding blank, key-only cookies with /\n+/
|
59
|
-
|
43
|
+
value.split(/\n+/).each { |v| buf << "#{key}: #{v}\r\n" }
|
60
44
|
else
|
61
45
|
buf << "#{key}: #{value}\r\n"
|
62
46
|
end
|
63
47
|
end
|
64
48
|
end
|
65
|
-
write(buf <<
|
49
|
+
write(buf << (alive ? "Connection: keep-alive\r\n\r\n".freeze
|
50
|
+
: "Connection: close\r\n\r\n".freeze))
|
66
51
|
|
67
52
|
if hijack
|
68
53
|
body = nil # ensure caller does not close body
|
@@ -152,30 +137,27 @@ def write_body_stream(body)
|
|
152
137
|
end # ! COPY_STREAM
|
153
138
|
|
154
139
|
if IO.method_defined?(:trysendfile) || COPY_STREAM
|
155
|
-
HTTP_RANGE = 'HTTP_RANGE'
|
156
|
-
Content_Range = 'Content-Range'.freeze
|
157
|
-
|
158
140
|
# This does not support multipart responses (does anybody actually
|
159
141
|
# use those?)
|
160
142
|
def sendfile_range(status, headers)
|
161
143
|
status = status.to_i
|
162
144
|
if 206 == status
|
163
|
-
if %r{\Abytes (\d+)-(\d+)/\d+\z} =~ headers[
|
145
|
+
if %r{\Abytes (\d+)-(\d+)/\d+\z} =~ headers['Content-Range'.freeze]
|
164
146
|
a, b = $1.to_i, $2.to_i
|
165
147
|
return 206, headers, [ a, b - a + 1 ]
|
166
148
|
end
|
167
149
|
return # wtf...
|
168
150
|
end
|
169
151
|
200 == status &&
|
170
|
-
/\Abytes=(\d+-\d*|\d*-\d+)\z/ =~ @hp.env[HTTP_RANGE] or
|
152
|
+
/\Abytes=(\d+-\d*|\d*-\d+)\z/ =~ @hp.env['HTTP_RANGE'] or
|
171
153
|
return
|
172
|
-
a, b = $1.split(
|
154
|
+
a, b = $1.split('-'.freeze)
|
173
155
|
|
174
156
|
# HeaderHash is quite expensive, and Rack::File currently
|
175
157
|
# uses a regular Ruby Hash with properly-cased headers the
|
176
158
|
# same way they're presented in rfc2616.
|
177
159
|
headers = Rack::Utils::HeaderHash.new(headers) unless Hash === headers
|
178
|
-
clen = headers[
|
160
|
+
clen = headers['Content-Length'.freeze] or return
|
179
161
|
size = clen.to_i
|
180
162
|
|
181
163
|
if b.nil? # bytes=M-
|
@@ -190,13 +172,14 @@ def sendfile_range(status, headers)
|
|
190
172
|
end
|
191
173
|
|
192
174
|
if 0 > count || offset >= size
|
193
|
-
headers[
|
194
|
-
headers[
|
175
|
+
headers['Content-Length'.freeze] = "0"
|
176
|
+
headers['Content-Range'.freeze] = "bytes */#{clen}"
|
195
177
|
return 416, headers, nil
|
196
178
|
else
|
197
179
|
count = size if count > size
|
198
|
-
headers[
|
199
|
-
headers[
|
180
|
+
headers['Content-Length'.freeze] = count.to_s
|
181
|
+
headers['Content-Range'.freeze] =
|
182
|
+
"bytes #{offset}-#{offset+count-1}/#{clen}"
|
200
183
|
return 206, headers, [ offset, count ]
|
201
184
|
end
|
202
185
|
end
|
data/lib/rainbows/revactor.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
# :enddoc:
|
3
|
-
require 'fcntl'
|
4
3
|
class Rainbows::Revactor::Client
|
5
4
|
autoload :TeeSocket, 'rainbows/revactor/client/tee_socket'
|
6
5
|
RD_ARGS = {}
|
@@ -11,7 +10,7 @@ class Rainbows::Revactor::Client
|
|
11
10
|
def initialize(client)
|
12
11
|
@client, @rd_args, @ts = client, [ nil ], nil
|
13
12
|
io = client.instance_variable_get(:@_io)
|
14
|
-
io.
|
13
|
+
io.close_on_exec = true
|
15
14
|
@kgio_addr = if Revactor::TCP::Socket === client
|
16
15
|
@rd_args << RD_ARGS
|
17
16
|
client.remote_addr
|
@@ -33,7 +32,7 @@ def timed_read(buf2)
|
|
33
32
|
end
|
34
33
|
|
35
34
|
def set_input(env, hp)
|
36
|
-
env[
|
35
|
+
env['rack.input'] = 0 == hp.content_length ?
|
37
36
|
NULL_IO : IC.new(@ts = TeeSocket.new(@client), hp)
|
38
37
|
end
|
39
38
|
|
@@ -22,7 +22,7 @@ def each
|
|
22
22
|
# (instead of Errno::EPIPE), so we need to limit the rescue
|
23
23
|
# to just readpartial and let EOFErrors during yield bubble up
|
24
24
|
begin
|
25
|
-
buf = readpartial(
|
25
|
+
buf = readpartial(16384)
|
26
26
|
rescue EOFError
|
27
27
|
break
|
28
28
|
end while yield(buf) || true
|
@@ -47,15 +47,6 @@ class Rainbows::ReverseProxy
|
|
47
47
|
autoload :EventMachine, 'rainbows/reverse_proxy/event_machine'
|
48
48
|
autoload :EvClient, 'rainbows/reverse_proxy/ev_client'
|
49
49
|
|
50
|
-
HTTP_X_FORWARDED_FOR = "HTTP_X_FORWARDED_FOR"
|
51
|
-
REMOTE_ADDR = "REMOTE_ADDR"
|
52
|
-
REQUEST_METHOD = "REQUEST_METHOD"
|
53
|
-
REQUEST_URI = "REQUEST_URI"
|
54
|
-
CRLF = "\r\n"
|
55
|
-
TR = %w(_ -)
|
56
|
-
CONTENT_LENGTH = "CONTENT_LENGTH"
|
57
|
-
HTTP_TRANSFER_ENCODING = "HTTP_TRANSFER_ENCODING"
|
58
|
-
RackInput = "rack.input"
|
59
50
|
E502 = [ 502, [ %w(Content-Length 0), %w(Content-Type text/plain) ], [] ]
|
60
51
|
|
61
52
|
def initialize(opts)
|
@@ -113,24 +104,23 @@ def call(env)
|
|
113
104
|
|
114
105
|
# returns request headers for sending to the upstream as a string
|
115
106
|
def build_headers(env, input)
|
116
|
-
remote_addr = env[REMOTE_ADDR]
|
117
|
-
xff = env[HTTP_X_FORWARDED_FOR]
|
107
|
+
remote_addr = env['REMOTE_ADDR']
|
108
|
+
xff = env['HTTP_X_FORWARDED_FOR']
|
118
109
|
xff = xff ? "#{xff},#{remote_addr}" : remote_addr
|
119
|
-
req = "#{env[REQUEST_METHOD]} #{env[REQUEST_URI]} HTTP/1.0\r\n" \
|
110
|
+
req = "#{env['REQUEST_METHOD']} #{env['REQUEST_URI']} HTTP/1.0\r\n" \
|
120
111
|
"Connection: close\r\n" \
|
121
112
|
"X-Forwarded-For: #{xff}\r\n"
|
122
|
-
uscore, dash = *TR
|
123
113
|
env.each do |key, value|
|
124
114
|
%r{\AHTTP_(\w+)\z} =~ key or next
|
125
115
|
key = $1
|
126
116
|
next if %r{\A(?:VERSION|CONNECTION|KEEP_ALIVE|X_FORWARDED_FOR)\z}x =~ key
|
127
|
-
key.tr!(
|
117
|
+
key.tr!('_'.freeze, '-'.freeze)
|
128
118
|
req << "#{key}: #{value}\r\n"
|
129
119
|
end
|
130
120
|
input and req << (input.respond_to?(:size) ?
|
131
121
|
"Content-Length: #{input.size}\r\n" :
|
132
|
-
"Transfer-Encoding: chunked\r\n")
|
133
|
-
req <<
|
122
|
+
"Transfer-Encoding: chunked\r\n".freeze)
|
123
|
+
req << "\r\n".freeze
|
134
124
|
end
|
135
125
|
|
136
126
|
def pick_upstream(env) # +env+ is reserved for future expansion
|
@@ -139,16 +129,16 @@ def pick_upstream(env) # +env+ is reserved for future expansion
|
|
139
129
|
end
|
140
130
|
|
141
131
|
def prepare_input!(env)
|
142
|
-
if cl = env[CONTENT_LENGTH]
|
132
|
+
if cl = env['CONTENT_LENGTH']
|
143
133
|
size = cl.to_i
|
144
134
|
size > 0 or return
|
145
|
-
elsif %r{\Achunked\z}i =~ env.delete(HTTP_TRANSFER_ENCODING)
|
135
|
+
elsif %r{\Achunked\z}i =~ env.delete('HTTP_TRANSFER_ENCODING')
|
146
136
|
# do people use multiple transfer-encodings?
|
147
137
|
else
|
148
138
|
return
|
149
139
|
end
|
150
140
|
|
151
|
-
input = env[
|
141
|
+
input = env['rack.input']
|
152
142
|
if input.respond_to?(:rewind)
|
153
143
|
if input.respond_to?(:size)
|
154
144
|
input.size # TeeInput-specific behavior
|