diff options
-rw-r--r-- | src/Uri.cpp | 88 | ||||
-rw-r--r-- | test/src/UriTests.cpp | 39 |
2 files changed, 127 insertions, 0 deletions
diff --git a/src/Uri.cpp b/src/Uri.cpp index 8bc2f4a..da1b9f8 100644 --- a/src/Uri.cpp +++ b/src/Uri.cpp @@ -6,7 +6,9 @@ * © 2018 by Richard Walters */ +#include <functional> #include <inttypes.h> +#include <memory> #include <string> #include <Uri/Uri.hpp> #include <vector> @@ -51,6 +53,83 @@ namespace { return true; } + /** + * This function takes a given "stillPassing" strategy + * and invokes it on the sequence of characters in the given + * string, to check if the string passes or not. + * + * @param[in] candidate + * This is the string to test. + * + * @param[in] stillPassing + * This is the strategy to invoke in order to test the string. + * + * @return + * An indication of whether or not the given candidate string + * passes the test is returned. + */ + bool FailsMatch( + const std::string& candidate, + std::function< bool(char, bool) > stillPassing + ) { + for (const auto c: candidate) { + if (!stillPassing(c, false)) { + return true; + } + } + return !stillPassing(' ', true); + } + + /** + * This function returns a strategy function that + * may be used with the FailsMatch function to test a scheme + * to make sure it is legal according to the standard. + * + * @return + * A strategy function that may be used with the + * FailsMatch function to test a scheme to make sure + * it is legal according to the standard is returned. + */ + std::function< bool(char, bool) > LegalSchemeCheckStrategy() { + auto isFirstCharacter = std::make_shared< bool >(true); + return [isFirstCharacter](char c, bool end){ + if (end) { + return !*isFirstCharacter; + } else { + bool check; + if (*isFirstCharacter) { + check = ( + ( + (c >= 'a') + && (c <= 'z') + ) || ( + (c >= 'A') + && (c <= 'Z') + ) + ); + } else { + check = ( + ( + (c >= 'a') + && (c <= 'z') + ) || ( + (c >= 'A') + && (c <= 'Z') + ) || ( + (c >= '0') + && (c <= '9') + ) + || (c == '+') + || (c == '-') + || (c == '.') + ); + } + *isFirstCharacter = false; + return check; + } + }; + } + } namespace Uri { @@ -200,6 +279,15 @@ namespace Uri { rest = uriString; } else { impl_->scheme = uriString.substr(0, schemeEnd); + bool isFirstCharacter = true; + if ( + FailsMatch( + impl_->scheme, + LegalSchemeCheckStrategy() + ) + ) { + return false; + } rest = uriString.substr(schemeEnd + 1); } diff --git a/test/src/UriTests.cpp b/test/src/UriTests.cpp index 6ee57c4..b4be2d5 100644 --- a/test/src/UriTests.cpp +++ b/test/src/UriTests.cpp @@ -235,3 +235,42 @@ TEST(UriTests, ParseFromStringTwiceFirstUserInfoThenWithout) { ASSERT_TRUE(uri.ParseFromString("/foo/bar")); ASSERT_TRUE(uri.GetUserInfo().empty()); } + +TEST(UriTests, ParseFromStringSchemeIllegalCharacters) { + const std::vector< std::string > testVectors{ + {"://www.example.com/"}, + {"0://www.example.com/"}, + {"+://www.example.com/"}, + {"@://www.example.com/"}, + {".://www.example.com/"}, + {"h@://www.example.com/"}, + }; + size_t index = 0; + for (const auto& testVector : testVectors) { + Uri::Uri uri; + ASSERT_FALSE(uri.ParseFromString(testVector)) << index; + ++index; + } +} + +TEST(UriTests, ParseFromStringSchemeBarelyLegal) { + struct TestVector { + std::string uriString; + std::string scheme; + }; + const std::vector< TestVector > testVectors{ + {"h://www.example.com/", "h"}, + {"x+://www.example.com/", "x+"}, + {"y-://www.example.com/", "y-"}, + {"z.://www.example.com/", "z."}, + {"aa://www.example.com/", "aa"}, + {"a0://www.example.com/", "a0"}, + }; + size_t index = 0; + for (const auto& testVector : testVectors) { + Uri::Uri uri; + ASSERT_TRUE(uri.ParseFromString(testVector.uriString)) << index; + ASSERT_EQ(testVector.scheme, uri.GetScheme()); + ++index; + } +} |