You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by kg...@apache.org on 2020/10/29 18:12:34 UTC

[qpid-dispatch] branch dev-protocol-adaptors-2 updated: DISPATCH-1784: allow parsing of folded headers

This is an automated email from the ASF dual-hosted git repository.

kgiusti pushed a commit to branch dev-protocol-adaptors-2
in repository https://gitbox.apache.org/repos/asf/qpid-dispatch.git


The following commit(s) were added to refs/heads/dev-protocol-adaptors-2 by this push:
     new 483add8  DISPATCH-1784: allow parsing of folded headers
483add8 is described below

commit 483add8d3c7a58da9af5d258675c05934ec5eec9
Author: Kenneth Giusti <kg...@apache.org>
AuthorDate: Wed Oct 28 15:10:19 2020 -0400

    DISPATCH-1784: allow parsing of folded headers
    
    This closes #904
---
 src/adaptors/http1/http1_codec.c    | 139 +++++++++++++++++++++++-------------
 tests/system_tests_http1_adaptor.py |  33 +++++++++
 2 files changed, 123 insertions(+), 49 deletions(-)

diff --git a/src/adaptors/http1/http1_codec.c b/src/adaptors/http1/http1_codec.c
index 5cb16b3..b9558b4 100644
--- a/src/adaptors/http1/http1_codec.c
+++ b/src/adaptors/http1/http1_codec.c
@@ -889,76 +889,117 @@ static int process_header(h1_codec_connection_t *conn, struct decoder_t *decoder
 }
 
 
