rainbows 4.4.3 → 4.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +1 -0
- data/.gitignore +1 -0
- data/Documentation/rainbows.1.txt +8 -4
- data/GIT-VERSION-GEN +34 -35
- data/GNUmakefile +4 -2
- data/HACKING +72 -0
- data/bin/rainbows +5 -0
- data/lib/rainbows/const.rb +3 -3
- data/lib/rainbows/coolio/client.rb +18 -6
- data/lib/rainbows/coolio/thread_client.rb +2 -0
- data/lib/rainbows/epoll/client.rb +35 -12
- data/lib/rainbows/ev_core.rb +5 -4
- data/lib/rainbows/event_machine/client.rb +9 -4
- data/lib/rainbows/process_client.rb +7 -3
- data/lib/rainbows/response.rb +54 -18
- data/lib/rainbows/revactor/client/methods.rb +1 -1
- data/lib/rainbows/stream_response_epoll.rb +34 -15
- data/lib/rainbows/stream_response_epoll/client.rb +11 -3
- data/lib/rainbows/writer_thread_pool/client.rb +2 -0
- data/lib/rainbows/xepoll/client.rb +0 -3
- data/lib/rainbows/xepoll_thread_pool/client.rb +1 -1
- data/lib/rainbows/xepoll_thread_spawn/client.rb +1 -1
- data/rainbows.gemspec +6 -3
- data/t/GNUmakefile +10 -3
- data/t/byte-range-common.sh +1 -1
- data/t/hijack.ru +56 -0
- data/t/t0000-simple-http.sh +9 -9
- data/t/t0001-unix-http.sh +8 -8
- data/t/t0003-reopen-logs.sh +8 -8
- data/t/t0004-heartbeat-timeout.sh +3 -3
- data/t/t0005-large-file-response.sh +1 -1
- data/t/t0010-keepalive-timeout-effective.sh +2 -2
- data/t/t0011-close-on-exec-set.sh +2 -2
- data/t/t0017-keepalive-timeout-zero.sh +2 -2
- data/t/t0024-pipelined-sendfile-response.sh +2 -2
- data/t/t0027-nil-copy_stream.sh +1 -1
- data/t/t0030-fast-pipe-response.sh +1 -1
- data/t/t0034-pipelined-pipe-response.sh +2 -2
- data/t/t0035-kgio-pipe-response.sh +1 -1
- data/t/t0040-keepalive_requests-setting.sh +4 -4
- data/t/t0043-quit-keepalive-disconnect.sh +3 -3
- data/t/t0044-autopush.sh +2 -2
- data/t/t0045-client_max_header_size.sh +2 -2
- data/t/t0100-rack-input-hammer-chunked.sh +4 -4
- data/t/t0100-rack-input-hammer-content-length.sh +4 -4
- data/t/t0106-rack-input-keepalive.sh +6 -6
- data/t/t0200-async-response.sh +5 -5
- data/t/t0202-async-response-one-oh.sh +5 -5
- data/t/t0300-async_sinatra.sh +5 -5
- data/t/t0400-em-async-app.sh +2 -2
- data/t/t0401-em-async-tailer.sh +2 -2
- data/t/t0402-async-keepalive.sh +23 -23
- data/t/t0500-cramp-streaming.sh +3 -3
- data/t/t0600-rack-fiber_pool.sh +1 -1
- data/t/t0700-app-deferred.sh +2 -2
- data/t/t0800-rack-hijack.sh +27 -0
- data/t/t9000-rack-app-pool.sh +3 -3
- data/t/t9001-sendfile-to-path.sh +1 -1
- data/t/t9100-thread-timeout.sh +1 -1
- data/t/t9101-thread-timeout-threshold.sh +1 -1
- data/t/test-lib.sh +15 -0
- data/t/test_isolate.rb +4 -3
- metadata +26 -6
- data/t/bin/utee +0 -12
data/.document
CHANGED
data/.gitignore
CHANGED
@@ -58,6 +58,10 @@ with rackup(1) but strongly discouraged.
|
|
58
58
|
For production deployments, specifying the "listen" directive in
|
59
59
|
CONFIG_FILE is recommended as it allows fine-tuning of socket
|
60
60
|
options.
|
61
|
+
-N, \--no-default-middleware
|
62
|
+
: Disables loading middleware implied by RACK_ENV. This bypasses the
|
63
|
+
configuration documented in the RACK ENVIRONMENT section, but still
|
64
|
+
allows RACK_ENV to be used for application/framework-specific purposes.
|
61
65
|
|
62
66
|
# RACKUP COMPATIBILITY OPTIONS
|
63
67
|
-o, \--host HOST
|
@@ -137,10 +141,10 @@ All unrecognized values for RACK_ENV are assumed to be
|
|
137
141
|
"none". Production deployments are strongly encouraged to use
|
138
142
|
"deployment" or "none" for maximum performance.
|
139
143
|
|
140
|
-
Note
|
141
|
-
|
142
|
-
individually specified in the
|
143
|
-
not require them.
|
144
|
+
Note the Rack::ContentLength and Rack::Chunked middlewares are also
|
145
|
+
loaded by "deployment" and "development", but no other values of
|
146
|
+
RACK_ENV. If needed, they must be individually specified in the
|
147
|
+
RACKUP_FILE, some frameworks do not require them.
|
144
148
|
|
145
149
|
# SEE ALSO
|
146
150
|
|
data/GIT-VERSION-GEN
CHANGED
@@ -1,40 +1,39 @@
|
|
1
|
-
#!/bin/
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
'
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
DEF_VER = "v4.5.0"
|
3
|
+
CONSTANT = "Rainbows::Const::RAINBOWS_VERSION"
|
4
|
+
RVF = "lib/rainbows/version.rb"
|
5
|
+
GVF = "GIT-VERSION-FILE"
|
6
|
+
vn = DEF_VER
|
8
7
|
|
9
8
|
# First see if there is a version file (included in release tarballs),
|
10
9
|
# then try git-describe, then default.
|
11
|
-
if
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
10
|
+
if File.exist?(".git")
|
11
|
+
describe = `git describe --abbrev=4 HEAD 2>/dev/null`.strip
|
12
|
+
case describe
|
13
|
+
when /\Av[0-9]*/
|
14
|
+
vn = describe
|
15
|
+
system(*%w(git update-index -q --refresh))
|
16
|
+
unless `git diff-index --name-only HEAD --`.chomp.empty?
|
17
|
+
vn << "-dirty"
|
18
|
+
end
|
19
|
+
vn.tr!('-', '.')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
vn = vn.sub!(/\Av/, "")
|
24
|
+
|
25
|
+
# generate the Ruby constant
|
26
|
+
new_ruby_version = "#{CONSTANT} = '#{vn}'\n"
|
27
|
+
cur_ruby_version = File.read(RVF) rescue nil
|
28
|
+
if new_ruby_version != cur_ruby_version
|
29
|
+
File.open(RVF, "w") { |fp| fp.write(new_ruby_version) }
|
30
|
+
end
|
28
31
|
|
29
|
-
|
32
|
+
# generate the makefile snippet
|
33
|
+
new_make_version = "GIT_VERSION = #{vn}\n"
|
34
|
+
cur_make_version = File.read(GVF) rescue nil
|
35
|
+
if new_make_version != cur_make_version
|
36
|
+
File.open(GVF, "w") { |fp| fp.write(new_make_version) }
|
37
|
+
end
|
30
38
|
|
31
|
-
if
|
32
|
-
then
|
33
|
-
VC=$(sed -e 's/^GIT_VERSION = //' <$GVF)
|
34
|
-
else
|
35
|
-
VC=unset
|
36
|
-
fi
|
37
|
-
test "$VN" = "$VC" || {
|
38
|
-
echo >&2 "GIT_VERSION = $VN"
|
39
|
-
echo "GIT_VERSION = $VN" >$GVF
|
40
|
-
}
|
39
|
+
puts vn if $0 == __FILE__
|
data/GNUmakefile
CHANGED
@@ -28,14 +28,16 @@ clean:
|
|
28
28
|
man html:
|
29
29
|
$(MAKE) -C Documentation install-$@
|
30
30
|
|
31
|
-
pkg_extra += $(man1_paths)
|
31
|
+
pkg_extra += $(man1_paths) lib/rainbows/version.rb
|
32
32
|
|
33
33
|
doc::
|
34
34
|
cat Documentation/comparison.css >> doc/rdoc.css
|
35
35
|
$(RM) $(man1_rdoc)
|
36
36
|
|
37
|
+
lib/rainbows/version.rb: GIT-VERSION-FILE
|
38
|
+
|
37
39
|
all:: test
|
38
|
-
test:
|
40
|
+
test: lib/rainbows/version.rb
|
39
41
|
$(MAKE) -C t
|
40
42
|
|
41
43
|
.PHONY: man html
|
data/HACKING
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
= Rainbows! Hacker's Guide
|
2
|
+
|
3
|
+
=== Tests
|
4
|
+
|
5
|
+
All tests are written in POSIX shell. See README file in the t/ directory.
|
6
|
+
|
7
|
+
=== Documentation
|
8
|
+
|
9
|
+
We use RDoc 3.9.x with Darkfish for documentation as much as possible,
|
10
|
+
if you're on Ruby 1.8 you want to install the latest "rdoc" gem. Due to
|
11
|
+
the lack of RDoc-to-manpage converters we know about, we're writing
|
12
|
+
manpages in Markdown and converting to troff/HTML with Pandoc.
|
13
|
+
|
14
|
+
Please wrap documentation at 72 characters-per-line or less (long URLs
|
15
|
+
are exempt) so it is comfortably readable from terminals.
|
16
|
+
|
17
|
+
When referencing mailing list posts, use
|
18
|
+
"http://mid.gmane.org/$MESSAGE_ID" if possible since the Message-ID
|
19
|
+
remains searchable even if Gmane becomes unavailable.
|
20
|
+
|
21
|
+
== Contributing
|
22
|
+
|
23
|
+
Contributions are welcome in the form of patches, pull requests, code
|
24
|
+
review, testing, documentation, user support or any other feedback is
|
25
|
+
welcome. The mailing list is the central coordination point for all
|
26
|
+
user and developer feedback and bug reports.
|
27
|
+
|
28
|
+
=== Submitting Patches
|
29
|
+
|
30
|
+
Follow conventions already established in the code and do not exceed 80
|
31
|
+
characters per line.
|
32
|
+
|
33
|
+
Inline patches (from "git format-patch -M") to the mailing list are
|
34
|
+
preferred because they allow code review and comments in the reply to
|
35
|
+
the patch.
|
36
|
+
|
37
|
+
We will adhere to mostly the same conventions for patch submissions as
|
38
|
+
git itself. See the Documentation/SubmittingPatches document
|
39
|
+
distributed with git on on patch submission guidelines to follow. Just
|
40
|
+
don't email the git mailing list or maintainer with Rainbows! patches :)
|
41
|
+
|
42
|
+
No subscription is required to post to the mailing list at
|
43
|
+
rainbows-talk@rubyforge.org
|
44
|
+
|
45
|
+
Please ask for Cc: if you are not subscribed (Cc:-by-default is uncommon
|
46
|
+
on Ruby mailing lists)
|
47
|
+
|
48
|
+
== Building a Gem
|
49
|
+
|
50
|
+
In order to build the gem, you must install the following components:
|
51
|
+
|
52
|
+
* wrongdoc
|
53
|
+
* pandoc
|
54
|
+
|
55
|
+
You can build the Unicorn gem with the following command:
|
56
|
+
|
57
|
+
gmake gem
|
58
|
+
|
59
|
+
== Running Development Versions
|
60
|
+
|
61
|
+
It is easy to install the contents of your git working directory:
|
62
|
+
|
63
|
+
Via RubyGems (recommended):
|
64
|
+
|
65
|
+
gmake install-gem
|
66
|
+
|
67
|
+
Without RubyGems (via setup.rb):
|
68
|
+
|
69
|
+
ruby setup.rb
|
70
|
+
|
71
|
+
It is not at all recommended to mix a RubyGems installation with an
|
72
|
+
installation done without RubyGems, however.
|
data/bin/rainbows
CHANGED
@@ -59,6 +59,11 @@ op = OptionParser.new("", 24, ' ') do |opts|
|
|
59
59
|
ENV["RACK_ENV"] = e
|
60
60
|
end
|
61
61
|
|
62
|
+
opts.on("-N", "--no-default-middleware",
|
63
|
+
"do not load middleware implied by RACK_ENV") do |e|
|
64
|
+
rackup_opts[:no_default_middleware] = true
|
65
|
+
end
|
66
|
+
|
62
67
|
opts.on("-D", "--daemonize", "run daemonized in the background") do |d|
|
63
68
|
rackup_opts[:daemonize] = !!d
|
64
69
|
end
|
data/lib/rainbows/const.rb
CHANGED
@@ -86,6 +86,12 @@ class Rainbows::Coolio::Client < Coolio::IO
|
|
86
86
|
@deferred = true
|
87
87
|
end
|
88
88
|
|
89
|
+
def hijacked
|
90
|
+
CONN.delete(self)
|
91
|
+
detach
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
|
89
95
|
def write_response_path(status, headers, body, alive)
|
90
96
|
io = body_to_io(body)
|
91
97
|
st = io.stat
|
@@ -93,7 +99,8 @@ class Rainbows::Coolio::Client < Coolio::IO
|
|
93
99
|
if st.file?
|
94
100
|
defer_file(status, headers, body, alive, io, st)
|
95
101
|
elsif st.socket? || st.pipe?
|
96
|
-
chunk = stream_response_headers(status, headers, alive)
|
102
|
+
chunk = stream_response_headers(status, headers, alive, body)
|
103
|
+
return hijacked if nil == chunk
|
97
104
|
stream_response_body(body, io, chunk)
|
98
105
|
else
|
99
106
|
# char or block device... WTF?
|
@@ -103,10 +110,11 @@ class Rainbows::Coolio::Client < Coolio::IO
|
|
103
110
|
|
104
111
|
def ev_write_response(status, headers, body, alive)
|
105
112
|
if body.respond_to?(:to_path)
|
106
|
-
write_response_path(status, headers, body, alive)
|
113
|
+
body = write_response_path(status, headers, body, alive)
|
107
114
|
else
|
108
|
-
write_response(status, headers, body, alive)
|
115
|
+
body = write_response(status, headers, body, alive)
|
109
116
|
end
|
117
|
+
return hijacked unless body
|
110
118
|
return quit unless alive && :close != @state
|
111
119
|
@state = :headers
|
112
120
|
end
|
@@ -117,9 +125,11 @@ class Rainbows::Coolio::Client < Coolio::IO
|
|
117
125
|
@env[RACK_INPUT] = input
|
118
126
|
@env[REMOTE_ADDR] = @_io.kgio_addr
|
119
127
|
@env[ASYNC_CALLBACK] = method(:write_async_response)
|
128
|
+
@hp.hijack_setup(@env, @_io)
|
120
129
|
status, headers, body = catch(:async) {
|
121
130
|
APP.call(@env.merge!(RACK_DEFAULTS))
|
122
131
|
}
|
132
|
+
return hijacked if @hp.hijacked?
|
123
133
|
|
124
134
|
(nil == status || -1 == status) ? @deferred = true :
|
125
135
|
ev_write_response(status, headers, body, @hp.next?)
|
@@ -186,12 +196,13 @@ class Rainbows::Coolio::Client < Coolio::IO
|
|
186
196
|
def defer_file(status, headers, body, alive, io, st)
|
187
197
|
if r = sendfile_range(status, headers)
|
188
198
|
status, headers, range = r
|
189
|
-
write_headers(status, headers, alive)
|
199
|
+
body = write_headers(status, headers, alive, body) or return hijacked
|
190
200
|
range and defer_file_stream(range[0], range[1], io, body)
|
191
201
|
else
|
192
|
-
write_headers(status, headers, alive)
|
202
|
+
write_headers(status, headers, alive, body) or return hijacked
|
193
203
|
defer_file_stream(0, st.size, io, body)
|
194
204
|
end
|
205
|
+
body
|
195
206
|
end
|
196
207
|
|
197
208
|
def stream_file_chunk(sf) # +sf+ is a Rainbows::StreamFile object
|
@@ -207,8 +218,9 @@ class Rainbows::Coolio::Client < Coolio::IO
|
|
207
218
|
end
|
208
219
|
else
|
209
220
|
def defer_file(status, headers, body, alive, io, st)
|
210
|
-
write_headers(status, headers, alive)
|
221
|
+
write_headers(status, headers, alive, body) or return hijacked
|
211
222
|
defer_file_stream(0, st.size, io, body)
|
223
|
+
body
|
212
224
|
end
|
213
225
|
|
214
226
|
def stream_file_chunk(body)
|
@@ -14,6 +14,7 @@ class Rainbows::Coolio::ThreadClient < Rainbows::Coolio::Client
|
|
14
14
|
|
15
15
|
# this is only called in the master thread
|
16
16
|
def response_write(response)
|
17
|
+
return hijacked if @hp.hijacked?
|
17
18
|
ev_write_response(*response, @hp.next?)
|
18
19
|
rescue => e
|
19
20
|
handle_error(e)
|
@@ -25,6 +26,7 @@ class Rainbows::Coolio::ThreadClient < Rainbows::Coolio::Client
|
|
25
26
|
def app_response
|
26
27
|
begin
|
27
28
|
@env[REMOTE_ADDR] = @_io.kgio_addr
|
29
|
+
@hp.hijack_setup(@env, @_io)
|
28
30
|
APP.call(@env.merge!(RACK_DEFAULTS))
|
29
31
|
rescue => e
|
30
32
|
Rainbows::Error.app(e) # we guarantee this does not raise
|
@@ -6,14 +6,14 @@ module Rainbows::Epoll::Client
|
|
6
6
|
include Rainbows::EvCore
|
7
7
|
APP = Rainbows.server.app
|
8
8
|
Server = Rainbows::Epoll::Server
|
9
|
-
IN = SleepyPenguin::Epoll::IN | SleepyPenguin::Epoll::
|
10
|
-
OUT = SleepyPenguin::Epoll::OUT | SleepyPenguin::Epoll::
|
9
|
+
IN = SleepyPenguin::Epoll::IN | SleepyPenguin::Epoll::ONESHOT
|
10
|
+
OUT = SleepyPenguin::Epoll::OUT | SleepyPenguin::Epoll::ONESHOT
|
11
|
+
EPINOUT = IN | OUT
|
11
12
|
KATO = {}
|
12
13
|
KATO.compare_by_identity if KATO.respond_to?(:compare_by_identity)
|
13
14
|
Rainbows.at_quit { KATO.each_key { |k| k.timeout! }.clear }
|
14
15
|
Rainbows.config!(self, :keepalive_timeout)
|
15
16
|
EP = Rainbows::EP
|
16
|
-
ReRun = []
|
17
17
|
@@last_expire = Time.now
|
18
18
|
|
19
19
|
def self.expire
|
@@ -28,9 +28,6 @@ module Rainbows::Epoll::Client
|
|
28
28
|
def self.loop
|
29
29
|
begin
|
30
30
|
EP.wait(nil, 1000) { |_, obj| obj.epoll_run }
|
31
|
-
while obj = ReRun.shift
|
32
|
-
obj.epoll_run
|
33
|
-
end
|
34
31
|
expire
|
35
32
|
rescue Errno::EINTR
|
36
33
|
rescue => e
|
@@ -52,6 +49,7 @@ module Rainbows::Epoll::Client
|
|
52
49
|
when String
|
53
50
|
on_read(rv)
|
54
51
|
return if @wr_queue[0] || closed?
|
52
|
+
return hijacked if @hp.hijacked?
|
55
53
|
when :wait_readable
|
56
54
|
KATO[self] = @@last_expire if :headers == @state
|
57
55
|
return EP.set(self, IN)
|
@@ -67,7 +65,9 @@ module Rainbows::Epoll::Client
|
|
67
65
|
def app_call input # called by on_read()
|
68
66
|
@env[RACK_INPUT] = input
|
69
67
|
@env[REMOTE_ADDR] = kgio_addr
|
68
|
+
@hp.hijack_setup(@env, self)
|
70
69
|
status, headers, body = APP.call(@env.merge!(RACK_DEFAULTS))
|
70
|
+
return hijacked if @hp.hijacked?
|
71
71
|
ev_write_response(status, headers, body, @hp.next?)
|
72
72
|
end
|
73
73
|
|
@@ -78,7 +78,8 @@ module Rainbows::Epoll::Client
|
|
78
78
|
if st.file?
|
79
79
|
defer_file(status, headers, body, alive, io, st)
|
80
80
|
elsif st.socket? || st.pipe?
|
81
|
-
chunk = stream_response_headers(status, headers, alive)
|
81
|
+
chunk = stream_response_headers(status, headers, alive, body)
|
82
|
+
return hijacked if nil == chunk
|
82
83
|
stream_response_body(body, io, chunk)
|
83
84
|
else
|
84
85
|
# char or block device... WTF?
|
@@ -102,7 +103,28 @@ module Rainbows::Epoll::Client
|
|
102
103
|
else
|
103
104
|
write_response(status, headers, body, alive)
|
104
105
|
end
|
105
|
-
|
106
|
+
return hijacked if @hp.hijacked?
|
107
|
+
# try to read more if we didn't have to buffer writes
|
108
|
+
next_request if alive && 0 == @wr_queue.size
|
109
|
+
end
|
110
|
+
|
111
|
+
def hijacked
|
112
|
+
KATO.delete(self)
|
113
|
+
Server.decr # no other place to do this
|
114
|
+
EP.delete(self)
|
115
|
+
nil
|
116
|
+
end
|
117
|
+
|
118
|
+
def next_request
|
119
|
+
if 0 == @buf.size
|
120
|
+
want_more
|
121
|
+
else
|
122
|
+
# pipelined request (already in buffer)
|
123
|
+
on_read(Z)
|
124
|
+
return if @wr_queue[0] || closed?
|
125
|
+
return hijacked if @hp.hijacked?
|
126
|
+
close if :close == @state
|
127
|
+
end
|
106
128
|
end
|
107
129
|
|
108
130
|
def epoll_run
|
@@ -115,12 +137,12 @@ module Rainbows::Epoll::Client
|
|
115
137
|
end
|
116
138
|
|
117
139
|
def want_more
|
118
|
-
|
140
|
+
EP.set(self, EPINOUT)
|
119
141
|
end
|
120
142
|
|
121
143
|
def on_deferred_write_complete
|
122
144
|
:close == @state and return close
|
123
|
-
|
145
|
+
next_request
|
124
146
|
end
|
125
147
|
|
126
148
|
def handle_error(e)
|
@@ -185,13 +207,14 @@ module Rainbows::Epoll::Client
|
|
185
207
|
true
|
186
208
|
end
|
187
209
|
|
210
|
+
# Rack apps should not hijack here, but they may...
|
188
211
|
def defer_file(status, headers, body, alive, io, st)
|
189
212
|
if r = sendfile_range(status, headers)
|
190
213
|
status, headers, range = r
|
191
|
-
write_headers(status, headers, alive)
|
214
|
+
write_headers(status, headers, alive, body) or return hijacked
|
192
215
|
range and defer_file_stream(range[0], range[1], io, body)
|
193
216
|
else
|
194
|
-
write_headers(status, headers, alive)
|
217
|
+
write_headers(status, headers, alive, body) or return hijacked
|
195
218
|
defer_file_stream(0, st.size, io, body)
|
196
219
|
end
|
197
220
|
end
|
data/lib/rainbows/ev_core.rb
CHANGED
@@ -52,16 +52,17 @@ module Rainbows::EvCore
|
|
52
52
|
end
|
53
53
|
|
54
54
|
# returns whether to enable response chunking for autochunk models
|
55
|
-
|
55
|
+
# returns nil if request was hijacked in response stage
|
56
|
+
def stream_response_headers(status, headers, alive, body)
|
56
57
|
headers = Rack::Utils::HeaderHash.new(headers) unless Hash === headers
|
57
58
|
if headers.include?(Content_Length)
|
58
|
-
write_headers(status, headers, alive)
|
59
|
+
write_headers(status, headers, alive, body) or return
|
59
60
|
return false
|
60
61
|
end
|
61
62
|
|
62
63
|
case @env[HTTP_VERSION]
|
63
64
|
when "HTTP/1.0" # disable HTTP/1.0 keepalive to stream
|
64
|
-
write_headers(status, headers, false)
|
65
|
+
write_headers(status, headers, false, body) or return
|
65
66
|
@hp.clear
|
66
67
|
false
|
67
68
|
when nil # "HTTP/0.9"
|
@@ -69,7 +70,7 @@ module Rainbows::EvCore
|
|
69
70
|
else
|
70
71
|
rv = !!(headers[Transfer_Encoding] =~ %r{\Achunked\z}i)
|
71
72
|
rv = false unless @env["rainbows.autochunk"]
|
72
|
-
write_headers(status, headers, alive)
|
73
|
+
write_headers(status, headers, alive, body) or return
|
73
74
|
rv
|
74
75
|
end
|
75
76
|
end
|