about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2010-12-28 01:14:43 +0000
committerEric Wong <normalperson@yhbt.net>2010-12-28 01:34:49 +0000
commit46d79be0ad3de48ef0a677537becb3508ccad31e (patch)
treed04ae95b99c7bee6a1e72fb050685403bcdc4a5d
parentef66567984780b2ce8daa155c367bcf7e049ab77 (diff)
This will allow servers to limit the number of keepalive
requests that can be made over a single connection to
prevent denial-of-service and also to improve fairness
in load-balancing.
-rw-r--r--lib/rainbows/configurator.rb9
-rw-r--r--lib/rainbows/http_server.rb8
-rwxr-xr-xt/t0040-keepalive_requests-setting.sh51
3 files changed, 67 insertions, 1 deletions
diff --git a/lib/rainbows/configurator.rb b/lib/rainbows/configurator.rb
index e69a3fb..3203c5a 100644
--- a/lib/rainbows/configurator.rb
+++ b/lib/rainbows/configurator.rb
@@ -13,6 +13,7 @@ module Rainbows::Configurator
   #     worker_connections 400
   #     keepalive_timeout 0 # zero disables keepalives entirely
   #     client_max_body_size 5*1024*1024 # 5 megabytes
+  #     keepalive_requests 666 # default:100
   #   end
   #
   #   # the rest of the Unicorn configuration
@@ -33,6 +34,14 @@ module Rainbows::Configurator
   # The default +client_max_body_size+ is 1 megabyte (1024 * 1024 bytes),
   # setting this to +nil+ will disable body size checks and allow any
   # size to be specified.
+  #
+  # The default +keepalive_requests+ is 100, meaning a client may
+  # complete 100 keepalive requests after the initial request before
+  # \Rainbows! forces a disconnect.  Lowering this can improve
+  # load-balancing characteristics as it forces HTTP/1.1 clients to
+  # reconnect after the specified number of requests, hopefully to a
+  # less busy host or worker process.  This may also be used to mitigate
+  # denial-of-service attacks that use HTTP pipelining.
   def Rainbows!(&block)
     block_given? or raise ArgumentError, "Rainbows! requires a block"
     Rainbows::HttpServer.setup(block)
diff --git a/lib/rainbows/http_server.rb b/lib/rainbows/http_server.rb
index d02af72..906fa0a 100644
--- a/lib/rainbows/http_server.rb
+++ b/lib/rainbows/http_server.rb
@@ -90,10 +90,16 @@ class Rainbows::HttpServer < Unicorn::HttpServer
 
   def keepalive_timeout(nr)
     (Integer === nr && nr >= 0) or
-      raise ArgumentError, "keepalive must be a non-negative Integer"
+      raise ArgumentError, "keepalive_timeout must be a non-negative Integer"
     G.kato = nr
   end
 
+  def keepalive_requests(nr)
+    Integer === nr or
+      raise ArgumentError, "keepalive_requests must be a non-negative Integer"
+    Unicorn::HttpRequest.keepalive_requests = nr
+  end
+
   def client_max_body_size(nr)
     err = "client_max_body_size must be nil or a non-negative Integer"
     case nr
diff --git a/t/t0040-keepalive_requests-setting.sh b/t/t0040-keepalive_requests-setting.sh
new file mode 100755
index 0000000..aee6cd3
--- /dev/null
+++ b/t/t0040-keepalive_requests-setting.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+. ./test-lib.sh
+t_plan 6 "keepalive_requests limit tests for $model"
+
+t_begin "setup and start" && {
+        rainbows_setup $model 50 666
+        rtmpfiles curl_out curl_err
+        grep 'keepalive_timeout 666' $unicorn_config
+        ed -s $unicorn_config <<EOF
+,s/listen.*/&, :tcp_nodelay => true/
+w
+EOF
+        grep nodelay $unicorn_config
+        rainbows -E none -D env.ru -c $unicorn_config
+        rainbows_wait_start
+}
+
+t_begin "curl requests hit default keepalive_requests limit" && {
+        curl -sSfv http://$listen/[0-101] > $curl_out 2> $curl_err
+        test 1 -eq $(grep 'Connection: close' $curl_err |wc -l)
+        test 101 -eq $(grep 'Connection: keep-alive' $curl_err |wc -l)
+}
+
+t_begin "reload with smaller keepalive_requests limit" && {
+        ed -s $unicorn_config <<EOF
+,g/Rainbows!/
+a
+  keepalive_requests 5
+.
+w
+EOF
+        kill -HUP $rainbows_pid
+        test x"$(cat $fifo)" = xSTART
+}
+
+t_begin "curl requests hit smaller keepalive_requests limit" && {
+        rm -f $curl_out $curl_err
+        curl -sSfv http://$listen/[1-13] > $curl_out 2> $curl_err
+        test 2 -eq $(grep 'Connection: close' $curl_err |wc -l)
+        test 11 -eq $(grep 'Connection: keep-alive' $curl_err |wc -l)
+}
+
+t_begin "killing succeeds" && {
+        kill $rainbows_pid
+}
+
+t_begin "check stderr" && {
+        check_stderr
+}
+
+t_done