cmogstored dev/user discussion/issues/patches/etc
 help / color / mirror / code / Atom feed
* [PATCH 0/2] improve RFC 7230 conformance
@ 2020-03-17  6:56 Eric Wong
  2020-03-17  6:56 ` [PATCH 1/2] http: reject non-chunked Transfer-Encoding Eric Wong
  2020-03-17  6:56 ` [PATCH 2/2] http: favor chunked over Content-Length Eric Wong
  0 siblings, 2 replies; 3+ messages in thread
From: Eric Wong @ 2020-03-17  6:56 UTC (permalink / raw)
  To: cmogstored-public

This ought to provide more consistent behavior with strange,
misbehaving clients and existing proxies, although it's probably
rare for cmogstored itself to be behind an HTTP proxy.

Eric Wong (2):
  http: reject non-chunked Transfer-Encoding
  http: favor chunked over Content-Length

 http_parser.rl       | 17 +++++++++++++++--
 test/http-parser-1.c |  9 +++++++++
 test/http_put.rb     | 27 +++++++++++++++++++++++++++
 3 files changed, 51 insertions(+), 2 deletions(-)

^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH 1/2] http: reject non-chunked Transfer-Encoding
  2020-03-17  6:56 [PATCH 0/2] improve RFC 7230 conformance Eric Wong
@ 2020-03-17  6:56 ` Eric Wong
  2020-03-17  6:56 ` [PATCH 2/2] http: favor chunked over Content-Length Eric Wong
  1 sibling, 0 replies; 3+ messages in thread
From: Eric Wong @ 2020-03-17  6:56 UTC (permalink / raw)
  To: cmogstored-public

RFC 7230 3.3.3, point 3 states:
> If a Transfer-Encoding header field
> is present in a request and the chunked transfer coding is not
> the final encoding, the message body length cannot be determined
> reliably; the server MUST respond with the 400 (Bad Request)
> status code and then close the connection.

And no MogileFS client is known to send "gzip", "deflate", or
"compress" as part of the Transfer-Encoding, so we'll only
accept "chunked".
---
 http_parser.rl       |  6 +++++-
 test/http-parser-1.c |  9 +++++++++
 test/http_put.rb     | 11 +++++++++++
 3 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/http_parser.rl b/http_parser.rl
index 9f848b0..0685d27 100644
--- a/http_parser.rl
+++ b/http_parser.rl
@@ -112,7 +112,11 @@ static char *skip_header(struct mog_http *http, char *buf, const char *pe)
 		}
 		eor @ { http->_p.has_range = 1; };
 	transfer_encoding_chunked = "Transfer-Encoding:"i sep
-		"chunked"i eor > { http->_p.chunked = 1; };
+		# XXX we don't know how to deal with "gzip", "deflate", or
+		# "compress" as described in RFC 7230, so reject them, here.
+		"chunked"i
+		$! { errno = EINVAL; fbreak; }
+		eor @ { http->_p.chunked = 1; };
 	trailer = "Trailer:"i sep
 		(("Content-MD5"i @ { http->_p.has_md5 = 1; })
 		 | header_name | ',')
diff --git a/test/http-parser-1.c b/test/http-parser-1.c
index 4b4d4f9..5c19529 100644
--- a/test/http-parser-1.c
+++ b/test/http-parser-1.c
@@ -157,6 +157,15 @@ int main(void)
 		       && "buffer repositioned to body start");
 		assert(!http->_p.usage_txt && "not a usage request");
 	}
+	if ("HTTP/1.1 PUT Transfer-Encoding: bogus header") {
+		buf_set("PUT /foo HTTP/1.1\r\n"
+		        "Host: 127.6.6.6\r\n"
+		        "Transfer-Encoding: bogus\r\n"
+		        "\r\n"
+		        "16\r\npartial...");
+		state = mog_http_parse(http, buf, len);
+		assert(state == MOG_PARSER_ERROR && "parser not errored");
+	}
 
 	if ("HTTP/1.1 PUT with Content-Range") {
 		buf_set("PUT /foo HTTP/1.1\r\n"
diff --git a/test/http_put.rb b/test/http_put.rb
index 21d65c7..0479629 100644
--- a/test/http_put.rb
+++ b/test/http_put.rb
@@ -160,6 +160,17 @@ def test_put_content_len_overflow
     assert( ! File.exist?("#@tmpdir/dev666/foo") )
   end
 
+  def test_put_bogus
+    max = 0xffffffff << 64
+    req = "PUT /dev666/foo HTTP/1.1\r\n" \
+          "Transfer-Encoding: bogus\r\n" \
+          "\r\n"
+    @client.write(req)
+    resp = @client.read
+    assert_match(%r{\AHTTP/1\.1 400 Bad Request\r\n}, resp)
+    assert( ! File.exist?("#@tmpdir/dev666/foo") )
+  end
+
   def test_put_range_beg_overflow
     max = 0xffffffff << 64
     req = "PUT /dev666/foo HTTP/1.1\r\n" \

^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH 2/2] http: favor chunked over Content-Length
  2020-03-17  6:56 [PATCH 0/2] improve RFC 7230 conformance Eric Wong
  2020-03-17  6:56 ` [PATCH 1/2] http: reject non-chunked Transfer-Encoding Eric Wong
@ 2020-03-17  6:56 ` Eric Wong
  1 sibling, 0 replies; 3+ messages in thread
From: Eric Wong @ 2020-03-17  6:56 UTC (permalink / raw)
  To: cmogstored-public

RFC 7230 is actually explicit about favoring the
"Transfer-Encoding: chunked" over a Content-Length header
when a client specifies both.
---
 http_parser.rl   | 13 +++++++++++--
 test/http_put.rb | 16 ++++++++++++++++
 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 @@ def test_put_bad_content_length
     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" \

^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2020-03-17  6:56 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-03-17  6:56 [PATCH 0/2] improve RFC 7230 conformance Eric Wong
2020-03-17  6:56 ` [PATCH 1/2] http: reject non-chunked Transfer-Encoding Eric Wong
2020-03-17  6:56 ` [PATCH 2/2] http: favor chunked over Content-Length Eric Wong

Code repositories for project(s) associated with this public inbox

	https://yhbt.net/cmogstored.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).