rainbows 0.94.0 → 0.95.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. data/.document +1 -0
  2. data/.manifest +18 -0
  3. data/ChangeLog +394 -226
  4. data/GIT-VERSION-FILE +1 -1
  5. data/GIT-VERSION-GEN +1 -1
  6. data/GNUmakefile +6 -4
  7. data/NEWS +18 -0
  8. data/README +13 -5
  9. data/Static_Files +71 -0
  10. data/TODO +12 -0
  11. data/Test_Suite +1 -1
  12. data/bin/rainbows +1 -4
  13. data/lib/rainbows/actor_spawn.rb +1 -1
  14. data/lib/rainbows/app_pool.rb +1 -1
  15. data/lib/rainbows/base.rb +79 -89
  16. data/lib/rainbows/byte_slice.rb +17 -0
  17. data/lib/rainbows/configurator.rb +46 -0
  18. data/lib/rainbows/const.rb +2 -2
  19. data/lib/rainbows/dev_fd_response.rb +52 -44
  20. data/lib/rainbows/error.rb +1 -0
  21. data/lib/rainbows/ev_core.rb +3 -2
  22. data/lib/rainbows/event_machine.rb +26 -24
  23. data/lib/rainbows/fiber/base.rb +30 -40
  24. data/lib/rainbows/fiber/body.rb +34 -0
  25. data/lib/rainbows/fiber/io.rb +28 -8
  26. data/lib/rainbows/fiber/queue.rb +1 -0
  27. data/lib/rainbows/fiber/rev.rb +4 -2
  28. data/lib/rainbows/fiber.rb +1 -0
  29. data/lib/rainbows/fiber_pool.rb +2 -2
  30. data/lib/rainbows/fiber_spawn.rb +2 -2
  31. data/lib/rainbows/http_response.rb +20 -31
  32. data/lib/rainbows/http_server.rb +3 -4
  33. data/lib/rainbows/max_body.rb +1 -0
  34. data/lib/rainbows/never_block/event_machine.rb +2 -0
  35. data/lib/rainbows/never_block.rb +5 -4
  36. data/lib/rainbows/queue_pool.rb +1 -0
  37. data/lib/rainbows/response/body.rb +119 -0
  38. data/lib/rainbows/response.rb +43 -0
  39. data/lib/rainbows/rev/client.rb +79 -9
  40. data/lib/rainbows/rev/core.rb +4 -0
  41. data/lib/rainbows/rev/deferred_response.rb +1 -44
  42. data/lib/rainbows/rev/heartbeat.rb +1 -0
  43. data/lib/rainbows/rev/master.rb +1 -0
  44. data/lib/rainbows/rev/sendfile.rb +26 -0
  45. data/lib/rainbows/rev/thread.rb +2 -1
  46. data/lib/rainbows/rev.rb +2 -0
  47. data/lib/rainbows/rev_fiber_spawn.rb +3 -1
  48. data/lib/rainbows/rev_thread_pool.rb +7 -5
  49. data/lib/rainbows/rev_thread_spawn.rb +2 -2
  50. data/lib/rainbows/revactor.rb +146 -146
  51. data/lib/rainbows/sendfile.rb +10 -21
  52. data/lib/rainbows/server_token.rb +39 -0
  53. data/lib/rainbows/stream_file.rb +14 -0
  54. data/lib/rainbows/tee_input.rb +1 -0
  55. data/lib/rainbows/thread_pool.rb +12 -7
  56. data/lib/rainbows/thread_spawn.rb +2 -3
  57. data/lib/rainbows/writer_thread_pool.rb +13 -7
  58. data/lib/rainbows/writer_thread_spawn.rb +12 -9
  59. data/lib/rainbows.rb +16 -45
  60. data/rainbows.gemspec +8 -8
  61. data/t/.gitignore +1 -1
  62. data/t/GNUmakefile +26 -16
  63. data/t/README +1 -1
  64. data/t/async-response-no-autochunk.ru +0 -1
  65. data/t/async-response.ru +0 -1
  66. data/t/cramp/rainsocket.ru +26 -0
  67. data/t/fork-sleep.ru +0 -1
  68. data/t/my-tap-lib.sh +3 -2
  69. data/t/simple-http_ActorSpawn.ru +9 -0
  70. data/t/t0009-broken-app.sh +1 -1
  71. data/t/t0009.ru +0 -1
  72. data/t/t0011-close-on-exec-set.sh +1 -1
  73. data/t/t0015-working_directory.sh +56 -0
  74. data/t/t0016-onenine-encoding-is-tricky.sh +28 -0
  75. data/t/t0016.rb +15 -0
  76. data/t/t0020-large-sendfile-response.sh +141 -0
  77. data/t/t0300-async_sinatra.sh +0 -6
  78. data/t/t0501-cramp-rainsocket.sh +38 -0
  79. data/t/t9001-sendfile-to-path.sh +5 -4
  80. data/t/t9002-server-token.sh +37 -0
  81. data/t/t9002.ru +4 -0
  82. data/t/test-lib.sh +1 -1
  83. data/t/test_isolate.rb +14 -11
  84. metadata +87 -18
