public static int TryParseQuery(string text, int length, int current, out nextStep next, out KeyValuePair <string, string>[] @params) { next = nextStep.Error; @params = null; var last = current; var paramsList = new List <KeyValuePair <string, string> >(16); string paramsKey = null; var decode = false; var parsingKey = true; char c; for (; ; ++current) { if (current < length) { c = text[current]; switch (c) { case '%': case '+': decode = true; break; } } else { // use '\uFFFF' as end-of-string marker c = END_OF_STRING; } if ( ((c >= 'a') && (c <= '~')) || // one of: abcdefghijklmnopqrstuvwxyz{|}~ ((c >= '?') && (c <= '_')) || // one of: ?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ ((c >= '\'') && (c <= ';')) || // one of: '()*+,-./0123456789:; (c == '$') || (c == '%') || (c == '!') || char.IsLetterOrDigit(c) ) { // valid character, keep parsing } else if ((c == '&') || (c == '#') || (c == END_OF_STRING)) { if (parsingKey) { if (current != last) { // add non-empty key with empty value paramsKey = text.Substring(last, current - last); if (decode) { paramsKey = Decode(paramsKey); decode = false; } paramsList.Add(new KeyValuePair <string, string>(paramsKey, null)); } else if (c == '&') { // this occurs in the degenerate case of two consecutive ampersands (e.g. "&&") paramsList.Add(new KeyValuePair <string, string>("", null)); } } else { // add key with value var paramsValue = text.Substring(last, current - last); if (decode) { paramsValue = Decode(paramsValue); decode = false; } paramsList.Add(new KeyValuePair <string, string>(paramsKey, paramsValue)); parsingKey = true; } // check if we found a query parameter separator if (c == '&') { last = current + 1; continue; } // we're done parsing the query string break; } else if (c == '=') { if (parsingKey) { paramsKey = text.Substring(last, current - last); if (decode) { paramsKey = Decode(paramsKey); decode = false; } last = current + 1; parsingKey = false; } } else { return(-1); } } // initialize return values next = (nextStep)c; @params = paramsList.ToArray(); return(current + 1); }
public static int TryParsePath(string text, int length, int current, out nextStep next, ref bool trailingSlash, out string[] segments) { next = nextStep.Error; segments = null; var last = current; var hasLeadingBackslashes = false; var segmentList = new List <string>(16); var leading = true; char c; for (; ; ++current) { c = (current < length) ? text[current] : END_OF_STRING; if ((c == '/') || (c == '\\')) { if (leading) { hasLeadingBackslashes = hasLeadingBackslashes || (c == '\\'); } else { var segment = text.Substring(last, current - last); if (hasLeadingBackslashes) { segment = segment.Replace('\\', '/'); hasLeadingBackslashes = false; } segmentList.Add(segment); last = current + 1; leading = true; } } else if ( ((c >= 'a') && (c <= '~')) || // one of: abcdefghijklmnopqrstuvwxyz{|}~ ((c >= '@') && (c <= '_')) || // one of: @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ ((c >= '$') && (c <= ';')) || // one of: $%&'()*+,-./0123456789:; (c == '=') || (c == '!') || char.IsLetterOrDigit(c) ) { // no longer accept leading '/' or '\' characters leading = false; } else if ((c == '?') || (c == '#') || (c == END_OF_STRING)) { if (last == current) { trailingSlash = true; } else { var segment = text.Substring(last, current - last); if (hasLeadingBackslashes) { segment = segment.Replace('\\', '/'); } segmentList.Add(segment); } // we're done parsing the path string break; } else { return(-1); } } // initialize return values segments = segmentList.ToArray(); next = (nextStep)c; return(current + 1); }
public static int TryParseAuthority(string text, int length, int current, out nextStep next, out string user, out string password, out string hostname, out int port) { var last = current; next = nextStep.Error; user = null; password = null; hostname = null; port = -1; // check first character; it could tell us if we're parsing an IPv6 address var decode = false; char c; if (current < length) { c = text[current]; switch (c) { case '%': case '+': decode = true; break; case '[': goto ipv6; } } else { // use '\uFFFF' as end-of-string marker c = END_OF_STRING; } // parse hostname -OR- user-info string hostnameOrUsername; for (;;) { if ( ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || ((c >= '0') && (c <= '9')) || ((c >= '$') && (c <= '.')) || // one of: $%&'()*+,-. (c == '!') || (c == ';') || (c == '=') || (c == '_') || (c == '~') || char.IsLetterOrDigit(c) ) { // valid character, keep parsing } else if (c == ':') { // part before ':' is either a username or hostname hostnameOrUsername = text.Substring(last, current - last); last = current + 1; goto hostnameOrUserInfoAfterColon; } else if (c == '@') { // part before '@' must be username since we didn't find ':' user = text.Substring(last, current - last); if (decode) { user = Decode(user); decode = false; } last = current + 1; goto hostnameOrIPv6Address; } else if ((c == '/') || (c == '\\') || (c == '?') || (c == '#') || (c == END_OF_STRING)) { // part before '/', '\', '?', '#' must be hostname if (decode) { // hostname cannot contain encoded characters return(-1); } hostname = text.Substring(last, current - last); next = (nextStep)c; return(current + 1); } else { return(-1); } // continue on by reading the next character ++current; if (current < length) { c = text[current]; switch (c) { case '%': case '+': decode = true; break; } } else { // use '\uFFFF' as end-of-string marker c = END_OF_STRING; } } throw new ShouldNeverHappenException("hostnameOrUsername"); // parse hostname -OR- user-info AFTER we're parsed a colon (':') hostnameOrUserInfoAfterColon: for (;;) { ++current; if (current < length) { c = text[current]; switch (c) { case '%': case '+': decode = true; break; } } else { // use '\uFFFF' as end-of-string marker c = END_OF_STRING; } if ( ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || ((c >= '0') && (c <= '9')) || ((c >= '$') && (c <= '.')) || // one of: $%&'()*+,-. (c == '!') || (c == ';') || (c == '=') || (c == '_') || (c == '~') || char.IsLetterOrDigit(c) ) { // valid character, keep parsing } else if (c == '@') { // part before ':' was username user = hostnameOrUsername; if (decode) { user = Decode(user); } // part after ':' is password password = text.Substring(last, current - last); if (decode) { password = Decode(password); } last = current + 1; decode = false; goto hostnameOrIPv6Address; } else if ((c == '/') || (c == '\\') || (c == '?') || (c == '#') || (c == END_OF_STRING)) { // part before ':' was hostname if (decode) { // hostname cannot contain encoded characters return(-1); } hostname = hostnameOrUsername; // part after ':' is port, parse and validate it if (!int.TryParse(text.Substring(last, current - last), out port) || (port < 0) || (port > ushort.MaxValue)) { return(-1); } next = (nextStep)c; return(current + 1); } else { return(-1); } } throw new ShouldNeverHappenException("hostnameOrUserInfoAfterColon"); hostnameOrIPv6Address: ++current; if (current < length) { c = text[current]; switch (c) { case '%': case '+': decode = true; break; case '[': // NOTE (steveb): we want to include the leading character in the final result last = current; // IPv6 addresses start with '[' goto ipv6; } } else { // use '\uFFFF' as end-of-string marker c = END_OF_STRING; } for (;;) { if ( ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || ((c >= '0') && (c <= '9')) || ((c >= '$') && (c <= '.')) || // one of: $%&'()*+,-. (c == '!') || (c == ';') || (c == '=') || (c == '_') || (c == '~') || char.IsLetterOrDigit(c) ) { // valid character, keep parsing } else if (c == ':') { if (decode) { // hostname cannot contain encoded characters return(-1); } hostname = text.Substring(last, current - last); last = current + 1; goto portNumber; } else if ((c == '/') || (c == '\\') || (c == '?') || (c == '#') || (c == END_OF_STRING)) { if (decode) { // hostname cannot contain encoded characters return(-1); } hostname = text.Substring(last, current - last); next = (nextStep)c; return(current + 1); } else { return(-1); } // continue on by reading the next character ++current; if (current < length) { c = text[current]; switch (c) { case '%': case '+': decode = true; break; } } else { // use '\uFFFF' as end-of-string marker c = END_OF_STRING; } } throw new ShouldNeverHappenException("hostname"); portNumber: for (;;) { ++current; c = (current < length) ? text[current] : END_OF_STRING; if ((c >= '0') && (c <= '9')) { // valid character, keep parsing } else if ((c == '/') || (c == '\\') || (c == '?') || (c == '#') || (c == END_OF_STRING)) { if (!int.TryParse(text.Substring(last, current - last), out port) || (port < 0) || (port > ushort.MaxValue)) { return(-1); } next = (nextStep)c; return(current + 1); } else { return(-1); } } throw new ShouldNeverHappenException("portNumber"); ipv6: for (;;) { ++current; c = (current < length) ? text[current] : END_OF_STRING; if (((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'F')) || ((c >= '0') && (c <= '9')) || (c == ':') || (c == '.')) { // valid character, keep parsing } else if (c == ']') { hostname = text.Substring(last, current - last + 1); // check next character to determine correct state to transition to ++current; c = (current < length) ? text[current] : END_OF_STRING; if (c == ':') { last = current + 1; goto portNumber; } else if ((c == '/') || (c == '\\') || (c == '?') || (c == '#') || (c == END_OF_STRING)) { next = (nextStep)c; return(current + 1); } else { return(-1); } } else { return(-1); } } throw new ShouldNeverHappenException("ipv6"); }
public static int TryParseQuery(string text, int length, int current, out nextStep next, out KeyValuePair<string, string>[] @params) { next = nextStep.Error; @params = null; var last = current; var paramsList = new List<KeyValuePair<string, string>>(16); string paramsKey = null; var decode = false; var parsingKey = true; char c; for(; ; ++current) { if(current < length) { c = text[current]; switch(c) { case '%': case '+': decode = true; break; } } else { // use '\uFFFF' as end-of-string marker c = END_OF_STRING; } if( ((c >= 'a') && (c <= '~')) || // one of: abcdefghijklmnopqrstuvwxyz{|}~ ((c >= '?') && (c <= '_')) || // one of: ?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ ((c >= '\'') && (c <= ';')) || // one of: '()*+,-./0123456789:; (c == '$') || (c == '%') || (c == '!') || char.IsLetterOrDigit(c) ) { // valid character, keep parsing } else if((c == '&') || (c == '#') || (c == END_OF_STRING)) { if(parsingKey) { if(current != last) { // add non-empty key with empty value paramsKey = text.Substring(last, current - last); if(decode) { paramsKey = Decode(paramsKey); decode = false; } paramsList.Add(new KeyValuePair<string, string>(paramsKey, null)); } else if(c == '&') { // this occurs in the degenerate case of two consecutive ampersands (e.g. "&&") paramsList.Add(new KeyValuePair<string, string>("", null)); } } else { // add key with value var paramsValue = text.Substring(last, current - last); if(decode) { paramsValue = Decode(paramsValue); decode = false; } paramsList.Add(new KeyValuePair<string, string>(paramsKey, paramsValue)); parsingKey = true; } // check if we found a query parameter separator if(c == '&') { last = current + 1; continue; } // we're done parsing the query string break; } else if(c == '=') { if(parsingKey) { paramsKey = text.Substring(last, current - last); if(decode) { paramsKey = Decode(paramsKey); decode = false; } last = current + 1; parsingKey = false; } } else { return -1; } } // initialize return values next = (nextStep)c; @params = paramsList.ToArray(); return current + 1; }
public static int TryParsePath(string text, int length, int current, out nextStep next, ref bool trailingSlash, out string[] segments) { next = nextStep.Error; segments = null; var last = current; var hasLeadingBackslashes = false; var segmentList = new List<string>(16); var leading = true; char c; for(; ; ++current) { c = (current < length) ? text[current] : END_OF_STRING; if((c == '/') || (c == '\\')) { if(leading) { hasLeadingBackslashes = hasLeadingBackslashes || (c == '\\'); } else { var segment = text.Substring(last, current - last); if(hasLeadingBackslashes) { segment = segment.Replace('\\', '/'); hasLeadingBackslashes = false; } segmentList.Add(segment); last = current + 1; leading = true; } } else if( ((c >= 'a') && (c <= '~')) || // one of: abcdefghijklmnopqrstuvwxyz{|}~ ((c >= '@') && (c <= '_')) || // one of: @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ ((c >= '$') && (c <= ';')) || // one of: $%&'()*+,-./0123456789:; (c == '=') || (c == '!') || char.IsLetterOrDigit(c) ) { // no longer accept leading '/' or '\' characters leading = false; } else if((c == '?') || (c == '#') || (c == END_OF_STRING)) { if(last == current) { trailingSlash = true; } else { var segment = text.Substring(last, current - last); if(hasLeadingBackslashes) { segment = segment.Replace('\\', '/'); } segmentList.Add(segment); } // we're done parsing the path string break; } else { return -1; } } // initialize return values segments = segmentList.ToArray(); next = (nextStep)c; return current + 1; }
public static int TryParseAuthority(string text, int length, int current, out nextStep next, out string user, out string password, out string hostname, out int port) { var last = current; next = nextStep.Error; user = null; password = null; hostname = null; port = -1; // check first character; it could tell us if we're parsing an IPv6 address var decode = false; char c; if(current < length) { c = text[current]; switch(c) { case '%': case '+': decode = true; break; case '[': goto ipv6; } } else { // use '\uFFFF' as end-of-string marker c = END_OF_STRING; } // parse hostname -OR- user-info string hostnameOrUsername; for(;;) { if( ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || ((c >= '0') && (c <= '9')) || ((c >= '$') && (c <= '.')) || // one of: $%&'()*+,-. (c == '!') || (c == ';') || (c == '=') || (c == '_') || (c == '~') || char.IsLetterOrDigit(c) ) { // valid character, keep parsing } else if(c == ':') { // part before ':' is either a username or hostname hostnameOrUsername = text.Substring(last, current - last); last = current + 1; goto hostnameOrUserInfoAfterColon; } else if(c == '@') { // part before '@' must be username since we didn't find ':' user = text.Substring(last, current - last); if(decode) { user = Decode(user); decode = false; } last = current + 1; goto hostnameOrIPv6Address; } else if((c == '/') || (c == '\\') || (c == '?') || (c == '#') || (c == END_OF_STRING)) { // part before '/', '\', '?', '#' must be hostname if(decode) { // hostname cannot contain encoded characters return -1; } hostname = text.Substring(last, current - last); next = (nextStep)c; return current + 1; } else { return -1; } // continue on by reading the next character ++current; if(current < length) { c = text[current]; switch(c) { case '%': case '+': decode = true; break; } } else { // use '\uFFFF' as end-of-string marker c = END_OF_STRING; } } throw new ShouldNeverHappenException("hostnameOrUsername"); // parse hostname -OR- user-info AFTER we're parsed a colon (':') hostnameOrUserInfoAfterColon: for(;;) { ++current; if(current < length) { c = text[current]; switch(c) { case '%': case '+': decode = true; break; } } else { // use '\uFFFF' as end-of-string marker c = END_OF_STRING; } if( ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || ((c >= '0') && (c <= '9')) || ((c >= '$') && (c <= '.')) || // one of: $%&'()*+,-. (c == '!') || (c == ';') || (c == '=') || (c == '_') || (c == '~') || char.IsLetterOrDigit(c) ) { // valid character, keep parsing } else if(c == '@') { // part before ':' was username user = hostnameOrUsername; if(decode) { user = Decode(user); } // part after ':' is password password = text.Substring(last, current - last); if(decode) { password = Decode(password); } last = current + 1; decode = false; goto hostnameOrIPv6Address; } else if((c == '/') || (c == '\\') || (c == '?') || (c == '#') || (c == END_OF_STRING)) { // part before ':' was hostname if(decode) { // hostname cannot contain encoded characters return -1; } hostname = hostnameOrUsername; // part after ':' is port, parse and validate it if(!int.TryParse(text.Substring(last, current - last), out port) || (port < 0) || (port > ushort.MaxValue)) { return -1; } next = (nextStep)c; return current + 1; } else { return -1; } } throw new ShouldNeverHappenException("hostnameOrUserInfoAfterColon"); hostnameOrIPv6Address: ++current; if(current < length) { c = text[current]; switch(c) { case '%': case '+': decode = true; break; case '[': // NOTE (steveb): we want to include the leading character in the final result last = current; // IPv6 addresses start with '[' goto ipv6; } } else { // use '\uFFFF' as end-of-string marker c = END_OF_STRING; } for(;;) { if( ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || ((c >= '0') && (c <= '9')) || ((c >= '$') && (c <= '.')) || // one of: $%&'()*+,-. (c == '!') || (c == ';') || (c == '=') || (c == '_') || (c == '~') || char.IsLetterOrDigit(c) ) { // valid character, keep parsing } else if(c == ':') { if(decode) { // hostname cannot contain encoded characters return -1; } hostname = text.Substring(last, current - last); last = current + 1; goto portNumber; } else if((c == '/') || (c == '\\') || (c == '?') || (c == '#') || (c == END_OF_STRING)) { if(decode) { // hostname cannot contain encoded characters return -1; } hostname = text.Substring(last, current - last); next = (nextStep)c; return current + 1; } else { return -1; } // continue on by reading the next character ++current; if(current < length) { c = text[current]; switch(c) { case '%': case '+': decode = true; break; } } else { // use '\uFFFF' as end-of-string marker c = END_OF_STRING; } } throw new ShouldNeverHappenException("hostname"); portNumber: for(;;) { ++current; c = (current < length) ? text[current] : END_OF_STRING; if((c >= '0') && (c <= '9')) { // valid character, keep parsing } else if((c == '/') || (c == '\\') || (c == '?') || (c == '#') || (c == END_OF_STRING)) { if(!int.TryParse(text.Substring(last, current - last), out port) || (port < 0) || (port > ushort.MaxValue)) { return -1; } next = (nextStep)c; return current + 1; } else { return -1; } } throw new ShouldNeverHappenException("portNumber"); ipv6: for(;;) { ++current; c = (current < length) ? text[current] : END_OF_STRING; if(((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'F')) || ((c >= '0') && (c <= '9')) || (c == ':') || (c == '.')) { // valid character, keep parsing } else if(c == ']') { hostname = text.Substring(last, current - last + 1); // check next character to determine correct state to transition to ++current; c = (current < length) ? text[current] : END_OF_STRING; if(c == ':') { last = current + 1; goto portNumber; } else if((c == '/') || (c == '\\') || (c == '?') || (c == '#') || (c == END_OF_STRING)) { next = (nextStep)c; return current + 1; } else { return -1; } } else { return -1; } } throw new ShouldNeverHappenException("ipv6"); }