Mercurial > projects > hoofbaby
diff deps/Platinum/ThirdParty/Neptune/Source/Core/NptUri.cpp @ 0:3425707ddbf6
Initial import (hopefully this mercurial stuff works...)
author | fraserofthenight |
---|---|
date | Mon, 06 Jul 2009 08:06:28 -0700 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/deps/Platinum/ThirdParty/Neptune/Source/Core/NptUri.cpp Mon Jul 06 08:06:28 2009 -0700 @@ -0,0 +1,736 @@ +/***************************************************************** +| +| Neptune - URI +| +| Copyright (c) 2002-2008, Axiomatic Systems, LLC. +| All rights reserved. +| +| Redistribution and use in source and binary forms, with or without +| modification, are permitted provided that the following conditions are met: +| * Redistributions of source code must retain the above copyright +| notice, this list of conditions and the following disclaimer. +| * Redistributions in binary form must reproduce the above copyright +| notice, this list of conditions and the following disclaimer in the +| documentation and/or other materials provided with the distribution. +| * Neither the name of Axiomatic Systems nor the +| names of its contributors may be used to endorse or promote products +| derived from this software without specific prior written permission. +| +| THIS SOFTWARE IS PROVIDED BY AXIOMATIC SYSTEMS ''AS IS'' AND ANY +| EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +| DISCLAIMED. IN NO EVENT SHALL AXIOMATIC SYSTEMS BE LIABLE FOR ANY +| DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +| ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +| + ***************************************************************/ + +/*---------------------------------------------------------------------- +| includes ++---------------------------------------------------------------------*/ +#include "NptUri.h" +#include "NptUtils.h" +#include "NptResults.h" + +/*---------------------------------------------------------------------- +| NPT_Uri::ParseScheme ++---------------------------------------------------------------------*/ +NPT_Uri::SchemeId +NPT_Uri::ParseScheme(const NPT_String& scheme) +{ + if (scheme == "http") { + return SCHEME_ID_HTTP; + } else { + return SCHEME_ID_UNKNOWN; + } +} + +/*---------------------------------------------------------------------- +| NPT_Uri::SetScheme ++---------------------------------------------------------------------*/ +void +NPT_Uri::SetScheme(const char* scheme) +{ + m_Scheme = scheme; + m_Scheme.MakeLowercase(); + m_SchemeId = ParseScheme(m_Scheme); +} + +/*---------------------------------------------------------------------- +| NPT_Uri::SetSchemeFromUri ++---------------------------------------------------------------------*/ +NPT_Result +NPT_Uri::SetSchemeFromUri(const char* uri) +{ + const char* start = uri; + char c; + while ((c =*uri++)) { + if (c == ':') { + m_Scheme.Assign(start, (NPT_Size)(uri-start-1)); + m_Scheme.MakeLowercase(); + m_SchemeId = ParseScheme(m_Scheme); + return NPT_SUCCESS; + } else if ((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + (c == '+') || + (c == '.') || + (c == '-')) { + continue; + } else { + break; + } + } + return NPT_ERROR_INVALID_SYNTAX; +} + +/*---------------------------------------------------------------------- +Appendix A. Collected ABNF for URI + + URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] + + hier-part = "//" authority path-abempty + / path-absolute + / path-rootless + / path-empty + + URI-reference = URI / relative-ref + + absolute-URI = scheme ":" hier-part [ "?" query ] + + relative-ref = relative-part [ "?" query ] [ "#" fragment ] + + relative-part = "//" authority path-abempty + / path-absolute + / path-noscheme + / path-empty + + scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + + authority = [ userinfo "@" ] host [ ":" port ] + userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) + host = IP-literal / IPv4address / reg-name + port = *DIGIT + + IP-literal = "[" ( IPv6address / IPvFuture ) "]" + + IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) + + IPv6address = 6( h16 ":" ) ls32 + / "::" 5( h16 ":" ) ls32 + / [ h16 ] "::" 4( h16 ":" ) ls32 + / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 + / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 + / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 + / [ *4( h16 ":" ) h16 ] "::" ls32 + / [ *5( h16 ":" ) h16 ] "::" h16 + / [ *6( h16 ":" ) h16 ] "::" + + h16 = 1*4HEXDIG + ls32 = ( h16 ":" h16 ) / IPv4address + IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet + dec-octet = DIGIT ; 0-9 + / %x31-39 DIGIT ; 10-99 + / "1" 2DIGIT ; 100-199 + / "2" %x30-34 DIGIT ; 200-249 + / "25" %x30-35 ; 250-255 + + reg-name = *( unreserved / pct-encoded / sub-delims ) + + path = path-abempty ; begins with "/" or is empty + / path-absolute ; begins with "/" but not "//" + / path-noscheme ; begins with a non-colon segment + / path-rootless ; begins with a segment + / path-empty ; zero characters + + path-abempty = *( "/" segment ) + path-absolute = "/" [ segment-nz *( "/" segment ) ] + path-noscheme = segment-nz-nc *( "/" segment ) + path-rootless = segment-nz *( "/" segment ) + path-empty = 0<pchar> + + segment = *pchar + segment-nz = 1*pchar + segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) + ; non-zero-length segment without any colon ":" + + pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + + query = *( pchar / "/" / "?" ) + + fragment = *( pchar / "/" / "?" ) + + pct-encoded = "%" HEXDIG HEXDIG + + unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + reserved = gen-delims / sub-delims + gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" + sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + / "*" / "+" / "," / ";" / "=" + +---------------------------------------------------------------------*/ + +#define NPT_URI_ALWAYS_ENCODE " !\"<>\\^`{|}" + +/*---------------------------------------------------------------------- +| NPT_Uri::PathCharsToEncode ++---------------------------------------------------------------------*/ +const char* const +NPT_Uri::PathCharsToEncode = NPT_URI_ALWAYS_ENCODE "?#[]"; + +/*---------------------------------------------------------------------- +| NPT_Uri::QueryCharsToEncode ++---------------------------------------------------------------------*/ +const char* const +NPT_Uri::QueryCharsToEncode = NPT_URI_ALWAYS_ENCODE "#[]"; + +/*---------------------------------------------------------------------- +| NPT_Uri::FragmentCharsToEncode ++---------------------------------------------------------------------*/ +const char* const +NPT_Uri::FragmentCharsToEncode = NPT_URI_ALWAYS_ENCODE "[]"; + +/*---------------------------------------------------------------------- +| NPT_Uri::UnsafeCharsToEncode ++---------------------------------------------------------------------*/ +const char* const +NPT_Uri::UnsafeCharsToEncode = NPT_URI_ALWAYS_ENCODE; // and ' ? + +/*---------------------------------------------------------------------- +| NPT_Uri::PercentEncode ++---------------------------------------------------------------------*/ +NPT_String +NPT_Uri::PercentEncode(const char* str, const char* chars, bool encode_percents) +{ + NPT_String encoded; + + // check args + if (str == NULL) return encoded; + + // reserve at least the size of the current uri + encoded.Reserve(NPT_StringLength(str)); + + // process each character + char escaped[3]; + escaped[0] = '%'; + while (unsigned char c = *str++) { + bool encode = false; + if (encode_percents && c == '%') { + encode = true; + } else if (c < ' ' || c > '~') { + encode = true; + } else { + const char* match = chars; + while (*match) { + if (c == *match) { + encode = true; + break; + } + ++match; + } + } + if (encode) { + // encode + NPT_ByteToHex(c, &escaped[1]); + encoded.Append(escaped, 3); + } else { + // no encoding required + encoded += c; + } + } + + return encoded; +} + +/*---------------------------------------------------------------------- +| NPT_Uri::PercentDecode ++---------------------------------------------------------------------*/ +NPT_String +NPT_Uri::PercentDecode(const char* str) +{ + NPT_String decoded; + + // check args + if (str == NULL) return decoded; + + // reserve at least the size of the current uri + decoded.Reserve(NPT_StringLength(str)); + + // process each character + while (unsigned char c = *str++) { + if (c == '%') { + // needs to be unescaped + unsigned char unescaped; + if (NPT_SUCCEEDED(NPT_HexToByte(str, unescaped))) { + decoded += unescaped; + str += 2; + } else { + // not a valid escape sequence, just keep the % + decoded += c; + } + } else { + // no unescaping required + decoded += c; + } + } + + return decoded; +} + +/*---------------------------------------------------------------------- +| NPT_UrlQuery::NPT_UrlQuery ++---------------------------------------------------------------------*/ +NPT_UrlQuery::NPT_UrlQuery(const char* query) +{ + Parse(query); +} + +/*---------------------------------------------------------------------- +| NPT_UrlQuery::UrlEncode ++---------------------------------------------------------------------*/ +NPT_String +NPT_UrlQuery::UrlEncode(const char* str, bool encode_percents) +{ + NPT_String encoded = NPT_Uri::PercentEncode( + str, + ";/?:@&=+$," /* reserved as defined in RFC 2396 */ + "\"#<>\\^`{|}", /* other unsafe chars */ + encode_percents); + encoded.Replace(' ','+'); + + return encoded; +} + +/*---------------------------------------------------------------------- +| NPT_UrlQuery::UrlDecode ++---------------------------------------------------------------------*/ +NPT_String +NPT_UrlQuery::UrlDecode(const char* str) +{ + NPT_String decoded = NPT_Uri::PercentDecode(str); + decoded.Replace('+', ' '); + return decoded; +} + +/*---------------------------------------------------------------------- +| NPT_UrlQuery::ToString ++---------------------------------------------------------------------*/ +NPT_String +NPT_UrlQuery::ToString() +{ + NPT_String encoded; + bool separator = false; + for (NPT_List<Field>::Iterator it = m_Fields.GetFirstItem(); + it; + ++it) { + Field& field = *it; + if (separator) encoded += "&"; + separator = true; + encoded += UrlEncode(field.m_Name, false); + encoded += "="; + encoded += UrlEncode(field.m_Value, false); + } + + return encoded; +} + +/*---------------------------------------------------------------------- +| NPT_UrlQuery::Parse ++---------------------------------------------------------------------*/ +NPT_Result +NPT_UrlQuery::Parse(const char* query) +{ + const char* cursor = query; + NPT_String name; + NPT_String value; + bool in_name = true; + do { + if (*cursor == '\0' || *cursor == '&') { + if (!name.IsEmpty() && !value.IsEmpty()) { + AddField(UrlDecode(name), UrlDecode(value)); + } + name.SetLength(0); + value.SetLength(0); + in_name = true; + } else if (*cursor == '=' && in_name) { + in_name = false; + } else { + if (in_name) { + name += *cursor; + } else { + value += *cursor; + } + } + } while (*cursor++); + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_UrlQuery::AddField ++---------------------------------------------------------------------*/ +NPT_Result +NPT_UrlQuery::AddField(const char* name, const char* value) +{ + return m_Fields.Add(Field(name, value)); +} + +/*---------------------------------------------------------------------- +| NPT_UrlQuery::GetField ++---------------------------------------------------------------------*/ +const char* +NPT_UrlQuery::GetField(const char* name) +{ + for (NPT_List<Field>::Iterator it = m_Fields.GetFirstItem(); + it; + ++it) { + Field& field = *it; + if (field.m_Name == name) return field.m_Value; + } + + // field not found + return NULL; +} + +/*---------------------------------------------------------------------- +| types ++---------------------------------------------------------------------*/ +typedef enum { + NPT_URL_PARSER_STATE_START, + NPT_URL_PARSER_STATE_SCHEME, + NPT_URL_PARSER_STATE_LEADING_SLASH, + NPT_URL_PARSER_STATE_HOST, + NPT_URL_PARSER_STATE_PORT, + NPT_URL_PARSER_STATE_PATH, + NPT_URL_PARSER_STATE_QUERY +} NPT_UrlParserState; + +/*---------------------------------------------------------------------- +| NPT_Url::NPT_Url ++---------------------------------------------------------------------*/ +NPT_Url::NPT_Url() : + m_Port(NPT_URL_INVALID_PORT), + m_Path("/"), + m_HasQuery(false), + m_HasFragment(false) +{ +} + +/*---------------------------------------------------------------------- +| NPT_Url::NPT_Url ++---------------------------------------------------------------------*/ +NPT_Url::NPT_Url(const char* url, SchemeId expected_scheme, NPT_UInt16 default_port) : + m_Port(NPT_URL_INVALID_PORT), + m_HasQuery(false), + m_HasFragment(false) +{ + // check parameters + if (url == NULL) return; + + // set the uri scheme + if (NPT_FAILED(SetSchemeFromUri(url))) { + return; + } + if (expected_scheme != SCHEME_ID_UNKNOWN) { + // check that we got the expected scheme + if (m_SchemeId != expected_scheme) return; + } + url += m_Scheme.GetLength()+1; + + // intialize the parser + NPT_UrlParserState state = NPT_URL_PARSER_STATE_START; + const char* mark = url; + + // parse the URL + char c; + do { + c = *url++; + switch (state) { + case NPT_URL_PARSER_STATE_START: + if (c == '/') { + state = NPT_URL_PARSER_STATE_LEADING_SLASH; + } else { + return; + } + break; + + case NPT_URL_PARSER_STATE_LEADING_SLASH: + if (c == '/') { + state = NPT_URL_PARSER_STATE_HOST; + mark = url; + } else { + return; + } + break; + + case NPT_URL_PARSER_STATE_HOST: + if (c == ':' || c == '/' || c == '\0') { + m_Host.Assign(mark, (NPT_Size)(url-1-mark)); + if (c == ':') { + mark = url; + state = NPT_URL_PARSER_STATE_PORT; + } else { + mark = url-1; + m_Port = default_port; + state = NPT_URL_PARSER_STATE_PATH; + } + } + break; + + case NPT_URL_PARSER_STATE_PORT: + if (c >= '0' && c <= '9') { + unsigned int val = m_Port*10+(c-'0'); + if (val > 65535) { + m_Port = NPT_URL_INVALID_PORT; + return; + } + m_Port = val; + } else if (c == '/' || c == '\0') { + mark = url-1; + state = NPT_URL_PARSER_STATE_PATH; + } else { + // invalid character + m_Port = NPT_URL_INVALID_PORT; + return; + } + break; + + case NPT_URL_PARSER_STATE_PATH: + if (*mark) { + SetPathPlus(mark); + return; + } + break; + + default: + break; + } + } while (c); + + // if we get here, the path is implicit + m_Path = "/"; +} + +/*---------------------------------------------------------------------- +| NPT_Url::NPT_Url ++---------------------------------------------------------------------*/ +NPT_Url::NPT_Url(const char* scheme, + const char* host, + NPT_UInt16 port, + const char* path, + const char* query, + const char* fragment) : + m_Host(host), + m_Port(port), + m_Path(path), + m_HasQuery(query != NULL), + m_Query(query), + m_HasFragment(fragment != NULL), + m_Fragment(fragment) +{ + SetScheme(scheme); +} + +/*---------------------------------------------------------------------- +| NPT_Url::IsValid ++---------------------------------------------------------------------*/ +bool +NPT_Url::IsValid() const +{ + return m_Port != NPT_URL_INVALID_PORT && !m_Host.IsEmpty(); +} + +/*---------------------------------------------------------------------- +| NPT_Url::SetHost ++---------------------------------------------------------------------*/ +NPT_Result +NPT_Url::SetHost(const char* host) +{ + const char* port = host; + while (*port && *port != ':') port++; + if (*port) { + m_Host.Assign(host, (NPT_Size)(port-host)); + NPT_UInt32 port_number; + if (NPT_SUCCEEDED(NPT_ParseInteger32U(port+1, port_number, false))) { + m_Port = (short)port_number; + } + } else { + m_Host = host; + } + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_Url::SetHost ++---------------------------------------------------------------------*/ +NPT_Result +NPT_Url::SetPort(NPT_UInt16 port) +{ + m_Port = port; + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_Url::SetPath ++---------------------------------------------------------------------*/ +NPT_Result +NPT_Url::SetPath(const char* path) +{ + m_Path = path; + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_Url::SetPathPlus ++---------------------------------------------------------------------*/ +NPT_Result +NPT_Url::SetPathPlus(const char* path_plus) +{ + // check parameters + if (path_plus == NULL) return NPT_ERROR_INVALID_PARAMETERS; + + // reset any existing values + m_Path.SetLength(0); + m_Query.SetLength(0); + m_Fragment.SetLength(0); + m_HasQuery = false; + m_HasFragment = false; + + // intialize the parser + NPT_UrlParserState state = NPT_URL_PARSER_STATE_PATH; + const char* mark = path_plus; + + // parse the path+ + char c; + do { + c = *path_plus++; + switch (state) { + case NPT_URL_PARSER_STATE_PATH: + if (c == '\0' || c == '?' || c == '#') { + if (path_plus-1 > mark) { + m_Path.Append(mark, (NPT_Size)(path_plus-1-mark)); + m_Path = PercentDecode(m_Path); + } + if (c == '?') { + m_HasQuery = true; + state = NPT_URL_PARSER_STATE_QUERY; + mark = path_plus; + } else if (c == '#') { + m_HasFragment = true; + m_Fragment = path_plus; + m_Fragment = PercentDecode(m_Fragment); + return NPT_SUCCESS; + } + } + break; + + case NPT_URL_PARSER_STATE_QUERY: + if (c == '\0' || c == '#') { + m_Query.Assign(mark, (NPT_Size)(path_plus-1-mark)); + // do not decode query so it can be parsed properly by NPT_UrlQuery + if (c == '#') { + m_HasFragment = true; + m_Fragment = path_plus; + m_Fragment = PercentDecode(m_Fragment); + } + return NPT_SUCCESS; + } + break; + + default: + break; + } + } while (c); + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_Url::SetQuery ++---------------------------------------------------------------------*/ +NPT_Result +NPT_Url::SetQuery(const char* query) +{ + m_Query = query; + m_HasQuery = query!=NULL && NPT_StringLength(query)>0; + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_Url::SetFragment ++---------------------------------------------------------------------*/ +NPT_Result +NPT_Url::SetFragment(const char* fragment) +{ + m_Fragment = fragment; + m_HasFragment = fragment!=NULL; + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_Url::ToRequestString ++---------------------------------------------------------------------*/ +NPT_String +NPT_Url::ToRequestString(bool with_fragment) const +{ + NPT_String result; + NPT_Size length = m_Path.GetLength()+1; + if (m_HasQuery) length += 1+m_Query.GetLength(); + if (with_fragment) length += 1+m_Fragment.GetLength(); + result.Reserve(length); + + if (m_Path.IsEmpty()) { + result += "/"; + } else { + result += PercentEncode(m_Path, PathCharsToEncode, false); + } + if (m_HasQuery) { + result += "?"; + result += PercentEncode(m_Query, QueryCharsToEncode, false); + } + if (with_fragment && m_HasFragment) { + result += "#"; + result += PercentEncode(m_Fragment, FragmentCharsToEncode, false); + } + return result; +} + +/*---------------------------------------------------------------------- +| NPT_Url::ToStringWithDefaultPort ++---------------------------------------------------------------------*/ +NPT_String +NPT_Url::ToStringWithDefaultPort(NPT_UInt16 default_port, bool with_fragment) const +{ + NPT_String result; + NPT_String request = ToRequestString(with_fragment); + NPT_Size length = m_Scheme.GetLength()+3+m_Host.GetLength()+6+request.GetLength(); + + result.Reserve(length); + result += m_Scheme; + result += "://"; + result += m_Host; + if (m_Port != default_port) { + NPT_String port = NPT_String::FromInteger(m_Port); + result += ":"; + result += port; + } + result += request; + return result; +} + +/*---------------------------------------------------------------------- +| NPT_Url::ToString ++---------------------------------------------------------------------*/ +NPT_String +NPT_Url::ToString(bool with_fragment) const +{ + return ToStringWithDefaultPort(0, with_fragment); +}