Skip to content
Browse files

Fix inconsistencies with URI::Path

URI paths are now defined as "there is a slash between each segment".
So, "http://localhost/" ends up with two path segments, both empty.
In practice, this means a trailing slash always has a trailing empty
element, and a leading slash (an absolute path) always has a leading
empty element. The path "/" has two empty elements, which seems a bit
odd, but makes everything else work generally, especially the parser.
A single empty element doesn't make much sense, but is handled okay,
and treated as if it is a fully empty path.

Add a helper function for easily appending a path element without
creating a double-slash (replaces the last element with new element
if the last element is empty).

Add some unit tests, and also fix a few minor URI bugs:
 * scheme was defined incorrectly (it must start with an alpha)
 * Fix serializing of a URI with no authority, and an absolute path
   starting with two slashes
 * Fix transform where the base uri does not have a path

Change-Id: Ib6b86a14886dabfe3f717631c56323b768ea44dc
Reviewed-on: https://gerrit.dechocorp.com/10207
Reviewed-by: Punky Brewster (labs admin) <punkyb@example.com>
Reviewed-by: Jeremy Stanley <jeremy@mozy.com>
  • Loading branch information...
1 parent 10ddc74 commit b72b1dcf70285dad25471847374978c36c660949 @ccutrer committed Aug 26, 2010
Showing with 278 additions and 107 deletions.
  1. +12 −18 autoexp.dat
  2. +4 −4 mordor/http/http_parser.rl
  3. +2 −2 mordor/http/parser.h
  4. +1 −2 mordor/http/proxy.cpp
  5. +3 −5 mordor/tests/http_server.cpp
  6. +105 −3 mordor/tests/uri.cpp
  7. +42 −11 mordor/uri.h
  8. +109 −62 mordor/uri.rl
