using System.Collections.Generic;
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;


public static class AxiHttp
{
	public const char T = '\n';
	public const string CT = "\r\n";
	public const string CTRL = "\r\n\r\n";
	public const string Content_Length_Str = "content-length: ";
	public const string Content_Length_Str_M = "Content-Length: ";
	public const string Content_Length = "content-length";
	public const string Content_Encoding = "content-encoding";
	public const string Transfer_Encoding = "transfer-encoding";
	public const string Connection = "connection";
	public static long index = 0;
	static int singlePkgMaxRead = 1024;

	public class WaitAxiRequest : UnityEngine.CustomYieldInstruction
	{
		public AxiRespInfo mReqAsync;
		public WaitAxiRequest(AxiRespInfo reqAsync)
		{
			mReqAsync = reqAsync;
		}
		~WaitAxiRequest()
		{
			mReqAsync = null;
		}
		public override bool keepWaiting
		{
			get { return !mReqAsync.isDone; }
		}
	}

	public static void Log(string log)
	{
		UnityEngine.Debug.Log(log);
		//Console.WriteLine(log);
	}

	static Dictionary<string, IPAddress> dictIP2Address = new Dictionary<string, IPAddress>();

	public class AxiRespInfo
	{
		public bool isDone = false;
		public AxiDownLoadMode downloadMode = AxiDownLoadMode.NotDownLoad;
		public string Err = null;
		public string host = "";//host主机头
		public string url = "";//pathAndQuery
		public int port = 80;
		public string requestRaw = "";
		public string encoding = "";
		public string header = "";
		public string text { get { return body; } }
		public string body = "";
		public string reuqestBody = "";
		public string reuqestHeader = "";
		public Dictionary<string, string> headers = new Dictionary<string, string>();
		public string response = "";
		//public string gzip = "";
		public bool isGzip = false;
		public int length = 0;
		public int code = 0;
		public int location = 0;
		public int runTime = 0;//获取网页消耗时间,毫秒
		public int sleepTime = 0;//休息时间
		public string cookies = "";
		public bool bTimeOut = false;

		public int NeedloadedLenght;
		public int loadedLenght;
		public byte[] data { get { return bodyRaw; } }
		public byte[] bodyRaw;
		public string fileName;
		public float DownLoadPr =>
			NeedloadedLenght <= 0 ? -1 : (float)loadedLenght / NeedloadedLenght;
		public BinaryWriter binaryWriter;
	}

	public static IPAddress GetDnsIP(string str)
	{
		if (!dictIP2Address.ContainsKey(str))
		{
			try
			{
				IPAddress ip = Dns.GetHostEntry(str).AddressList[0];
				dictIP2Address[str] = ip;
			}
			catch
			{
				return null;
			}
		}
		return dictIP2Address[str];
	}

	public enum AxiDownLoadMode
	{
		NotDownLoad = 0,
		DownLoadBytes = 1,
		DownloadToBinaryWriter = 2
	}

	public static AxiRespInfo AxiRequest(string url)
	{
		AxiRespInfo respInfo = new AxiRespInfo();
		respInfo.downloadMode = AxiDownLoadMode.NotDownLoad;
		SendAxiRequest(url, ref respInfo);
		return respInfo;
	}

	public static WaitAxiRequest AxiRequestAsync(string url)
	{
		AxiRespInfo respInfo = new AxiRespInfo();
		respInfo.downloadMode = AxiDownLoadMode.NotDownLoad;
		WaitAxiRequest respAsync = new WaitAxiRequest(respInfo);
		Task task = new Task(() => SendAxiRequest(url, ref respInfo));
		task.Start();
		return respAsync;
	}

	public static AxiRespInfo AxiDownload(string url)
	{
		AxiRespInfo respInfo = new AxiRespInfo(); 
		respInfo.downloadMode = AxiDownLoadMode.DownLoadBytes;
		SendAxiRequest(url, ref respInfo);
		return respInfo;
	}

	public static AxiRespInfo AxiDownloadAsync(string url)
	{
		AxiRespInfo respInfo = new AxiRespInfo(); 
		respInfo.downloadMode = AxiDownLoadMode.DownLoadBytes;
		Task task = new Task(() => SendAxiRequest(url, ref respInfo));
		task.Start();
		return respInfo;
	}

