about summary refs log tree commit homepage
path: root/ext/kcar/kcar.rl
diff options
context:
space:
mode:
Diffstat (limited to 'ext/kcar/kcar.rl')
-rw-r--r--ext/kcar/kcar.rl41
1 files changed, 38 insertions, 3 deletions
diff --git a/ext/kcar/kcar.rl b/ext/kcar/kcar.rl
index ea3dacc..6330910 100644
--- a/ext/kcar/kcar.rl
+++ b/ext/kcar/kcar.rl
@@ -201,6 +201,13 @@ static void finalize_header(struct http_parser *hp, VALUE hdr)
   if (hp->has_trailer && !hp->chunked)
     rb_raise(eParserError, "trailer but not chunked");
   if (hp->is_request) {
+    if (hp->chunked) {
+      if (hp->len.chunk >= 0)
+        rb_raise(eParserError, "Content-Length set with chunked encoding");
+      else
+        hp->len.chunk = 0;
+    }
+
     if (!hp->has_query)
       rb_hash_aset(hdr, g_QUERY_STRING, rb_str_new(NULL, 0));
     if (hp->has_header) {
@@ -831,6 +838,8 @@ static VALUE body_bytes_left_set(VALUE self, VALUE bytes)
   if (hp->chunked)
     rb_raise(rb_eRuntimeError, "body_bytes_left= is not for chunked bodies");
   hp->len.content = NUM2OFFT(bytes);
+  if (hp->len.content == 0)
+      hp->body_eof_seen = 1;
   return bytes;
 }
 
@@ -973,8 +982,12 @@ static VALUE keepalive(VALUE self)
 
   if (hp->persistent) {
     if (hp->has_header && hp->has_body) {
-      if (hp->chunked || (hp->len.content >= 0))
-        return Qtrue;
+      if (hp->chunked || (hp->len.content >= 0)) {
+        if (!hp->is_request)
+          return Qtrue;
+        else
+          return hp->body_eof_seen ? Qtrue : Qfalse;
+      }
 
       /* unknown Content-Length and not chunked, we must assume close */
       return Qfalse;
@@ -1012,12 +1025,34 @@ static VALUE filter_body(VALUE self, VALUE dst, VALUE src)
 
   StringValue(dst);
   rb_str_modify(dst);
-  rb_str_resize(dst, slen); /* we can never copy more than slen bytes */
   OBJ_TAINT(dst); /* keep weirdo $SAFE users happy */
 
+  /*
+   * for now, only support filter_body for identity requests,
+   * not responses; it's rather inefficient to blindly memcpy
+   * giant request bodies; on the other hand, it simplifies
+   * server-side code.
+   */
+  if (hp->is_request && !hp->chunked) {
+    /* no need to enter the Ragel machine for unchunked transfers */
+    assert(hp->len.content >= 0 && "negative Content-Length");
+    if (hp->len.content > 0) {
+      long nr = MIN(slen, hp->len.content);
+
+      rb_str_resize(dst, nr);
+      memcpy(RSTRING_PTR(dst), sptr, nr);
+      hp->len.content -= nr;
+      if (hp->len.content == 0)
+        hp->body_eof_seen = 1;
+      advance_str(src, nr);
+    }
+    return dst;
+  }
+
   if (!hp->chunked)
     rb_raise(rb_eRuntimeError, "filter_body is only for chunked bodies");
 
+  rb_str_resize(dst, slen); /* we can never copy more than slen bytes */
   if (!chunked_eof(hp)) {
     hp->s.dest_offset = 0;
     http_parser_execute(hp, dst, sptr, slen);