rainbows 0.95.1 → 0.96.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 (57) hide show
  1. data/Documentation/comparison.haml +1 -1
  2. data/GIT-VERSION-GEN +1 -1
  3. data/GNUmakefile +2 -1
  4. data/Rakefile +16 -3
  5. data/Static_Files +11 -11
  6. data/TODO +1 -6
  7. data/lib/rainbows.rb +3 -2
  8. data/lib/rainbows/base.rb +12 -9
  9. data/lib/rainbows/const.rb +2 -4
  10. data/lib/rainbows/dev_fd_response.rb +16 -13
  11. data/lib/rainbows/error.rb +2 -0
  12. data/lib/rainbows/ev_core.rb +13 -0
  13. data/lib/rainbows/event_machine.rb +85 -128
  14. data/lib/rainbows/event_machine/response_chunk_pipe.rb +25 -0
  15. data/lib/rainbows/event_machine/response_pipe.rb +30 -0
  16. data/lib/rainbows/event_machine/try_defer.rb +27 -0
  17. data/lib/rainbows/fiber/body.rb +6 -4
  18. data/lib/rainbows/fiber/io.rb +4 -4
  19. data/lib/rainbows/fiber/rev.rb +16 -8
  20. data/lib/rainbows/http_response.rb +5 -6
  21. data/lib/rainbows/response.rb +37 -22
  22. data/lib/rainbows/response/body.rb +19 -16
  23. data/lib/rainbows/response/range.rb +34 -0
  24. data/lib/rainbows/rev.rb +2 -1
  25. data/lib/rainbows/rev/client.rb +105 -77
  26. data/lib/rainbows/rev/deferred_chunk_response.rb +16 -0
  27. data/lib/rainbows/rev/deferred_response.rb +16 -24
  28. data/lib/rainbows/rev/sendfile.rb +4 -13
  29. data/lib/rainbows/rev/thread.rb +3 -12
  30. data/lib/rainbows/rev_thread_pool.rb +2 -2
  31. data/lib/rainbows/revactor.rb +16 -9
  32. data/lib/rainbows/revactor/body.rb +42 -0
  33. data/lib/rainbows/revactor/proxy.rb +55 -0
  34. data/lib/rainbows/sendfile.rb +12 -14
  35. data/lib/rainbows/stream_file.rb +3 -3
  36. data/lib/rainbows/writer_thread_pool.rb +12 -12
  37. data/lib/rainbows/writer_thread_spawn.rb +6 -7
  38. data/t/GNUmakefile +2 -2
  39. data/t/close-pipe-response.ru +25 -0
  40. data/t/cramp/rainsocket.ru +1 -1
  41. data/t/fast-pipe-response.ru +10 -0
  42. data/t/file-wrap-to_path.ru +24 -0
  43. data/t/t0015-working_directory.sh +5 -1
  44. data/t/t0020-large-sendfile-response.sh +3 -3
  45. data/t/t0021-sendfile-wrap-to_path.sh +108 -0
  46. data/t/t0022-copy_stream-byte-range.sh +139 -0
  47. data/t/t0023-sendfile-byte-range.sh +63 -0
  48. data/t/t0024-pipelined-sendfile-response.sh +89 -0
  49. data/t/t0030-fast-pipe-response.sh +63 -0
  50. data/t/t0031-close-pipe-response.sh +96 -0
  51. data/t/t0034-pipelined-pipe-response.sh +87 -0
  52. data/t/t0105-rack-input-limit-bigger.sh +5 -2
  53. data/t/t0500-cramp-streaming.sh +0 -1
  54. data/t/t0501-cramp-rainsocket.sh +1 -0
  55. data/t/t9000-rack-app-pool.sh +1 -1
  56. data/t/test_isolate.rb +1 -0
  57. metadata +29 -5
data/t/GNUmakefile CHANGED
@@ -27,7 +27,6 @@ models += Rev
27
27
  models += EventMachine
28
28
  models += NeverBlock
29
29
  models += RevThreadSpawn
30
- models += RevThreadPool
31
30
 
32
31
  ifeq ($(RUBY_ENGINE),ruby)
33
32
  rp := )
@@ -37,6 +36,7 @@ ifeq ($(RUBY_ENGINE),ruby)
37
36
  models += FiberSpawn
38
37
  models += RevFiberSpawn
39
38
  models += FiberPool
39
+ models += RevThreadPool
40
40
  endif
