diff --git a/CHANGELOG.md b/CHANGELOG.md index ea4e0b90..f82fc0bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +1.0.0-b16 + +* Make value optional in param-list + +-------------------------------------------------------------------------------- + 1.0.0-b15 * rfc7230 section 3.3.2 compliance diff --git a/include/beast/http/detail/rfc7230.hpp b/include/beast/http/detail/rfc7230.hpp index dc9285a5..191eaeed 100644 --- a/include/beast/http/detail/rfc7230.hpp +++ b/include/beast/http/detail/rfc7230.hpp @@ -233,6 +233,14 @@ skip_ows(FwdIt& it, FwdIt const& end) } } +template +void +skip_token(FwdIt& it, FwdIt const& last) +{ + while(it != last && is_tchar(*it)) + ++it; +} + inline boost::string_ref trim(boost::string_ref const& s) @@ -258,14 +266,14 @@ struct param_iter using iter_type = boost::string_ref::const_iterator; iter_type it; - iter_type begin; - iter_type end; + iter_type first; + iter_type last; std::pair v; bool empty() const { - return begin == it; + return first == it; } template @@ -279,59 +287,48 @@ param_iter:: increment() { /* - ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] ) - ext = token param-list - param-list = *( OWS ";" OWS param ) - param = token OWS "=" OWS ( token / quoted-string ) - - quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE - qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text - quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) - obs-text = %x80-FF - - Example: - chunked;a=b;i=j,gzip;windowBits=12 - x,y + param-list = *( OWS ";" OWS param ) + param = token OWS [ "=" OWS ( token / quoted-string ) ] + quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE + qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text + quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) + obs-text = %x80-FF */ auto const err = [&] { - it = begin; + it = first; }; v.first.clear(); v.second.clear(); - detail::skip_ows(it, end); - begin = it; - if(it == end) + detail::skip_ows(it, last); + first = it; + if(it == last) return err(); if(*it != ';') return err(); ++it; - detail::skip_ows(it, end); - if(it == end) + detail::skip_ows(it, last); + if(it == last) return err(); // param if(! detail::is_tchar(*it)) return err(); auto const p0 = it; - for(;;) - { - ++it; - if(it == end) - return err(); - if(! detail::is_tchar(*it)) - break; - } + skip_token(++it, last); auto const p1 = it; - detail::skip_ows(it, end); - if(it == end) - return err(); + v.first = { &*p0, static_cast(p1 - p0) }; + detail::skip_ows(it, last); + if(it == last) + return; + if(*it == ';') + return; if(*it != '=') return err(); ++it; - detail::skip_ows(it, end); - if(it == end) - return err(); + detail::skip_ows(it, last); + if(it == last) + return; if(*it == '"') { // quoted-string @@ -339,7 +336,7 @@ increment() ++it; for(;;) { - if(it == end) + if(it == last) return err(); auto c = *it++; if(c == '"') @@ -348,13 +345,12 @@ increment() continue; if(c != '\\') return err(); - if(it == end) + if(it == last) return err(); c = *it++; if(! detail::is_qpchar(c)) return err(); } - v.first = { &*p0, static_cast(p1 - p0) }; v.second = { &*p2, static_cast(it - p2) }; } else @@ -363,15 +359,7 @@ increment() if(! detail::is_tchar(*it)) return err(); auto const p2 = it; - for(;;) - { - it++; - if(it == end) - break; - if(! detail::is_tchar(*it)) - break; - } - v.first = { &*p0, static_cast(p1 - p0) }; + skip_token(++it, last); v.second = { &*p2, static_cast(it - p2) }; } } diff --git a/include/beast/http/impl/rfc7230.ipp b/include/beast/http/impl/rfc7230.ipp index feffa23e..4de3a0fa 100644 --- a/include/beast/http/impl/rfc7230.ipp +++ b/include/beast/http/impl/rfc7230.ipp @@ -36,8 +36,8 @@ public: { return other.pi_.it == pi_.it && - other.pi_.end == pi_.end && - other.pi_.begin == pi_.begin; + other.pi_.last == pi_.last && + other.pi_.first == pi_.first; } bool @@ -76,11 +76,11 @@ public: private: friend class param_list; - const_iterator(iter_type begin, iter_type end) + const_iterator(iter_type first, iter_type last) { - pi_.it = begin; - pi_.begin = begin; - pi_.end = end; + pi_.it = first; + pi_.first = first; + pi_.last = last; increment(); } @@ -158,10 +158,11 @@ increment() pi_.increment(); if(pi_.empty()) { - pi_.it = pi_.end; - pi_.begin = pi_.end; + pi_.it = pi_.last; + pi_.first = pi_.last; } - else if(pi_.v.second.front() == '"') + else if(! pi_.v.second.empty() && + pi_.v.second.front() == '"') { s_ = unquote(pi_.v.second); pi_.v.second = boost::string_ref{ @@ -175,8 +176,8 @@ class ext_list::const_iterator { ext_list::value_type v_; iter_type it_; - iter_type begin_; - iter_type end_; + iter_type first_; + iter_type last_; public: using value_type = ext_list::value_type; @@ -192,8 +193,8 @@ public: { return other.it_ == it_ && - other.begin_ == begin_ && - other.end_ == end_; + other.first_ == first_ && + other.last_ == last_; } bool @@ -235,8 +236,8 @@ private: const_iterator(iter_type begin, iter_type end) { it_ = begin; - begin_ = begin; - end_ = end; + first_ = begin; + last_ = end; increment(); } @@ -320,16 +321,16 @@ increment() auto const err = [&] { - it_ = end_; - begin_ = end_; + it_ = last_; + first_ = last_; }; - auto need_comma = it_ != begin_; + auto need_comma = it_ != first_; v_.first = {}; - begin_ = it_; + first_ = it_; for(;;) { - detail::skip_ows(it_, end_); - if(it_ == end_) + detail::skip_ows(it_, last_); + if(it_ == last_) return err(); auto const c = *it_; if(detail::is_tchar(c)) @@ -340,7 +341,7 @@ increment() for(;;) { ++it_; - if(it_ == end_) + if(it_ == last_) break; if(! detail::is_tchar(*it_)) break; @@ -349,8 +350,8 @@ increment() static_cast(it_ - p0)}; detail::param_iter pi; pi.it = it_; - pi.begin = it_; - pi.end = end_; + pi.first = it_; + pi.last = last_; for(;;) { pi.increment(); @@ -375,8 +376,8 @@ class token_list::const_iterator { token_list::value_type v_; iter_type it_; - iter_type begin_; - iter_type end_; + iter_type first_; + iter_type last_; public: using value_type = token_list::value_type; @@ -392,8 +393,8 @@ public: { return other.it_ == it_ && - other.begin_ == begin_ && - other.end_ == end_; + other.first_ == first_ && + other.last_ == last_; } bool @@ -435,8 +436,8 @@ private: const_iterator(iter_type begin, iter_type end) { it_ = begin; - begin_ = begin; - end_ = end; + first_ = begin; + last_ = end; increment(); } @@ -492,16 +493,16 @@ increment() auto const err = [&] { - it_ = end_; - begin_ = end_; + it_ = last_; + first_ = last_; }; - auto need_comma = it_ != begin_; + auto need_comma = it_ != first_; v_ = {}; - begin_ = it_; + first_ = it_; for(;;) { - detail::skip_ows(it_, end_); - if(it_ == end_) + detail::skip_ows(it_, last_); + if(it_ == last_) return err(); auto const c = *it_; if(detail::is_tchar(c)) @@ -512,7 +513,7 @@ increment() for(;;) { ++it_; - if(it_ == end_) + if(it_ == last_) break; if(! detail::is_tchar(*it_)) break; diff --git a/include/beast/http/rfc7230.hpp b/include/beast/http/rfc7230.hpp index 3b6f52c4..c6bd24dc 100644 --- a/include/beast/http/rfc7230.hpp +++ b/include/beast/http/rfc7230.hpp @@ -15,15 +15,14 @@ namespace http { /** A list of parameters in a HTTP extension field value. - This container allows iteration of the parameter list - in a HTTP extension. The parameter list is a series - of "name = value" pairs with each pair starting with - a semicolon. + This container allows iteration of the parameter list in a HTTP + extension. The parameter list is a series of name/value pairs + with each pair starting with a semicolon. The value is optional. BNF: @code param-list = *( OWS ";" OWS param ) - param = token OWS "=" OWS ( token / quoted-string ) + param = token OWS [ "=" OWS ( token / quoted-string ) ] @endcode If a parsing error is encountered while iterating the string, @@ -48,8 +47,9 @@ class param_list public: /** The type of each element in the list. - The first string in the pair is the name of the - parameter, and the second string in the pair is its value. + The first string in the pair is the name of the parameter, + and the second string in the pair is its value (which may + be empty). */ using value_type = std::pair; @@ -101,7 +101,7 @@ public: ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] ) ext = token param-list param-list = *( OWS ";" OWS param ) - param = token OWS "=" OWS ( token / quoted-string ) + param = token OWS [ "=" OWS ( token / quoted-string ) ] @endcode If a parsing error is encountered while iterating the string, @@ -196,13 +196,13 @@ public: /** A list of tokens in a comma separated HTTP field value. - This container allows iteration of the extensions in a HTTP - field value. The extension list is a comma separated list of - token parameter list pairs. + This container allows iteration of a list of items in a + header field value. The input is a comma separated list of + tokens. BNF: @code - token-list = *( "," OWS ) token *( OWS "," [ OWS ext ] ) + token-list = *( "," OWS ) token *( OWS "," [ OWS token ] ) @endcode If a parsing error is encountered while iterating the string, @@ -226,12 +226,7 @@ class token_list boost::string_ref s_; public: - /** The type of each element in the token list. - - The first element of the pair is the extension token, and the - second element of the pair is an iterable container holding the - extension's name/value parameters. - */ + /// The type of each element in the token list. using value_type = boost::string_ref; /// A constant iterator to the list diff --git a/test/http/rfc7230.cpp b/test/http/rfc7230.cpp index 49b1c952..a1a6e8ae 100644 --- a/test/http/rfc7230.cpp +++ b/test/http/rfc7230.cpp @@ -43,8 +43,11 @@ public: { s.push_back(';'); s.append(str(p.first)); - s.push_back('='); - s.append(str(p.second)); + if(! p.second.empty()) + { + s.push_back('='); + s.append(str(p.second)); + } } return s; } @@ -73,18 +76,19 @@ public: BEAST_EXPECTS(got == good, fmt(got)); }; + ce(""); + ce(";x"); + ce(";xy"); + ce(";x;y"); + ce(""); cs(" ;\t i =\t 1 \t", ";i=1"); cq("\t; \t xyz=1 ; ijk=\"q\\\"t\"", ";xyz=1;ijk=q\"t"); + ce(";x;y"); // invalid strings cs(";", ""); cs(";,", ""); - cs(";xy", ""); - cs(";xy", ""); - cs(";xy ", ""); - cs(";xy,", ""); - cq(";x=,", ""); cq(";xy=\"", ""); cq(";xy=\"\x7f", ""); @@ -136,7 +140,6 @@ public: param-list = *( OWS ";" OWS param ) param = token OWS "=" OWS ( token / quoted-string ) */ - ce(""); cs(",", ""); cs(", ", ""); cs(",\t", ""); @@ -147,12 +150,16 @@ public: cs("\t , \t", ""); cs(",,", ""); cs(" , \t,, \t,", ""); + cs( "permessage-deflate; client_no_context_takeover; client_max_window_bits", + "permessage-deflate;client_no_context_takeover;client_max_window_bits"); ce("a"); ce("ab"); ce("a,b"); cs(" a ", "a"); cs("\t a, b\t , c\t", "a,b,c"); + ce("a;b"); + ce("a;b;c"); cs("a; \t i\t=\t \t1\t ", "a;i=1"); ce("a;i=1;j=2;k=3");