about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2011-06-11 06:35:12 +0000
committerEric Wong <normalperson@yhbt.net>2011-06-11 06:35:12 +0000
commit281ef555c40cf292809ad10d623d0571fafc790e (patch)
treee13fb2af08c6896b53f244bcf6efb36a9c730051
parent1f3e7b1c7ee89aefdcadd4aebd7b0527baa188a3 (diff)
Lowering this will lower worst-case memory usage and mitigate some
denial-of-service attacks.  This should be larger than
client_header_buffer_size.

The default value is carried over from Mongrel and Unicorn.
-rw-r--r--lib/rainbows/configurator.rb13
-rw-r--r--lib/rainbows/http_server.rb4
-rwxr-xr-xt/t0045-client_max_header_size.sh90
3 files changed, 107 insertions, 0 deletions
diff --git a/lib/rainbows/configurator.rb b/lib/rainbows/configurator.rb
index 1b93fc7..433158b 100644
--- a/lib/rainbows/configurator.rb
+++ b/lib/rainbows/configurator.rb
@@ -27,6 +27,7 @@ module Rainbows::Configurator
     :keepalive_requests => 100,
     :client_max_body_size => 1024 * 1024,
     :client_header_buffer_size => 1024,
+    :client_max_header_size => 112 * 1024,
     :copy_stream => IO.respond_to?(:copy_stream) ? IO : false,
   })
 
@@ -147,6 +148,18 @@ module Rainbows::Configurator
     set[:client_max_body_size] = bytes
   end
 
+  # Limits the maximum size of a request header for all requests.
+  #
+  # Default: 112 kilobytes (114688 bytes)
+  #
+  # Lowering this will lower worst-case memory usage and mitigate some
+  # denial-of-service attacks.  This should be larger than
+  # client_header_buffer_size.
+  def client_max_header_size(bytes)
+    check!
+    set_int(:client_max_header_size, bytes, 8)
+  end
+
   # This governs the amount of memory allocated for an individual read(2) or
   # recv(2) system call when reading headers.  Applications that make minimal
   # use of cookies should not increase this from the default.
diff --git a/lib/rainbows/http_server.rb b/lib/rainbows/http_server.rb
index be02630..746d534 100644
--- a/lib/rainbows/http_server.rb
+++ b/lib/rainbows/http_server.rb
@@ -97,4 +97,8 @@ class Rainbows::HttpServer < Unicorn::HttpServer
   def keepalive_requests
     Unicorn::HttpRequest.keepalive_requests
   end
+
+  def client_max_header_size=(bytes)
+    Unicorn::HttpParser.max_header_len = bytes
+  end
 end
diff --git a/t/t0045-client_max_header_size.sh b/t/t0045-client_max_header_size.sh
new file mode 100755
index 0000000..17cbc29
--- /dev/null
+++ b/t/t0045-client_max_header_size.sh
@@ -0,0 +1,90 @@
+#!/bin/sh
+. ./test-lib.sh
+skip_models StreamResponseEpoll
+
+t_plan 11 "client_max_header_size tests for $model"
+
+t_begin "setup Rainbows!" && {
+        rainbows_setup $model
+}
+
+t_begin "fails with zero size" && {
+        ed -s $unicorn_config <<EOF
+,s/^  client_max_body_size.*/  client_max_header_size 0/
+w
+EOF
+        grep "client_max_header_size 0" $unicorn_config
+        rainbows -D env.ru -c $unicorn_config && die "should fail"
+}
+
+t_begin "fails with negative value" && {
+        ed -s $unicorn_config <<EOF
+,s/^  client_max_header_size.*/  client_max_header_size -1/
+w
+EOF
+        grep "client_max_header_size -1" $unicorn_config
+        rainbows -D env.ru -c $unicorn_config && die "should fail"
+}
+
+t_begin "fails with small size" && {
+        ed -s $unicorn_config <<EOF
+,s/^  client_max_header_size.*/  client_max_header_size 7/
+w
+EOF
+        grep "client_max_header_size 7" $unicorn_config
+        rainbows -D env.ru -c $unicorn_config && die "should fail"
+}
+
+t_begin "starts with minimum value" && {
+        ed -s $unicorn_config <<EOF
+,s/^  client_max_header_size.*/  client_max_header_size 8/
+w
+EOF
+        grep 'client_max_header_size 8$' $unicorn_config
+        rainbows -D env.ru -c $unicorn_config
+        rainbows_wait_start
+}
+
+t_begin "smallest HTTP/0.9 request works right" && {
+        (
+                cat $fifo > $tmp &
+                printf 'GET /\r\n'
+                wait
+                echo ok > $ok
+        ) | socat - TCP:$listen > $fifo
+        wait
+        test xok = x"$(cat $ok)"
+        test 1 -eq $(wc -l < $tmp)
+        grep HTTP_VERSION $tmp && die "unexpected HTTP_VERSION in HTTP/0.9 request"
+}
+
+t_begin "HTTP/1.1 request fails" && {
+        curl -vsSf http://$listen/ > $tmp 2>&1 && die "unexpected curl success"
+        grep '400$' $tmp
+}
+
+t_begin "increase client_max_header_size on reload" && {
+        ed -s $unicorn_config <<EOF
+,s/^  client_max_header_size.*/  client_max_header_size 512/
+w
+EOF
+        grep 'client_max_header_size 512$' $unicorn_config
+        kill -HUP $rainbows_pid
+        test xSTART = x"$(cat $fifo)"
+}
+
+t_begin "HTTP/1.1 request succeeds" && {
+        curl -sSf http://$listen/ > $tmp
+        test 1 -eq $(wc -l < $tmp)
+        dbgcat tmp
+}
+
+t_begin "no errors in stderr" && {
+        check_stderr
+}
+
+t_begin "shutdown" && {
+        kill $rainbows_pid
+}
+
+t_done