Refactored HttpUtility.cs

This commit is contained in:
sta 2014-08-17 22:45:43 +09:00
parent 3854f218b4
commit ca7916a8d8

View File

@ -52,52 +52,37 @@ namespace WebSocketSharp.Net
{ {
internal sealed class HttpUtility internal sealed class HttpUtility
{ {
#region Private Static Fields #region Private Fields
private static Dictionary<string, char> _entities; private static Dictionary<string, char> _entities;
private static char [] _hexChars = "0123456789abcdef".ToCharArray (); private static char[] _hexChars = "0123456789abcdef".ToCharArray ();
private static object _sync = new object (); private static object _sync = new object ();
#endregion #endregion
#region Private Static Properties
private static Dictionary<string, char> Entities {
get {
lock (_sync) {
if (_entities == null)
initEntities ();
return _entities;
}
}
}
#endregion
#region Private Methods #region Private Methods
private static int getChar (byte [] bytes, int offset, int length) private static int getChar (byte[] bytes, int offset, int length)
{ {
var value = 0; var val = 0;
var end = length + offset; var end = length + offset;
for (int i = offset; i < end; i++) { for (var i = offset; i < end; i++) {
var current = getInt (bytes [i]); var current = getInt (bytes[i]);
if (current == -1) if (current == -1)
return -1; return -1;
value = (value << 4) + current; val = (val << 4) + current;
} }
return value; return val;
} }
private static int getChar (string s, int offset, int length) private static int getChar (string s, int offset, int length)
{ {
var value = 0; var val = 0;
var end = length + offset; var end = length + offset;
for (int i = offset; i < end; i++) { for (var i = offset; i < end; i++) {
var c = s [i]; var c = s[i];
if (c > 127) if (c > 127)
return -1; return -1;
@ -105,17 +90,27 @@ namespace WebSocketSharp.Net
if (current == -1) if (current == -1)
return -1; return -1;
value = (value << 4) + current; val = (val << 4) + current;
} }
return value; return val;
} }
private static char [] getChars (MemoryStream buffer, Encoding encoding) private static char[] getChars (MemoryStream buffer, Encoding encoding)
{ {
return encoding.GetChars (buffer.GetBuffer (), 0, (int) buffer.Length); return encoding.GetChars (buffer.GetBuffer (), 0, (int) buffer.Length);
} }
private static Dictionary<string, char> getEntities ()
{
lock (_sync) {
if (_entities == null)
initEntities ();
return _entities;
}
}
private static int getInt (byte b) private static int getInt (byte b)
{ {
var c = (char) b; var c = (char) b;
@ -411,16 +406,16 @@ namespace WebSocketSharp.Net
var i = (int) c; var i = (int) c;
var idx = i >> 12; var idx = i >> 12;
result.WriteByte ((byte) _hexChars [idx]); result.WriteByte ((byte) _hexChars[idx]);
idx = (i >> 8) & 0x0F; idx = (i >> 8) & 0x0F;
result.WriteByte ((byte) _hexChars [idx]); result.WriteByte ((byte) _hexChars[idx]);
idx = (i >> 4) & 0x0F; idx = (i >> 4) & 0x0F;
result.WriteByte ((byte) _hexChars [idx]); result.WriteByte ((byte) _hexChars[idx]);
idx = i & 0x0F; idx = i & 0x0F;
result.WriteByte ((byte) _hexChars [idx]); result.WriteByte ((byte) _hexChars[idx]);
return; return;
} }
@ -450,15 +445,16 @@ namespace WebSocketSharp.Net
} }
var idx = ((int) c) >> 4; var idx = ((int) c) >> 4;
result.WriteByte ((byte) _hexChars [idx]); result.WriteByte ((byte) _hexChars[idx]);
idx = ((int) c) & 0x0F; idx = ((int) c) & 0x0F;
result.WriteByte ((byte) _hexChars [idx]); result.WriteByte ((byte) _hexChars[idx]);
return;
} }
else {
result.WriteByte ((byte) c); result.WriteByte ((byte) c);
} }
}
private static void urlPathEncodeChar (char c, Stream result) private static void urlPathEncodeChar (char c, Stream result)
{ {
@ -468,28 +464,35 @@ namespace WebSocketSharp.Net
result.WriteByte ((byte) '%'); result.WriteByte ((byte) '%');
var i = ((int) b) >> 4; var i = ((int) b) >> 4;
result.WriteByte ((byte) _hexChars [i]); result.WriteByte ((byte) _hexChars[i]);
i = ((int) b) & 0x0F; i = ((int) b) & 0x0F;
result.WriteByte ((byte) _hexChars [i]); result.WriteByte ((byte) _hexChars[i]);
} }
return;
} }
else if (c == ' ') {
if (c == ' ') {
result.WriteByte ((byte) '%'); result.WriteByte ((byte) '%');
result.WriteByte ((byte) '2'); result.WriteByte ((byte) '2');
result.WriteByte ((byte) '0'); result.WriteByte ((byte) '0');
return;
} }
else {
result.WriteByte ((byte) c); result.WriteByte ((byte) c);
} }
}
private static void writeCharBytes (IList buffer, char c, Encoding encoding) private static void writeCharBytes (IList buffer, char c, Encoding encoding)
{ {
if (c > 255) if (c > 255) {
foreach (var b in encoding.GetBytes (new [] { c })) foreach (var b in encoding.GetBytes (new[] { c }))
buffer.Add (b); buffer.Add (b);
else
return;
}
buffer.Add ((byte) c); buffer.Add ((byte) c);
} }
@ -503,7 +506,7 @@ namespace WebSocketSharp.Net
if (requestUri == null || requestUri.Length == 0 || host == null || host.Length == 0) if (requestUri == null || requestUri.Length == 0 || host == null || host.Length == 0)
return null; return null;
string scheme = null; string schm = null;
string path = null; string path = null;
if (requestUri.StartsWith ("/")) { if (requestUri.StartsWith ("/")) {
path = requestUri; path = requestUri;
@ -511,8 +514,8 @@ namespace WebSocketSharp.Net
else if (requestUri.MaybeUri ()) { else if (requestUri.MaybeUri ()) {
Uri uri; Uri uri;
var valid = Uri.TryCreate (requestUri, UriKind.Absolute, out uri) && var valid = Uri.TryCreate (requestUri, UriKind.Absolute, out uri) &&
(((scheme = uri.Scheme).StartsWith ("http") && !websocketRequest) || (((schm = uri.Scheme).StartsWith ("http") && !websocketRequest) ||
(scheme.StartsWith ("ws") && websocketRequest)); (schm.StartsWith ("ws") && websocketRequest));
if (!valid) if (!valid)
return null; return null;
@ -527,14 +530,14 @@ namespace WebSocketSharp.Net
host = requestUri; host = requestUri;
} }
if (scheme == null) if (schm == null)
scheme = (websocketRequest ? "ws" : "http") + (secure ? "s" : String.Empty); schm = (websocketRequest ? "ws" : "http") + (secure ? "s" : String.Empty);
var colon = host.IndexOf (':'); var colon = host.IndexOf (':');
if (colon == -1) if (colon == -1)
host = String.Format ("{0}:{1}", host, scheme == "http" || scheme == "ws" ? 80 : 443); host = String.Format ("{0}:{1}", host, schm == "http" || schm == "ws" ? 80 : 443);
var url = String.Format ("{0}://{1}{2}", scheme, host, path); var url = String.Format ("{0}://{1}{2}", schm, host, path);
Uri res; Uri res;
if (!Uri.TryCreate (url, UriKind.Absolute, out res)) if (!Uri.TryCreate (url, UriKind.Absolute, out res))
@ -558,10 +561,10 @@ namespace WebSocketSharp.Net
internal static NameValueCollection ParseQueryStringInternally (string query, Encoding encoding) internal static NameValueCollection ParseQueryStringInternally (string query, Encoding encoding)
{ {
int len; int len;
if (query == null || (len = query.Length) == 0 || (len == 1 && query [0] == '?')) if (query == null || (len = query.Length) == 0 || (len == 1 && query[0] == '?'))
return new NameValueCollection (1); return new NameValueCollection (1);
if (query [0] == '?') if (query[0] == '?')
query = query.Substring (1); query = query.Substring (1);
var res = new QueryStringCollection (); var res = new QueryStringCollection ();
@ -585,16 +588,15 @@ namespace WebSocketSharp.Net
} }
internal static string UrlDecodeInternally ( internal static string UrlDecodeInternally (
byte [] bytes, int offset, int count, Encoding encoding) byte[] bytes, int offset, int count, Encoding encoding)
{ {
var output = new StringBuilder (); var output = new StringBuilder ();
using (var acc = new MemoryStream ()) { using (var acc = new MemoryStream ()) {
var end = count + offset; var end = count + offset;
for (var i = offset; i < end; i++) {
if (bytes[i] == '%' && i + 2 < count && bytes[i + 1] != '%') {
int xchar; int xchar;
for (int i = offset; i < end; i++) { if (bytes[i + 1] == (byte) 'u' && i + 5 < end) {
if (bytes [i] == '%' && i + 2 < count && bytes [i + 1] != '%') {
if (bytes [i + 1] == (byte) 'u' && i + 5 < end) {
if (acc.Length > 0) { if (acc.Length > 0) {
output.Append (getChars (acc, encoding)); output.Append (getChars (acc, encoding));
acc.SetLength (0); acc.SetLength (0);
@ -621,10 +623,12 @@ namespace WebSocketSharp.Net
acc.SetLength (0); acc.SetLength (0);
} }
if (bytes [i] == '+') if (bytes[i] == '+') {
output.Append (' '); output.Append (' ');
else continue;
output.Append ((char) bytes [i]); }
output.Append ((char) bytes[i]);
} }
if (acc.Length > 0) if (acc.Length > 0)
@ -634,12 +638,12 @@ namespace WebSocketSharp.Net
return output.ToString (); return output.ToString ();
} }
internal static byte [] UrlDecodeToBytesInternally (byte [] bytes, int offset, int count) internal static byte[] UrlDecodeToBytesInternally (byte[] bytes, int offset, int count)
{ {
using (var res = new MemoryStream ()) { using (var res = new MemoryStream ()) {
var end = offset + count; var end = offset + count;
for (int i = offset; i < end; i++) { for (var i = offset; i < end; i++) {
var c = (char) bytes [i]; var c = (char) bytes[i];
if (c == '+') { if (c == '+') {
c = ' '; c = ' ';
} }
@ -659,19 +663,19 @@ namespace WebSocketSharp.Net
} }
} }
internal static byte [] UrlEncodeToBytesInternally (byte [] bytes, int offset, int count) internal static byte[] UrlEncodeToBytesInternally (byte[] bytes, int offset, int count)
{ {
using (var res = new MemoryStream ()) { using (var res = new MemoryStream ()) {
var end = offset + count; var end = offset + count;
for (int i = offset; i < end; i++) for (var i = offset; i < end; i++)
urlEncodeChar ((char) bytes [i], res, false); urlEncodeChar ((char) bytes[i], res, false);
res.Close (); res.Close ();
return res.ToArray (); return res.ToArray ();
} }
} }
internal static byte [] UrlEncodeUnicodeToBytesInternally (string s) internal static byte[] UrlEncodeUnicodeToBytesInternally (string s)
{ {
using (var res = new MemoryStream ()) { using (var res = new MemoryStream ()) {
foreach (var c in s) foreach (var c in s)
@ -702,8 +706,7 @@ namespace WebSocketSharp.Net
? "&lt;" ? "&lt;"
: c == '>' : c == '>'
? "&gt;" ? "&gt;"
: c.ToString () : c.ToString ());
);
return output.ToString (); return output.ToString ();
} }
@ -738,6 +741,7 @@ namespace WebSocketSharp.Net
// 2 -> between '&' and ';' but no '#' // 2 -> between '&' and ';' but no '#'
// 3 -> '#' found after '&' and getting numbers // 3 -> '#' found after '&' and getting numbers
var state = 0; var state = 0;
var number = 0; var number = 0;
var haveTrailingDigits = false; var haveTrailingDigits = false;
foreach (var c in s) { foreach (var c in s) {
@ -788,8 +792,9 @@ namespace WebSocketSharp.Net
entity.Append (c); entity.Append (c);
if (c == ';') { if (c == ';') {
var key = entity.ToString (); var key = entity.ToString ();
if (key.Length > 1 && Entities.ContainsKey (key.Substring (1, key.Length - 2))) var entities = getEntities ();
key = Entities [key.Substring (1, key.Length - 2)].ToString (); if (key.Length > 1 && entities.ContainsKey (key.Substring (1, key.Length - 2)))
key = entities[key.Substring (1, key.Length - 2)].ToString ();
output.Append (key); output.Append (key);
state = 0; state = 0;
@ -941,10 +946,7 @@ namespace WebSocketSharp.Net
if (query == null) if (query == null)
throw new ArgumentNullException ("query"); throw new ArgumentNullException ("query");
if (encoding == null) return ParseQueryStringInternally (query, encoding ?? Encoding.UTF8);
throw new ArgumentNullException ("encoding");
return ParseQueryStringInternally (query, encoding);
} }
public static string UrlDecode (string s) public static string UrlDecode (string s)
@ -960,14 +962,13 @@ namespace WebSocketSharp.Net
if (encoding == null) if (encoding == null)
encoding = Encoding.UTF8; encoding = Encoding.UTF8;
var len = s.Length;
var buff = new List<byte> (); var buff = new List<byte> ();
var len = s.Length;
for (var i = 0; i < len; i++) {
var c = s[i];
if (c == '%' && i + 2 < len && s[i + 1] != '%') {
int xchar; int xchar;
for (int i = 0; i < len; i++) { if (s[i + 1] == 'u' && i + 5 < len) {
var c = s [i];
if (c == '%' && i + 2 < len && s [i + 1] != '%') {
if (s [i + 1] == 'u' && i + 5 < len) {
// Unicode hex sequence. // Unicode hex sequence.
xchar = getChar (s, i + 2, 4); xchar = getChar (s, i + 2, 4);
if (xchar != -1) { if (xchar != -1) {
@ -989,31 +990,28 @@ namespace WebSocketSharp.Net
continue; continue;
} }
if (c == '+') if (c == '+') {
writeCharBytes (buff, ' ', encoding); writeCharBytes (buff, ' ', encoding);
else continue;
}
writeCharBytes (buff, c, encoding); writeCharBytes (buff, c, encoding);
} }
return encoding.GetString (buff.ToArray ()); return encoding.GetString (buff.ToArray ());
} }
public static string UrlDecode (byte [] bytes, Encoding encoding) public static string UrlDecode (byte[] bytes, Encoding encoding)
{ {
if (bytes == null) int len;
return null; return bytes == null
? null
var len = bytes.Length; : (len = bytes.Length) == 0
if (len == 0) ? String.Empty
return String.Empty; : UrlDecodeInternally (bytes, 0, len, encoding ?? Encoding.UTF8);
if (encoding == null)
encoding = Encoding.UTF8;
return UrlDecodeInternally (bytes, 0, len, encoding);
} }
public static string UrlDecode (byte [] bytes, int offset, int count, Encoding encoding) public static string UrlDecode (byte[] bytes, int offset, int count, Encoding encoding)
{ {
if (bytes == null) if (bytes == null)
return null; return null;
@ -1028,13 +1026,10 @@ namespace WebSocketSharp.Net
if (count < 0 || count > len - offset) if (count < 0 || count > len - offset)
throw new ArgumentOutOfRangeException ("count"); throw new ArgumentOutOfRangeException ("count");
if (encoding == null) return UrlDecodeInternally (bytes, offset, count, encoding ?? Encoding.UTF8);
encoding = Encoding.UTF8;
return UrlDecodeInternally (bytes, offset, count, encoding);
} }
public static byte [] UrlDecodeToBytes (byte [] bytes) public static byte[] UrlDecodeToBytes (byte[] bytes)
{ {
int len; int len;
return bytes != null && (len = bytes.Length) > 0 return bytes != null && (len = bytes.Length) > 0
@ -1042,34 +1037,31 @@ namespace WebSocketSharp.Net
: bytes; : bytes;
} }
public static byte [] UrlDecodeToBytes (string s) public static byte[] UrlDecodeToBytes (string s)
{ {
return UrlDecodeToBytes (s, Encoding.UTF8); return UrlDecodeToBytes (s, Encoding.UTF8);
} }
public static byte [] UrlDecodeToBytes (string s, Encoding encoding) public static byte[] UrlDecodeToBytes (string s, Encoding encoding)
{ {
if (s == null) if (s == null)
return null; return null;
if (s.Length == 0) if (s.Length == 0)
return new byte [0]; return new byte[0];
if (encoding == null) var bytes = (encoding ?? Encoding.UTF8).GetBytes (s);
encoding = Encoding.UTF8;
var bytes = encoding.GetBytes (s);
return UrlDecodeToBytesInternally (bytes, 0, bytes.Length); return UrlDecodeToBytesInternally (bytes, 0, bytes.Length);
} }
public static byte [] UrlDecodeToBytes (byte [] bytes, int offset, int count) public static byte[] UrlDecodeToBytes (byte[] bytes, int offset, int count)
{ {
int len; int len;
if (bytes == null || (len = bytes.Length) == 0) if (bytes == null || (len = bytes.Length) == 0)
return bytes; return bytes;
if (count == 0) if (count == 0)
return new byte [0]; return new byte[0];
if (offset < 0 || offset >= len) if (offset < 0 || offset >= len)
throw new ArgumentOutOfRangeException ("offset"); throw new ArgumentOutOfRangeException ("offset");
@ -1080,7 +1072,7 @@ namespace WebSocketSharp.Net
return UrlDecodeToBytesInternally (bytes, offset, count); return UrlDecodeToBytesInternally (bytes, offset, count);
} }
public static string UrlEncode (byte [] bytes) public static string UrlEncode (byte[] bytes)
{ {
int len; int len;
return bytes == null return bytes == null
@ -1119,13 +1111,13 @@ namespace WebSocketSharp.Net
encoding = Encoding.UTF8; encoding = Encoding.UTF8;
// Avoided GetByteCount call. // Avoided GetByteCount call.
var bytes = new byte [encoding.GetMaxByteCount (len)]; var bytes = new byte[encoding.GetMaxByteCount (len)];
var realLen = encoding.GetBytes (s, 0, len, bytes, 0); var realLen = encoding.GetBytes (s, 0, len, bytes, 0);
return Encoding.ASCII.GetString (UrlEncodeToBytesInternally (bytes, 0, realLen)); return Encoding.ASCII.GetString (UrlEncodeToBytesInternally (bytes, 0, realLen));
} }
public static string UrlEncode (byte [] bytes, int offset, int count) public static string UrlEncode (byte[] bytes, int offset, int count)
{ {
var encoded = UrlEncodeToBytes (bytes, offset, count); var encoded = UrlEncodeToBytes (bytes, offset, count);
return encoded == null return encoded == null
@ -1135,7 +1127,7 @@ namespace WebSocketSharp.Net
: Encoding.ASCII.GetString (encoded); : Encoding.ASCII.GetString (encoded);
} }
public static byte [] UrlEncodeToBytes (byte [] bytes) public static byte[] UrlEncodeToBytes (byte[] bytes)
{ {
int len; int len;
return bytes != null && (len = bytes.Length) > 0 return bytes != null && (len = bytes.Length) > 0
@ -1143,34 +1135,31 @@ namespace WebSocketSharp.Net
: bytes; : bytes;
} }
public static byte [] UrlEncodeToBytes (string s) public static byte[] UrlEncodeToBytes (string s)
{ {
return UrlEncodeToBytes (s, Encoding.UTF8); return UrlEncodeToBytes (s, Encoding.UTF8);
} }
public static byte [] UrlEncodeToBytes (string s, Encoding encoding) public static byte[] UrlEncodeToBytes (string s, Encoding encoding)
{ {
if (s == null) if (s == null)
return null; return null;
if (s.Length == 0) if (s.Length == 0)
return new byte [0]; return new byte[0];
if (encoding == null) var bytes = (encoding ?? Encoding.UTF8).GetBytes (s);
encoding = Encoding.UTF8;
var bytes = encoding.GetBytes (s);
return UrlEncodeToBytesInternally (bytes, 0, bytes.Length); return UrlEncodeToBytesInternally (bytes, 0, bytes.Length);
} }
public static byte [] UrlEncodeToBytes (byte [] bytes, int offset, int count) public static byte[] UrlEncodeToBytes (byte[] bytes, int offset, int count)
{ {
int len; int len;
if (bytes == null || (len = bytes.Length) == 0) if (bytes == null || (len = bytes.Length) == 0)
return bytes; return bytes;
if (count == 0) if (count == 0)
return new byte [0]; return new byte[0];
if (offset < 0 || offset >= len) if (offset < 0 || offset >= len)
throw new ArgumentOutOfRangeException ("offset"); throw new ArgumentOutOfRangeException ("offset");
@ -1188,12 +1177,12 @@ namespace WebSocketSharp.Net
: s; : s;
} }
public static byte [] UrlEncodeUnicodeToBytes (string s) public static byte[] UrlEncodeUnicodeToBytes (string s)
{ {
return s == null return s == null
? null ? null
: s.Length == 0 : s.Length == 0
? new byte [0] ? new byte[0]
: UrlEncodeUnicodeToBytesInternally (s); : UrlEncodeUnicodeToBytesInternally (s);
} }