	static void SendAxiRequest(string url, ref AxiRespInfo respinfo,int timeout = 1000 * 1000,	string encoding = "UTF-8")
	{
		if (url.ToLower().StartsWith("https://"))
			SendAxiRequestHttps(url, ref respinfo,timeout, encoding);// SendAxiRequestHttps(url, ref respinfo, timeout, encoding);
		else
			SendAxiRequestHttp(url, ref respinfo,timeout, encoding);
	}

	static void SendAxiRequestHttp(string url, ref AxiRespInfo respinfo, int timeout, string encoding)
	{
		Log("SendAxiRequestHttp");
		respinfo.url = url;
		Stopwatch sw = new Stopwatch();
		sw.Start();
		respinfo.loadedLenght = 0;
		try
		{
			string strURI = url;
			string strHost = "";
			string strIP = "";
			int port = 0;
			string strRelativePath = "";
			bool bSSL = false;
			bool foward_302 = true;

			if (!ParseURI(strURI, ref bSSL, ref strHost, ref strIP, ref port, ref strRelativePath))
			{
				Log("ParseURI False");
				respinfo.Err = "ParseURI False";
				respinfo.code = 0;
				respinfo.isDone = true;
				return;
			}



			var ip = GetDnsIP(strHost);
			var ipEndPoint = new IPEndPoint(ip, port);

			using (Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
			using (MemoryStream memoryStream = new MemoryStream())
			{
				client.Connect(ipEndPoint);
				if (!client.Connected)
				{
					client.Close();
					sw.Stop();
					respinfo.code = 0;
					respinfo.isDone = true;
					return;
				}

				//string requestRaw = $"GET {strRelativePath} HTTP/1.1\r\nHost: {strHost}\r\nConnection: Close\r\n\r\n";
				string request = $"GET {strURI} HTTP/1.1\r\nHost: {strHost}\r\nConnection: Close\r\n\r\n";

				checkContentLength(ref respinfo, ref request);
				respinfo.requestRaw = request;
				byte[] temp_responseBody = new byte[singlePkgMaxRead];

				byte[] buffer = Encoding.ASCII.GetBytes(request);
				client.Send(buffer);

				string tmp = "";
				int len = 0;
				StringBuilder sb = new StringBuilder();
				do
				{
					byte[] responseHeader = new byte[1];
					len = client.Receive(responseHeader, 1, SocketFlags.None);
					if (len == 1)
					{
						char c = (char)responseHeader[0];
						sb.Append(c);
						if (c.Equals(T))
						{
							tmp = String.Concat(sb[sb.Length - 4], sb[sb.Length - 3], sb[sb.Length - 2], c);
						}
					}
				} while (!tmp.Equals(CTRL)
				&& sw.ElapsedMilliseconds < timeout
				);


				respinfo.header = sb.ToString().Replace(CTRL, "");
				string[] headers = Regex.Split(respinfo.header, CT);
				if (headers != null && headers.Length > 0)
				{
					//处理header
					doHeader(ref respinfo, ref headers);
				}
				//自动修正编码
				if (!String.IsNullOrEmpty(respinfo.encoding))
				{
					encoding = respinfo.encoding;
				}
				Encoding encod = Encoding.GetEncoding(encoding);

				//302 301跳转
				if ((respinfo.code == 302 || respinfo.code == 301) && foward_302)
				{
					StringBuilder rsb = new StringBuilder(respinfo.requestRaw);
					int urlStart = respinfo.requestRaw.IndexOf(" ") + 1;
					int urlEnd = respinfo.requestRaw.IndexOf(" HTTP");
					if (urlStart != -1 && urlEnd != -1)
					{
						url = respinfo.requestRaw.Substring(urlStart, urlEnd - urlStart);
						rsb.Remove(urlStart, url.Length);
						String location = respinfo.headers["location"];
						if (!respinfo.headers["location"].StartsWith("/") && !respinfo.headers["location"].StartsWith("http"))
						{
							location = Tools.getCurrentPath(url) + location;
						}
						rsb.Insert(urlStart, location);
						//return sendHTTPRequest(count, host, port, payload, rsb.ToString(), timeout, encoding, false);
						client.Close();
						sw.Stop();
						SendAxiRequest(url, ref respinfo, timeout, encoding);
						return;
					}
				}

				//根据请求头解析
				if (respinfo.headers.ContainsKey(Content_Length))
				{
					Log("User Head");
					int length = int.Parse(respinfo.headers[Content_Length]);
					respinfo.NeedloadedLenght = length;

					//               while (respinfo.loadedLenght < length
					//	&& sw.ElapsedMilliseconds < timeout
					//	)
					//{
					//	int readsize = length - respinfo.loadedLenght;
					//	len = client.Receive(temp_responseBody, respinfo.loadedLenght, readsize, SocketFlags.None);

					//                   if (len > 0)
					//	{
					//		respinfo.loadedLenght += len;
					//	}
					//}

					while (respinfo.loadedLenght < length
						&& sw.ElapsedMilliseconds < timeout
						)
					{
						//len = client.Receive(temp_responseBody, respinfo.loadedLenght, readsize, SocketFlags.None);
						int readsize = length - respinfo.loadedLenght;
						readsize = Math.Min(readsize, singlePkgMaxRead);
						len = client.Receive(temp_responseBody, 0, readsize, SocketFlags.None);
						if (len > 0)
						{
							memoryStream.Write(temp_responseBody, 0, len);
							respinfo.loadedLenght += len;
						}
					}
				}
				//解析chunked传输
				else if (respinfo.headers.ContainsKey(Transfer_Encoding))
				{
					Log("User chunked");
					//读取长度
					int chunkedSize = 0;
					byte[] chunkedByte = new byte[1];
					//读取总长度
					respinfo.loadedLenght = 0;
					do
					{
						string ctmp = "";
						do
						{
							len = client.Receive(chunkedByte, 1, SocketFlags.None);
							ctmp += Encoding.UTF8.GetString(chunkedByte);

						} while ((ctmp.IndexOf(CT) == -1)
						&& (sw.ElapsedMilliseconds < timeout)
						);

						chunkedSize = Tools.convertToIntBy16(ctmp.Replace(CT, ""));
						//chunked的结束0\r\n\r\n是结束标志,单个chunked块\r\n结束
						if (ctmp.Equals(CT))
						{
							continue;
						}
						if (chunkedSize == 0)
						{
							//结束了
							break;
						}
						//int onechunkLen = 0;
						//while (onechunkLen < chunkedSize
						//	&& sw.ElapsedMilliseconds < timeout
						//	)
						//{

						//                      len = client.Receive(responseBody, respinfo.loadedLenght, chunkedSize - onechunkLen, SocketFlags.None);
						//	if (len > 0)
						//	{
						//		onechunkLen += len;
						//		respinfo.loadedLenght += len;
						//	}
						//}

						int onechunkLen = 0;
						while (onechunkLen < chunkedSize
							&& sw.ElapsedMilliseconds < timeout
							)
						{
							//len = client.Receive(responseBody, respinfo.loadedLenght, chunkedSize - onechunkLen, SocketFlags.None);

							int readsize = chunkedSize - onechunkLen;
							readsize = Math.Min(readsize, singlePkgMaxRead);
							len = client.Receive(temp_responseBody, 0, readsize, SocketFlags.None);
							if (len > 0)
							{
								memoryStream.Write(temp_responseBody, 0, len);
								onechunkLen += len;
								respinfo.loadedLenght += len;
							}
						}

						//判断
					} while (sw.ElapsedMilliseconds < timeout);
				}
				//connection close方式或未知body长度
				else
				{
					Log("connection close or Unknow bodylenght");
					while (sw.ElapsedMilliseconds < timeout)
					{
						if (client.Poll(timeout, SelectMode.SelectRead))
						{
							if (client.Available > 0)
							{
								//len = client.Receive(responseBody, respinfo.loadedLenght, (1024 * 200) - respinfo.loadedLenght, SocketFlags.None);
								int readsize = (1024 * 200) - respinfo.loadedLenght;
								readsize = Math.Min(readsize, singlePkgMaxRead);
								len = client.Receive(temp_responseBody, 0, readsize, SocketFlags.None);

								if (len > 0)
								{
									memoryStream.Write(temp_responseBody, 0, len);
									respinfo.loadedLenght += len;
								}
							}
							else
							{
								break;
							}
						}
					}
				}


				byte[] responseBody = memoryStream.ToArray();
				//如果是下载
				if (respinfo.downloadMode > AxiDownLoadMode.NotDownLoad)
				{
					//判断是否gzip
					if (respinfo.headers.ContainsKey(Content_Encoding))
					{
						respinfo.bodyRaw = unGzipBytes(responseBody, respinfo.loadedLenght);
					}
					else
					{
						respinfo.bodyRaw = responseBody;
					}

					// 使用Uri类解析URL  
					Uri uri = new Uri(url);
					respinfo.fileName = Path.GetFileName(uri.LocalPath);
				}
				else
				{
					//判断是否gzip
					if (respinfo.headers.ContainsKey(Content_Encoding))
					{
						respinfo.body = unGzip(responseBody, respinfo.loadedLenght, encod);
					}
					else
					{
						respinfo.body = encod.GetString(responseBody, 0, respinfo.loadedLenght);
					}
				}

				client.Close();
			}

		}
		catch (Exception ex)
		{
			respinfo.Err = $"ex : {ex.ToString()}";
		}
		finally
		{
			sw.Stop();
			respinfo.length = respinfo.loadedLenght;
			respinfo.runTime = (int)sw.ElapsedMilliseconds;
			respinfo.bTimeOut = sw.ElapsedMilliseconds >= timeout;
			//if (socket != null)
			//{
			//	clientSocket.Close();
			//}
			respinfo.isDone = true;
		}
	}


	static void SendAxiRequestHttps(string url, ref AxiRespInfo respinfo, int timeout, string encoding)
	{
		respinfo.url = url;
		Stopwatch sw = new Stopwatch();
		sw.Start();
		respinfo.loadedLenght = 0;
		TcpClient client = null;
		try
		{
			string strURI = url;
			string strHost = "";
			string strIP = "";
			int port = 0;
			string strRelativePath = "";
			bool bSSL = false;
			bool foward_302 = true;

			if (!ParseURI(strURI, ref bSSL, ref strHost, ref strIP, ref port, ref strRelativePath))
			{
				Log("ParseURI False");
				respinfo.Err = "ParseURI False";
				respinfo.code = 0;
				respinfo.isDone = true;
				return;
			}

			//var ip = Dns.GetHostEntry(strHost).AddressList[0];
			//var ipEndPoint = new IPEndPoint(ip, port);

			//using (Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
			//using (TcpClient tcpclient = new TcpClient())
			using (MemoryStream memoryStream = new MemoryStream())
			{
				//client.Connect(ipEndPoint);

				TimeOutSocket tos = new TimeOutSocket();
				client = tos.Connect(strHost, port, timeout);
				if (!client.Connected)
				{
					client.Close();
					sw.Stop();
					respinfo.code = 0;
					respinfo.isDone = true;
					return;
				}
				SslStream ssl = null;

				//string requestRaw = $"GET {strRelativePath} HTTP/1.1\r\nHost: {strHost}\r\nConnection: Close\r\n\r\n";
				string request = $"GET {strURI} HTTP/1.1\r\nHost: {strHost}\r\nConnection: Close\r\n\r\n";

				ssl = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback(ValidateServerCertificate));
				SslProtocols protocol = SslProtocols.Ssl3 | SslProtocols.Ssl2 | SslProtocols.Tls;
				ssl.AuthenticateAsClient(strHost, null, protocol, false);
				if (ssl.IsAuthenticated)
				{
					checkContentLength(ref respinfo, ref request);
					respinfo.requestRaw = request;
					byte[] requestByte = Encoding.UTF8.GetBytes(request);
					ssl.Write(requestByte);
					ssl.Flush();
				}


				checkContentLength(ref respinfo, ref request);
				respinfo.requestRaw = request;
				byte[] temp_responseBody = new byte[singlePkgMaxRead];

				//byte[] buffer = Encoding.ASCII.GetBytes(requestRaw);
				//client.Send(buffer);

				string tmp = "";
				int len = 0;
				StringBuilder sb = new StringBuilder();
				do
				{
					byte[] responseHeader = new byte[1];
					int read = ssl.ReadByte();

					char c = (char)read;
					sb.Append(c);
					if (c.Equals(T))
					{
						tmp = String.Concat(sb[sb.Length - 4], sb[sb.Length - 3], sb[sb.Length - 2], c);
					}

				} while (!tmp.Equals(CTRL)
				&& sw.ElapsedMilliseconds < timeout
				);


				respinfo.header = sb.ToString().Replace(CTRL, "");
				string[] headers = Regex.Split(respinfo.header, CT);
				if (headers != null && headers.Length > 0)
				{
					//处理header
					doHeader(ref respinfo, ref headers);
				}
				//自动修正编码
				if (!String.IsNullOrEmpty(respinfo.encoding))
				{
					encoding = respinfo.encoding;
				}
				Encoding encod = Encoding.GetEncoding(encoding);

				//302 301跳转
				if ((respinfo.code == 302 || respinfo.code == 301) && foward_302)
				{
					int urlStart = respinfo.requestRaw.IndexOf(" ");
					int urlEnd = respinfo.requestRaw.IndexOf(" HTTP");
					if (urlStart != -1 && urlEnd != -1)
					{
						url = respinfo.requestRaw.Substring(urlStart + 1, urlEnd - urlStart - 1);
						if (!respinfo.headers["location"].StartsWith("/") && !respinfo.headers["location"].StartsWith("https"))
						{
							respinfo.requestRaw = respinfo.requestRaw.Replace(url, Tools.getCurrentPath(url) + respinfo.headers["location"]);
						}
						else
						{
							respinfo.requestRaw = respinfo.requestRaw.Replace(url, respinfo.headers["location"]);
						}
						//return sendHTTPRequest(count, host, port, payload, rsb.ToString(), timeout, encoding, false);
						client.Close();
						sw.Stop();
						SendAxiRequest(url, ref respinfo, timeout, encoding);
						return;
					}
				}

				//根据请求头解析
				if (respinfo.headers.ContainsKey(Content_Length))
				{
					Log("Use Head");
					int length = int.Parse(respinfo.headers[Content_Length]);
					respinfo.NeedloadedLenght = length;
					while (respinfo.loadedLenght < length && sw.ElapsedMilliseconds < timeout)
					{
						//len = ssl.Read(responseBody, respinfo.loadedLenght, length - respinfo.loadedLenght);
						int readsize = length - respinfo.loadedLenght;
						readsize = Math.Min(readsize, singlePkgMaxRead);
						len = ssl.Read(temp_responseBody, 0, readsize);
						if (len > 0)
						{
							memoryStream.Write(temp_responseBody, 0, len);
							respinfo.loadedLenght += len;
						}
					}
				}
				//解析chunked传输
				else if (respinfo.headers.ContainsKey(Transfer_Encoding))
				{
					Log("User chunked");
					//读取长度
					int chunkedSize = 0;
					byte[] chunkedByte = new byte[1];
					//读取总长度
					respinfo.loadedLenght = 0;
					do
					{
						string ctmp = "";
						do
						{
							len = ssl.Read(chunkedByte, 0, 1);
							ctmp += Encoding.UTF8.GetString(chunkedByte);

						} while (ctmp.IndexOf(CT) == -1 && sw.ElapsedMilliseconds < timeout);


						chunkedSize = Tools.convertToIntBy16(ctmp.Replace(CT, ""));

						//chunked的结束0\r\n\r\n是结束标志,单个chunked块\r\n结束
						if (ctmp.Equals(CT))
						{
							continue;
						}
						if (chunkedSize == 0)
						{
							//结束了
							break;
						}
						int onechunkLen = 0;
						while (onechunkLen < chunkedSize && sw.ElapsedMilliseconds < timeout)
						{
							//len = ssl.Read(responseBody, respinfo.loadedLenght, chunkedSize - onechunkLen);
							int readsize = chunkedSize - onechunkLen;
							readsize = Math.Min(readsize, singlePkgMaxRead);
							len = ssl.Read(temp_responseBody, 0, readsize);
							if (len > 0)
							{
								memoryStream.Write(temp_responseBody, 0, len);
								onechunkLen += len;
								respinfo.loadedLenght += len;
							}
						}

						//判断
					} while (sw.ElapsedMilliseconds < timeout);
				}
				//connection close方式或未知body长度
				else
				{
					Log("connection close or Unknow bodylenght");
					while (sw.ElapsedMilliseconds < timeout)
					{
						while (sw.ElapsedMilliseconds < timeout)
						{
							if (client.Client.Poll(timeout, SelectMode.SelectRead))
							{
								if (client.Available > 0)
								{
									//len = ssl.Read(responseBody, respinfo.loadedLenght, (1024 * 200) - respinfo.loadedLenght);
									int readsize = (1024 * 200) - respinfo.loadedLenght;
									readsize = Math.Min(readsize, singlePkgMaxRead);
									len = ssl.Read(temp_responseBody, 0, readsize);
									if (len > 0)
									{
										memoryStream.Write(temp_responseBody, 0, len);
										respinfo.loadedLenght += len;
									}
								}
								else
								{
									break;
								}
							}
						}
					}
				}

				byte[] responseBody = memoryStream.ToArray();
				//如果是下载
				if (respinfo.downloadMode > AxiDownLoadMode.NotDownLoad)
				{
					//判断是否gzip
					if (respinfo.isGzip)
					{
						respinfo.bodyRaw = unGzipBytes(responseBody, respinfo.loadedLenght);
					}
					else
					{
						respinfo.bodyRaw = responseBody;
					}

					// 使用Uri类解析URL  
					Uri uri = new Uri(url);
					respinfo.fileName = Path.GetFileName(uri.LocalPath);
				}
				else
				{
					//判断是否gzip
					if (respinfo.isGzip)
					{
						respinfo.body = unGzip(responseBody, respinfo.loadedLenght, encod);
					}
					else
					{
						respinfo.body = encod.GetString(responseBody, 0, respinfo.loadedLenght);
					}
				}

			}

		}
		catch (Exception ex)
		{
			respinfo.Err = $"ex : {ex.ToString()}";
		}
		finally
		{
			client?.Close();
			sw.Stop();
			respinfo.length = respinfo.loadedLenght;
			respinfo.runTime = (int)sw.ElapsedMilliseconds;
			respinfo.bTimeOut = sw.ElapsedMilliseconds >= timeout;
			//if (socket != null)
			//{
			//	clientSocket.Close();
			//}
			respinfo.isDone = true;
		}
	}


