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.rl33
1 files changed, 28 insertions, 5 deletions
diff --git a/ext/kcar/kcar.rl b/ext/kcar/kcar.rl
index 5b620ac..78437e7 100644
--- a/ext/kcar/kcar.rl
+++ b/ext/kcar/kcar.rl
@@ -70,12 +70,27 @@ struct http_parser {
 #define MARK(M,FPC) (hp->M = (FPC) - buffer)
 #define PTR_TO(F) (buffer + hp->F)
 #define STR_NEW(M,FPC) rb_str_new(PTR_TO(M), LEN(M, FPC))
+#define STRIPPED_STR_NEW(M,FPC) stripped_str_new(PTR_TO(M), LEN(M, FPC))
 
 #define HP_FL_TEST(hp,fl) ((hp)->flags & (UH_FL_##fl))
 #define HP_FL_SET(hp,fl) ((hp)->flags |= (UH_FL_##fl))
 #define HP_FL_UNSET(hp,fl) ((hp)->flags &= ~(UH_FL_##fl))
 #define HP_FL_ALL(hp,fl) (HP_FL_TEST(hp, fl) == (UH_FL_##fl))
 
+static int is_lws(char c)
+{
+  return (c == ' ' || c == '\t');
+}
+
+static VALUE stripped_str_new(const char *str, long len)
+{
+  long end;
+
+  for (end = len - 1; end >= 0 && is_lws(str[end]); end--);
+
+  return rb_str_new(str, end + 1);
+}
+
 static void finalize_header(struct http_parser *hp)
 {
   if ((HP_FL_TEST(hp, HASTRAILER) && ! HP_FL_TEST(hp, CHUNKED)))
@@ -144,6 +159,9 @@ static void write_cont_value(struct http_parser *hp,
                              char *buffer, const char *p)
 {
   char *vptr;
+  long end;
+  long len = LEN(mark, p);
+  long cont_len;
 
   if (hp->cont == Qfalse)
     rb_raise(eParserError, "invalid continuation line");
@@ -154,19 +172,24 @@ static void write_cont_value(struct http_parser *hp,
   assert(TYPE(hp->cont) == T_STRING && "continuation line is not a string");
   assert(hp->mark > 0 && "impossible continuation line offset");
 
-  if (LEN(mark, p) == 0)
+  if (len == 0)
     return;
 
-  if (RSTRING_LEN(hp->cont) > 0)
+  cont_len = RSTRING_LEN(hp->cont);
+  if (cont_len > 0) {
     --hp->mark;
+    len = LEN(mark, p);
+  }
 
   vptr = PTR_TO(mark);
 
-  if (RSTRING_LEN(hp->cont) > 0) {
+  /* normalize tab to space */
+  if (cont_len > 0) {
     assert((' ' == *vptr || '\t' == *vptr) && "invalid leading white space");
     *vptr = ' ';
   }
-  rb_str_buf_cat(hp->cont, vptr, LEN(mark, p));
+  for (end = len - 1; end >= 0 && is_lws(vptr[end]); end--);
+  rb_str_buf_cat(hp->cont, vptr, end + 1);
 }
 
 static void write_value(VALUE hdr, struct http_parser *hp,
@@ -192,7 +215,7 @@ static void write_value(VALUE hdr, struct http_parser *hp,
   VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE);
   VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
   f = rb_str_new(fptr, (long)flen);
-  v = rb_str_new(vptr, (long)vlen);
+  v = stripped_str_new(vptr, (long)vlen);
 
   /* needs more tests for error-checking here */
   /*