41
41
  endif
42
42
 
@@ -118,7 +118,7 @@ $(MODEL_T): script = $(subst $(model).,,$@)
118
118
  $(MODEL_T): export RUBY := $(RUBY)
119
119
  $(MODEL_T): export PATH := $(bindir):$(PATH)
120
120
  $(MODEL_T): $(t_deps)
121
- RUBYLIB=$(rainbows_lib):$$(cat $(libs)) \
121
+ RUBYLIB=$(rainbows_lib):$$(cat $(libs)):$(RUBYLIB) \
122
122
  $(TRACER) $(SHELL) $(SH_TEST_OPTS) $(script) $(TEST_OPTS)
123
123
 
124
124
  trash/.gitignore:
@@ -0,0 +1,25 @@
1
+ # must be run without Rack::Lint since that clobbers to_path
2
+ class CloseWrapper < Struct.new(:to_io)
3
+ def each(&block)
4
+ to_io.each(&block)
5
+ end
6
+
7
+ def close
8
+ ::File.open(ENV['fifo'], 'wb') do |fp|
9
+ fp.syswrite("CLOSING #{to_io}\n")
10
+ if to_io.respond_to?(:close) && ! to_io.closed?
11
+ to_io.close
12
+ end
13
+ end
14
+ end
15
+ end
16
+ use Rainbows::DevFdResponse
17
+ run(lambda { |env|
18
+ io = IO.popen('cat random_blob', 'rb')
19
+ [ 200,
20
+ {
21
+ 'Content-Length' => ::File.stat('random_blob').size.to_s,
22
+ 'Content-Type' => 'application/octet-stream',
23
+ },
24
+ CloseWrapper[io] ]
25
+ })
@@ -19,7 +19,7 @@ class WelcomeController < Cramp::Controller::Websocket
19
19
  end
20
20
 
21
21
  def send_hello_world
22
- render "Hello from the Server!\n"
22
+ render("Hello from the Server!\n" * 256)
23
23
  end
24
24
  end
25
25
 
@@ -0,0 +1,10 @@
1
+ # must be run without Rack::Lint since that clobbers to_path
2
+ use Rainbows::DevFdResponse
3
+ run(lambda { |env|
4
+ [ 200,
5
+ {
6
+ 'Content-Length' => ::File.stat('random_blob').size.to_s,
7
+ 'Content-Type' => 'application/octet-stream',
8
+ },
9
+ IO.popen('cat random_blob', 'rb') ]
10
+ })
@@ -0,0 +1,24 @@
1
+ # must be run without Rack::Lint since that clobbers to_path
2
+ class Wrapper < Struct.new(:app)
3
+ def call(env)
4
+ status, headers, body = app.call(env)
5
+ body = Body.new(body) if body.respond_to?(:to_path)
6
+ [ status, headers, body ]
7
+ end
8
+
9
+ class Body < Struct.new(:body)
10
+ def to_path
11
+ body.to_path
12
+ end
13
+
14
+ def each(&block)
15
+ body.each(&block)
16
+ end
17
+
18
+ def close
19
+ ::File.open(ENV['fifo'], 'wb') { |fp| fp.puts "CLOSING" }
20
+ end
21
+ end
22
+ end
23
+ use Wrapper
24
+ run Rack::File.new(Dir.pwd)
@@ -6,7 +6,7 @@ then
6
6
  fi
7
7
  . ./test-lib.sh
8
8
 
9
- t_plan 4 "config.ru inside alt working_directory"
9
+ t_plan 5 "config.ru inside alt working_directory"
10
10
 
