about summary refs log tree commit homepage
diff options
context:
space:
mode:
-rw-r--r--ext/unicorn_http/unicorn_http.rl29
-rw-r--r--test/unit/test_http_parser_ng.rb54
2 files changed, 83 insertions, 0 deletions
diff --git a/ext/unicorn_http/unicorn_http.rl b/ext/unicorn_http/unicorn_http.rl
index 106cc6b..f799337 100644
--- a/ext/unicorn_http/unicorn_http.rl
+++ b/ext/unicorn_http/unicorn_http.rl
@@ -631,6 +631,34 @@ static VALUE HttpParser_clear(VALUE self)
 
 /**
  * call-seq:
+ *    parser.chunk_ready! => parser
+ *
+ * Resets the parser to a state suitable for dechunking response bodies
+ *
+ */
+static VALUE HttpParser_dechunk_bang(VALUE self)
+{
+  struct http_parser *hp = data_get(self);
+
+  http_parser_init(hp);
+
+  /*
+   * we don't care about trailers in dechunk-only mode,
+   * but if we did we'd set UH_FL_HASTRAILER and clear hp->env
+   */
+  if (0) {
+    rb_funcall(hp->env, id_clear, 0);
+    hp->flags = UH_FL_HASTRAILER;
+  }
+
+  hp->flags |= UH_FL_HASBODY | UH_FL_INBODY | UH_FL_CHUNKED;
+  hp->cs = http_parser_en_ChunkedBody;
+
+  return self;
+}
+
+/**
+ * call-seq:
  *    parser.reset => nil
  *
  * Resets the parser to it's initial state so that you can reuse it
@@ -954,6 +982,7 @@ void Init_unicorn_http(void)
   rb_define_method(cHttpParser, "initialize", HttpParser_init, 0);
   rb_define_method(cHttpParser, "clear", HttpParser_clear, 0);
   rb_define_method(cHttpParser, "reset", HttpParser_reset, 0);
+  rb_define_method(cHttpParser, "dechunk!", HttpParser_dechunk_bang, 0);
   rb_define_method(cHttpParser, "parse", HttpParser_parse, 0);
   rb_define_method(cHttpParser, "add_parse", HttpParser_add_parse, 1);
   rb_define_method(cHttpParser, "headers", HttpParser_headers, 2);
diff --git a/test/unit/test_http_parser_ng.rb b/test/unit/test_http_parser_ng.rb
index e57428c..2b2fe41 100644
--- a/test/unit/test_http_parser_ng.rb
+++ b/test/unit/test_http_parser_ng.rb
@@ -237,6 +237,17 @@ class HttpParserNgTest < Test::Unit::TestCase
     assert @parser.keepalive?
   end
 
+  def test_chunked_empty
+    str = @parser.buf
+    req = @parser.env
+    str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
+    assert_equal req, @parser.parse, "msg=#{str}"
+    assert_equal 0, str.size
+    tmp = ""
+    assert_equal str, @parser.filter_body(tmp, str << "0\r\n\r\n")
+    assert_equal "", tmp
+  end
+
   def test_two_chunks
     str = @parser.buf
     str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
@@ -651,4 +662,47 @@ class HttpParserNgTest < Test::Unit::TestCase
     assert_equal expect, @parser.parse
     assert ! @parser.next?
   end
+
+  def test_chunk_only
+    tmp = ""
+    assert_equal @parser, @parser.dechunk!
+    assert_nil @parser.filter_body(tmp, "6\r\n")
+    assert_equal "", tmp
+    assert_nil @parser.filter_body(tmp, "abcdef")
+    assert_equal "abcdef", tmp
+    assert_nil @parser.filter_body(tmp, "\r\n")
+    assert_equal "", tmp
+    src = "0\r\n\r\n"
+    assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
+    assert_equal "", tmp
+  end
+
+  def test_chunk_only_bad_align
+    tmp = ""
+    assert_equal @parser, @parser.dechunk!
+    assert_nil @parser.filter_body(tmp, "6\r\na")
+    assert_equal "a", tmp
+    assert_nil @parser.filter_body(tmp, "bcde")
+    assert_equal "bcde", tmp
+    assert_nil @parser.filter_body(tmp, "f\r")
+    assert_equal "f", tmp
+    src = "\n0\r\n\r\n"
+    assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
+    assert_equal "", tmp
+  end
+
+  def test_chunk_only_reset_ok
+    tmp = ""
+    assert_equal @parser, @parser.dechunk!
+    src = "1\r\na\r\n0\r\n\r\n"
+    assert_nil @parser.filter_body(tmp, src)
+    assert_equal "a", tmp
+    assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
+
+    assert_equal @parser, @parser.dechunk!
+    src = "0\r\n\r\n"
+    assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
+    assert_equal "", tmp
+    assert_equal src, @parser.filter_body(tmp, src)
+  end
 end