rainbows 2.1.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +2 -3
- data/Rakefile +5 -2
- data/lib/rainbows.rb +72 -54
- data/lib/rainbows/base.rb +7 -9
- data/lib/rainbows/client.rb +25 -4
- data/lib/rainbows/const.rb +1 -1
- data/lib/rainbows/coolio.rb +6 -3
- data/lib/rainbows/coolio/client.rb +78 -57
- data/lib/rainbows/coolio/core.rb +1 -4
- data/lib/rainbows/coolio/heartbeat.rb +2 -3
- data/lib/rainbows/coolio/master.rb +3 -2
- data/lib/rainbows/coolio/{deferred_chunk_response.rb → response_chunk_pipe.rb} +1 -2
- data/lib/rainbows/coolio/{deferred_response.rb → response_pipe.rb} +1 -1
- data/lib/rainbows/coolio/thread_client.rb +4 -6
- data/lib/rainbows/coolio_fiber_spawn.rb +1 -1
- data/lib/rainbows/coolio_thread_pool.rb +1 -1
- data/lib/rainbows/coolio_thread_pool/watcher.rb +2 -4
- data/lib/rainbows/coolio_thread_spawn.rb +1 -1
- data/lib/rainbows/dev_fd_response.rb +2 -2
- data/lib/rainbows/error.rb +5 -7
- data/lib/rainbows/ev_core.rb +20 -7
- data/lib/rainbows/event_machine.rb +6 -5
- data/lib/rainbows/event_machine/client.rb +46 -53
- data/lib/rainbows/event_machine/response_pipe.rb +2 -3
- data/lib/rainbows/fiber/base.rb +5 -5
- data/lib/rainbows/fiber/body.rb +4 -13
- data/lib/rainbows/fiber/coolio/heartbeat.rb +1 -3
- data/lib/rainbows/fiber/coolio/server.rb +4 -7
- data/lib/rainbows/fiber_pool.rb +1 -1
- data/lib/rainbows/fiber_spawn.rb +2 -2
- data/lib/rainbows/http_parser.rb +12 -0
- data/lib/rainbows/http_server.rb +5 -7
- data/lib/rainbows/max_body.rb +2 -2
- data/lib/rainbows/never_block/core.rb +1 -1
- data/lib/rainbows/process_client.rb +15 -29
- data/lib/rainbows/queue_pool.rb +1 -3
- data/lib/rainbows/rack_input.rb +3 -3
- data/lib/rainbows/response.rb +164 -38
- data/lib/rainbows/revactor.rb +5 -65
- data/lib/rainbows/revactor/client.rb +60 -0
- data/lib/rainbows/revactor/{body.rb → client/methods.rb} +14 -14
- data/lib/rainbows/revactor/{tee_socket.rb → client/tee_socket.rb} +1 -1
- data/lib/rainbows/sendfile.rb +1 -2
- data/lib/rainbows/server_token.rb +1 -1
- data/lib/rainbows/thread_pool.rb +9 -9
- data/lib/rainbows/thread_spawn.rb +7 -6
- data/lib/rainbows/thread_timeout.rb +1 -1
- data/lib/rainbows/writer_thread_pool.rb +9 -25
- data/lib/rainbows/writer_thread_pool/client.rb +44 -1
- data/lib/rainbows/writer_thread_spawn.rb +2 -11
- data/lib/rainbows/writer_thread_spawn/client.rb +53 -13
- data/rainbows.gemspec +3 -12
- data/t/async_chunk_app.ru +62 -0
- data/t/byte-range-common.sh +142 -0
- data/t/t0022-copy_stream-byte-range.sh +2 -111
- data/t/t0023-sendfile-byte-range.sh +2 -32
- data/t/t0025-write-on-close.sh +23 -0
- data/t/t0040-keepalive_requests-setting.sh +0 -5
- data/t/t0402-async-keepalive.sh +146 -0
- data/t/t0500-cramp-streaming.sh +2 -0
- data/t/t0501-cramp-rainsocket.sh +2 -0
- data/t/t9000-rack-app-pool.sh +1 -1
- data/t/test_isolate.rb +5 -10
- data/t/test_isolate_cramp.rb +26 -0
- data/t/write-on-close.ru +11 -0
- metadata +33 -30
- data/lib/rainbows/coolio/sendfile.rb +0 -17
- data/lib/rainbows/response/body.rb +0 -127
- data/lib/rainbows/response/range.rb +0 -34
- data/lib/rainbows/timed_read.rb +0 -28
@@ -19,19 +19,11 @@ require 'thread'
|
|
19
19
|
# vulnerable to slow client denial-of-service attacks.
|
20
20
|
|
21
21
|
module Rainbows::WriterThreadSpawn
|
22
|
-
# :stopdoc:
|
23
22
|
include Rainbows::Base
|
24
|
-
|
25
|
-
def write_body(my_sock, body, range) # :nodoc:
|
26
|
-
if body.respond_to?(:close)
|
27
|
-
Rainbows::SyncClose.new(body) { |body| my_sock.queue_body(body, range) }
|
28
|
-
else
|
29
|
-
my_sock.queue_body(body, range)
|
30
|
-
end
|
31
|
-
end
|
23
|
+
autoload :Client, 'rainbows/writer_thread_spawn/client'
|
32
24
|
|
33
25
|
def process_client(client) # :nodoc:
|
34
|
-
|
26
|
+
Client.new(client).process_loop
|
35
27
|
end
|
36
28
|
|
37
29
|
def worker_loop(worker) # :nodoc:
|
@@ -42,4 +34,3 @@ module Rainbows::WriterThreadSpawn
|
|
42
34
|
# :startdoc:
|
43
35
|
end
|
44
36
|
# :enddoc:
|
45
|
-
require 'rainbows/writer_thread_spawn/client'
|
@@ -3,17 +3,60 @@
|
|
3
3
|
# used to wrap a BasicSocket to use with +q+ for all writes
|
4
4
|
# this is compatible with IO.select
|
5
5
|
class Rainbows::WriterThreadSpawn::Client < Struct.new(:to_io, :q, :thr)
|
6
|
-
include Rainbows::Response
|
7
6
|
include Rainbows::SocketProxy
|
7
|
+
include Rainbows::ProcessClient
|
8
8
|
include Rainbows::WorkerYield
|
9
9
|
|
10
10
|
CUR = {} # :nodoc:
|
11
11
|
|
12
|
+
module Methods
|
13
|
+
def write_body_each(body)
|
14
|
+
q << [ :write_body_each, body ]
|
15
|
+
end
|
16
|
+
|
17
|
+
def write_response_close(status, headers, body, alive)
|
18
|
+
to_io.instance_variable_set(:@hp, @hp) # XXX ugh
|
19
|
+
Rainbows::SyncClose.new(body) { |sync_body|
|
20
|
+
q << [ :write_response, status, headers, sync_body, alive ]
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
if IO.respond_to?(:copy_stream) || IO.method_defined?(:sendfile_nonblock)
|
25
|
+
def write_response(status, headers, body, alive)
|
26
|
+
self.q ||= queue_writer
|
27
|
+
if body.respond_to?(:close)
|
28
|
+
write_response_close(status, headers, body, alive)
|
29
|
+
elsif body.respond_to?(:to_path)
|
30
|
+
write_response_path(status, headers, body, alive)
|
31
|
+
else
|
32
|
+
super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def write_body_file(body, range)
|
37
|
+
q << [ :write_body_file, body, range ]
|
38
|
+
end
|
39
|
+
|
40
|
+
def write_body_stream(body)
|
41
|
+
q << [ :write_body_stream, body ]
|
42
|
+
end
|
43
|
+
else # each-only body response
|
44
|
+
def write_response(status, headers, body, alive)
|
45
|
+
self.q ||= queue_writer
|
46
|
+
if body.respond_to?(:close)
|
47
|
+
write_response_close(status, headers, body, alive)
|
48
|
+
else
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end # each-only body response
|
53
|
+
end # module Methods
|
54
|
+
include Methods
|
55
|
+
|
12
56
|
def self.quit
|
13
|
-
g = Rainbows::G
|
14
57
|
CUR.delete_if do |t,q|
|
15
58
|
q << nil
|
16
|
-
|
59
|
+
Rainbows.tick
|
17
60
|
t.alive? ? t.join(0.01) : true
|
18
61
|
end until CUR.empty?
|
19
62
|
end
|
@@ -27,16 +70,17 @@ class Rainbows::WriterThreadSpawn::Client < Struct.new(:to_io, :q, :thr)
|
|
27
70
|
|
28
71
|
q = Queue.new
|
29
72
|
self.thr = Thread.new(to_io, q) do |io, q|
|
30
|
-
while
|
73
|
+
while op = q.shift
|
31
74
|
begin
|
32
|
-
|
33
|
-
case
|
34
|
-
when
|
75
|
+
op, *rest = op
|
76
|
+
case op
|
77
|
+
when String
|
78
|
+
io.kgio_write(op)
|
35
79
|
when :close
|
36
80
|
io.close unless io.closed?
|
37
81
|
break
|
38
82
|
else
|
39
|
-
io.
|
83
|
+
io.__send__ op, *rest
|
40
84
|
end
|
41
85
|
rescue => e
|
42
86
|
Rainbows::Error.write(io, e)
|
@@ -51,10 +95,6 @@ class Rainbows::WriterThreadSpawn::Client < Struct.new(:to_io, :q, :thr)
|
|
51
95
|
(self.q ||= queue_writer) << buf
|
52
96
|
end
|
53
97
|
|
54
|
-
def queue_body(body, range)
|
55
|
-
(self.q ||= queue_writer) << [ :body, body, range ]
|
56
|
-
end
|
57
|
-
|
58
98
|
def close
|
59
99
|
if q
|
60
100
|
q << :close
|
@@ -64,6 +104,6 @@ class Rainbows::WriterThreadSpawn::Client < Struct.new(:to_io, :q, :thr)
|
|
64
104
|
end
|
65
105
|
|
66
106
|
def closed?
|
67
|
-
|
107
|
+
to_io.closed?
|
68
108
|
end
|
69
109
|
end
|
data/rainbows.gemspec
CHANGED
@@ -1,14 +1,6 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
-
|
3
2
|
ENV["VERSION"] or abort "VERSION= must be specified"
|
4
3
|
manifest = File.readlines('.manifest').map! { |x| x.chomp! }
|
5
|
-
|
6
|
-
# don't bother with tests that fork, not worth our time to get working
|
7
|
-
# with `gem check -t` ... (of course we care for them when testing with
|
8
|
-
# GNU make when they can run in parallel)
|
9
|
-
test_files = manifest.grep(%r{\Atest/unit/test_.*\.rb\z}).map do |f|
|
10
|
-
File.readlines(f).grep(/\bfork\b/).empty? ? f : nil
|
11
|
-
end.compact
|
12
4
|
require 'wrongdoc'
|
13
5
|
extend Wrongdoc::Gemspec
|
14
6
|
name, summary, title = readme_metadata
|
@@ -29,16 +21,15 @@ Gem::Specification.new do |s|
|
|
29
21
|
s.rdoc_options = rdoc_options
|
30
22
|
s.require_paths = %w(lib)
|
31
23
|
s.rubyforge_project = %q{rainbows}
|
32
|
-
|
33
|
-
s.test_files = test_files
|
24
|
+
s.test_files = []
|
34
25
|
|
35
26
|
# we want a newer Rack for a valid HeaderHash#each
|
36
27
|
s.add_dependency(%q<rack>, ['~> 1.1'])
|
37
28
|
|
38
29
|
# we need Unicorn for the HTTP parser and process management
|
39
|
-
s.add_dependency(%q<unicorn>, ["~> 3.
|
30
|
+
s.add_dependency(%q<unicorn>, ["~> 3.3"])
|
40
31
|
s.add_development_dependency(%q<isolate>, "~> 3.0.0")
|
41
|
-
s.add_development_dependency(%q<wrongdoc>, "~> 1.
|
32
|
+
s.add_development_dependency(%q<wrongdoc>, "~> 1.1")
|
42
33
|
|
43
34
|
# optional runtime dependencies depending on configuration
|
44
35
|
# see t/test_isolate.rb for the exact versions we've tested with
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# based on async_examples/async_app.ru by James Tucker
|
2
|
+
class DeferrableChunkBody
|
3
|
+
include EventMachine::Deferrable
|
4
|
+
|
5
|
+
def call(*body)
|
6
|
+
body.each do |chunk|
|
7
|
+
@body_callback.call("#{chunk.size.to_s(16)}\r\n")
|
8
|
+
@body_callback.call(chunk)
|
9
|
+
@body_callback.call("\r\n")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def each(&block)
|
14
|
+
@body_callback = block
|
15
|
+
end
|
16
|
+
|
17
|
+
def finish
|
18
|
+
@body_callback.call("0\r\n\r\n")
|
19
|
+
end
|
20
|
+
end if defined?(EventMachine)
|
21
|
+
|
22
|
+
class AsyncChunkApp
|
23
|
+
def call(env)
|
24
|
+
headers = {
|
25
|
+
'Content-Type' => 'text/plain',
|
26
|
+
'Transfer-Encoding' => 'chunked',
|
27
|
+
}
|
28
|
+
delay = env["HTTP_X_DELAY"].to_i
|
29
|
+
|
30
|
+
case env["rainbows.model"]
|
31
|
+
when :EventMachine, :NeverBlock
|
32
|
+
body = DeferrableChunkBody.new
|
33
|
+
body.callback { body.finish }
|
34
|
+
task = lambda {
|
35
|
+
env['async.callback'].call([ 200, headers, body ])
|
36
|
+
EM.add_timer(1) {
|
37
|
+
body.call "Hello "
|
38
|
+
|
39
|
+
EM.add_timer(1) {
|
40
|
+
body.call "World #{env['PATH_INFO']}\n"
|
41
|
+
body.succeed
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
delay == 0 ? EM.next_tick(&task) : EM.add_timer(delay, &task)
|
46
|
+
when :Coolio
|
47
|
+
# Cool.io only does one-shot responses due to the lack of the
|
48
|
+
# equivalent of EM::Deferrables
|
49
|
+
body = [ "Hello ", "World #{env['PATH_INFO']}\n", '' ].map do |chunk|
|
50
|
+
"#{chunk.size.to_s(16)}\r\n#{chunk}\r\n"
|
51
|
+
end
|
52
|
+
|
53
|
+
next_tick = Coolio::TimerWatcher.new(delay, false)
|
54
|
+
next_tick.on_timer { env['async.callback'].call([ 200, headers, body ]) }
|
55
|
+
next_tick.attach(Coolio::Loop.default)
|
56
|
+
else
|
57
|
+
raise "Not supported: #{env['rainbows.model']}"
|
58
|
+
end
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
run AsyncChunkApp.new
|
@@ -0,0 +1,142 @@
|
|
1
|
+
t_begin "byte-range setup vars" && {
|
2
|
+
random_blob_size=$(wc -c < random_blob)
|
3
|
+
rb_1=$(( $random_blob_size - 1 ))
|
4
|
+
range_head=-r-365
|
5
|
+
range_tail=-r155-
|
6
|
+
range_mid=-r200-300
|
7
|
+
range_n1=-r0-$rb_1
|
8
|
+
range_n2=-r0-$(($rb_1 - 1))
|
9
|
+
range_1b_head=-r0-0
|
10
|
+
range_1b_tail=-r$rb_1-$rb_1
|
11
|
+
range_1b_mid=-r200-200
|
12
|
+
range_all=-r0-$random_blob_size
|
13
|
+
url=http://$listen/random_blob
|
14
|
+
}
|
15
|
+
|
16
|
+
check_content_range () {
|
17
|
+
grep '^< HTTP/1\.1 206 Partial Content' $err
|
18
|
+
grep 'Range:' $err
|
19
|
+
# Content-Range: bytes #{offset}-#{offset+count-1}/#{clen}
|
20
|
+
d='\([0-9]\+\)'
|
21
|
+
start= end= size=
|
22
|
+
eval $(< $err sed -n -e \
|
23
|
+
"s/^< Content-Range: bytes $d-$d\/$d"'.*$/start=\1 end=\2 size=\3/p')
|
24
|
+
test -n "$start"
|
25
|
+
test -n "$end"
|
26
|
+
test -n "$size"
|
27
|
+
|
28
|
+
# ensure we didn't screw up the sed invocation
|
29
|
+
expect="< Content-Range: bytes $start-$end/$size"
|
30
|
+
test x"$(grep -F "$expect" $err)" = x"$(grep '^< Content-Range:' $err)"
|
31
|
+
|
32
|
+
test $start -le $end
|
33
|
+
test $end -lt $size
|
34
|
+
}
|
35
|
+
|
36
|
+
t_begin "read random blob sha1s" && {
|
37
|
+
sha1_head=$(curl -sSff $range_head file://random_blob | rsha1)
|
38
|
+
sha1_tail=$(curl -sSff $range_tail file://random_blob | rsha1)
|
39
|
+
sha1_mid=$(curl -sSff $range_mid file://random_blob | rsha1)
|
40
|
+
sha1_n1=$(curl -sSff $range_n1 file://random_blob | rsha1)
|
41
|
+
sha1_n2=$(curl -sSff $range_n2 file://random_blob | rsha1)
|
42
|
+
sha1_1b_head=$(curl -sSff $range_1b_head file://random_blob | rsha1)
|
43
|
+
sha1_1b_tail=$(curl -sSff $range_1b_tail file://random_blob | rsha1)
|
44
|
+
sha1_1b_mid=$(curl -sSff $range_1b_mid file://random_blob | rsha1)
|
45
|
+
sha1_all=$(rsha1 < random_blob)
|
46
|
+
echo "$sha1_all=$sha1_n1"
|
47
|
+
}
|
48
|
+
|
49
|
+
t_begin "normal full request matches" && {
|
50
|
+
sha1="$(curl -v 2>$err -sSf $url | rsha1)"
|
51
|
+
test x"$sha1_all" = x"$sha1"
|
52
|
+
grep 'Content-Range:' $err && die "Content-Range unexpected"
|
53
|
+
grep 'HTTP/1.1 200 OK' $err || die "200 response expected"
|
54
|
+
}
|
55
|
+
|
56
|
+
t_begin "crazy offset goes over" && {
|
57
|
+
range_insane=-r$(($random_blob_size * 2))-$(($random_blob_size * 4))
|
58
|
+
curl -vsS 2>$err $range_insane $url >/dev/null
|
59
|
+
grep '^< HTTP/1\.[01] 416 ' $err || die "expected 416 error"
|
60
|
+
grep '^< Content-Range: bytes \*/'$random_blob_size $err || \
|
61
|
+
die "expected Content-Range: bytes */SIZE"
|
62
|
+
}
|
63
|
+
|
64
|
+
t_begin "keepalive/pipelining is supported on 416 responses" && {
|
65
|
+
rm -f $tmp
|
66
|
+
(
|
67
|
+
cat $fifo > $tmp &
|
68
|
+
printf 'GET /byte-range-common.sh HTTP/1.1\r\n'
|
69
|
+
printf 'Host: %s\r\n' $listen
|
70
|
+
printf 'Range: bytes=9999999999-9999999999\r\n\r\n'
|
71
|
+
printf 'GET /byte-range-common.sh HTTP/1.1\r\n'
|
72
|
+
printf 'Host: %s\r\n' $listen
|
73
|
+
printf 'Connection: close\r\n'
|
74
|
+
printf 'Range: bytes=0-0\r\n\r\n'
|
75
|
+
wait
|
76
|
+
) | socat - TCP:$listen > $fifo
|
77
|
+
|
78
|
+
< $tmp awk '
|
79
|
+
/^HTTP\/1\.1 / && NR == 1 && $2 == 416 { first = $2 }
|
80
|
+
/^HTTP\/1\.1 / && NR != 1 && $2 == 206 { second = $2 }
|
81
|
+
END { exit((first == 416 && second == 206) ? 0 : 1) }
|
82
|
+
'
|
83
|
+
}
|
84
|
+
|
85
|
+
t_begin "full request matches with explicit ranges" && {
|
86
|
+
sha1="$(curl -v 2>$err $range_all -sSf $url | rsha1)"
|
87
|
+
check_content_range
|
88
|
+
test x"$sha1_all" = x"$sha1"
|
89
|
+
|
90
|
+
sha1="$(curl -v 2>$err $range_n1 -sSf $url | rsha1)"
|
91
|
+
check_content_range
|
92
|
+
test x"$sha1_all" = x"$sha1"
|
93
|
+
|
94
|
+
range_over=-r0-$(($random_blob_size * 2))
|
95
|
+
sha1="$(curl -v 2>$err $range_over -sSf $url | rsha1)"
|
96
|
+
check_content_range
|
97
|
+
test x"$sha1_all" = x"$sha1"
|
98
|
+
}
|
99
|
+
|
100
|
+
t_begin "no fence post errors" && {
|
101
|
+
sha1="$(curl -v 2>$err $range_n2 -sSf $url | rsha1)"
|
102
|
+
check_content_range
|
103
|
+
test x"$sha1_n2" = x"$sha1"
|
104
|
+
|
105
|
+
sha1="$(curl -v 2>$err $range_1b_head -sSf $url | rsha1)"
|
106
|
+
check_content_range
|
107
|
+
test x"$sha1_1b_head" = x"$sha1"
|
108
|
+
|
109
|
+
sha1="$(curl -v 2>$err $range_1b_tail -sSf $url | rsha1)"
|
110
|
+
check_content_range
|
111
|
+
test x"$sha1_1b_tail" = x"$sha1"
|
112
|
+
|
113
|
+
sha1="$(curl -v 2>$err $range_1b_mid -sSf $url | rsha1)"
|
114
|
+
check_content_range
|
115
|
+
test x"$sha1_1b_mid" = x"$sha1"
|
116
|
+
}
|
117
|
+
|
118
|
+
t_begin "head range matches" && {
|
119
|
+
sha1="$(curl -sSfv 2>$err $range_head $url | rsha1)"
|
120
|
+
check_content_range
|
121
|
+
test x"$sha1_head" = x"$sha1"
|
122
|
+
}
|
123
|
+
|
124
|
+
t_begin "tail range matches" && {
|
125
|
+
sha1="$(curl -sSfv 2>$err $range_tail $url | rsha1)"
|
126
|
+
check_content_range
|
127
|
+
test x"$sha1_tail" = x"$sha1"
|
128
|
+
}
|
129
|
+
|
130
|
+
t_begin "mid range matches" && {
|
131
|
+
sha1="$(curl -sSfv 2>$err $range_mid $url | rsha1)"
|
132
|
+
check_content_range
|
133
|
+
test x"$sha1_mid" = x"$sha1"
|
134
|
+
}
|
135
|
+
|
136
|
+
t_begin "shutdown server" && {
|
137
|
+
kill -QUIT $rainbows_pid
|
138
|
+
}
|
139
|
+
|
140
|
+
t_begin "check stderr" && check_stderr
|
141
|
+
|
142
|
+
t_done
|
@@ -17,7 +17,7 @@ ThreadSpawn|WriterThreadSpawn|ThreadPool|WriterThreadPool|Base) ;;
|
|
17
17
|
;;
|
18
18
|
esac
|
19
19
|
|
20
|
-
t_plan
|
20
|
+
t_plan 13 "IO.copy_stream byte range response for $model"
|
21
21
|
|
22
22
|
t_begin "setup and startup" && {
|
23
23
|
rtmpfiles out err
|
@@ -25,115 +25,6 @@ t_begin "setup and startup" && {
|
|
25
25
|
# can't load Rack::Lint here since it clobbers body#to_path
|
26
26
|
rainbows -E none -D large-file-response.ru -c $unicorn_config
|
27
27
|
rainbows_wait_start
|
28
|
-
random_blob_size=$(wc -c < random_blob)
|
29
|
-
rb_1=$(( $random_blob_size - 1 ))
|
30
|
-
range_head=-r-365
|
31
|
-
range_tail=-r155-
|
32
|
-
range_mid=-r200-300
|
33
|
-
range_n1=-r0-$rb_1
|
34
|
-
range_n2=-r0-$(($rb_1 - 1))
|
35
|
-
range_1b_head=-r0-0
|
36
|
-
range_1b_tail=-r$rb_1-$rb_1
|
37
|
-
range_1b_mid=-r200-200
|
38
|
-
range_all=-r0-$random_blob_size
|
39
|
-
url=http://$listen/random_blob
|
40
28
|
}
|
41
29
|
|
42
|
-
|
43
|
-
# Content-Range: bytes #{offset}-#{offset+count-1}/#{clen}
|
44
|
-
awk -F/ -v E=0 -v size=$random_blob_size '
|
45
|
-
$2 == size && /^< Content-Range: bytes [0-9]+-[0-9]+\// {
|
46
|
-
split($1, a, /-/);
|
47
|
-
if (a[1] < size) {
|
48
|
-
E = 0;
|
49
|
-
exit(0);
|
50
|
-
}
|
51
|
-
}
|
52
|
-
END { exit(E) }
|
53
|
-
' < $err
|
54
|
-
}
|
55
|
-
|
56
|
-
t_begin "read random blob sha1s" && {
|
57
|
-
sha1_head=$(curl -sSff $range_head file://random_blob | rsha1)
|
58
|
-
sha1_tail=$(curl -sSff $range_tail file://random_blob | rsha1)
|
59
|
-
sha1_mid=$(curl -sSff $range_mid file://random_blob | rsha1)
|
60
|
-
sha1_n1=$(curl -sSff $range_n1 file://random_blob | rsha1)
|
61
|
-
sha1_n2=$(curl -sSff $range_n2 file://random_blob | rsha1)
|
62
|
-
sha1_1b_head=$(curl -sSff $range_1b_head file://random_blob | rsha1)
|
63
|
-
sha1_1b_tail=$(curl -sSff $range_1b_tail file://random_blob | rsha1)
|
64
|
-
sha1_1b_mid=$(curl -sSff $range_1b_mid file://random_blob | rsha1)
|
65
|
-
sha1_all=$(rsha1 < random_blob)
|
66
|
-
echo "$sha1_all=$sha1_n1"
|
67
|
-
}
|
68
|
-
|
69
|
-
t_begin "normal full request matches" && {
|
70
|
-
sha1="$(curl -v 2>$err -sSf $url | rsha1)"
|
71
|
-
test x"$sha1_all" = x"$sha1"
|
72
|
-
grep 'Content-Range:' $err && die "Content-Range unexpected"
|
73
|
-
grep 'HTTP/1.1 200 OK' $err || die "200 response expected"
|
74
|
-
}
|
75
|
-
|
76
|
-
t_begin "crazy offset goes over" && {
|
77
|
-
range_insane=-r$(($random_blob_size * 2))-$(($random_blob_size * 4))
|
78
|
-
curl -vsS 2>$err $range_insane $url
|
79
|
-
grep 'HTTP/1\.[01] 416 ' $err || die "expected 416 error"
|
80
|
-
}
|
81
|
-
|
82
|
-
t_begin "full request matches with explicit ranges" && {
|
83
|
-
sha1="$(curl -v 2>$err $range_all -sSf $url | rsha1)"
|
84
|
-
check_content_range
|
85
|
-
test x"$sha1_all" = x"$sha1"
|
86
|
-
|
87
|
-
sha1="$(curl -v 2>$err $range_n1 -sSf $url | rsha1)"
|
88
|
-
check_content_range
|
89
|
-
test x"$sha1_all" = x"$sha1"
|
90
|
-
|
91
|
-
range_over=-r0-$(($random_blob_size * 2))
|
92
|
-
sha1="$(curl -v 2>$err $range_over -sSf $url | rsha1)"
|
93
|
-
check_content_range
|
94
|
-
test x"$sha1_all" = x"$sha1"
|
95
|
-
}
|
96
|
-
|
97
|
-
t_begin "no fence post errors" && {
|
98
|
-
sha1="$(curl -v 2>$err $range_n2 -sSf $url | rsha1)"
|
99
|
-
check_content_range
|
100
|
-
test x"$sha1_n2" = x"$sha1"
|
101
|
-
|
102
|
-
sha1="$(curl -v 2>$err $range_1b_head -sSf $url | rsha1)"
|
103
|
-
check_content_range
|
104
|
-
test x"$sha1_1b_head" = x"$sha1"
|
105
|
-
|
106
|
-
sha1="$(curl -v 2>$err $range_1b_tail -sSf $url | rsha1)"
|
107
|
-
check_content_range
|
108
|
-
test x"$sha1_1b_tail" = x"$sha1"
|
109
|
-
|
110
|
-
sha1="$(curl -v 2>$err $range_1b_mid -sSf $url | rsha1)"
|
111
|
-
check_content_range
|
112
|
-
test x"$sha1_1b_mid" = x"$sha1"
|
113
|
-
}
|
114
|
-
|
115
|
-
t_begin "head range matches" && {
|
116
|
-
sha1="$(curl -sSfv $range_head $url | rsha1)"
|
117
|
-
check_content_range
|
118
|
-
test x"$sha1_head" = x"$sha1"
|
119
|
-
}
|
120
|
-
|
121
|
-
t_begin "tail range matches" && {
|
122
|
-
sha1="$(curl -sSf $range_tail $url | rsha1)"
|
123
|
-
check_content_range
|
124
|
-
test x"$sha1_tail" = x"$sha1"
|
125
|
-
}
|
126
|
-
|
127
|
-
t_begin "mid range matches" && {
|
128
|
-
sha1="$(curl -sSf $range_mid $url | rsha1)"
|
129
|
-
check_content_range
|
130
|
-
test x"$sha1_mid" = x"$sha1"
|
131
|
-
}
|
132
|
-
|
133
|
-
t_begin "shutdown server" && {
|
134
|
-
kill -QUIT $rainbows_pid
|
135
|
-
}
|
136
|
-
|
137
|
-
t_begin "check stderr" && check_stderr
|
138
|
-
|
139
|
-
t_done
|
30
|
+
. ./byte-range-common.sh
|