using System.Text;
using System.Web.Util;
+#if NET_2_0
+using System.Collections.Generic;
+#endif
+
namespace System.Web {
// CAS - no InheritanceDemand here as the class is sealed
[AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
public sealed class HttpUtility {
-
#region Fields
static Hashtable entities;
if (null == s)
return null;
- if (s.IndexOf ('&') == -1 && s.IndexOf ('"') == -1)
+ bool needEncode = false;
+ for (int i = 0; i < s.Length; i++) {
+ if (s [i] == '&' || s [i] == '"' || s [i] == '<') {
+ needEncode = true;
+ break;
+ }
+ }
+
+ if (!needEncode)
return s;
StringBuilder output = new StringBuilder ();
- foreach (char c in s)
- switch (c) {
+ int len = s.Length;
+ for (int i = 0; i < len; i++)
+ switch (s [i]) {
case '&' :
output.Append ("&");
break;
case '"' :
output.Append (""");
break;
+ case '<':
+ output.Append ("<");
+ break;
default:
- output.Append (c);
+ output.Append (s [i]);
break;
}
return UrlDecode(str, Encoding.UTF8);
}
- private static char [] GetChars (MemoryStream b, Encoding e)
+ static char [] GetChars (MemoryStream b, Encoding e)
{
return e.GetChars (b.GetBuffer (), 0, (int) b.Length);
}
+ static void WriteCharBytes (IList buf, char ch, Encoding e)
+ {
+ if (ch > 255) {
+ foreach (byte b in e.GetBytes (new char[] { ch }))
+ buf.Add (b);
+ } else
+ buf.Add ((byte)ch);
+ }
+
public static string UrlDecode (string s, Encoding e)
{
if (null == s)
if (s.IndexOf ('%') == -1 && s.IndexOf ('+') == -1)
return s;
-
+
if (e == null)
e = Encoding.UTF8;
-
- StringBuilder output = new StringBuilder ();
+
long len = s.Length;
- MemoryStream bytes = new MemoryStream ();
+#if NET_2_0
+ var bytes = new List <byte> ();
+#else
+ ArrayList bytes = new ArrayList ();
+#endif
int xchar;
-
+ char ch;
+
for (int i = 0; i < len; i++) {
- if (s [i] == '%' && i + 2 < len && s [i + 1] != '%') {
+ ch = s [i];
+ if (ch == '%' && i + 2 < len && s [i + 1] != '%') {
if (s [i + 1] == 'u' && i + 5 < len) {
- if (bytes.Length > 0) {
- output.Append (GetChars (bytes, e));
- bytes.SetLength (0);
- }
-
+ // unicode hex sequence
xchar = GetChar (s, i + 2, 4);
if (xchar != -1) {
- output.Append ((char) xchar);
+ WriteCharBytes (bytes, (char)xchar, e);
i += 5;
- } else {
- output.Append ('%');
- }
+ } else
+ WriteCharBytes (bytes, '%', e);
} else if ((xchar = GetChar (s, i + 1, 2)) != -1) {
- bytes.WriteByte ((byte) xchar);
+ WriteCharBytes (bytes, (char)xchar, e);
i += 2;
} else {
- output.Append ('%');
+ WriteCharBytes (bytes, '%', e);
}
continue;
}
- if (bytes.Length > 0) {
- output.Append (GetChars (bytes, e));
- bytes.SetLength (0);
- }
-
- if (s [i] == '+') {
- output.Append (' ');
- } else {
- output.Append (s [i]);
- }
- }
-
- if (bytes.Length > 0) {
- output.Append (GetChars (bytes, e));
+ if (ch == '+')
+ WriteCharBytes (bytes, ' ', e);
+ else
+ WriteCharBytes (bytes, ch, e);
}
-
+
+#if NET_2_0
+ byte[] buf = bytes.ToArray ();
+#else
+ byte[] buf = (byte[])bytes.ToArray (typeof (byte));
+#endif
bytes = null;
- return output.ToString ();
+ return e.GetString (buf);
+
}
public static string UrlDecode (byte [] bytes, Encoding e)
return UrlDecode (bytes, 0, bytes.Length, e);
}
- private static int GetInt (byte b)
+ static int GetInt (byte b)
{
char c = (char) b;
if (c >= '0' && c <= '9')
return -1;
}
- private static int GetChar (byte [] bytes, int offset, int length)
+ static int GetChar (byte [] bytes, int offset, int length)
{
int value = 0;
int end = length + offset;
return value;
}
- private static int GetChar (string str, int offset, int length)
+ static int GetChar (string str, int offset, int length)
{
int val = 0;
int end = length + offset;
if (s == "")
return "";
- byte [] bytes = Enc.GetBytes (s);
- return Encoding.ASCII.GetString (UrlEncodeToBytes (bytes, 0, bytes.Length));
+ bool needEncode = false;
+ int len = s.Length;
+ for (int i = 0; i < len; i++) {
+ char c = s [i];
+ if ((c < '0') || (c < 'A' && c > '9') || (c > 'Z' && c < 'a') || (c > 'z')) {
+ if (NotEncoded (c))
+ continue;
+
+ needEncode = true;
+ break;
+ }
+ }
+
+ if (!needEncode)
+ return s;
+
+ // avoided GetByteCount call
+ byte [] bytes = new byte[Enc.GetMaxByteCount(s.Length)];
+ int realLen = Enc.GetBytes (s, 0, s.Length, bytes, 0);
+ return Encoding.ASCII.GetString (UrlEncodeToBytes (bytes, 0, realLen));
}
public static string UrlEncode (byte [] bytes)
}
static char [] hexChars = "0123456789abcdef".ToCharArray ();
- const string notEncoded = "!'()*-._";
+
+ static bool NotEncoded (char c)
+ {
+ return (c == '!' || c == '\'' || c == '(' || c == ')' || c == '*' || c == '-' || c == '.' || c == '_');
+ }
static void UrlEncodeChar (char c, Stream result, bool isUnicode) {
if (c > 255) {
return;
}
- if (c>' ' && notEncoded.IndexOf (c)!=-1) {
+ if (c > ' ' && NotEncoded (c)) {
result.WriteByte ((byte)c);
return;
}
if (s == null)
return null;
+ bool needEncode = false;
+ for (int i = 0; i < s.Length; i++) {
+ char c = s [i];
+ if (c == '&' || c == '"' || c == '<' || c == '>' || c > 159) {
+ needEncode = true;
+ break;
+ }
+ }
+
+ if (!needEncode)
+ return s;
+
StringBuilder output = new StringBuilder ();
- foreach (char c in s)
- switch (c) {
+ int len = s.Length;
+ for (int i = 0; i < len; i++)
+ switch (s [i]) {
case '&' :
output.Append ("&");
break;
// MS starts encoding with &# from 160 and stops at 255.
// We don't do that. One reason is the 65308/65310 unicode
// characters that look like '<' and '>'.
- if (c > 159) {
+#if TARGET_JVM
+ if (s [i] > 159 && s [i] < 256) {
+#else
+ if (s [i] > 159) {
+#endif
output.Append ("&#");
- output.Append (((int) c).ToString (CultureInfo.InvariantCulture));
+ output.Append (((int) s [i]).ToString (CultureInfo.InvariantCulture));
output.Append (";");
} else {
- output.Append (c);
+ output.Append (s [i]);
}
break;
}
}
static void UrlPathEncodeChar (char c, Stream result) {
+#if NET_2_0
+ if (c < 33 || c > 126) {
+#else
if (c > 127) {
+#endif
byte [] bIn = Encoding.UTF8.GetBytes (c.ToString ());
for (int i = 0; i < bIn.Length; i++) {
result.WriteByte ((byte) '%');
#endif
#if NET_2_0
+ class HttpQSCollection : NameValueCollection {
+ public override string ToString ()
+ {
+ int count = Count;
+ if (count == 0)
+ return "";
+ StringBuilder sb = new StringBuilder ();
+ string [] keys = AllKeys;
+ for (int i = 0; i < count; i++) {
+ sb.AppendFormat ("{0}={1}&", keys [i], this [keys [i]]);
+ }
+ if (sb.Length > 0)
+ sb.Length--;
+ return sb.ToString ();
+ }
+ }
+
public static NameValueCollection ParseQueryString (string query)
{
return ParseQueryString (query, Encoding.UTF8);
if (query[0] == '?')
query = query.Substring (1);
- NameValueCollection result = new NameValueCollection ();
+ NameValueCollection result = new HttpQSCollection ();
ParseQueryString (query, encoding, result);
return result;
}
if (query.Length == 0)
return;
+ string decoded = HtmlDecode (query);
+ int decodedLength = decoded.Length;
int namePos = 0;
- while (namePos <= query.Length) {
+ bool first = true;
+ while (namePos <= decodedLength) {
int valuePos = -1, valueEnd = -1;
- for (int q = namePos; q < query.Length; q++) {
- if (valuePos == -1 && query[q] == '=') {
+ for (int q = namePos; q < decodedLength; q++) {
+ if (valuePos == -1 && decoded [q] == '=') {
valuePos = q + 1;
- } else if (query[q] == '&') {
+ } else if (decoded [q] == '&') {
valueEnd = q;
break;
}
}
+ if (first) {
+ first = false;
+ if (decoded [namePos] == '?')
+ namePos++;
+ }
+
string name, value;
if (valuePos == -1) {
name = null;
valuePos = namePos;
} else {
- name = UrlDecode (query.Substring (namePos, valuePos - namePos - 1), encoding);
+ name = UrlDecode (decoded.Substring (namePos, valuePos - namePos - 1), encoding);
}
if (valueEnd < 0) {
namePos = -1;
- valueEnd = query.Length;
+ valueEnd = decoded.Length;
} else {
namePos = valueEnd + 1;
}
- value = UrlDecode (query.Substring (valuePos, valueEnd - valuePos), encoding);
+ value = UrlDecode (decoded.Substring (valuePos, valueEnd - valuePos), encoding);
result.Add (name, value);
- if (namePos == -1) break;
+ if (namePos == -1)
+ break;
}
}
#endregion // Methods