--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "url_escape.h"
+
+#include <stdexcept>
+#include <sstream>
+
+std::string url_escape(const std::string& s)
+{
+ std::string out;
+ for (auto c : s) {
+ if (std::isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~' ||
+ c == '/') {
+ out.push_back(c);
+ } else {
+ char t[4];
+ snprintf(t, sizeof(t), "%%%02x", (int)(unsigned char)c);
+ out.append(t);
+ }
+ }
+ return out;
+}
+
+std::string url_unescape(const std::string& s)
+{
+ std::string out;
+ const char *end = s.c_str() + s.size();
+ for (const char *c = s.c_str(); c < end; ++c) {
+ switch (*c) {
+ case '%':
+ {
+ unsigned char v = 0;
+ for (unsigned i=0; i<2; ++i) {
+ ++c;
+ if (c >= end) {
+ std::ostringstream ss;
+ ss << "invalid escaped string at pos " << (c - s.c_str()) << " of '"
+ << s << "'";
+ throw std::runtime_error(ss.str());
+ }
+ v <<= 4;
+ if (*c >= '0' && *c <= '9') {
+ v += *c - '0';
+ } else if (*c >= 'a' && *c <= 'f') {
+ v += *c - 'a' + 10;
+ } else if (*c >= 'A' && *c <= 'F') {
+ v += *c - 'A' + 10;
+ } else {
+ std::ostringstream ss;
+ ss << "invalid escaped string at pos " << (c - s.c_str()) << " of '"
+ << s << "'";
+ throw std::runtime_error(ss.str());
+ }
+ }
+ out.push_back(v);
+ }
+ break;
+ default:
+ out.push_back(*c);
+ }
+ }
+ return out;
+}
add_ceph_unittest(unittest_safe_io ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest_safe_io)
target_link_libraries(unittest_safe_io global)
+# unittest_url_escape
+add_executable(unittest_url_escape
+ test_url_escape.cc
+ )
+add_ceph_unittest(unittest_url_escape ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest_url_escape)
+target_link_libraries(unittest_url_escape ceph-common)
+
# unittest_readahead
add_executable(unittest_readahead
Readahead.cc
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "common/url_escape.h"
+
+#include "gtest/gtest.h"
+
+TEST(url_escape, escape) {
+ ASSERT_EQ(url_escape("foo bar"), std::string("foo%20bar"));
+ ASSERT_EQ(url_escape("foo\nbar"), std::string("foo%0abar"));
+}
+
+TEST(url_escape, unescape) {
+ ASSERT_EQ(url_unescape("foo%20bar"), std::string("foo bar"));
+ ASSERT_EQ(url_unescape("foo%0abar"), std::string("foo\nbar"));
+ ASSERT_EQ(url_unescape("%20"), std::string(" "));
+ ASSERT_EQ(url_unescape("\0%20"), std::string("\0 "));
+ ASSERT_EQ(url_unescape("\x01%20"), std::string("\x01 "));
+}
+
+TEST(url_escape, all_chars) {
+ std::string a;
+ for (unsigned j=0; j<256; ++j) {
+ a.push_back((char)j);
+ }
+ std::string b = url_escape(a);
+ std::cout << "escaped: " << b << std::endl;
+ ASSERT_EQ(a, url_unescape(b));
+}
+
+TEST(url_escape, invalid) {
+ ASSERT_THROW(url_unescape("foo%xx"), std::runtime_error);
+ ASSERT_THROW(url_unescape("foo%%"), std::runtime_error);
+ ASSERT_THROW(url_unescape("foo%"), std::runtime_error);
+ ASSERT_THROW(url_unescape("foo%0"), std::runtime_error);
+}