about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorMishael A Sibiryakov <death@junki.org>2016-10-20 12:05:32 +0300
committerEric Wong <e@80x24.org>2016-10-20 17:39:11 +0000
commitd5fbbf547203061b1eaafbe80c4538a37fce5a34 (patch)
tree1609758e298a21d921c4bd09f4c7da6e29eb4c66
parentc47d13d2c5ce75cca5ac11eda6d6a0e54015e711 (diff)
downloadunicorn-d5fbbf547203061b1eaafbe80c4538a37fce5a34.tar.gz
Hi all.

We're implementing client certificate authentication with nginx and
unicorn. 

Nginx configured in the following way:

proxy_set_header X-SSL-Client-Cert $ssl_client_cert;

When client submits certificate and nginx passes it to the unicorn,
unicorn responds with 400 (Bad Request). This caused because nginx
doesn't use "\r\n" they using just "\n" and multilne headers is failed
to parse (I've added test).

Accorording to RFC2616 section 19.3:
https://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.3

"The line terminator for message-header fields is the sequence CRLF.
However, we recommend that applications, when parsing such headers,
recognize a single LF as a line terminator and ignore the leading CR."

CRLF changed to ("\r\n" | "\n")

Github commit
https://github.com/uno4ki/unicorn/commit/ed127b66e162aaf176de05720f6be758f8b41b1f

PS: Googling "nginx unicorn ssl_client_cert" shows the problem. 
-rw-r--r--ext/unicorn_http/unicorn_http_common.rl2
-rw-r--r--test/unit/test_http_parser.rb18
2 files changed, 19 insertions, 1 deletions
diff --git a/ext/unicorn_http/unicorn_http_common.rl b/ext/unicorn_http/unicorn_http_common.rl
index cc1d455..0988b54 100644
--- a/ext/unicorn_http/unicorn_http_common.rl
+++ b/ext/unicorn_http/unicorn_http_common.rl
@@ -4,7 +4,7 @@
 
 #### HTTP PROTOCOL GRAMMAR
 # line endings
-  CRLF = "\r\n";
+  CRLF = ("\r\n" | "\n");
 
 # character types
   CTL = (cntrl | 127);
diff --git a/test/unit/test_http_parser.rb b/test/unit/test_http_parser.rb
index c72f7f2..7cbc0f8 100644
--- a/test/unit/test_http_parser.rb
+++ b/test/unit/test_http_parser.rb
@@ -230,6 +230,24 @@ class HttpParserTest < Test::Unit::TestCase
     assert_equal expect, req['HTTP_X_SSL_BULLSHIT']
   end
 
+  def test_multiline_header_0d0a
+    parser = HttpParser.new
+    parser.buf << "GET / HTTP/1.0\r\n" \
+      "X-Multiline-Header: foo bar\r\n\tcha cha\r\n\tzha zha\r\n\r\n"
+    req = parser.env
+    assert_equal req, parser.parse
+    assert_equal 'foo bar cha cha zha zha', req['HTTP_X_MULTILINE_HEADER']
+  end
+
+  def test_multiline_header_0a
+    parser = HttpParser.new
+    parser.buf << "GET / HTTP/1.0\n" \
+      "X-Multiline-Header: foo bar\n\tcha cha\n\tzha zha\n\n"
+    req = parser.env
+    assert_equal req, parser.parse
+    assert_equal 'foo bar cha cha zha zha', req['HTTP_X_MULTILINE_HEADER']
+  end
+
   def test_continuation_eats_leading_spaces
     parser = HttpParser.new
     header = "GET / HTTP/1.1\r\n" \