data/GIT-VERSION-FILE CHANGED
@@ -1 +1 @@
1
- GIT_VERSION = 0.94.0
1
+ GIT_VERSION = 0.95.0
data/GIT-VERSION-GEN CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v0.94.0.GIT
4
+ DEF_VER=v0.95.0.GIT
5
5
 
6
6
  LF='
7
7
  '
data/GNUmakefile CHANGED
@@ -1,5 +1,6 @@
1
1
  # use GNU Make to run tests in parallel, and without depending on RubyGems
2
2
  all::
3
+ MRI = ruby
3
4
  RUBY = ruby
4
5
  RAKE = rake
5
6
  RSYNC = rsync
@@ -15,6 +16,7 @@ endif
15
16
  ifeq ($(RUBY_VERSION),)
16
17
  RUBY_VERSION := $(shell $(RUBY) -e 'puts RUBY_VERSION')
17
18
  endif
19
+ RUBY_ENGINE := $(shell $(RUBY) -e 'puts((RUBY_ENGINE rescue "ruby"))')
18
20
 
19
21
  base_bins := rainbows
20
22
  bins := $(addprefix bin/, $(base_bins))
@@ -60,7 +62,7 @@ NEWS: GIT-VERSION-FILE
60
62
  $(RAKE) -s news_rdoc > $@+
61
63
  mv $@+ $@
62
64
 
63
- SINCE = 0.92.0
65
+ SINCE = 0.94.0
64
66
  ChangeLog: LOG_VERSION = \
65
67
  $(shell git rev-parse -q "$(GIT_VERSION)" >/dev/null 2>&1 && \
66
68
  echo $(GIT_VERSION) || git describe)
@@ -91,16 +93,16 @@ doc: .document NEWS ChangeLog
91
93
  < $${i}_1.html > tmp && mv tmp $${i}_1.html; \
92
94
  ln $${i}_1.html $${i}.1.html; \
93
95
  done
94
- $(RUBY) -i -p -e \
96
+ $(MRI) -i -p -e \
95
97
  '$$_.gsub!("</title>",%q{\&$(call atom,$(cgit_atom))})' \
96
98
  doc/ChangeLog.html
97
- $(RUBY) -i -p -e \
99
+ $(MRI) -i -p -e \
98
100
  '$$_.gsub!("</title>",%q{\&$(call atom,$(news_atom))})' \
99
101
  doc/NEWS.html doc/README.html
100
102
  $(RAKE) -s news_atom > doc/NEWS.atom.xml
101
103
  cd doc && ln README.html tmp && mv tmp index.html
102
104
  $(MAKE) -C Documentation comparison.html
103
- $(RUBY) -i -p -e \
105
+ $(MRI) -i -p -e \
104
106
  '$$_.gsub!(/INCLUDE/){File.read("Documentation/comparison.html")}' \
105
107
  doc/Summary.html
106
108
  cat Documentation/comparison.css >> doc/rdoc.css
data/NEWS CHANGED
@@ -1,3 +1,21 @@
1
+ === 0.95.0 / 2010-07-10 08:45 UTC
2
+
3
+ In addition to the 1.9-only IO.copy_stream, the new sendfile
4
+ 1.0.0 gem may optionally be used with most concurrency models
5
+ (even under 1.8).
6
+
7
+ See http://rainbows.rubyforge.org/Static_Files.html for more info
8
+
9
+ Other changes:
10
+
11
+ * 1.9 encoding bugfix for (Rev)FiberSpawn and FiberPool
12
+ * fixed potential rack.input corruption with Revactor
13
+ * ThreadPool graceful shutdown no longer blocks until timeout
14
+ * optional ServerToken middleware for to display Server: header
15
+ * Dependencies bumped to Rack 1.1+ and Unicorn 1.1.0+
16
+ * numerous internal cleanups, small bugfixes and speedups
17
+ * more concise website oriented at users
18
+
1
19
  === 0.94.0 / 2010-06-04 08:42 UTC
