rainbows 2.1.0 → 3.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.
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