	private static void doHeader(ref AxiRespInfo respinfo, ref string[] headers)
	{

		for (int i = 0; i < headers.Length; i++)
		{
			if (i == 0)
			{

				respinfo.code = Tools.convertToInt(headers[i].Split(' ')[1]);

			}
			else
			{
				String[] kv = Regex.Split(headers[i], ": ");
				String key = kv[0].ToLower();
				if (!respinfo.headers.ContainsKey(key))
				{
					//自动识别编码
					if ("content-type".Equals(key))
					{
						String hecnode = getHTMLEncoding(kv[1], "");
						if (!String.IsNullOrEmpty(hecnode))
						{
							respinfo.encoding = hecnode;
						}
					}
					if (kv.Length > 1)
					{
						respinfo.headers.Add(key, kv[1]);
					}
					else
					{
						respinfo.headers.Add(key, "");
					}
				}
			}
			respinfo.isGzip = respinfo.headers.ContainsKey(Content_Encoding);
		}

	}
	public static string unGzip(byte[] data, int len, Encoding encoding)
	{

		string str = "";
		MemoryStream ms = new MemoryStream(data, 0, len);
		GZipStream gs = new GZipStream(ms, CompressionMode.Decompress);
		MemoryStream outbuf = new MemoryStream();
		byte[] block = new byte[1024];

		try
		{

			while (true)
			{
				int bytesRead = gs.Read(block, 0, block.Length);
				if (bytesRead <= 0)
				{
					break;
				}
				else
				{
					outbuf.Write(block, 0, bytesRead);
				}
			}
			str = encoding.GetString(outbuf.ToArray());
		}
		catch (Exception e)
		{
			Log("解压Gzip发生异常----" + e.Message);
		}
		finally
		{
			outbuf.Close();
			gs.Close();
			ms.Close();

		}
		return str;

	}

