rainbows 0.94.0 → 0.95.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 (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