rainbows 0.95.1 → 0.96.0

Sign up to get free protection for your applications and to get access to all the features.
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