	public static byte[] unGzipBytes(byte[] data, int len)
	{
		MemoryStream ms = new MemoryStream(data, 0, len);
		GZipStream gs = new GZipStream(ms, CompressionMode.Decompress);
		MemoryStream outbuf = new MemoryStream();
		byte[] block = new byte[1024];
		byte[] result;
		try
		{

			while (true)
			{
				int bytesRead = gs.Read(block, 0, block.Length);
				if (bytesRead <= 0)
				{
					break;
				}
				else
				{
					outbuf.Write(block, 0, bytesRead);
				}
			}
			result = outbuf.ToArray();
		}
		catch (Exception e)
		{
			Log("解压Gzip发生异常----" + e.Message);
			result = new byte[0];
		}
		finally
		{
			outbuf.Close();
			gs.Close();
			ms.Close();

		}
		return result;

	}
	public static string getHTMLEncoding(string header, string body)
	{
		if (string.IsNullOrEmpty(header) && string.IsNullOrEmpty(body))
		{
			return "";
		}
		body = body.ToUpper();
		Match m = Regex.Match(header, @"charset\b\s*=\s*""?(?<charset>[^""]*)", RegexOptions.IgnoreCase);
		if (m.Success)
		{
			return m.Groups["charset"].Value.ToUpper();
		}
		else
		{
			if (string.IsNullOrEmpty(body))
			{
				return "";
			}
			m = Regex.Match(body, @"charset\b\s*=\s*""?(?<charset>[^""]*)", RegexOptions.IgnoreCase);
			if (m.Success)
			{
				return m.Groups["charset"].Value.ToUpper();
			}
		}
		return "";
	}
	private static void checkContentLength(ref AxiRespInfo respinfo, ref string request)
	{

		//重新计算并设置Content-length
		int sindex = request.IndexOf(CTRL);
		respinfo.reuqestHeader = request;
		if (sindex != -1)
		{
			respinfo.reuqestHeader = request.Substring(0, sindex);
			respinfo.reuqestBody = request.Substring(sindex + 4, request.Length - sindex - 4);
			int contentLength = Encoding.UTF8.GetBytes(respinfo.reuqestBody).Length;
			String newContentLength = Content_Length_Str_M + contentLength;

			if (request.IndexOf(Content_Length_Str_M) != -1)
			{
				request = Regex.Replace(request, Content_Length_Str_M + "\\d+", newContentLength);
			}
			else
			{
				request = request.Insert(sindex, "\r\n" + newContentLength);
			}
		}
		else
		{
			request = Regex.Replace(request, Content_Length_Str + "\\d+", Content_Length_Str_M + "0");
			request += CTRL;
		}


	}
	private static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
	{
		return true;
	}
	public static bool ParseURI(string strURI, ref bool bIsSSL, ref string strHost, ref string strIP, ref int Port, ref string strRelativePath)
	{
		string strAddressRet;
		string strPortRet;
		string strRelativePathRet;
		string strIPRet;

		/*string strProtocol = strURI.Substring(0, 7);
        if (strProtocol != "http://"
            ||
            strProtocol != "https://")
            return false;*/

		if (!strURI.ToLower().StartsWith("http://") || strURI.ToLower().StartsWith("https://"))
			return false;

		bIsSSL = strURI.ToLower().StartsWith("https://");

		string strLeft = strURI.Substring(7, strURI.Length - 7);
		int nIndexPort = strLeft.IndexOf(':');
		if (nIndexPort == -1)
		{
			if (bIsSSL)
				strPortRet = "443";
			else
				strPortRet = "80";
			int nIndexRelative = strLeft.IndexOf('/');
			if (nIndexRelative != -1)
			{
				strAddressRet = strLeft.Substring(0, nIndexRelative);
				strRelativePathRet = strLeft.Substring(nIndexRelative, strLeft.Length - nIndexRelative);
			}
			else
				return false;
		}
		else
		{
			strAddressRet = strLeft.Substring(0, nIndexPort);
			int nIndexRelative = strLeft.IndexOf('/');
			if (nIndexRelative != -1)
			{
				strPortRet = strLeft.Substring(nIndexPort + 1, nIndexRelative - (nIndexPort + 1));
				strRelativePathRet = strLeft.Substring(nIndexRelative, strLeft.Length - nIndexRelative);
			}
			else
				return false;
		}
		strHost = strAddressRet;
		try
		{
			//IPHostEntry hostinfo = Dns.GetHostEntry(strAddressRet);
			//IPAddress[] aryIP = hostinfo.AddressList;
			//strIPRet = aryIP[0].ToString();
			strIPRet = GetDnsIP(strAddressRet).ToString();
		}
		catch
		{
			return false;
		}

		strIP = strIPRet;
		Port = int.Parse(strPortRet);
		strRelativePath = UrlEncode(strRelativePathRet);
		return true;
	}


