rainbows 2.1.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/GIT-VERSION-GEN +1 -1
  2. data/GNUmakefile +2 -3
  3. data/Rakefile +5 -2
  4. data/lib/rainbows.rb +72 -54
  5. data/lib/rainbows/base.rb +7 -9
  6. data/lib/rainbows/client.rb +25 -4
  7. data/lib/rainbows/const.rb +1 -1
  8. data/lib/rainbows/coolio.rb +6 -3
  9. data/lib/rainbows/coolio/client.rb +78 -57
  10. data/lib/rainbows/coolio/core.rb +1 -4
  11. data/lib/rainbows/coolio/heartbeat.rb +2 -3
  12. data/lib/rainbows/coolio/master.rb +3 -2
  13. data/lib/rainbows/coolio/{deferred_chunk_response.rb → response_chunk_pipe.rb} +1 -2
  14. data/lib/rainbows/coolio/{deferred_response.rb → response_pipe.rb} +1 -1
  15. data/lib/rainbows/coolio/thread_client.rb +4 -6
  16. data/lib/rainbows/coolio_fiber_spawn.rb +1 -1
  17. data/lib/rainbows/coolio_thread_pool.rb +1 -1
  18. data/lib/rainbows/coolio_thread_pool/watcher.rb +2 -4
  19. data/lib/rainbows/coolio_thread_spawn.rb +1 -1
  20. data/lib/rainbows/dev_fd_response.rb +2 -2
  21. data/lib/rainbows/error.rb +5 -7
  22. data/lib/rainbows/ev_core.rb +20 -7
  23. data/lib/rainbows/event_machine.rb +6 -5
  24. data/lib/rainbows/event_machine/client.rb +46 -53
  25. data/lib/rainbows/event_machine/response_pipe.rb +2 -3
  26. data/lib/rainbows/fiber/base.rb +5 -5
  27. data/lib/rainbows/fiber/body.rb +4 -13
  28. data/lib/rainbows/fiber/coolio/heartbeat.rb +1 -3
  29. data/lib/rainbows/fiber/coolio/server.rb +4 -7
  30. data/lib/rainbows/fiber_pool.rb +1 -1
  31. data/lib/rainbows/fiber_spawn.rb +2 -2
  32. data/lib/rainbows/http_parser.rb +12 -0
  33. data/lib/rainbows/http_server.rb +5 -7
  34. data/lib/rainbows/max_body.rb +2 -2
  35. data/lib/rainbows/never_block/core.rb +1 -1
  36. data/lib/rainbows/process_client.rb +15 -29
  37. data/lib/rainbows/queue_pool.rb +1 -3
  38. data/lib/rainbows/rack_input.rb +3 -3
  39. data/lib/rainbows/response.rb +164 -38
  40. data/lib/rainbows/revactor.rb +5 -65
  41. data/lib/rainbows/revactor/client.rb +60 -0
  42. data/lib/rainbows/revactor/{body.rb → client/methods.rb} +14 -14
  43. data/lib/rainbows/revactor/{tee_socket.rb → client/tee_socket.rb} +1 -1
  44. data/lib/rainbows/sendfile.rb +1 -2
  45. data/lib/rainbows/server_token.rb +1 -1
  46. data/lib/rainbows/thread_pool.rb +9 -9
  47. data/lib/rainbows/thread_spawn.rb +7 -6
  48. data/lib/rainbows/thread_timeout.rb +1 -1
  49. data/lib/rainbows/writer_thread_pool.rb +9 -25
  50. data/lib/rainbows/writer_thread_pool/client.rb +44 -1
  51. data/lib/rainbows/writer_thread_spawn.rb +2 -11
  52. data/lib/rainbows/writer_thread_spawn/client.rb +53 -13
  53. data/rainbows.gemspec +3 -12
  54. data/t/async_chunk_app.ru +62 -0
  55. data/t/byte-range-common.sh +142 -0
  56. data/t/t0022-copy_stream-byte-range.sh +2 -111
  57. data/t/t0023-sendfile-byte-range.sh +2 -32
  58. data/t/t0025-write-on-close.sh +23 -0
  59. data/t/t0040-keepalive_requests-setting.sh +0 -5
  60. data/t/t0402-async-keepalive.sh +146 -0
  61. data/t/t0500-cramp-streaming.sh +2 -0
  62. data/t/t0501-cramp-rainsocket.sh +2 -0
  63. data/t/t9000-rack-app-pool.sh +1 -1
  64. data/t/test_isolate.rb +5 -10
  65. data/t/test_isolate_cramp.rb +26 -0
  66. data/t/write-on-close.ru +11 -0
  67. metadata +33 -30
  68. data/lib/rainbows/coolio/sendfile.rb +0 -17
  69. data/lib/rainbows/response/body.rb +0 -127
  70. data/lib/rainbows/response/range.rb +0 -34
  71. 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
- super(Client.new(client))
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
- g.tick
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 response = q.shift
73
+ while op = q.shift
31
74
  begin
32
- arg1, arg2, arg3 = response
33
- case arg1
34
- when :body then write_body(io, arg2, arg3)
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.write(arg1)
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
- false
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.2"])
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.0.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 11 "IO.copy_stream byte range response for $model"
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
- check_content_range () {
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