From ca25eee0dd00a4e1b88f0813f2090757241e9456 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Tue, 6 Jun 2017 12:49:03 -0700 Subject: [PATCH] Use field in basic_fields and call sites --- CHANGELOG.md | 1 + doc/concept/FieldSequence.qbk | 41 ++++++++++++ examples/doc_http_samples.hpp | 16 ++--- examples/http_async_server.hpp | 12 ++-- examples/http_crawl.cpp | 4 +- examples/http_sync_server.hpp | 12 ++-- examples/ssl/http_ssl_example.cpp | 4 +- include/beast/http/connection.hpp | 2 +- include/beast/http/fields.hpp | 86 ++++++++++++++++++++++++- include/beast/http/impl/fields.ipp | 16 +++++ include/beast/http/impl/message.ipp | 8 +++ include/beast/http/message.hpp | 4 +- include/beast/websocket/impl/stream.ipp | 23 +++---- include/beast/websocket/stream.hpp | 8 +-- test/http/doc_http_samples.cpp | 6 +- test/http/doc_snippets.cpp | 8 +-- test/http/message.cpp | 4 +- test/http/write.cpp | 34 +++++----- 18 files changed, 220 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 059d3677..7d65443a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Version 50 * Add field enumeration * Use allocator more in basic_fields * Fix basic_fields allocator awareness +* Use field in basic_fields and call sites API Changes: diff --git a/doc/concept/FieldSequence.qbk b/doc/concept/FieldSequence.qbk index 2ca075d8..ead9f0a9 100644 --- a/doc/concept/FieldSequence.qbk +++ b/doc/concept/FieldSequence.qbk @@ -59,3 +59,44 @@ In this table: ] [endsect] + + + +[/ + + +[section:Fields Fields] + +A type meeting the requirements of [*Fields] represents a container +used to store HTTP header fields. + +In this table: + +* `X` denotes a type that meets the requirements of [*Fields]. + +* `c` is a value of type `X const`. + + +[table FieldSequence requirements +[[expression][type][semantics, pre/post-conditions]] +[ + [] + [] + [ + ] +] +] + +[endsect] + +Operations we'd like to perform + +Determine if a field exists + bool exists(field f) const; + bool exists(string_view s) const; + + iterator find(field f) const; + iterator find(string_view s); + + +] diff --git a/examples/doc_http_samples.hpp b/examples/doc_http_samples.hpp index c319cf3e..c5912b98 100644 --- a/examples/doc_http_samples.hpp +++ b/examples/doc_http_samples.hpp @@ -136,7 +136,7 @@ receive_expect_100_continue( response res; res.version = 11; res.result(status::continue_); - res.insert("Server", "test"); + res.insert(field::server, "test"); write(stream, res, ec); if(ec) return; @@ -198,8 +198,8 @@ send_cgi_response( res.result(status::ok); res.version = 11; - res.insert("Server", "Beast"); - res.insert("Transfer-Encoding", "chunked"); + res.insert(field::server, "Beast"); + res.insert(field::transfer_encoding, "chunked"); // No data yet, but we set more = true to indicate // that it might be coming later. Otherwise the @@ -307,7 +307,7 @@ void do_server_head( // Set up the response, starting with the common fields response res; res.version = 11; - res.insert("Server", "test"); + res.insert(field::server, "test"); // Now handle request-specific fields switch(req.method()) @@ -319,7 +319,7 @@ void do_server_head( // set of headers that would be sent for a GET request, // including the Content-Length, except for the body. res.result(status::ok); - res.insert("Content-Length", payload.size()); + res.content_length(payload.size()); // For GET requests, we include the body if(req.method() == verb::get) @@ -337,7 +337,7 @@ void do_server_head( // We return responses indicating an error if // we do not recognize the request method. res.result(status::bad_request); - res.insert("Content-Type", "text/plain"); + res.insert(field::content_type, "text/plain"); res.body = "Invalid request-method '" + req.method_string().to_string() + "'"; break; } @@ -397,11 +397,11 @@ do_head_request( req.version = 11; req.method(verb::head); req.target(target); - req.insert("User-Agent", "test"); + req.insert(field::user_agent, "test"); // A client MUST send a Host header field in all HTTP/1.1 request messages. // https://tools.ietf.org/html/rfc7230#section-5.4 - req.insert("Host", "localhost"); + req.insert(field::host, "localhost"); // Now send it write(stream, req, ec); diff --git a/examples/http_async_server.hpp b/examples/http_async_server.hpp index ac29b129..ce884f09 100644 --- a/examples/http_async_server.hpp +++ b/examples/http_async_server.hpp @@ -236,8 +236,8 @@ private: response res; res.result(status::not_found); res.version = req_.version; - res.insert("Server", "http_async_server"); - res.insert("Content-Type", "text/html"); + res.insert(field::server, "http_async_server"); + res.insert(field::content_type, "text/html"); res.body = "The file '" + path + "' was not found"; res.prepare(); async_write(sock_, std::move(res), @@ -250,8 +250,8 @@ private: resp_type res; res.result(status::ok); res.version = req_.version; - res.insert("Server", "http_async_server"); - res.insert("Content-Type", mime_type(path)); + res.insert(field::server, "http_async_server"); + res.insert(field::content_type, mime_type(path)); res.body = path; res.prepare(); async_write(sock_, std::move(res), @@ -263,8 +263,8 @@ private: response res; res.result(status::internal_server_error); res.version = req_.version; - res.insert("Server", "http_async_server"); - res.insert("Content-Type", "text/html"); + res.insert(field::server, "http_async_server"); + res.insert(field::content_type, "text/html"); res.body = std::string{"An internal error occurred"} + e.what(); res.prepare(); diff --git a/examples/http_crawl.cpp b/examples/http_crawl.cpp index 0df6884b..f10c6c79 100644 --- a/examples/http_crawl.cpp +++ b/examples/http_crawl.cpp @@ -40,9 +40,9 @@ int main(int, char const*[]) req.method(verb::get); req.version = 11; req.target("/"); - req.insert("Host", host + std::string(":") + + req.insert(field::host, host + std::string(":") + boost::lexical_cast(ep.port())); - req.insert("User-Agent", "beast/http"); + req.insert(field::user_agent, "beast/http"); req.prepare(); write(sock, req); response res; diff --git a/examples/http_sync_server.hpp b/examples/http_sync_server.hpp index 98f8bfaa..ed82d9d1 100644 --- a/examples/http_sync_server.hpp +++ b/examples/http_sync_server.hpp @@ -166,8 +166,8 @@ private: response res; res.result(status::not_found); res.version = req.version; - res.insert("Server", "http_sync_server"); - res.insert("Content-Type", "text/html"); + res.insert(field::server, "http_sync_server"); + res.insert(field::content_type, "text/html"); res.body = "The file '" + path + "' was not found"; res.prepare(); write(sock, res, ec); @@ -181,8 +181,8 @@ private: res.result(status::ok); res.reason("OK"); res.version = req.version; - res.insert("Server", "http_sync_server"); - res.insert("Content-Type", mime_type(path)); + res.insert(field::server, "http_sync_server"); + res.insert(field::content_type, mime_type(path)); res.body = path; res.prepare(); write(sock, res, ec); @@ -195,8 +195,8 @@ private: res.result(status::internal_server_error); res.reason("Internal Error"); res.version = req.version; - res.insert("Server", "http_sync_server"); - res.insert("Content-Type", "text/html"); + res.insert(field::server, "http_sync_server"); + res.insert(field::content_type, "text/html"); res.body = std::string{"An internal error occurred: "} + e.what(); res.prepare(); diff --git a/examples/ssl/http_ssl_example.cpp b/examples/ssl/http_ssl_example.cpp index 9a3749c4..20a8c652 100644 --- a/examples/ssl/http_ssl_example.cpp +++ b/examples/ssl/http_ssl_example.cpp @@ -38,9 +38,9 @@ int main() req.method(beast::http::verb::get); req.target("/"); req.version = 11; - req.insert("Host", host + ":" + + req.insert(beast::http::field::host, host + ":" + boost::lexical_cast(sock.remote_endpoint().port())); - req.insert("User-Agent", "Beast"); + req.insert(beast::http::field::user_agent, "Beast"); req.prepare(); beast::http::write(stream, req); diff --git a/include/beast/http/connection.hpp b/include/beast/http/connection.hpp index 68a9e291..f0bc58ad 100644 --- a/include/beast/http/connection.hpp +++ b/include/beast/http/connection.hpp @@ -59,7 +59,7 @@ connection_impl<_>::upgrade; req.version = 11; req.method(verb::upgrade); req.target("/"); - req.insert("User-Agent", "Beast"); + req.insert(field::user_agent, "Beast"); req.prepare(connection::close, connection::upgrade); @endcode diff --git a/include/beast/http/fields.hpp b/include/beast/http/fields.hpp index dbba0b34..23da7894 100644 --- a/include/beast/http/fields.hpp +++ b/include/beast/http/fields.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -226,7 +227,19 @@ public: void clear() noexcept; - /** Remove a field. + /** Remove zero or more known fields. + + If more than one field with the specified name exists, all + matching fields will be removed. + + @param f The known field constant. + + @return The number of fields removed. + */ + std::size_t + erase(field f); + + /** Remove zero or more fields by name. If more than one field with the specified name exists, all matching fields will be removed. @@ -238,7 +251,22 @@ public: std::size_t erase(string_view name); - /** Insert a field value. + /** Insert a value for a known field. + + If a field with the same name already exists, the + existing field is untouched and a new field value pair + is inserted into the container. + + @param f The known field constant. + + @param name The name of the field. + + @param value A string holding the value of the field. + */ + void + insert(field f, string_view value); + + /** Insert a value for a field by name. If a field with the same name already exists, the existing field is untouched and a new field value pair @@ -314,16 +342,70 @@ protected: // for `header // + /** Returns the stored request-method string. + + @note This is called by the @ref header implementation. + */ string_view method_impl() const; + + /** Returns the stored request-target string. + + @note This is called by the @ref header implementation. + */ string_view target_impl() const; + + /** Returns the stored obsolete reason-phrase string. + + @note This is called by the @ref header implementation. + */ string_view reason_impl() const; + + /** Set or clear the stored request-method string. + + @note This is called by the @ref header implementation. + */ void method_impl(string_view s); + + /** Set or clear the stored request-target string. + + @note This is called by the @ref header implementation. + */ void target_impl(string_view s); + + /** Set or clear the stored obsolete reason-phrase string. + + @note This is called by the @ref header implementation. + */ void reason_impl(string_view s); + + /** Set the Content-Length field to the specified value. + + @note This is called by the @ref header implementation. + */ void content_length_impl(std::uint64_t n); + + /** Add close to the Connection field. + + @note This is called by the @ref header implementation. + */ void connection_impl(close_t); + + /** Add keep-alive to the Connection field. + + @note This is called by the @ref header implementation. + */ void connection_impl(keep_alive_t); + + /** Add upgrade to the Connection field. + + @note This is called by the @ref header implementation. + */ void connection_impl(upgrade_t); + + /** Add chunked to the Transfer-Encoding field. + + @note This is called by the @ref header implementation. + */ void chunked_impl(); private: diff --git a/include/beast/http/impl/fields.ipp b/include/beast/http/impl/fields.ipp index c8625f1d..af5f7628 100644 --- a/include/beast/http/impl/fields.ipp +++ b/include/beast/http/impl/fields.ipp @@ -300,6 +300,14 @@ clear() noexcept list_.clear(); } +template +std::size_t +basic_fields:: +erase(field f) +{ + return erase(to_string(f)); +} + template std::size_t basic_fields:: @@ -323,6 +331,14 @@ erase(string_view name) return n; } +template +void +basic_fields:: +insert(field f, string_view value) +{ + insert(to_string(f), value); +} + template void basic_fields:: diff --git a/include/beast/http/impl/message.ipp b/include/beast/http/impl/message.ipp index 12600c50..f4effbae 100644 --- a/include/beast/http/impl/message.ipp +++ b/include/beast/http/impl/message.ipp @@ -103,6 +103,14 @@ size() const Body, decltype(*this)>{}); } +template +void +message:: +content_length(std::uint64_t n) +{ + this->content_length_impl(n); +} + template template void diff --git a/include/beast/http/message.hpp b/include/beast/http/message.hpp index b94f8b38..3f28665b 100644 --- a/include/beast/http/message.hpp +++ b/include/beast/http/message.hpp @@ -559,6 +559,8 @@ struct message : header The value of the Content-Length field will be unconditionally set to the specified number of octets. + + @para n The number of octets to set for the Content-Length field. */ void content_length(std::uint64_t n); @@ -575,7 +577,7 @@ struct message : header req.version = 11; req.method(verb::upgrade); req.target("/"); - req.insert("User-Agent", "Beast"); + req.insert(field::user_agent, "Beast"); req.prepare(connection::close, connection::upgrade); @endcode diff --git a/include/beast/websocket/impl/stream.ipp b/include/beast/websocket/impl/stream.ipp index 9d9cc9f1..2b5a4e82 100644 --- a/include/beast/websocket/impl/stream.ipp +++ b/include/beast/websocket/impl/stream.ipp @@ -163,12 +163,12 @@ build_request(detail::sec_ws_key_type& key, req.target(target); req.version = 11; req.method(http::verb::get); - req.insert("Host", host); - req.insert("Upgrade", "websocket"); - req.insert("Connection", "upgrade"); + req.insert(http::field::host, host); + req.insert(http::field::upgrade, "websocket"); + req.insert(http::field::connection, "upgrade"); detail::make_sec_ws_key(key, maskgen_); - req.insert("Sec-WebSocket-Key", key); - req.insert("Sec-WebSocket-Version", "13"); + req.insert(http::field::sec_websocket_key, key); + req.insert(http::field::sec_websocket_version, "13"); if(pmd_opts_.client_enable) { detail::pmd_offer config; @@ -187,7 +187,7 @@ build_request(detail::sec_ws_key_type& key, if(! req.exists("User-Agent")) { static_string<20> s(BEAST_VERSION_STRING); - req.insert("User-Agent", s); + req.insert(http::field::user_agent, s); } return req; } @@ -205,8 +205,9 @@ build_response(http::header const& req, decorator(res); if(! res.exists("Server")) { + BOOST_STATIC_ASSERT(sizeof(BEAST_VERSION_STRING) < 20); static_string<20> s(BEAST_VERSION_STRING); - res.insert("Server", s); + res.insert(http::field::server, s); } }; auto err = @@ -245,7 +246,7 @@ build_response(http::header const& req, response_type res; res.result(http::status::upgrade_required); res.version = req.version; - res.insert("Sec-WebSocket-Version", "13"); + res.insert(http::field::sec_websocket_version, "13"); res.prepare(); decorate(res); return res; @@ -261,12 +262,12 @@ build_response(http::header const& req, } res.result(http::status::switching_protocols); res.version = req.version; - res.insert("Upgrade", "websocket"); - res.insert("Connection", "upgrade"); + res.insert(http::field::upgrade, "websocket"); + res.insert(http::field::connection, "upgrade"); { detail::sec_ws_accept_type accept; detail::make_sec_ws_accept(accept, key); - res.insert("Sec-WebSocket-Accept", accept); + res.insert(http::field::sec_websocket_accept, accept); } decorate(res); return res; diff --git a/include/beast/websocket/stream.hpp b/include/beast/websocket/stream.hpp index 9c8367f5..dd0f20aa 100644 --- a/include/beast/websocket/stream.hpp +++ b/include/beast/websocket/stream.hpp @@ -1563,7 +1563,7 @@ public: ws.handshake("localhost", "/", [](request_type& req) { - req.insert("User-Agent", "Beast"); + req.insert(field::user_agent, "Beast"); }); } catch(...) @@ -1625,7 +1625,7 @@ public: ws.handshake(res, "localhost", "/", [](request_type& req) { - req.insert("User-Agent", "Beast"); + req.insert(field::user_agent, "Beast"); }); } catch(...) @@ -1771,7 +1771,7 @@ public: ws.handshake("localhost", "/", [](request_type& req) { - req.insert("User-Agent", "Beast"); + req.insert(field::user_agent, "Beast"); }, ec); if(ec) @@ -1833,7 +1833,7 @@ public: ws.handshake(res, "localhost", "/", [](request_type& req) { - req.insert("User-Agent", "Beast"); + req.insert(field::user_agent, "Beast"); }, ec); if(ec) diff --git a/test/http/doc_http_samples.cpp b/test/http/doc_http_samples.cpp index 6afab8e6..bf528633 100644 --- a/test/http/doc_http_samples.cpp +++ b/test/http/doc_http_samples.cpp @@ -70,7 +70,7 @@ public: req.version = 11; req.method("POST"); req.target("/"); - req.insert("User-Agent", "test"); + req.insert(field::user_agent, "test"); req.body = "Hello, world!"; req.prepare(); @@ -103,7 +103,7 @@ public: req.version = 11; req.method("POST"); req.target("/"); - req.insert("User-Agent", "test"); + req.insert(field::user_agent, "test"); req.body = "Hello, world!"; req.prepare(); @@ -155,7 +155,7 @@ public: req.version = 11; req.method(verb::get); req.target("/"); - req.insert("User-Agent", "test"); + req.insert(field::user_agent, "test"); error_code ec; write_ostream(os, req, ec); BEAST_EXPECTS(! ec, ec.message()); diff --git a/test/http/doc_snippets.cpp b/test/http/doc_snippets.cpp index 7e7d6a0c..32e16b97 100644 --- a/test/http/doc_snippets.cpp +++ b/test/http/doc_snippets.cpp @@ -36,8 +36,8 @@ void fxx() { req.version = 11; // HTTP/1.1 req.method(verb::get); req.target("/index.htm"); - req.insert("Accept", "text/html"); - req.insert("User-Agent", "Beast"); + req.insert(field::accept, "text/html"); + req.insert(field::user_agent, "Beast"); req.prepare(connection::close); //] @@ -49,7 +49,7 @@ void fxx() { response res; res.version = 11; // HTTP/1.1 res.result(status::ok); - res.insert("Server", "Beast"); + res.insert(field::server, "Beast"); res.body = "Hello, world!"; res.prepare(); @@ -101,7 +101,7 @@ void fxx() { response res; res.version = 11; res.result(status::ok); - res.insert("Server", "Beast"); + res.insert(field::server, "Beast"); res.body = "Hello, world!"; write(sock, res, ec); diff --git a/test/http/message.cpp b/test/http/message.cpp index 1f766ab9..4522c802 100644 --- a/test/http/message.cpp +++ b/test/http/message.cpp @@ -115,14 +115,14 @@ public: { header h; - h.insert("User-Agent", "test"); + h.insert(field::user_agent, "test"); message m{Arg1{}, h}; BEAST_EXPECT(h["User-Agent"] == "test"); BEAST_EXPECT(m["User-Agent"] == "test"); } { header h; - h.insert("User-Agent", "test"); + h.insert(field::user_agent, "test"); message m{Arg1{}, std::move(h)}; BEAST_EXPECT(! h.exists("User-Agent")); BEAST_EXPECT(m["User-Agent"] == "test"); diff --git a/test/http/write.cpp b/test/http/write.cpp index 1119df31..bb461f3f 100644 --- a/test/http/write.cpp +++ b/test/http/write.cpp @@ -293,7 +293,7 @@ public: m.version = 10; m.result(status::ok); m.reason("OK"); - m.insert("Server", "test"); + m.insert(field::server, "test"); m.insert("Content-Length", "5"); m.body = "*****"; error_code ec; @@ -312,7 +312,7 @@ public: m.version = 11; m.result(status::ok); m.reason("OK"); - m.insert("Server", "test"); + m.insert(field::server, "test"); m.insert("Transfer-Encoding", "chunked"); m.body = "*****"; error_code ec; @@ -345,7 +345,7 @@ public: m.method(verb::get); m.target("/"); m.version = 10; - m.insert("User-Agent", "test"); + m.insert(field::user_agent, "test"); m.insert("Content-Length", "5"); m.body = "*****"; try @@ -376,7 +376,7 @@ public: m.method(verb::get); m.target("/"); m.version = 10; - m.insert("User-Agent", "test"); + m.insert(field::user_agent, "test"); m.insert("Transfer-Encoding", "chunked"); m.body = "*****"; error_code ec; @@ -409,7 +409,7 @@ public: m.method(verb::get); m.target("/"); m.version = 10; - m.insert("User-Agent", "test"); + m.insert(field::user_agent, "test"); m.insert("Transfer-Encoding", "chunked"); m.body = "*****"; error_code ec; @@ -442,7 +442,7 @@ public: m.method(verb::get); m.target("/"); m.version = 10; - m.insert("User-Agent", "test"); + m.insert(field::user_agent, "test"); m.insert("Content-Length", "5"); m.body = "*****"; error_code ec; @@ -470,7 +470,7 @@ public: m.method(verb::get); m.target("/"); m.version = 10; - m.insert("User-Agent", "test"); + m.insert(field::user_agent, "test"); m.insert("Content-Length", "5"); m.body = "*****"; error_code ec; @@ -499,7 +499,7 @@ public: m.method(verb::get); m.target("/"); m.version = 10; - m.insert("User-Agent", "test"); + m.insert(field::user_agent, "test"); m.body = "*"; m.prepare(); BEAST_EXPECT(str(m) == @@ -516,7 +516,7 @@ public: m.method(verb::get); m.target("/"); m.version = 10; - m.insert("User-Agent", "test"); + m.insert(field::user_agent, "test"); m.body = "*"; m.prepare(connection::keep_alive); BEAST_EXPECT(str(m) == @@ -534,7 +534,7 @@ public: m.method(verb::get); m.target("/"); m.version = 10; - m.insert("User-Agent", "test"); + m.insert(field::user_agent, "test"); m.body = "*"; try { @@ -552,7 +552,7 @@ public: m.method(verb::get); m.target("/"); m.version = 10; - m.insert("User-Agent", "test"); + m.insert(field::user_agent, "test"); m.body = "*"; m.prepare(); test::string_ostream ss(ios_); @@ -572,7 +572,7 @@ public: m.method(verb::get); m.target("/"); m.version = 11; - m.insert("User-Agent", "test"); + m.insert(field::user_agent, "test"); m.body = "*"; m.prepare(); BEAST_EXPECT(str(m) == @@ -589,7 +589,7 @@ public: m.method(verb::get); m.target("/"); m.version = 11; - m.insert("User-Agent", "test"); + m.insert(field::user_agent, "test"); m.body = "*"; m.prepare(connection::close); test::string_ostream ss(ios_); @@ -611,7 +611,7 @@ public: m.method(verb::get); m.target("/"); m.version = 11; - m.insert("User-Agent", "test"); + m.insert(field::user_agent, "test"); m.prepare(connection::upgrade); BEAST_EXPECT(str(m) == "GET / HTTP/1.1\r\n" @@ -626,7 +626,7 @@ public: m.method(verb::get); m.target("/"); m.version = 11; - m.insert("User-Agent", "test"); + m.insert(field::user_agent, "test"); m.body = "*"; m.prepare(); test::string_ostream ss(ios_); @@ -651,7 +651,7 @@ public: m.method(verb::get); m.target("/"); m.version = 11; - m.insert("User-Agent", "test"); + m.insert(field::user_agent, "test"); m.body = "*"; BEAST_EXPECT(boost::lexical_cast(m) == "GET / HTTP/1.1\r\nUser-Agent: test\r\n\r\n*"); @@ -785,7 +785,7 @@ public: m0.version = 11; m0.result(status::ok); m0.reason("OK"); - m0.insert("Server", "test"); + m0.insert(field::server, "test"); m0.body.s = "Hello, world!\n"; {