11
11
  t_begin "setup and start" && {
12
12
  rainbows_setup
@@ -53,4 +53,8 @@ t_begin "response body ppid == 1 (daemonized)" && {
53
53
  test "$body" -eq 1
54
54
  }
55
55
 
56
+ t_begin "cleanup working directory" && {
57
+ rm -r $t_pfx.app
58
+ }
59
+
56
60
  t_done
@@ -46,7 +46,7 @@ t_begin "send a batch of abortive HTTP/1.1 requests in parallel" && {
46
46
  do
47
47
  rm -f $i
48
48
  (
49
- curl -sSf --max-time 5 --limit-rate 1K \
49
+ curl -sSf --max-time 2 --limit-rate 1K \
50
50
  http://$listen/random_blob >/dev/null || echo ok > $i
51
51
  ) &
52
52
  done
@@ -77,11 +77,11 @@ t_begin "check proc to ensure file is closed properly (Linux only)" && {
77
77
 
78
78
  t_begin "send a bunch of HTTP/1.1 requests in parallel" && {
79
79
  (
80
- curl -sSf --limit-rate 1M http://$listen/random_blob | \
80
+ curl -sSf --limit-rate 5M http://$listen/random_blob | \
81
81
  rsha1 > $slow_a
82
82
  ) &
83
83
  (
84
- curl -sSf --limit-rate 750K http://$listen/random_blob | \
84
+ curl -sSf --limit-rate 6M http://$listen/random_blob | \
85
85
  rsha1 > $slow_b
86
86
  ) &
87
87
  for i in $a $b $c
@@ -0,0 +1,108 @@
1
+ #!/bin/sh
2
+ . ./test-lib.sh
3
+ test -r random_blob || die "random_blob required, run with 'make $0'"
4
+ case $RUBY_ENGINE in
5
+ ruby) ;;
6
+ *)
7
+ t_info "skipping $T since it can't load the sendfile gem, yet"
8
+ exit 0
9
+ ;;
10
+ esac
11
+
12
+ t_plan 16 "sendfile wrap body response for $model"
13
+
14
+ t_begin "setup and startup" && {
15
+ rtmpfiles out err http_fifo sub_ok
16
+ rainbows_setup $model
17
+ echo 'require "sendfile"' >> $unicorn_config
18
+ echo 'def (::IO).copy_stream(*x); abort "NO"; end' >> $unicorn_config
19
+
20
+ # can't load Rack::Lint here since it clobbers body#to_path
21
+ export fifo
22
+ rainbows -E none -D file-wrap-to_path.ru -c $unicorn_config
23
+ rainbows_wait_start
24
+ }
25
+
26
+ t_begin "read random blob sha1" && {
27
+ random_blob_sha1=$(rsha1 < random_blob)
28
+ }
29
+
30
+ t_begin "start FIFO reader" && {
31
+ cat $fifo > $out &
32
+ }
33
+
34
+ t_begin "single request matches" && {
35
+ sha1=$(curl -sSfv 2> $err http://$listen/random_blob | rsha1)
36
+ test -n "$sha1"
37
+ test x"$sha1" = x"$random_blob_sha1"
38
+ }
39
+
40
+ t_begin "body.close called" && {
41
+ wait # for cat $fifo
42
+ grep CLOSING $out || die "body.close not logged"
43
+ }
44
+
45
+ t_begin "start FIFO reader for abortive HTTP/1.1 request" && {
46
+ cat $fifo > $out &
47
+ }
48
+
49
+ t_begin "send abortive HTTP/1.1 request" && {
50
+ rm -f $ok
51
+ (
52
+ printf 'GET /random_blob HTTP/1.1\r\nHost: example.com\r\n\r\n'
53
+ dd bs=4096 count=1 < $http_fifo >/dev/null
54
+ echo ok > $ok
55
+ ) | socat - TCP:$listen > $http_fifo || :
56
+ test xok = x$(cat $ok)
57
+ }
58
+
59
+ t_begin "body.close called for aborted HTTP/1.1 request" && {
60
+ wait # for cat $fifo
61
+ grep CLOSING $out || die "body.close not logged"
62
+ }
63
+
64
+ t_begin "start FIFO reader for abortive HTTP/1.0 request" && {
65
+ cat $fifo > $out &
66
+ }
67
+
68
+ t_begin "send abortive HTTP/1.0 request" && {
69
+ rm -f $ok
70
+ (
71
+ printf 'GET /random_blob HTTP/1.0\r\n\r\n'
72
+ dd bs=4096 count=1 < $http_fifo >/dev/null
73
+ echo ok > $ok
74
+ ) | socat - TCP:$listen > $http_fifo || :
75
+ test xok = x$(cat $ok)
76
+ }
77
+
78
+ t_begin "body.close called for aborted HTTP/1.0 request" && {
79
+ wait # for cat $fifo
80
+ grep CLOSING $out || die "body.close not logged"
81
+ }
82
+
83
+ t_begin "start FIFO reader for abortive HTTP/0.9 request" && {
84
+ cat $fifo > $out &
85
+ }
86
+
87
+ t_begin "send abortive HTTP/0.9 request" && {
88
+ rm -f $ok
89
+ (
90
+ printf 'GET /random_blob\r\n'
91
+ dd bs=4096 count=1 < $http_fifo >/dev/null
92
+ echo ok > $ok
93
+ ) | socat - TCP:$listen > $http_fifo || :
94
+ test xok = x$(cat $ok)
95
+ }
96
+
97
+ t_begin "body.close called for aborted HTTP/0.9 request" && {
98
+ wait # for cat $fifo
99
+ grep CLOSING $out || die "body.close not logged"
100
+ }
101
+
102
+ t_begin "shutdown server" && {
103
+ kill -QUIT $rainbows_pid
104
+ }
105
+
106
+ t_begin "check stderr" && check_stderr
107
+
108
+ t_done
@@ -0,0 +1,139 @@
1
+ #!/bin/sh
2
+ . ./test-lib.sh
3
+ test -r random_blob || die "random_blob required, run with 'make $0'"
4
+ case $RUBY_VERSION in
5
+ 1.9.*) ;;
6
+ *)
7
+ t_info "skipping $T since it can't IO.copy_stream"
8
+ exit 0
9
+ ;;
10
+ esac
11
+
12
+ case $model in
13
+ ThreadSpawn|WriterThreadSpawn|ThreadPool|WriterThreadPool|Base) ;;
14
+ *)
15
+ t_info "skipping $T since it doesn't use IO.copy_stream"
16
+ exit 0
17
+ ;;
18
+ esac
19
+
20
+ t_plan 11 "IO.copy_stream byte range response for $model"
21
+
22
+ t_begin "setup and startup" && {
23
+ rtmpfiles out err
24
+ rainbows_setup $model
25
+ # can't load Rack::Lint here since it clobbers body#to_path
26
+ rainbows -E none -D large-file-response.ru -c $unicorn_config
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
+ }
41
+
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
@@ -0,0 +1,63 @@
1
+ #!/bin/sh
2
+ . ./test-lib.sh
3
+ test -r random_blob || die "random_blob required, run with 'make $0'"
4
+ case $RUBY_ENGINE in
5
+ ruby) ;;
6
+ *)
7
+ t_info "skipping $T since it can't load the sendfile gem, yet"
8
+ exit 0
9
+ ;;
10
+ esac
11
+
12
+ case $model in
13
+ EventMachine|NeverBlock)
14
+ t_info "skipping $T since it's not compatible with $model"
15
+ exit 0
16
+ ;;
17
+ *) ;;
18
+ esac
19
+
20
+ t_plan 7 "sendfile byte range response for $model"
21
+
22
+ t_begin "setup and startup" && {
23
+ rtmpfiles out err
24
+ rainbows_setup $model
25
+ echo 'require "sendfile"' >> $unicorn_config
26
+ echo 'def (::IO).copy_stream(*x); abort "NO"; end' >> $unicorn_config
27
+
28
+ # can't load Rack::Lint here since it clobbers body#to_path
29
+ rainbows -E none -D large-file-response.ru -c $unicorn_config
30
+ rainbows_wait_start
31
+ range_head=-r-365
32
+ range_tail=-r155-
33
+ range_mid=-r200-300
34
+ }
35
+
36
+ t_begin "read random blob sha1s" && {
37
+ sha1_head=$(curl -sSf $range_head file://random_blob | rsha1)
38
+ sha1_tail=$(curl -sSf $range_tail file://random_blob | rsha1)
39
+ sha1_mid=$(curl -sSf $range_mid file://random_blob | rsha1)
40
+ }
41
+
42
+ t_begin "head range matches" && {
43
+ sha1="$(curl -sSv $range_head http://$listen/random_blob | rsha1)"
44
+ test x"$sha1_head" = x"$sha1"
45
+ }
46
+
47
+ t_begin "tail range matches" && {
48
+ sha1="$(curl -sS $range_tail http://$listen/random_blob | rsha1)"
49
+ test x"$sha1_tail" = x"$sha1"
50
+ }
51
+
52
+ t_begin "mid range matches" && {
53
+ sha1="$(curl -sS $range_mid http://$listen/random_blob | rsha1)"
54
+ test x"$sha1_mid" = x"$sha1"
55
+ }
56
+
57
+ t_begin "shutdown server" && {
58
+ kill -QUIT $rainbows_pid
59
+ }
60
+
61
+ t_begin "check stderr" && check_stderr
62
+
63
+ t_done