about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <e@yhbt.net>2020-03-17 06:56:52 +0000
committerEric Wong <e@yhbt.net>2020-03-19 02:35:59 +0000
commita4af139431e74bf0c5d8c0b361c9dc154637cfb2 (patch)
tree326f8b0bd24925fa3eeb9879070e0bbf86b5b6f7
parent5ba9c3ef8a90a64ff34dc069d4ed89f91d38606a (diff)
downloadcmogstored-a4af139431e74bf0c5d8c0b361c9dc154637cfb2.tar.gz
RFC 7230 is actually explicit about favoring the
"Transfer-Encoding: chunked" over a Content-Length header
when a client specifies both.
-rw-r--r--http_parser.rl13
-rw-r--r--test/http_put.rb16
2 files changed, 27 insertions, 2 deletions
diff --git a/http_parser.rl b/http_parser.rl
index 0685d27..835ba65 100644
--- a/http_parser.rl
+++ b/http_parser.rl
@@ -62,7 +62,12 @@ static char *skip_header(struct mog_http *http, char *buf, const char *pe)
 
         content_length = "Content-Length:"i sep
                 (digit+) $ {
-                        if (!length_incr(&http->_p.content_len, fc))
+                        /*
+                         * RFC 7230 3.3.2, 3.3.3,:
+                         * favor Transfer-Encoding over Content-Length
+                         */
+                        if (!http->_p.chunked &&
+                                        !length_incr(&http->_p.content_len, fc))
                                 fbreak;
                 }
                 $! { errno = EINVAL; fbreak; }
@@ -116,7 +121,11 @@ static char *skip_header(struct mog_http *http, char *buf, const char *pe)
                 # "compress" as described in RFC 7230, so reject them, here.
                 "chunked"i
                 $! { errno = EINVAL; fbreak; }
-                eor @ { http->_p.chunked = 1; };
+                eor @ {
+                        http->_p.chunked = 1;
+                        /* RFC 7230 3.3.2, 3.3.3,: ignore length if chunked */
+                        http->_p.content_len = 0;
+                };
         trailer = "Trailer:"i sep
                 (("Content-MD5"i @ { http->_p.has_md5 = 1; })
                  | header_name | ',')
diff --git a/test/http_put.rb b/test/http_put.rb
index 0479629..34e2cdb 100644
--- a/test/http_put.rb
+++ b/test/http_put.rb
@@ -226,6 +226,22 @@ class TestHTTPPut < Test::Unit::TestCase
     assert( ! File.exist?("#@tmpdir/dev666/foo") )
   end
 
+  def test_put_favors_chunked
+    order = [ "Transfer-Encoding: chunked", "Content-Length: 123" ]
+    %w[a b].each do
+      path = '/dev666/a'
+      req = "PUT #{path} HTTP/1.1\r\n" \
+            "#{order.join("\r\n")}\r\n\r\n" \
+            "a\r\nhelloworld\r\n0\r\n\r\n"
+      order.reverse!
+      @client.write(req)
+      resp = @client.readpartial(4096)
+      assert_match(%r{\AHTTP/1\.1 201 Created\r\n}, resp, "#{path} created")
+      assert(File.exist?("#@tmpdir#{path}"))
+      assert_equal('helloworld', File.read("#@tmpdir#{path}"))
+    end
+  end
+
   def test_content_md5_good
     req = "PUT /dev666/foo HTTP/1.1\r\n" \
           "Host: #@host:#@port\r\n" \