void Toolbox::SplitUriComponents(UriComponents& components, const std::string& uri) { static const char URI_SEPARATOR = '/'; components.clear(); if (uri.size() == 0 || uri[0] != URI_SEPARATOR) { throw OrthancException(ErrorCode_UriSyntax); } // Count the number of slashes in the URI to make an assumption // about the number of components in the URI unsigned int estimatedSize = 0; for (unsigned int i = 0; i < uri.size(); i++) { if (uri[i] == URI_SEPARATOR) estimatedSize++; } components.reserve(estimatedSize - 1); unsigned int start = 1; unsigned int end = 1; while (end < uri.size()) { // This is the loop invariant assert(uri[start - 1] == '/' && (end >= start)); if (uri[end] == '/') { components.push_back(std::string(&uri[start], end - start)); end++; start = end; } else { end++; } } if (start < uri.size()) { components.push_back(std::string(&uri[start], end - start)); } for (size_t i = 0; i < components.size(); i++) { if (components[i].size() == 0) { // Empty component, as in: "/coucou//e" throw OrthancException(ErrorCode_UriSyntax); } } }
TEST(ParseGetQuery, Test2) { UriComponents uri; HttpHandler::Arguments a; HttpHandler::ParseGetQuery(uri, a, "/instances/test/world"); ASSERT_EQ(3u, uri.size()); ASSERT_EQ("instances", uri[0]); ASSERT_EQ("test", uri[1]); ASSERT_EQ("world", uri[2]); ASSERT_EQ(0u, a.size()); }
TEST(ParseGetQuery, Test1) { UriComponents uri; HttpHandler::Arguments a; HttpHandler::ParseGetQuery(uri, a, "/instances/test/world?aaa=baaa&bb=a&aa=c"); ASSERT_EQ(3u, uri.size()); ASSERT_EQ("instances", uri[0]); ASSERT_EQ("test", uri[1]); ASSERT_EQ("world", uri[2]); ASSERT_EQ(3u, a.size()); ASSERT_EQ(a["aaa"], "baaa"); ASSERT_EQ(a["bb"], "a"); ASSERT_EQ(a["aa"], "c"); }
TEST(ParseGetQuery, Test2) { UriComponents uri; IHttpHandler::GetArguments b; HttpToolbox::ParseGetQuery(uri, b, "/instances/test/world"); IHttpHandler::Arguments a; HttpToolbox::CompileGetArguments(a, b); ASSERT_EQ(3u, uri.size()); ASSERT_EQ("instances", uri[0]); ASSERT_EQ("test", uri[1]); ASSERT_EQ("world", uri[2]); ASSERT_EQ(0u, a.size()); }
bool Toolbox::IsChildUri(const UriComponents& baseUri, const UriComponents& testedUri) { if (testedUri.size() < baseUri.size()) { return false; } for (size_t i = 0; i < baseUri.size(); i++) { if (baseUri[i] != testedUri[i]) return false; } return true; }
static void OutputDirectoryContent(HttpOutput& output, const IHttpHandler::Arguments& headers, const UriComponents& uri, const boost::filesystem::path& p) { namespace fs = boost::filesystem; std::string s; s += "<html>"; s += " <body>"; s += " <h1>Subdirectories</h1>"; s += " <ul>"; if (uri.size() > 0) { std::string h = Toolbox::FlattenUri(uri) + "/.."; s += "<li><a href=\"" + h + "\">..</a></li>"; } fs::directory_iterator end; for (fs::directory_iterator it(p) ; it != end; ++it) { #if BOOST_HAS_FILESYSTEM_V3 == 1 std::string f = it->path().filename().string(); #else std::string f = it->path().filename(); #endif std::string h = Toolbox::FlattenUri(uri) + "/" + f; if (fs::is_directory(it->status())) s += "<li><a href=\"" + h + "\">" + f + "</a></li>"; } s += " </ul>"; s += " <h1>Files</h1>"; s += " <ul>"; for (fs::directory_iterator it(p) ; it != end; ++it) { #if BOOST_HAS_FILESYSTEM_V3 == 1 std::string f = it->path().filename().string(); #else std::string f = it->path().filename(); #endif std::string h = Toolbox::FlattenUri(uri) + "/" + f; if (SystemToolbox::IsRegularFile(it->path().string())) { s += "<li><a href=\"" + h + "\">" + f + "</a></li>"; } } s += " </ul>"; s += " </body>"; s += "</html>"; output.SetContentType("text/html"); output.Answer(s); }
std::string Toolbox::FlattenUri(const UriComponents& components, size_t fromLevel) { if (components.size() <= fromLevel) { return "/"; } else { std::string r; for (size_t i = fromLevel; i < components.size(); i++) { r += "/" + components[i]; } return r; } }
bool FilesystemHttpHandler::Handle( HttpOutput& output, RequestOrigin /*origin*/, const char* /*remoteIp*/, const char* /*username*/, HttpMethod method, const UriComponents& uri, const Arguments& headers, const GetArguments& arguments, const char* /*bodyData*/, size_t /*bodySize*/) { if (!Toolbox::IsChildUri(pimpl_->baseUri_, uri)) { // This URI is not served by this handler return false; } if (method != HttpMethod_Get) { output.SendMethodNotAllowed("GET"); return true; } namespace fs = boost::filesystem; fs::path p = pimpl_->root_; for (size_t i = pimpl_->baseUri_.size(); i < uri.size(); i++) { p /= uri[i]; } if (fs::exists(p) && fs::is_regular_file(p)) { FilesystemHttpSender sender(p); output.Answer(sender); // TODO COMPRESSION } else if (listDirectoryContent_ && fs::exists(p) && fs::is_directory(p)) { OutputDirectoryContent(output, headers, uri, p); } else { output.SendStatus(HttpStatus_404_NotFound); } return true; }
bool FilesystemHttpHandler::Handle( HttpOutput& output, HttpMethod method, const UriComponents& uri, const Arguments& headers, const GetArguments& arguments, const std::string&) { if (!Toolbox::IsChildUri(pimpl_->baseUri_, uri)) { // This URI is not served by this handler return false; } if (method != HttpMethod_Get) { output.SendMethodNotAllowed("GET"); return true; } namespace fs = boost::filesystem; fs::path p = pimpl_->root_; for (size_t i = pimpl_->baseUri_.size(); i < uri.size(); i++) { p /= uri[i]; } if (fs::exists(p) && fs::is_regular_file(p)) { FilesystemHttpSender(p).Send(output); //output.AnswerFileAutodetectContentType(p.string()); } else if (listDirectoryContent_ && fs::exists(p) && fs::is_directory(p)) { OutputDirectoryContent(output, uri, p); } else { output.SendStatus(HttpStatus_404_NotFound); } return true; }
bool RestApiPath::Match(IHttpHandler::Arguments& components, UriComponents& trailing, const UriComponents& uri) const { assert(uri_.size() == components_.size()); if (uri.size() < uri_.size()) { return false; } if (!hasTrailing_ && uri.size() > uri_.size()) { return false; } components.clear(); trailing.clear(); assert(uri_.size() <= uri.size()); for (size_t i = 0; i < uri_.size(); i++) { if (components_[i].size() == 0) { // This URI component is not a free parameter if (uri_[i] != uri[i]) { return false; } } else { // This URI component is a free parameter components[components_[i]] = uri[i]; } } if (hasTrailing_) { trailing.assign(uri.begin() + uri_.size(), uri.end()); } return true; }
void Toolbox::TruncateUri(UriComponents& target, const UriComponents& source, size_t fromLevel) { target.clear(); if (source.size() > fromLevel) { target.resize(source.size() - fromLevel); size_t j = 0; for (size_t i = fromLevel; i < source.size(); i++, j++) { target[j] = source[i]; } assert(j == target.size()); } }
TEST(Uri, SplitUriComponents) { UriComponents c; Toolbox::SplitUriComponents(c, "/cou/hello/world"); ASSERT_EQ(3u, c.size()); ASSERT_EQ("cou", c[0]); ASSERT_EQ("hello", c[1]); ASSERT_EQ("world", c[2]); Toolbox::SplitUriComponents(c, "/cou/hello/world/"); ASSERT_EQ(3u, c.size()); ASSERT_EQ("cou", c[0]); ASSERT_EQ("hello", c[1]); ASSERT_EQ("world", c[2]); Toolbox::SplitUriComponents(c, "/cou/hello/world/a"); ASSERT_EQ(4u, c.size()); ASSERT_EQ("cou", c[0]); ASSERT_EQ("hello", c[1]); ASSERT_EQ("world", c[2]); ASSERT_EQ("a", c[3]); Toolbox::SplitUriComponents(c, "/"); ASSERT_EQ(0u, c.size()); Toolbox::SplitUriComponents(c, "/hello"); ASSERT_EQ(1u, c.size()); ASSERT_EQ("hello", c[0]); Toolbox::SplitUriComponents(c, "/hello/"); ASSERT_EQ(1u, c.size()); ASSERT_EQ("hello", c[0]); ASSERT_THROW(Toolbox::SplitUriComponents(c, ""), OrthancException); ASSERT_THROW(Toolbox::SplitUriComponents(c, "a"), OrthancException); ASSERT_THROW(Toolbox::SplitUriComponents(c, "/coucou//coucou"), OrthancException); }
TEST(RestApi, RestApiPath) { IHttpHandler::Arguments args; UriComponents trail; { RestApiPath uri("/coucou/{abc}/d/*"); ASSERT_TRUE(uri.Match(args, trail, "/coucou/moi/d/e/f/g")); ASSERT_EQ(1u, args.size()); ASSERT_EQ(3u, trail.size()); ASSERT_EQ("moi", args["abc"]); ASSERT_EQ("e", trail[0]); ASSERT_EQ("f", trail[1]); ASSERT_EQ("g", trail[2]); ASSERT_FALSE(uri.Match(args, trail, "/coucou/moi/f")); ASSERT_TRUE(uri.Match(args, trail, "/coucou/moi/d/")); ASSERT_FALSE(uri.Match(args, trail, "/a/moi/d")); ASSERT_FALSE(uri.Match(args, trail, "/coucou/moi")); ASSERT_EQ(3u, uri.GetLevelCount()); ASSERT_TRUE(uri.IsUniversalTrailing()); ASSERT_EQ("coucou", uri.GetLevelName(0)); ASSERT_THROW(uri.GetWildcardName(0), OrthancException); ASSERT_EQ("abc", uri.GetWildcardName(1)); ASSERT_THROW(uri.GetLevelName(1), OrthancException); ASSERT_EQ("d", uri.GetLevelName(2)); ASSERT_THROW(uri.GetWildcardName(2), OrthancException); } { RestApiPath uri("/coucou/{abc}/d"); ASSERT_FALSE(uri.Match(args, trail, "/coucou/moi/d/e/f/g")); ASSERT_TRUE(uri.Match(args, trail, "/coucou/moi/d")); ASSERT_EQ(1u, args.size()); ASSERT_EQ(0u, trail.size()); ASSERT_EQ("moi", args["abc"]); ASSERT_EQ(3u, uri.GetLevelCount()); ASSERT_FALSE(uri.IsUniversalTrailing()); ASSERT_EQ("coucou", uri.GetLevelName(0)); ASSERT_THROW(uri.GetWildcardName(0), OrthancException); ASSERT_EQ("abc", uri.GetWildcardName(1)); ASSERT_THROW(uri.GetLevelName(1), OrthancException); ASSERT_EQ("d", uri.GetLevelName(2)); ASSERT_THROW(uri.GetWildcardName(2), OrthancException); } { RestApiPath uri("/*"); ASSERT_TRUE(uri.Match(args, trail, "/a/b/c")); ASSERT_EQ(0u, args.size()); ASSERT_EQ(3u, trail.size()); ASSERT_EQ("a", trail[0]); ASSERT_EQ("b", trail[1]); ASSERT_EQ("c", trail[2]); ASSERT_EQ(0u, uri.GetLevelCount()); ASSERT_TRUE(uri.IsUniversalTrailing()); } }