	public static string UrlEncode(string str)
	{
		string sb = "";
		List<char> filter = new List<char>() { '!', '#', '$', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '=', '?', '@', '_', '~' };
		byte[] byStr = System.Text.Encoding.UTF8.GetBytes(str); //System.Text.Encoding.Default.GetBytes(str)
		for (int i = 0; i < byStr.Length; i++)
		{
			if (filter.Contains((char)byStr[i]))
				sb += (char)byStr[i];
			else if ((char)byStr[i] >= 'a' && (char)byStr[i] <= 'z')
				sb += (char)byStr[i];
			else if ((char)byStr[i] >= 'A' && (char)byStr[i] <= 'Z')
				sb += (char)byStr[i];
			else if ((char)byStr[i] >= '0' && (char)byStr[i] <= '9')
				sb += (char)byStr[i];
			else
				sb += (@"%" + Convert.ToString(byStr[i], 16));
		}
		return sb;
	}


	class Tools
	{
		public static long currentMillis()
		{
			return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
		}

		/// <summary>
		/// 将16进制转换成10进制
		/// </summary>
		/// <param name="str">16进制字符串</param>
		/// <returns></returns>
		public static int convertToIntBy16(String str)
		{
			try
			{
				Log($"convertToIntBy16 str- {str} lenght->{str.Length}");
				if (str.Length == 0)
					return 0;
				return Convert.ToInt32(str, 16);
			}
			catch (Exception e)
			{
				Log($"convertToIntBy16 - {e.ToString()}");
			}
			return 0;

		}
		public static String getCurrentPath(String url)
		{
			int index = url.LastIndexOf("/");

			if (index != -1)
			{
				return url.Substring(0, index) + "/";
			}
			else
			{
				return "";
			}
		}


