From c94621bd29ed6060e942c79760592258ae95e56e Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Sat, 13 Jul 2013 13:28:56 -0500 Subject: [PATCH] case insensitive header comparisons fixes #220 and #275 --- changelog.md | 1 + test/http/parser.cpp | 28 ++++++++++++++++++++++++ test/utility/utilities.cpp | 6 ++++++ websocketpp/http/parser.hpp | 3 ++- websocketpp/impl/utilities_impl.hpp | 6 ++++++ websocketpp/utilities.hpp | 33 ++++++++++++++++++++++++++++- 6 files changed, 75 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 7d9e7144b9..29fd51f888 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,5 @@ HEAD +- HTTP header comparisons are now case insensitive. #220, #275 - Refactors URI to be exception free and not use the regular expressions. This eliminates the dependency on boost or C++11 regex libraries. - Updates handling of Server and User-Agent headers diff --git a/test/http/parser.cpp b/test/http/parser.cpp index c982d2ca26..57a1de6fc5 100644 --- a/test/http/parser.cpp +++ b/test/http/parser.cpp @@ -355,6 +355,34 @@ BOOST_AUTO_TEST_CASE( extract_parameters ) { BOOST_CHECK_EQUAL( a.find("bar")->second, "a \"b\" c" ); } +BOOST_AUTO_TEST_CASE( case_insensitive_headers ) { + websocketpp::http::parser::parser r; + + r.replace_header("foo","bar"); + + BOOST_CHECK_EQUAL( r.get_header("foo"), "bar" ); + BOOST_CHECK_EQUAL( r.get_header("FOO"), "bar" ); + BOOST_CHECK_EQUAL( r.get_header("Foo"), "bar" ); +} + +BOOST_AUTO_TEST_CASE( case_insensitive_headers_overwrite ) { + websocketpp::http::parser::parser r; + + r.replace_header("foo","bar"); + + BOOST_CHECK_EQUAL( r.get_header("foo"), "bar" ); + BOOST_CHECK_EQUAL( r.get_header("Foo"), "bar" ); + + r.replace_header("Foo","baz"); + + BOOST_CHECK_EQUAL( r.get_header("foo"), "baz" ); + BOOST_CHECK_EQUAL( r.get_header("Foo"), "baz" ); + + r.remove_header("FoO"); + + BOOST_CHECK_EQUAL( r.get_header("foo"), "" ); + BOOST_CHECK_EQUAL( r.get_header("Foo"), "" ); +} BOOST_AUTO_TEST_CASE( blank_consume ) { websocketpp::http::parser::request r; diff --git a/test/utility/utilities.cpp b/test/utility/utilities.cpp index 3dd78fba2b..557dedb8cb 100644 --- a/test/utility/utilities.cpp +++ b/test/utility/utilities.cpp @@ -56,6 +56,12 @@ BOOST_AUTO_TEST_CASE( substr_not_found ) { BOOST_CHECK(websocketpp::utility::ci_find_substr(haystack,needle) == haystack.end()); } +BOOST_AUTO_TEST_CASE( to_lower ) { + std::string in = "AbCd"; + + BOOST_CHECK_EQUAL(websocketpp::utility::to_lower(in), "abcd"); +} + BOOST_AUTO_TEST_CASE( string_replace_all ) { std::string source = "foo \"bar\" baz"; std::string dest = "foo \\\"bar\\\" baz"; diff --git a/websocketpp/http/parser.hpp b/websocketpp/http/parser.hpp index 34dea64793..ff59d52a54 100644 --- a/websocketpp/http/parser.hpp +++ b/websocketpp/http/parser.hpp @@ -32,6 +32,7 @@ #include #include +#include #include namespace websocketpp { @@ -47,7 +48,7 @@ namespace state { }; } -typedef std::map header_list; +typedef std::map header_list; /// Read and return the next token in the stream /** diff --git a/websocketpp/impl/utilities_impl.hpp b/websocketpp/impl/utilities_impl.hpp index 72f4a1b015..9614e092eb 100644 --- a/websocketpp/impl/utilities_impl.hpp +++ b/websocketpp/impl/utilities_impl.hpp @@ -31,6 +31,12 @@ namespace websocketpp { namespace utility { +inline std::string to_lower(std::string const & in) { + std::string out = in; + std::transform(out.begin(),out.end(),out.begin(),::tolower); + return out; +} + inline std::string to_hex(const std::string& input) { std::string output; std::string hex = "0123456789ABCDEF"; diff --git a/websocketpp/utilities.hpp b/websocketpp/utilities.hpp index 9141adcd77..4472ddfe8c 100644 --- a/websocketpp/utilities.hpp +++ b/websocketpp/utilities.hpp @@ -28,9 +28,11 @@ #ifndef WEBSOCKETPP_UTILITIES_HPP #define WEBSOCKETPP_UTILITIES_HPP -#include #include +#include +#include + namespace websocketpp { /// Generic non-websocket specific utility functions and data structures namespace utility { @@ -64,6 +66,28 @@ private: std::locale const & m_loc; }; +/// Helper less than functor for case insensitive find +/** + * Based on code from + * http://stackoverflow.com/questions/3152241/case-insensitive-stdstring-find + */ +struct ci_less : std::binary_function { + // case-independent (ci) compare_less binary function + struct nocase_compare + : public std::binary_function + { + bool operator() (unsigned char const & c1, unsigned char const & c2) const { + return std::tolower (c1) < std::tolower (c2); + } + }; + bool operator() (std::string const & s1, std::string const & s2) const { + return std::lexicographical_compare + (s1.begin (), s1.end (), // source range + s2.begin (), s2.end (), // dest range + nocase_compare ()); // comparison + } +}; + /// Find substring (case insensitive) /** * @param [in] haystack The string to search in @@ -105,6 +129,13 @@ typename T::const_iterator ci_find_substr(T const & haystack, needle, needle+size, my_equal(loc) ); } +/// Convert a string to lowercase +/** + * @param [in] in The string to convert + * @return The converted string + */ +std::string to_lower(std::string const & in); + /// Replace all occurrances of a substring with another /** * @param [in] subject The string to search in