View
30 autoexp.dat
@@ -10,7 +10,7 @@ Mordor::URI{
";")
) #else ( "" ),
#if ($e.authority.m_hostDefined) ( #("//", $e.authority) ) #else ( "" ),
- #if ($e.path.type == ABSOLUTE || $e.path.segments._Myfirst != $e.path.segments._Mylast) ( $e.path ) #else ( "" ),
+ #if ($e.path.segments._Myfirst != $e.path.segments._Mylast) ( $e.path ) #else ( "" ),
#if ($e.m_queryDefined) ( #(
"?",
#if (($e.m_query._Myres) < ($e.m_query._BUF_SIZE)) ( [$e.m_query._Bx._Buf,sb] ) #else ( [$e.m_query._Bx._Ptr,sb] ))
@@ -25,7 +25,7 @@ Mordor::URI{
#(
#if ($e.m_schemeDefined) ( #(scheme: $e.m_scheme) ) #else ( #array(expr: 0, size: 0) ),
#if ($e.authority.m_hostDefined) ( #(authority: $e.authority) ) #else ( #array(expr: 0, size: 0) ),
- #if ($e.path.type == ABSOLUTE || $e.path.segments._Myfirst != $e.path.segments._Mylast) ( #(path: $e.path) ) #else ( #array(expr: 0, size: 0) ),
+ #if ($e.path.segments._Myfirst != $e.path.segments._Mylast) ( #(path: $e.path) ) #else ( #array(expr: 0, size: 0) ),
#if ($e.m_queryDefined) ( #(query: $e.m_query) ) #else ( #array(expr: 0, size: 0) ),
#if ($e.m_fragmentDefined) ( #(fragment: $e.m_fragment) ) #else ( #array(expr: 0, size: 0) ),
#(Actual Members: [$e,!])
@@ -53,24 +53,18 @@ Mordor::URI::Authority{
Mordor::URI::Path{
preview (
- #if ($e.type == RELATIVE && $e.segments._Myfirst == $e.segments._Mylast) (
+ #if ($e.segments._Myfirst == $e.segments._Mylast) (
""
) #else (
- #(
- #if ($e.type == ABSOLUTE) (
- "/"
- ) #else (
- ""
- ),
- #array(
- expr:
- #if ((($e.segments._Myfirst[$i])._Myres) < (($e.segments._Myfirst[$i])._BUF_SIZE)) (
- [($e.segments._Myfirst[$i])._Bx._Buf,sb]
- ) #else (
- [($e.segments._Myfirst[$i])._Bx._Ptr,sb]
- ),
- size: $e.segments._Mylast - $e.segments._Myfirst
- )
+ #array(
+ expr:
+ ;[$e.segments._Myfirst[$i],
+ #if ((($e.segments._Myfirst[$i])._Myres) < (($e.segments._Myfirst[$i])._BUF_SIZE)) (
+ [($e.segments._Myfirst[$i])._Bx._Buf,sb]
+ ) #else (
+ [($e.segments._Myfirst[$i])._Bx._Ptr,sb]
+ ),
+ size: $e.segments._Mylast - $e.segments._Myfirst
)
)
)
View
8 mordor/http/http_parser.rl
@@ -475,7 +475,7 @@ unquote(const std::string &str)
action set_request_uri {
m_uri = &m_request->requestLine.uri;
- m_path = &m_uri->path;
+ m_segments = &m_uri->path.segments;
}
action save_accept_list_element {
@@ -605,7 +605,7 @@ unquote(const std::string &str)
action set_referer {
m_uri = &m_request->request.referer;
- m_path = &m_uri->path;
+ m_segments = &m_uri->path.segments;
}
action save_accept_attribute {
@@ -721,7 +721,7 @@ Parser::adjustPointers(ptrdiff_t offset)
RequestParser::RequestParser(Request& request)
: m_request(&request),
m_ver(&request.requestLine.ver),
- m_path(&request.requestLine.uri.path),
+ m_segments(&request.requestLine.uri.path.segments),
m_general(&request.general),
m_entity(&request.entity)
{}
@@ -825,7 +825,7 @@ ResponseParser::ResponseParser(Response& response)
: m_response(&response),
m_ver(&response.status.ver),
m_uri(&response.response.location),
- m_path(&response.response.location.path),
+ m_segments(&response.response.location.path.segments),
m_general(&response.general),
m_entity(&response.entity)
{}
View
4 mordor/http/parser.h
@@ -55,7 +55,7 @@ class RequestParser : public Parser
Request *m_request;
Version *m_ver;
URI *m_uri;
- URI::Path *m_path;
+ std::vector<std::string> *m_segments;
GeneralHeaders *m_general;
EntityHeaders *m_entity;
};
@@ -74,7 +74,7 @@ class ResponseParser : public Parser
Response *m_response;
Version *m_ver;
URI *m_uri;
- URI::Path *m_path;
+ std::vector<std::string> *m_segments;
GeneralHeaders *m_general;
EntityHeaders *m_entity;
};
View
3 mordor/http/proxy.cpp
@@ -109,7 +109,6 @@ std::vector<URI> proxyFromList(const URI &uri, const std::string &proxy,
proxyUri.scheme(uri.scheme());
}
proxyUri.path.segments.clear();
- proxyUri.path.type = URI::Path::RELATIVE;
proxyUri.authority.userinfoDefined(false);
proxyUri.queryDefined(false);
proxyUri.fragmentDefined(false);
@@ -427,7 +426,7 @@ tunnel(RequestBroker::ptr requestBroker, const URI &proxy, const URI &target)
URI &requestUri = requestHeaders.requestLine.uri;
requestUri.scheme("http");
requestUri.authority = proxy.authority;
- requestUri.path.type = URI::Path::ABSOLUTE;
+ requestUri.path.segments.push_back(std::string());
requestUri.path.segments.push_back(os.str());
requestHeaders.request.host = os.str();
requestHeaders.general.connection.insert("Proxy-Connection");
View
8 mordor/tests/http_server.cpp
@@ -357,13 +357,11 @@ static void doRespondStream(RequestBroker &requestBroker, bool head,
Request requestHeaders;
if (head)
requestHeaders.requestLine.method = HEAD;
- requestHeaders.requestLine.uri.scheme("http");
- requestHeaders.requestLine.uri.authority.host("localhost");
- requestHeaders.requestLine.uri.path.type = URI::Path::ABSOLUTE;
+ requestHeaders.requestLine.uri = "http://localhost/";
if (!seekable)
- requestHeaders.requestLine.uri.path.segments.push_back("unseekable");
+ requestHeaders.requestLine.uri.path.append("unseekable");
if (!sizeable)
- requestHeaders.requestLine.uri.path.segments.push_back("unsizeable");
+ requestHeaders.requestLine.uri.path.append("unsizeable");
// Request the whole stream
MemoryStream response;
View
108 mordor/tests/uri.cpp
@@ -93,6 +93,8 @@ MORDOR_UNITTEST(URI, serializationAndParsing)
serializeAndParse("g#s/../x");
serializeAndParse("http://a/b/c/g#s/../x");
serializeAndParse("http:g");
+ serializeAndParse("http:/hi");
+ serializeAndParse("http:////hi");
}
MORDOR_UNITTEST(URI, pathNormalization)
@@ -123,7 +125,6 @@ MORDOR_UNITTEST(URI, normalization)
MORDOR_TEST_ASSERT(!lhs.authority.userinfoDefined());
MORDOR_TEST_ASSERT(!rhs.authority.userinfoDefined());
MORDOR_TEST_ASSERT_EQUAL(lhs.authority, rhs.authority);
- MORDOR_TEST_ASSERT_EQUAL(lhs.path.type, rhs.path.type);
MORDOR_TEST_ASSERT_EQUAL(lhs.path.segments, rhs.path.segments);
MORDOR_TEST_ASSERT_EQUAL(lhs.path, rhs.path);
MORDOR_TEST_ASSERT(!lhs.queryDefined());
@@ -183,6 +184,9 @@ MORDOR_UNITTEST(URI, transform)
MORDOR_TEST_ASSERT_EQUAL(URI::transform(base, URI("g#s/../x")), URI("http://a/b/c/g#s/../x"));
MORDOR_TEST_ASSERT_EQUAL(URI::transform(base, URI("http:g")), URI("http:g"));
+
+ MORDOR_TEST_ASSERT_EQUAL(URI::transform("http:hi", "bob"), "http:bob");
+ MORDOR_TEST_ASSERT_EQUAL(URI::transform("http://authority", "bob"), "http://authority/bob");
}
MORDOR_UNITTEST(URI, serializeCompleteOnBlockBoundary)
@@ -297,12 +301,12 @@ MORDOR_UNITTEST(URI, queryString)
MORDOR_UNITTEST(URI, encoding)
{
URI uri;
- uri.path.type = URI::Path::ABSOLUTE;
+ uri.path.segments.push_back(std::string());
uri.path.segments.push_back("WiX Tutorial \xe2\x80\x94 Introduction to the Windows Installer XML Toolset.URL");
MORDOR_TEST_ASSERT_EQUAL(uri.toString(),
"/WiX%20Tutorial%20%E2%80%94%20Introduction%20to%20the%20Windows%20Installer%20XML%20Toolset.URL");
- uri.path.segments[0] = "\xe5\xa4\x9a\xe8\xa8\x80\xe8\xaa\x9e\xe5\xaf\xbe\xe5\xbf\x9c\xe3\x82"
+ uri.path.segments[1] = "\xe5\xa4\x9a\xe8\xa8\x80\xe8\xaa\x9e\xe5\xaf\xbe\xe5\xbf\x9c\xe3\x82"
"\xb5\xe3\x83\xbc\xe3\x83\x81\xe3\x82\xa8\xe3\x83\xb3\xe3\x82\xb8\xe3"
"\x83\xb3\xe3\x81\xae\xe6\x97\xa5\xe6\x9c\xac\xe7\x89\x88\xe3\x80\x82"
"\xe3\x82\xa6\xe3\x82\xa7\xe3\x83\x96\xe3\x80\x81\xe3\x82\xa4\xe3\x83"
@@ -316,3 +320,101 @@ MORDOR_UNITTEST(URI, encoding)
"%A1%E3%83%BC%E3%82%B8%E3%81%8A%E3%82%88%E3%81%B3%E3"
"%83%8B%E3%83%A5%E3%83%BC%E3%82%B9%E6%A4%9C%E7%B4%A2.txt");
}
+
+MORDOR_UNITTEST(URI, emptyFirstComponent)
+{
+ serializeAndParse("/");
+ serializeAndParse("http://localhost/");
+ serializeAndParse("http://localhost//");
+ serializeAndParse("http://localhost///");
+
+ URI::Path path;
+ path = "/";
+ MORDOR_TEST_ASSERT_EQUAL(path.segments.size(), 2u);
+ MORDOR_TEST_ASSERT(path.isAbsolute());
+ MORDOR_TEST_ASSERT(path.segments.front().empty());
+ MORDOR_TEST_ASSERT(path.segments.back().empty());
+ path = "";
+ MORDOR_TEST_ASSERT(path.isRelative());
+ MORDOR_TEST_ASSERT(path.segments.empty());
+ path = "//";
+ MORDOR_TEST_ASSERT(path.isAbsolute());
+ MORDOR_TEST_ASSERT_EQUAL(path.segments.size(), 3u);
+ path = "a//";
+ MORDOR_TEST_ASSERT(path.isRelative());
+ MORDOR_TEST_ASSERT_EQUAL(path.segments.size(), 3u);
+
+ URI uri = "http://localhost/";
+ MORDOR_TEST_ASSERT_EQUAL(uri.toString(), "http://localhost/");
+ uri.path.append("hi");
+ MORDOR_TEST_ASSERT_EQUAL(uri.toString(), "http://localhost/hi");
+ uri.path.segments.push_back("");
+ MORDOR_TEST_ASSERT_EQUAL(uri.toString(), "http://localhost/hi/");
+ uri.path.segments.push_back("bye");
+ MORDOR_TEST_ASSERT_EQUAL(uri.toString(), "http://localhost/hi//bye");
+ uri.path.segments.push_back("");
+ MORDOR_TEST_ASSERT_EQUAL(uri.toString(), "http://localhost/hi//bye/");
+ uri.path.append("adios");
+ MORDOR_TEST_ASSERT_EQUAL(uri.toString(), "http://localhost/hi//bye/adios");
+ uri = "http://localhost/";
+ uri.path.append("hi");
+ MORDOR_TEST_ASSERT_EQUAL(uri.toString(), "http://localhost/hi");
+
+ // scheme == http, authority is not defined, path = "//hi";
+ // serialization has to add an extra // so it's not ambiguous with
+ // authority
+ uri = URI();
+ uri.scheme("http");
+ uri.path.segments.push_back(std::string());
+ uri.path.segments.push_back(std::string());
+ uri.path.segments.push_back("hi");
+ MORDOR_TEST_ASSERT_EQUAL(uri.toString(), "http:////hi");
+}
+
+MORDOR_UNITTEST(URI, append)
+{
+ URI uri = "http://localhost";
+ uri.path.append("hi");
+ MORDOR_TEST_ASSERT_EQUAL(uri, "http://localhost/hi");
+ uri.path.append("bye");
+ MORDOR_TEST_ASSERT_EQUAL(uri, "http://localhost/hi/bye");
+ uri.path.append(std::string());
+ MORDOR_TEST_ASSERT_EQUAL(uri, "http://localhost/hi/bye/");
+ uri.path.append(std::string());
+ MORDOR_TEST_ASSERT_EQUAL(uri, "http://localhost/hi/bye/");
+
+ uri = "http:";
+ uri.path.append("hi");
+ MORDOR_TEST_ASSERT_EQUAL(uri, "http:hi");
+ uri.path.append("bye");
+ MORDOR_TEST_ASSERT_EQUAL(uri, "http:hi/bye");
+
+ URI::Path path;
+ path.append("hi");
+ MORDOR_TEST_ASSERT_EQUAL(path, "hi");
+ path.append("bye");
+ MORDOR_TEST_ASSERT_EQUAL(path, "hi/bye");
+}
+
+MORDOR_UNITTEST(URI, makeAbsolute)
+{
+ URI::Path path;
+ path.makeAbsolute();
+ MORDOR_TEST_ASSERT_EQUAL(path, "/");
+ path.makeAbsolute();
+ MORDOR_TEST_ASSERT_EQUAL(path, "/");
+ path.makeRelative();
+ MORDOR_TEST_ASSERT(path.segments.empty());
+ path.makeRelative();
+ MORDOR_TEST_ASSERT(path.segments.empty());
+ path.append("hi");
+ MORDOR_TEST_ASSERT_EQUAL(path, "hi");
+ path.makeAbsolute();
+ MORDOR_TEST_ASSERT_EQUAL(path, "/hi");
+ path.makeAbsolute();
+ MORDOR_TEST_ASSERT_EQUAL(path, "/hi");
+ path.makeRelative();
+ MORDOR_TEST_ASSERT_EQUAL(path, "hi");
+ path.makeRelative();
+ MORDOR_TEST_ASSERT_EQUAL(path, "hi");
+}
View
53 mordor/uri.h
@@ -90,29 +90,57 @@ struct URI
};
Authority authority;
+ /// Represents segments in the path
+ ///
+ /// A single, empty segment is invalid. A leading empty segment indicates
+ /// an absolute path; a trailing empty segment indicates a trailing slash.
struct Path
{
- enum Type {
- ABSOLUTE,
- RELATIVE
- };
-
+ friend struct URI;
+ private:
+ Path(const URI &uri);
+ public:
Path();
Path(const char *path);
Path(const std::string& path);
Path& operator=(const std::string& path);
Path& operator=(const char *path) { return *this = std::string(path); }
- Type type;
-
- bool isEmpty() const { return type == RELATIVE && segments.empty(); }
-
+ bool isEmpty() const
+ {
+ return segments.empty() ||
+ (segments.size() == 1 && segments.front().empty());
+ }
+ bool isAbsolute() const
+ {
+ return segments.size() > 1 && segments.front().empty();
+ }
+ bool isRelative() const
+ {
+ return !isAbsolute();
+ }
+
+ void makeAbsolute();
+ void makeRelative();
+
+ /// Append a single segment
+ ///
+ /// This will remove a trailing empty segment before appending, if
+ /// necessary
+ /// I.e., Path("/hi/").append("bob") would result in "/hi/bob" instead
+ /// of "/hi//bob"
+ /// Also, if this path is part of a URI, and the URI has an authority
+ /// defined, and the path is empty, append will ensure the path becomes
+ /// absolute.
+ /// I.e., URI("http://localhost").path.append("bob") would result in
+ /// "http://localhost/bob"
+ void append(const std::string &segment);
void removeDotComponents();
void normalize(bool emptyPathValid = false);
- // Concatenate rhs to this object, dropping least significant component
- // of this object first
+ /// Concatenate rhs to this object, dropping least significant
+ /// component of this object first
void merge(const Path& rhs);
std::vector<std::string> segments;
@@ -130,6 +158,9 @@ struct URI
bool operator==(const Path &rhs) const;
bool operator!=(const Path &rhs) const
{ return !(*this == rhs); }
+
+ private:
+ const URI *m_uri;
};
Path path;
View
171 mordor/uri.rl
@@ -151,13 +151,14 @@ URI::decode(const std::string &str, CharacterClass charClass)
pct_encoded = "%" xdigit xdigit;
action marku { mark = fpc; }
+ action markh { mark = fpc; }
action save_scheme
{
m_uri->scheme(unescape(std::string(mark, fpc - mark)));
mark = NULL;
}
- scheme = (alpha | digit | "+" | "-" | ".")+ >marku %save_scheme;
+ scheme = (alpha (alpha | digit | "+" | "-" | ".")*) >marku %save_scheme;
action save_port
{
@@ -200,11 +201,11 @@ URI::decode(const std::string &str, CharacterClass charClass)
host = IP_literal | IPv4address | reg_name;
port = digit*;
- authority = ( (userinfo %save_userinfo "@")? host >marku %save_host (":" port >marku %save_port)? ) >marku;
+ authority = ( (userinfo %save_userinfo "@")? host >markh %save_host (":" port >markh %save_port)? ) >markh;
action save_segment
{
- m_path->segments.push_back(unescape(std::string(mark, fpc - mark)));
+ m_segments->push_back(unescape(std::string(mark, fpc - mark)));
mark = NULL;
}
@@ -213,20 +214,16 @@ URI::decode(const std::string &str, CharacterClass charClass)
segment_nz = pchar+ >marku %save_segment;
segment_nz_nc = (pchar - ":")+ >marku %save_segment;
- action set_absolute
+ action clear_segments
{
- m_path->type = URI::Path::ABSOLUTE;
- }
- action set_relative
- {
- m_path->type = URI::Path::RELATIVE;
+ m_segments->clear();
}
- path_abempty = ("/" segment >set_absolute)*;
- path_absolute = "/" (segment_nz ("/" segment)*)? >set_absolute;
- path_noscheme = segment_nz_nc >set_relative ("/" segment)*;
- path_rootless = segment_nz >set_relative ("/" segment)*;
- path_empty = "" %set_relative;
+ path_abempty = (("/" >marku >save_segment segment) %marku %save_segment)? ("/" segment)*;
+ path_absolute = ("/" >marku >save_segment (segment_nz ("/" segment)*)?) %marku %save_segment;
+ path_noscheme = segment_nz_nc ("/" segment)*;
+ path_rootless = segment_nz ("/" segment)*;
+ path_empty = "";
path = (path_abempty | path_absolute | path_noscheme | path_rootless | path_empty);
action save_query
@@ -244,9 +241,9 @@ URI::decode(const std::string &str, CharacterClass charClass)
query = (pchar | "/" | "?")* >marku %save_query;
fragment = (pchar | "/" | "?")* >marku %save_fragment;
- hier_part = ("//" authority path_abempty) | path_absolute | path_rootless | path_empty;
+ hier_part = ("//" %clear_segments authority path_abempty) | path_absolute | path_rootless | path_empty;
- relative_part = ("//" authority path_abempty) | path_absolute | path_noscheme | path_empty;
+ relative_part = ("//" %clear_segments authority path_abempty) | path_absolute | path_noscheme | path_empty;
relative_ref = relative_part ( "?" query )? ( "#" fragment )?;
absolute_URI = scheme ":" hier_part ( "?" query )? ;
@@ -270,7 +267,7 @@ public:
URIParser(URI& uri)
{
m_uri = &uri;
- m_path = &m_uri->path;
+ m_segments = &m_uri->path.segments;
}
void init()
@@ -310,7 +307,7 @@ public:
private:
URI *m_uri;
- URI::Path *m_path;
+ std::vector<std::string> *m_segments;
};
%%{
@@ -322,9 +319,9 @@ private:
class URIPathParser : public RagelParser
{
public:
- URIPathParser(URI::Path& path)
+ URIPathParser(std::vector<std::string> &segments)
{
- m_path = &path;
+ m_segments = &segments;
}
void init()
@@ -363,31 +360,42 @@ public:
}
private:
- URI::Path *m_path;
+ std::vector<std::string> *m_segments;
};
+#ifdef MSVC
+#pragma warning(push)
+#pragma warning(disable: 4355)
+#endif
URI::URI()
+: path(*this)
{
reset();
}
URI::URI(const std::string& uri)
+: path(*this)
{
reset();
*this = uri;
}
URI::URI(const char *uri)
+: path(*this)
{
reset();
*this = uri;
}
URI::URI(const Buffer &uri)
+: path(*this)
{
reset();
*this = uri;
}
+#ifdef MSVC
+#pragma warning(pop)
+#endif
URI&
URI::operator=(const std::string& uri)
@@ -416,7 +424,6 @@ URI::reset()
{
schemeDefined(false);
authority.hostDefined(false);
- path.type = Path::RELATIVE;
path.segments.clear();
queryDefined(false);
fragmentDefined(false);
@@ -510,11 +517,15 @@ operator<<(std::ostream& os, const URI::Authority& authority)
return os;
}
-URI::Path::Path()
+URI::Path::Path(const URI &uri)
+ : m_uri(&uri)
{
- type = RELATIVE;
}
+URI::Path::Path()
+ : m_uri(NULL)
+ {}
+
URI::Path::Path(const char *path)
{
*this = path;
@@ -528,19 +539,57 @@ URI::Path::Path(const std::string& path)
URI::Path&
URI::Path::operator=(const std::string& path)
{
- type = RELATIVE;
- segments.clear();
- URIPathParser parser(*this);
+ std::vector<std::string> result;
+ URIPathParser parser(result);
parser.run(path);
if (parser.error() || !parser.final())
MORDOR_THROW_EXCEPTION(std::invalid_argument("path"));
+ segments.swap(result);
return *this;
}
void
+URI::Path::makeAbsolute()
+{
+ if (segments.empty()) {
+ segments.push_back(std::string());
+ segments.push_back(std::string());
+ } else if (!segments.front().empty()) {
+ segments.insert(segments.begin(), std::string());
+ }
+}
+
+void
+URI::Path::makeRelative()
+{
+ if (!segments.empty() && segments.front().empty()) {
+ segments.erase(segments.begin());
+ if (segments.size() == 1u && segments.front().empty())
+ segments.clear();
+ }
+}
+
+void
+URI::Path::append(const std::string &segment)
+{
+ if (m_uri && segments.empty() && m_uri->authority.hostDefined()) {
+ segments.push_back(std::string());
+ segments.push_back(segment);
+ } else if (segments.empty() || !segments[segments.size() - 1].empty() ||
+ // Special case for degenerate single-empty-segment path
+ (segments.size() == 1 && segments.front().empty())) {
+ segments.push_back(segment);
+ } else {
+ segments[segments.size() - 1] = segment;
+ }
+}
+
+void
URI::Path::removeDotComponents()
{
for(size_t i = 0; i < segments.size(); ++i) {
+ if (i == 0 && segments[i].empty())
+ continue;
if (segments[i] == ".") {
if (i + 1 == segments.size()) {
segments[i].clear();
@@ -557,6 +606,11 @@ URI::Path::removeDotComponents()
--i;
continue;
}
+ if (i == 1 && segments.front().empty()) {
+ segments.erase(segments.begin() + i);
+ --i;
+ continue;
+ }
if (i + 1 == segments.size()) {
segments.resize(segments.size() - 1);
segments.back().clear();
@@ -574,14 +628,12 @@ void
URI::Path::normalize(bool emptyPathValid)
{
removeDotComponents();
- if (segments.empty() && !emptyPathValid)
- type = ABSOLUTE;
}
void
URI::Path::merge(const Path& rhs)
{
- MORDOR_ASSERT(rhs.type == RELATIVE);
+ MORDOR_ASSERT(rhs.isRelative());
if (!segments.empty()) {
segments.pop_back();
segments.insert(segments.end(), rhs.segments.begin(), rhs.segments.end());
@@ -602,18 +654,16 @@ URI::Path::serialize(bool schemeless) const
std::ostream&
operator<<(std::ostream& os, const URI::Path::path_serializer &p)
{
- if (p.p->segments.empty() && p.p->type == URI::Path::ABSOLUTE) {
- return os << "/";
- }
- for (size_t i = 0; i < p.p->segments.size(); ++i) {
- if (i != 0 || p.p->type == URI::Path::ABSOLUTE) {
- os << "/";
- }
- if (i == 0 && p.p->type == URI::Path::RELATIVE && p.schemeless) {
- os << escape(p.p->segments[i], segment_nc);
- } else {
- os << escape(p.p->segments[i], pchar);
- }
+ const std::vector<std::string> &segments = p.p->segments;
+ for (std::vector<std::string>::const_iterator it = segments.begin();
+ it != segments.end();
+ ++it) {
+ if (it != segments.begin())
+ os << '/';
+ if (it == segments.begin() && p.schemeless)
+ os << escape(*it, segment_nc);
+ else
+ os << escape(*it, pchar);
}
return os;
}
@@ -627,10 +677,6 @@ operator<<(std::ostream& os, const URI::Path& path)
int
URI::Path::cmp(const Path &rhs) const
{
- if (type == ABSOLUTE && rhs.type == RELATIVE)
- return -1;
- if (type == RELATIVE && rhs.type == ABSOLUTE)
- return 1;
std::vector<std::string>::const_iterator itl, itr;
itl = segments.begin(); itr = rhs.segments.begin();
while (true) {
@@ -649,7 +695,7 @@ URI::Path::cmp(const Path &rhs) const
bool
URI::Path::operator==(const Path &rhs) const
{
- return type == rhs.type && segments == rhs.segments;
+ return segments == rhs.segments;
}
void
@@ -698,30 +744,28 @@ URI::toString() const
std::ostream&
operator<<(std::ostream& os, const URI& uri)
{
- if (uri.schemeDefined()) {
+ MORDOR_ASSERT(!uri.authority.hostDefined() || uri.path.isAbsolute() ||
+ uri.path.isEmpty());
+ if (uri.schemeDefined())
os << escape(uri.scheme(), scheme) << ":";
- }
- if (uri.authority.hostDefined()) {
+ if (uri.authority.hostDefined())
os << "//" << uri.authority;
- }
// Has scheme, but no authority, must ensure that an absolute path
// doesn't begin with an empty segment (or could be mistaken for authority)
if (uri.schemeDefined() && !uri.authority.hostDefined() &&
- uri.path.type == URI::Path::ABSOLUTE &&
- uri.path.segments.size() > 0 && uri.path.segments.front().empty()) {
- os << "/";
+ uri.path.isAbsolute() &&
+ uri.path.segments.size() >= 3 && uri.path.segments[1].empty()) {
+ os << "//";
}
os << uri.path.serialize(!uri.schemeDefined());
- if (uri.queryDefined()) {
+ if (uri.queryDefined())
os << "?" << uri.m_query;
- }
- if (uri.fragmentDefined()) {
+ if (uri.fragmentDefined())
os << "#" << escape(uri.fragment(), query);
- }
return os;
}
@@ -755,13 +799,16 @@ URI::transform(const URI& base, const URI& relative)
target.m_queryDefined = base.m_queryDefined;
}
} else {
- if (relative.path.type == Path::ABSOLUTE) {
+ if (relative.path.isAbsolute()) {
target.path = relative.path;
} else {
- target.path = base.path;
+ if (base.authority.hostDefined() && base.path.isEmpty()) {
+ target.path.segments.push_back(std::string());
+ target.path.segments.push_back(std::string());
+ } else {
+ target.path = base.path;
+ }
target.path.merge(relative.path);
- if (!base.authority.hostDefined())
- target.path.type = Path::ABSOLUTE;
}
target.path.removeDotComponents();
target.m_query = relative.m_query;

0 comments on commit b72b1dc

Please sign in to comment.
Something went wrong with that request. Please try again.