		/// <summary>
		/// 将字符串转换成数字,错误返回0
		/// </summary>
		/// <param name="strs">字符串</param>
		/// <returns></returns>
		public static int convertToInt(String str)
		{

			try
			{
				return int.Parse(str);
			}
			catch (Exception e)
			{
				Log("info:-" + e.Message);
			}
			return 0;

		}

	}
	class TimeOutSocket
	{
		private bool IsConnectionSuccessful = false;
		private Exception socketexception = null;
		private ManualResetEvent TimeoutObject = new ManualResetEvent(false);
		public int useTime = 0;
		public TcpClient Connect(string host, int port, int timeoutMSec)
		{
			Stopwatch sw = new Stopwatch();
			sw.Start();
			TimeoutObject.Reset();
			socketexception = null;

			TcpClient tcpclient = new TcpClient();

			//IPHostEntry hostinfo = Dns.GetHostEntry("emu.axibug.com");
			//IPAddress[] aryIP = hostinfo.AddressList;
			//host = aryIP[0].ToString();

			Log($"BeginConnect {host}:{port} timeoutMSec=>{timeoutMSec}");
			tcpclient.BeginConnect(host, port, new AsyncCallback(CallBackMethod), tcpclient);


			if (TimeoutObject.WaitOne(timeoutMSec, false))
			{
				if (IsConnectionSuccessful)
				{
					sw.Stop();
					useTime = (int)sw.ElapsedMilliseconds;
					return tcpclient;
				}
				else
				{
					throw socketexception;
				}
			}
			else
			{
				tcpclient.Close();
				throw new TimeoutException("TimeOut Exception");
			}
		}
		private void CallBackMethod(IAsyncResult asyncresult)
		{
			try
			{
				IsConnectionSuccessful = false;
				TcpClient tcpclient = asyncresult.AsyncState as TcpClient;

				if (tcpclient.Client != null)
				{
					tcpclient.EndConnect(asyncresult);
					IsConnectionSuccessful = true;
				}
			}
			catch (Exception ex)
			{
				Log($"CallBackMethod - {ex.ToString()}");
				IsConnectionSuccessful = false;
				socketexception = ex;
			}
			finally
			{
				TimeoutObject.Set();
			}
		}
	}

}