-// Parse out the header key and value
+// Parse an HTTP header line.
+// See RFC7230 for details.  If header line folding (obs-folding) is detected,
+// replace the folding with spaces.
 //
 static bool parse_header(h1_codec_connection_t *conn, struct decoder_t *decoder)
 {
-    qd_iterator_pointer_t *rptr = &decoder->read_ptr;
-    qd_iterator_pointer_t line;
+    qd_iterator_pointer_t end_ptr = decoder->read_ptr;
     h1_codec_request_state_t *hrs = decoder->hrs;
+    qd_iterator_pointer_t line;
+
     assert(hrs);  // else state machine busted
 
-    if (read_line(rptr, &line)) {
-        debug_print_iterator_pointer("header:", &line);
+    if (!read_line(&end_ptr, &line))
+        // need more data
+        return false;
 
+    if (is_empty_line(&line)) {
+        decoder->read_ptr = end_ptr;
         hrs->in_octets += line.remaining;
+        return process_headers_done(conn, decoder);
+    }
 
-        if (is_empty_line(&line)) {
-            // end of headers
-            return process_headers_done(conn, decoder);
-        }
+    // check for header line folding
 
-        qd_iterator_pointer_t key = {0};
+    bool obs_fold = false;
+    while (true) {
+        qd_iterator_pointer_t peek = end_ptr;
+        uint8_t octet;
+        if (!get_octet(&peek, &octet))
+            // need more data
+            return false;
 
-        if (!parse_token(&line, &key)) {
-            decoder->error_msg = "Malformed Header";
-            decoder->error = (decoder->is_request) ? HTTP1_STATUS_BAD_REQ
-                : HTTP1_STATUS_SERVER_ERR;
+        if (octet != ' ' && octet != '\t')
+            break;
+
+        obs_fold = true;
+
+        if (!read_line(&end_ptr, &line))
             return false;
-        }
+    }
 
-        // advance line past the ':'
-        uint8_t octet;
-        while (get_octet(&line, &octet) && octet != ':')
-            ;
+    // end_ptr now points past the header line, advance decoder past header
+    // line and set 'line' to hold header
 
-        // line now contains the value. convert to C strings and post callback
-        ensure_scratch_size(&decoder->scratch, key.remaining + line.remaining + 2);
-        uint8_t *ptr = decoder->scratch.buf;
-        size_t avail = decoder->scratch.size;
-
-        uint8_t *key_str = ptr;
-        size_t offset = pointer_2_str(&key, key_str, avail);
-        ptr += offset;
-        avail -= offset;
-
-        uint8_t *value_str = ptr;
-        pointer_2_str(&line, value_str, avail);
-
-        // trim whitespace on both ends of value
-        while (isspace(*value_str))
-            ++value_str;
-        ptr = value_str + strlen((char*) value_str);
-        while (ptr-- > value_str) {
-            if (!isspace(*ptr))
-                break;
-            *ptr = 0;
-        }
+    line = decoder->read_ptr;
+    decoder->read_ptr = end_ptr;
+    line.remaining -= end_ptr.remaining;
 
-        process_header(conn, decoder, key_str, value_str);
+    debug_print_iterator_pointer("header:", &line);
 
-        if (!decoder->error) {
-            decoder->error = conn->config.rx_header(hrs, (char *)key_str, (char *)value_str);
-            if (decoder->error)
-                decoder->error_msg = "hrs_rx_header callback error";
-        }
+    hrs->in_octets += line.remaining;
 
-        return !!rptr->remaining;
+    // convert field to key and value strings
+
+    qd_iterator_pointer_t key;
+    if (!parse_token(&line, &key)) {
+        decoder->error_msg = "Malformed Header";
+        decoder->error = (decoder->is_request) ? HTTP1_STATUS_BAD_REQ
+            : HTTP1_STATUS_SERVER_ERR;
+        return false;
+    }
+
+    // advance line past the ':'
+    uint8_t octet;
+    while (get_octet(&line, &octet) && octet != ':')
+        ;
+
+    // line now contains the value. convert to C strings and post callback
+    ensure_scratch_size(&decoder->scratch, key.remaining + line.remaining + 2);
+    uint8_t *ptr = decoder->scratch.buf;
+    size_t avail = decoder->scratch.size;
+
+    uint8_t *key_str = ptr;
+    size_t offset = pointer_2_str(&key, key_str, avail);
+    ptr += offset;
+    avail -= offset;
+
+    uint8_t *value_str = ptr;
+    pointer_2_str(&line, value_str, avail);
+
+    // trim whitespace on both ends of value
+    while (isspace(*value_str))
+        ++value_str;
+    ptr = value_str + strlen((char*) value_str);
+    while (ptr-- > value_str) {
+        if (!isspace(*ptr))
+            break;
+        *ptr = 0;
+    }
+
+    // remove header line folding by overwriting all <CR> and <LF> chars with
+    // spaces as per RFC7230
+
+    if (obs_fold) {
+        ptr = value_str;
+        while ((ptr = (uint8_t*) strpbrk((char*) ptr, CRLF)) != 0)
+            *ptr = ' ';
     }
 
-    return false;  // pend for more data
+    process_header(conn, decoder, key_str, value_str);
+
+    if (!decoder->error) {
+        decoder->error = conn->config.rx_header(hrs, (char *)key_str, (char *)value_str);
+        if (decoder->error)
+            decoder->error_msg = "hrs_rx_header callback error";
+    }
+
+    return !!decoder->read_ptr.remaining;
 }
 
+
 //
 // Chunked body encoding parser
 //
diff --git a/tests/system_tests_http1_adaptor.py b/tests/system_tests_http1_adaptor.py
index 3e83279..9e7468f 100644
--- a/tests/system_tests_http1_adaptor.py
+++ b/tests/system_tests_http1_adaptor.py
@@ -532,6 +532,39 @@ class Http1AdaptorOneRouterTest(TestCase):
                           body=b'?')],
              ResponseValidator(expect_headers={'Content-Type': "text/plain;charset=utf-8"},
                                expect_body=b'?')),
+
+            # test support for "folded headers"
+
+            (RequestMsg("GET", "/GET/folded_header_01",
+                        headers={"Content-Length": 0}),
+             ResponseMsg(200, reason="OK",
+                         headers={"Content-Type": "text/plain;charset=utf-8",
+                                  "Content-Length": 1,
+                                  "folded-header": "One\r\n \r\n\tTwo"},
+                         body=b'X'),
+             ResponseValidator(expect_headers={"Content-Type":
+                                               "text/plain;charset=utf-8",
+                                               "folded-header":
+                                               "One     \tTwo"},
+                               expect_body=b'X')),
+
+            (RequestMsg("GET", "/GET/folded_header_02",
+                        headers={"Content-Length": 0}),
+             ResponseMsg(200, reason="OK",
+                         headers={"Content-Type": "text/plain;charset=utf-8",
+                                  "Content-Length": 1,
+                                  "folded-header": "\r\n \r\n\tTwo",
+                                  "another-header": "three"},
+                         body=b'X'),
+             ResponseValidator(expect_headers={"Content-Type":
+                                               "text/plain;charset=utf-8",
+                                               # trim leading and
+                                               # trailing ws:
+                                               "folded-header":
+                                               "Two",
+                                               "another-header":
+                                               "three"},
+                               expect_body=b'X')),
         ],
         #
         # HEAD


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org