rainbows 4.7.0 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|