2
20
 
3
21
  This release fixes corrupted large response bodies for Ruby 1.8
data/README CHANGED
@@ -2,9 +2,17 @@
2
2
 
3
3
  \Rainbows! is an HTTP server for sleepy Rack applications. It is based on
4
4
  Unicorn, but designed to handle applications that expect long
5
- request/response times and/or slow clients. For Rack applications not
6
- heavily bound by slow external network dependencies, consider Unicorn
7
- instead as it simpler and easier to debug.
5
+ request/response times and/or slow clients.
6
+
7
+ For Rack applications not heavily bound by slow external network
8
+ dependencies, consider Unicorn instead as it simpler and easier to
9
+ debug.
10
+
11
+ If you're on a small system, or write extremely tight and reliable code
12
+ and don't want multiple worker processes, check out
13
+ {Zbatery}[http://zbatery.bogomip.org/], too. Zbatery can use all the
14
+ crazy network concurrency options of \Rainbows! in a single worker
15
+ process.
8
16
 
9
17
  == \Rainbows! is about Diversity
10
18
 
@@ -125,8 +133,8 @@ config file:
125
133
  worker_connections 100
126
134
  end
127
135
 
128
- See the {Rainbows! configuration}[link:Rainbows.html#method-i-Rainbows!]
129
- {documentation}[link:Rainbows.html#method-i-Rainbows!]
136
+ See the {Rainbows! configuration}[link:Rainbows/Configurator.html]
137
+ {documentation}[link:Rainbows/Configurator.html]
130
138
  for more details.
131
139
 
132
140
  == Development
data/Static_Files ADDED
@@ -0,0 +1,71 @@
1
+ = Static file serving with \Rainbows!
2
+
3
+ While Ruby application servers aren't traditionally used to serve static
4
+ files, it'll be fun for us to see how far we can go with \Rainbows!
5
+
6
+ We aren't delusional enough (yet :) to compete with C-based servers like
7
+ nginx or lighttpd in terms of raw performance, but wouldn't it be nice
8
+ to simplify your deployments and only deploy one server?
9
+
10
+ == {sendfile}[http://rubygems.org/gems/sendfile] RubyGem
11
+
12
+ To enable the "sendfile" gem, just make sure you have 1.0.0 or later and
13
+ "require" it in your Rainbows!/Unicorn config file (not your Rack
14
+ config.ru):
15
+
16
+ require 'sendfile' # that's it! nothing else to do
17
+
18
+ # the rest of you Rainbows! config goes below:
19
+ worker_processes 4
20
+ stderr_path "/var/log/app/rainbows.err.log"
21
+ Rainbows! do
22
+ use :RevFiberSpawn
23
+ worker_connections 100
24
+ end
25
+
26
+ The sendfile gem is works for all of our concurrency models except
27
+ Revactor, NeverBlock and EventMachine (see below).
28
+
29
+ The sendfile gem is less buggy than current (Ruby 1.9.2-rc1)
30
+ IO.copy_stream and supports FreeBSD and Solaris in addition to Linux.
31
+ This RubyGem also works under Ruby 1.8 (even with threads) and should
32
+ work with rubinius.git, too.
33
+
34
+ \Rainbows! supports the sendfile gem since v0.95.0
35
+
36
+ == IO.copy_stream (Ruby 1.9 only)
37
+
38
+ Users of pure-Ruby Thread-based models ThreadPool, ThreadSpawn, and
39
+ their Writer* variants use the core IO.copy_stream method under Ruby
40
+ 1.9. IO.copy_stream uses sendfile() under Linux, and a pread()/write()
41
+ loop (implemented in C) on other systems.
42
+
43
+ IO.copy_stream under Linux with Ruby 1.9.2-rc1 (and before) is also
44
+ subject to hanging indefinitely when a client disconnected prematurely.
45
+ This issue is fixed in Ruby trunk (July 2010) and will be in the next
46
+ Ruby 1.9.2 release.
47
+
48
+ \Rainbows! supports IO.copy_stream since v0.93.0
49
+
50
+ == EventMachine FileStreamer
51
+
52
+ EventMachine and NeverBlock users automatically take advantage of
53
+ the mmap()-based FileStreamer class distributed with EventMachine.
54
+
55
+ \Rainbows! supports EventMachine FileStreamer since v0.4.0
56
+
57
+ == Performance
58
+
59
+ With large files and high-throughput clients, there should be little
60
+ performance difference compared to optimal C implementation such as
61
+ nginx and lighttpd. Ruby runtime overhead matters more when serving
62
+ slower clients and smaller files.
63
+
64
+ == The Future...
65
+
66
+ Future releases of \Rainbows! will have byte-range support to serve
67
+ partial and multipart responses, too. We'll also support an open file
68
+ cache (similar to nginx) which allows us to reuse open file descriptors.
69
+
70
+ Under Linux, we'll support the splice(2) system call for zero-copy
71
+ proxying {io_splice}[http://bogomips.org/ruby_io_splice/], too.
data/TODO CHANGED
@@ -3,9 +3,21 @@
3
3
  We're lazy and pick the easy items to do first, then the ones people
4
4
  care about.
5
5
 
6
+ * documentation improvements
7
+
6
8
  * Split out NeverBlock into NeverBlockEventMachine and NeverBlockReactor
7
9
  NeverBlock will default to one of them (depending on NB upstream).
8
10
 
11
+ * allow _OPTIONAL_ splice(2) with DevFdResponse under Linux
12
+ (splice is very broken under some older kernels)
13
+
14
+ * use IO#sendfile_nonblock for EventMachine/Revactor/NeverBlock
15
+
16
+ * Open file cache Rack app/middleware (idea from nginx), since sendfile
17
+ (and IO.copy_stream) allows pread(2)-style offsets
18
+
19
+ * Byte range responses for static files + sendfile
20
+
9
21
  * Improve test suite coverage. We won't waste cycles with puny
10
22
  unit tests, only integration tests that exercise externally
11
23
  visible parts.
data/Test_Suite CHANGED
@@ -12,7 +12,7 @@ easily portable to non-Ruby web servers.
12
12
  == Requirements
13
13
 
14
14
  * {Ruby 1.8 or 1.9}[http://www.ruby-lang.org/] (duh!)
15
- * {isolate ~> 2.0.2}[http://github.com/jbarnette/isolate] - for dependencies
15
+ * {isolate ~> 2.1.0}[http://github.com/jbarnette/isolate] - for dependencies
16
16
  * {GNU make}[http://www.gnu.org/software/make/]
17
17
  * {socat}[http://www.dest-unreach.org/socat/]
18
18
  * {curl >= 7.18.0}[http://curl.haxx.se/]
data/bin/rainbows CHANGED
@@ -110,10 +110,7 @@ opts = OptionParser.new("", 24, ' ') do |opts|
110
110
  opts.parse! ARGV
111
111
  end
112
112
 
113
- config = ARGV[0] || "config.ru"
114
- abort "configuration file #{config} not found" unless File.exist?(config)
115
-
116
- app = Unicorn.builder(config, opts)
113
+ app = Unicorn.builder(ARGV[0] || 'config.ru', opts)
117
114
  listeners << "#{host}:#{port}" if set_listener
118
115
 
119
116
  if $DEBUG
@@ -20,7 +20,7 @@ module Rainbows
20
20
  # runs inside each forked worker, this sits around and waits
21
21
  # for connections and doesn't die until the parent dies (or is
22
22
  # given a INT, QUIT, or TERM signal)
23
- def worker_loop(worker)
23
+ def worker_loop(worker) # :nodoc:
24
24
  Const::RACK_DEFAULTS["rack.multithread"] = true # :(
25
25
  init_worker_process(worker)
26
26
  accept_loop(Actor)
@@ -82,7 +82,7 @@ module Rainbows
82
82
  end
83
83
 
84
84
  # Rack application endpoint, +env+ is the Rack environment
85
- def call(env)
85
+ def call(env) # :nodoc:
86
86
 
87
87
  # we have to do this check at call time (and not initialize)
88
88
  # because of preload_app=true and models being changeable with SIGHUP
data/lib/rainbows/base.rb CHANGED
@@ -1,106 +1,96 @@
1
1
  # -*- encoding: binary -*-
2
+ require 'rainbows/tee_input'
2
3
 
3
- module Rainbows
4
+ # base class for Rainbows concurrency models, this is currently used by
5
+ # ThreadSpawn and ThreadPool models. Base is also its own
6
+ # (non-)concurrency model which is basically Unicorn-with-keepalive, and
7
+ # not intended for production use, as keepalive with a pure prefork
8
+ # concurrency model is extremely expensive.
9
+ module Rainbows::Base
4
10
 
5
- # base class for Rainbows concurrency models, this is currently
6
- # used by ThreadSpawn and ThreadPool models
7
- module Base
11
+ # :stopdoc:
12
+ include Rainbows::Const
13
+ include Rainbows::Response
8
14
 
9
- include Unicorn
10
- include Rainbows::Const
11
- G = Rainbows::G
15
+ # shortcuts...
16
+ G = Rainbows::G
17
+ NULL_IO = Unicorn::HttpRequest::NULL_IO
18
+ TeeInput = Rainbows::TeeInput
19
+ HttpParser = Unicorn::HttpParser
12
20
 
13
- def init_worker_process(worker)
14
- super(worker)
15
- MaxBody.setup
16
- G.tmp = worker.tmp
21
+ # this method is called by all current concurrency models
22
+ def init_worker_process(worker) # :nodoc:
23
+ super(worker)
24
+ Rainbows::Response.setup(self.class)
25
+ Rainbows::MaxBody.setup
26
+ G.tmp = worker.tmp
17
27
 
18
- # avoid spurious wakeups and blocking-accept() with 1.8 green threads
19
- if RUBY_VERSION.to_f < 1.9
20
- require "io/nonblock"
21
- HttpServer::LISTENERS.each { |l| l.nonblock = true }
22
- end
23
-
24
- # we're don't use the self-pipe mechanism in the Rainbows! worker
25
- # since we don't defer reopening logs
26
- HttpServer::SELF_PIPE.each { |x| x.close }.clear
27
- trap(:USR1) { reopen_worker_logs(worker.nr) }
28
- trap(:QUIT) { G.quit! }
29
- [:TERM, :INT].each { |sig| trap(sig) { exit!(0) } } # instant shutdown
30
- logger.info "Rainbows! #@use worker_connections=#@worker_connections"
28
+ # avoid spurious wakeups and blocking-accept() with 1.8 green threads
29
+ if ! defined?(RUBY_ENGINE) && RUBY_VERSION.to_f < 1.9
30
+ require "io/nonblock"
31
+ Rainbows::HttpServer::LISTENERS.each { |l| l.nonblock = true }
31
32
  end
32
33
 
33
- if IO.respond_to?(:copy_stream)
34
- def write_body(client, body)
35
- if body.respond_to?(:to_path)
36
- io = body.respond_to?(:to_io) ? body.to_io : body.to_path
37
- IO.copy_stream(io, client)
38
- else
39
- body.each { |chunk| client.write(chunk) }
40
- end
41
- ensure
42
- body.respond_to?(:close) and body.close
43
- end
44
- else
45
- def write_body(client, body)
46
- body.each { |chunk| client.write(chunk) }
47
- ensure
48
- body.respond_to?(:close) and body.close
49
- end
50
- end
51
-
52
- module_function :write_body
34
+ # we're don't use the self-pipe mechanism in the Rainbows! worker
35
+ # since we don't defer reopening logs
36
+ Rainbows::HttpServer::SELF_PIPE.each { |x| x.close }.clear
37
+ trap(:USR1) { reopen_worker_logs(worker.nr) }
38
+ trap(:QUIT) { G.quit! }
39
+ [:TERM, :INT].each { |sig| trap(sig) { exit!(0) } } # instant shutdown
40
+ logger.info "Rainbows! #@use worker_connections=#@worker_connections"
41
+ end
53
42
 
54
- # once a client is accepted, it is processed in its entirety here
55
- # in 3 easy steps: read request, call app, write app response
56
- # this is used by synchronous concurrency models
57
- # Base, ThreadSpawn, ThreadPool
58
- def process_client(client)
59
- buf = client.readpartial(CHUNK_SIZE) # accept filters protect us here
60
- hp = HttpParser.new
61
- env = {}
62
- alive = true
63
- remote_addr = Rainbows.addr(client)
43
+ def wait_headers_readable(client) # :nodoc:
44
+ IO.select([client], nil, nil, G.kato)
45
+ end
64
46
 
65
- begin # loop
66
- while ! hp.headers(env, buf)
67
- IO.select([client], nil, nil, G.kato) or return
68
- buf << client.readpartial(CHUNK_SIZE)
69
- end
47
+ # once a client is accepted, it is processed in its entirety here
48
+ # in 3 easy steps: read request, call app, write app response
49
+ # this is used by synchronous concurrency models
50
+ # Base, ThreadSpawn, ThreadPool
51
+ def process_client(client) # :nodoc:
52
+ buf = client.readpartial(CHUNK_SIZE) # accept filters protect us here
53
+ hp = HttpParser.new
54
+ env = {}
55
+ alive = true
56
+ remote_addr = Rainbows.addr(client)
70
57
 
71
- env[CLIENT_IO] = client
72
- env[RACK_INPUT] = 0 == hp.content_length ?
73
- HttpRequest::NULL_IO : TeeInput.new(client, env, hp, buf)
74
- env[REMOTE_ADDR] = remote_addr
75
- status, headers, body = app.call(env.update(RACK_DEFAULTS))
58
+ begin # loop
59
+ until hp.headers(env, buf)
60
+ wait_headers_readable(client) or return
61
+ buf << client.readpartial(CHUNK_SIZE)
62
+ end
76
63
 
77
- if 100 == status.to_i
78
- client.write(EXPECT_100_RESPONSE)
79
- env.delete(HTTP_EXPECT)
80
- status, headers, body = app.call(env)
81
- end
64
+ env[CLIENT_IO] = client
65
+ env[RACK_INPUT] = 0 == hp.content_length ?
66
+ NULL_IO : TeeInput.new(client, env, hp, buf)
67
+ env[REMOTE_ADDR] = remote_addr
68
+ response = app.call(env.update(RACK_DEFAULTS))
82
69
 
83
- alive = hp.keepalive? && G.alive
84
- if hp.headers?
85
- out = [ alive ? CONN_ALIVE : CONN_CLOSE ]
86
- client.write(HttpResponse.header_string(status, headers, out))
87
- end
88
- write_body(client, body)
89
- end while alive and hp.reset.nil? and env.clear
90
- # if we get any error, try to write something back to the client
91
- # assuming we haven't closed the socket, but don't get hung up
92
- # if the socket is already closed or broken. We'll always ensure
93
- # the socket is closed at the end of this function
94
- rescue => e
95
- Error.write(client, e)
96
- ensure
97
- client.close unless client.closed?
98
- end
70
+ if 100 == response[0].to_i
71
+ client.write(EXPECT_100_RESPONSE)
72
+ env.delete(HTTP_EXPECT)
73
+ response = app.call(env)
74
+ end
99
75
 
100
- def self.included(klass)
101
- klass.const_set :LISTENERS, HttpServer::LISTENERS
102
- klass.const_set :G, Rainbows::G
103
- end
76
+ alive = hp.keepalive? && G.alive
77
+ out = [ alive ? CONN_ALIVE : CONN_CLOSE ] if hp.headers?
78
+ write_response(client, response, out)
79
+ end while alive and hp.reset.nil? and env.clear
80
+ # if we get any error, try to write something back to the client
81
+ # assuming we haven't closed the socket, but don't get hung up
82
+ # if the socket is already closed or broken. We'll always ensure
83
+ # the socket is closed at the end of this function
84
+ rescue => e
85
+ Rainbows::Error.write(client, e)
86
+ ensure
87
+ client.close unless client.closed?
88
+ end
104
89
 
90
+ def self.included(klass) # :nodoc:
91
+ klass.const_set :LISTENERS, Rainbows::HttpServer::LISTENERS
92
+ klass.const_set :G, Rainbows::G
105
93
  end
94
+
95
+ # :startdoc:
106
96
  end
@@ -0,0 +1,17 @@
1
+ # -*- encoding: binary -*-
2
+ # :enddoc:
3
+ module Rainbows::ByteSlice
4
+ if String.method_defined?(:encoding)
5
+ def byte_slice(buf, range)
6
+ if buf.encoding != Encoding::BINARY
7
+ buf.dup.force_encoding(Encoding::BINARY)[range]
8
+ else
9
+ buf[range]
10
+ end
11
+ end
12
+ else
13
+ def byte_slice(buf, range)
14
+ buf[range]
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,46 @@
1
+ # -*- encoding: binary -*-
2
+ module Rainbows
3
+
4
+ # This module adds \Rainbows! to the
5
+ # {Unicorn::Configurator}[http://unicorn.bogomips.org/Unicorn/Configurator.html]
6
+ module Configurator
7
+
8
+ # configures \Rainbows! with a given concurrency model to +use+ and
9
+ # a +worker_connections+ upper-bound. This method may be called
10
+ # inside a Unicorn/\Rainbows! configuration file:
11
+ #
12
+ # Rainbows! do
13
+ # use :ThreadSpawn # concurrency model to use
14
+ # worker_connections 400
15
+ # keepalive_timeout 0 # zero disables keepalives entirely
16
+ # client_max_body_size 5*1024*1024 # 5 megabytes
17
+ # end
18
+ #
19
+ # # the rest of the Unicorn configuration
20
+ # worker_processes 8
21
+ #
22
+ # See the documentation for the respective Revactor, ThreadSpawn,
23
+ # and ThreadPool classes for descriptions and recommendations for
24
+ # each of them. The total number of clients we're able to serve is
25
+ # +worker_processes+ * +worker_connections+, so in the above example
26
+ # we can serve 8 * 400 = 3200 clients concurrently.
27
+ #
28
+ # The default is +keepalive_timeout+ is 5 seconds, which should be
29
+ # enough under most conditions for browsers to render the page and
30
+ # start retrieving extra elements for. Increasing this beyond 5
31
+ # seconds is not recommended. Zero disables keepalive entirely
32
+ # (but pipelining fully-formed requests is still works).
33
+ #
34
+ # The default +client_max_body_size+ is 1 megabyte (1024 * 1024 bytes),
35
+ # setting this to +nil+ will disable body size checks and allow any
36
+ # size to be specified.
37
+ def Rainbows!(&block)
38
+ block_given? or raise ArgumentError, "Rainbows! requires a block"
39
+ HttpServer.setup(block)
40
+ end
41
+
42
+ end
43
+ end
44
+
45
+ # inject the Rainbows! method into Unicorn::Configurator
46
+ Unicorn::Configurator.class_eval { include Rainbows::Configurator }
@@ -1,9 +1,9 @@
1
1
  # -*- encoding: binary -*-
2
-
2
+ # :enddoc:
3
3
  module Rainbows
4
4
 
5
5
  module Const
6
- RAINBOWS_VERSION = '0.94.0'
6
+ RAINBOWS_VERSION = '0.95.0'
7
7
 
8
8
  include Unicorn::Const
9
9
 
@@ -1,56 +1,64 @@
1
1
  # -*- encoding: binary -*-
2
2
 
3
- module Rainbows
3
+ # Rack response middleware wrapping any IO-like object with an
4
+ # OS-level file descriptor associated with it. May also be used to
5
+ # create responses from integer file descriptors or existing +IO+
6
+ # objects. This may be used in conjunction with the #to_path method
7
+ # on servers that support it to pass arbitrary file descriptors into
8
+ # the HTTP response without additional open(2) syscalls
9
+ #
10
+ # This middleware is currently a no-op for Rubinius, as it lacks
11
+ # IO.copy_stream in 1.9 and also due to a bug here:
12
+ # http://github.com/evanphx/rubinius/issues/379
4
13
 
5
- # Rack response middleware wrapping any IO-like object with an
6
- # OS-level file descriptor associated with it. May also be used to
7
- # create responses from integer file descriptors or existing +IO+
8
- # objects. This may be used in conjunction with the #to_path method
9
- # on servers that support it to pass arbitrary file descriptors into
10
- # the HTTP response without additional open(2) syscalls
14
+ class Rainbows::DevFdResponse < Struct.new(:app)
11
15
 
12
- class DevFdResponse < Struct.new(:app, :to_io, :to_path)
13
- include Rack::Utils
16
+ # :stopdoc:
17
+ #
18
+ # make this a no-op under Rubinius, it's pointless anyways
19
+ # since Rubinius doesn't have IO.copy_stream
20
+ def self.new(app)
21
+ app
22
+ end if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
23
+ include Rack::Utils
14
24
 
15
- # Rack middleware entry point, we'll just pass through responses
16
- # unless they respond to +to_io+ or +to_path+
17
- def call(env)
18
- status, headers, body = response = app.call(env)
25
+ # Rack middleware entry point, we'll just pass through responses
26
+ # unless they respond to +to_io+ or +to_path+
27
+ def call(env)
28
+ status, headers, body = response = app.call(env)
19
29
 
20
- # totally uninteresting to us if there's no body
21
- return response if STATUS_WITH_NO_ENTITY_BODY.include?(status)
30
+ # totally uninteresting to us if there's no body
31
+ return response if STATUS_WITH_NO_ENTITY_BODY.include?(status)
22
32
 
23
- io = body.to_io if body.respond_to?(:to_io)
24
- io ||= File.open(body.to_path, 'rb') if body.respond_to?(:to_path)
25
- return response if io.nil?
33
+ io = body.to_io if body.respond_to?(:to_io)
34
+ io ||= File.open(body.to_path, 'rb') if body.respond_to?(:to_path)
35
+ return response if io.nil?
26
36
 
27
- headers = HeaderHash.new(headers)
28
- st = io.stat
29
- if st.file?
30
- headers['Content-Length'] ||= st.size.to_s
31
- headers.delete('Transfer-Encoding')
32
- elsif st.pipe? || st.socket? # epoll-able things
33
- if env['rainbows.autochunk']
34
- headers['Transfer-Encoding'] = 'chunked'
35
- headers.delete('Content-Length')
36
- else
37
- headers['X-Rainbows-Autochunk'] = 'no'
38
- end
37
+ headers = HeaderHash.new(headers)
38
+ st = io.stat
39
+ if st.file?
40
+ headers['Content-Length'] ||= st.size.to_s
41
+ headers.delete('Transfer-Encoding')
42
+ elsif st.pipe? || st.socket? # epoll-able things
43
+ if env['rainbows.autochunk']
44
+ headers['Transfer-Encoding'] = 'chunked'
45
+ headers.delete('Content-Length')
46
+ else
47
+ headers['X-Rainbows-Autochunk'] = 'no'
48
+ end
39
49
 
40
- # we need to make sure our pipe output is Fiber-compatible
41
- case env["rainbows.model"]
42
- when :FiberSpawn, :FiberPool, :RevFiberSpawn
43
- return [ status, headers.to_hash, Fiber::IO.new(io,::Fiber.current) ]
44
- end
45
- else # unlikely, char/block device file, directory, ...
46
- return response
50
+ # we need to make sure our pipe output is Fiber-compatible
51
+ case env["rainbows.model"]
52
+ when :FiberSpawn, :FiberPool, :RevFiberSpawn
53
+ return [ status, headers, Rainbows::Fiber::IO.new(io,::Fiber.current) ]
47
54
  end
48
- resp = dup # be reentrant here
49
- resp.to_path = "/dev/fd/#{io.fileno}"
50
- resp.to_io = io
51
- [ status, headers.to_hash, resp ]
55
+ else # unlikely, char/block device file, directory, ...
56
+ return response
52
57
  end
58
+ [ status, headers, Body.new(io, "/dev/fd/#{io.fileno}") ]
59
+ end
53
60
 
61
+ class Body < Struct.new(:to_io, :to_path)
54
62
  # called by the webserver or other middlewares if they can't
55
63
  # handle #to_path
56
64
  def each(&block)
@@ -70,6 +78,6 @@ module Rainbows
70
78
  rescue IOError # could've been IO::new()'ed and closed
71
79
  end
72
80
  end
73
-
74
- end # class
75
- end
81
+ end
82
+ #:startdoc:
83
+ end # class
@@ -1,4 +1,5 @@
1
1
  # -*- encoding: binary -*-
2
+ # :enddoc:
2
3
  module Rainbows
3
4
 
4
5
  class Error
@@ -1,5 +1,5 @@
1
1
  # -*- encoding: binary -*-
2
-
2
+ # :enddoc:
3
3
  module Rainbows
4
4
 
5
5
  # base module for evented models like Rev and EventMachine
@@ -7,6 +7,7 @@ module Rainbows
7
7
  include Unicorn
8
8
  include Rainbows::Const
9
9
  G = Rainbows::G
10
+ NULL_IO = Unicorn::HttpRequest::NULL_IO
10
11
 
11
12
  # Apps may return this Rack response: AsyncResponse = [ -1, {}, [] ]
12
13
  ASYNC_CALLBACK = "async.callback".freeze
@@ -40,7 +41,7 @@ module Rainbows
40
41
  @state = :body
41
42
  len = @hp.content_length
42
43
  if len == 0
43
- @input = HttpRequest::NULL_IO
44
+ @input = NULL_IO
44
45
  app_call # common case
45
46
  else # nil or len > 0
46
47
  # since we don't do streaming input, we have no choice but