diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/App.cs b/AxibugEmuOnline.Client/Assets/Script/AppMain/App.cs index b08b0c1..239549f 100644 --- a/AxibugEmuOnline.Client/Assets/Script/AppMain/App.cs +++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/App.cs @@ -1,4 +1,4 @@ -using AxibugEmuOnline.Client.Manager; +using AxibugEmuOnline.Client.Manager; using AxibugEmuOnline.Client.Network; using System.Collections; using System.IO; @@ -116,14 +116,21 @@ namespace AxibugEmuOnline.Client.ClientCore yield break; } - UnityWebRequest request = UnityWebRequest.Get($"{App.httpAPI.WebSiteApi}/CheckStandInfo?platform={platform}&version={Application.version}"); + AxiHttpProxy.SendWebRequestProxy request = AxiHttpProxy.Get($"{App.httpAPI.WebSiteApi}/CheckStandInfo?platform={platform}&version={Application.version}"); + yield return request.SendWebRequest; + if (!request.downloadHandler.isDone) + yield break; + Resp_CheckStandInfo resp = JsonUtility.FromJson(request.downloadHandler.text); + + /*UnityWebRequest request = UnityWebRequest.Get($"{App.httpAPI.WebSiteApi}/CheckStandInfo?platform={platform}&version={Application.version}"); yield return request.SendWebRequest(); if (request.result != UnityWebRequest.Result.Success) yield break; App.log.Debug($"ApiResp => {request.downloadHandler.text}"); - Resp_CheckStandInfo resp = JsonUtility.FromJson(request.downloadHandler.text); + Resp_CheckStandInfo resp = JsonUtility.FromJson(request.downloadHandler.text);*/ + //需要更新 if (resp.needUpdateClient == 1) { diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/AxiHttp.meta b/AxibugEmuOnline.Client/Assets/Script/AppMain/AxiHttp.meta new file mode 100644 index 0000000..8defad1 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/AxiHttp.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5034475933564b54db148790f0ae2501 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/AxiHttp/AxiHttp.cs b/AxibugEmuOnline.Client/Assets/Script/AppMain/AxiHttp/AxiHttp.cs new file mode 100644 index 0000000..7b5d29d --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/AxiHttp/AxiHttp.cs @@ -0,0 +1,1142 @@ +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 dictIP2Address = new Dictionary(); + + 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 headers = new Dictionary(); + 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*""?(?[^""]*)", 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*""?(?[^""]*)", 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 filter = new List() { '!', '#', '$', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '=', '?', '@', '_', '~' }; + 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; + } + + /// + /// 将16进制转换成10进制 + /// + /// 16进制字符串 + /// + 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 ""; + } + } + + + /// + /// 将字符串转换成数字,错误返回0 + /// + /// 字符串 + /// + 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(); + } + } + } + +} \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/AxiHttp/AxiHttp.cs.meta b/AxibugEmuOnline.Client/Assets/Script/AppMain/AxiHttp/AxiHttp.cs.meta new file mode 100644 index 0000000..d25c76e --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/AxiHttp/AxiHttp.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5a498f74133ebfa4fa238a33a1c4c0c3 \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/AxiHttp/AxiHttpProxy.cs b/AxibugEmuOnline.Client/Assets/Script/AppMain/AxiHttp/AxiHttpProxy.cs new file mode 100644 index 0000000..ec6172e --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/AxiHttp/AxiHttpProxy.cs @@ -0,0 +1,101 @@ +using System.Collections; +using UnityEngine; +using static AxiHttp; + +public static class AxiHttpProxy +{ + public static SendWebRequestProxy Get(string url) + { + return new SendWebRequestProxy(AxiRequestAsync(url)); + } + + public static SendDownLoadProxy GetDownLoad(string url) + { + return new SendDownLoadProxy(AxiDownloadAsync(url)); + } + + + public class SendWebRequestProxy + { + public WaitAxiRequest SendWebRequest; + public AxiRespInfo downloadHandler => SendWebRequest.mReqAsync; + public SendWebRequestProxy(WaitAxiRequest request) + { + SendWebRequest = request; + } + ~SendWebRequestProxy() + { + SendWebRequest = null; + } + } + + + public class SendDownLoadProxy + { + public AxiRespInfo downloadHandler; + public SendDownLoadProxy(AxiRespInfo re) + { + downloadHandler = re; + } + ~SendDownLoadProxy() + { + downloadHandler = null; + } + } + + + public static void ShowAxiHttpDebugInfo(AxiRespInfo resp) + { + Debug.Log($""); + Debug.Log($"==== request ===="); + Debug.Log($"url =>{resp.url}"); + Debug.Log($"Raw =>{resp.requestRaw}"); + Debug.Log($"code =>{resp.code}"); + Debug.Log($"respInfo.bTimeOut =>{resp.bTimeOut}"); + Debug.Log($""); + Debug.Log($"==== response ===="); + Debug.Log($"==== header ===="); + Debug.Log($"header =>{resp.header}"); + Debug.Log($"HeadersCount =>{resp.headers.Count}"); + foreach (var kv in resp.headers) + Debug.Log($"{kv.Key} => {kv.Value}"); + Debug.Log($""); + Debug.Log($"==== body ===="); + Debug.Log($"body_text =>{resp.body}"); + Debug.Log($"body_text.Length =>{resp.body.Length}"); + Debug.Log($"bodyRaw.Length =>{resp.bodyRaw?.Length}"); + Debug.Log($""); + Debug.Log($"==== download ===="); + Debug.Log($"downloadMode =>{resp.downloadMode}"); + Debug.Log($"respInfo.fileName =>{resp.fileName}"); + Debug.Log($"respInfo.NeedloadedLenght =>{resp.NeedloadedLenght}"); + Debug.Log($"respInfo.loadedLenght =>{resp.loadedLenght}"); + //if (resp.downloadMode == AxiDownLoadMode.DownLoadBytes) + //{ + // if (resp.bTimeOut) + // { + // Debug.Log($"DownLoad Timeout!"); + // return; + // } + // string downloadSavePath; + // if (Application.platform == RuntimePlatform.PSP2) + // { + // downloadSavePath = dataAxibugPath + "/" + resp.fileName; + // } + // else + // { + // downloadSavePath = persistentDataPath + "/" + resp.fileName; + // } + // try + // { + // File.WriteAllBytes(downloadSavePath, resp.bodyRaw); + // Debug.Log($"DownLoad OK"); + // } + // catch (Exception ex) + // { + // Debug.Log($"DownLoad Err {ex.ToString()}"); + // } + //} + } + +} \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/AxiHttp/AxiHttpProxy.cs.meta b/AxibugEmuOnline.Client/Assets/Script/AppMain/AxiHttp/AxiHttpProxy.cs.meta new file mode 100644 index 0000000..ece4507 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/AxiHttp/AxiHttpProxy.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ec8e2e405992b234eb08896da0f85317 \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/Manager/CacheManager.cs b/AxibugEmuOnline.Client/Assets/Script/AppMain/Manager/CacheManager.cs index 2b91c87..1ccf46c 100644 --- a/AxibugEmuOnline.Client/Assets/Script/AppMain/Manager/CacheManager.cs +++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/Manager/CacheManager.cs @@ -18,7 +18,7 @@ namespace AxibugEmuOnline.Client GetCacheData(url, TextureCacheDirPath, callback); } - /// Ƴļ + /// 移除文件缓存 public void ClearCaches() { if (Directory.Exists(CacheDirPath)) @@ -27,9 +27,28 @@ namespace AxibugEmuOnline.Client IEnumerator DownloadFromURL(string url, string path, Action callback) { + AxiHttpProxy.SendDownLoadProxy request = AxiHttpProxy.GetDownLoad($"{App.httpAPI.WebHost}/{url}"); + + while (!request.downloadHandler.isDone) + { + yield return null; + //Debug.Log($"下载进度:{respInfo.DownLoadPr} ->{respInfo.loadedLenght}/{respInfo.NeedloadedLenght}"); + } + AxiHttpProxy.ShowAxiHttpDebugInfo(request.downloadHandler); + + if (request.downloadHandler.Err != null) + { + Directory.CreateDirectory(path); + File.WriteAllBytes($"{path}/{url.GetHashCode()}", request.downloadHandler.data); + callback.Invoke(request.downloadHandler.data); + } + else + callback.Invoke(null); + + /* var request = UnityWebRequest.Get($"{App.httpAPI.WebHost}/{url}"); yield return request.SendWebRequest(); - + if (request.result == UnityWebRequest.Result.Success) { Directory.CreateDirectory(path); @@ -38,6 +57,7 @@ namespace AxibugEmuOnline.Client } else callback.Invoke(null); + */ } private Dictionary cachesInMemory = new Dictionary(); diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/Manager/HttpAPI.cs b/AxibugEmuOnline.Client/Assets/Script/AppMain/Manager/HttpAPI.cs index d18ab34..d59566a 100644 --- a/AxibugEmuOnline.Client/Assets/Script/AppMain/Manager/HttpAPI.cs +++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/Manager/HttpAPI.cs @@ -1,4 +1,4 @@ -using AxibugEmuOnline.Client.ClientCore; +using AxibugEmuOnline.Client.ClientCore; using System; using System.Collections; using System.Collections.Generic; @@ -26,6 +26,16 @@ namespace AxibugEmuOnline.Client } private IEnumerator SearchNesRomListFlow(string searchKey, int page, int pageSize, Action callback) { + + AxiHttpProxy.SendWebRequestProxy request = AxiHttpProxy.Get($"{WebSiteApi}/NesRomList?Page={page}&PageSize={pageSize}&SearchKey={searchKey}"); + yield return request.SendWebRequest; + if (!request.downloadHandler.isDone) + { + callback.Invoke(null); + yield break; + } + + /* UnityWebRequest request = UnityWebRequest.Get($"{WebSiteApi}/NesRomList?Page={page}&PageSize={pageSize}&SearchKey={searchKey}"); yield return request.SendWebRequest(); @@ -33,13 +43,21 @@ namespace AxibugEmuOnline.Client { callback.Invoke(null); yield break; - } + }*/ var resp = JsonUtility.FromJson(request.downloadHandler.text); callback.Invoke(resp); } private IEnumerator GetNesRomListFlow(int page, int pageSize, Action callback) { + AxiHttpProxy.SendWebRequestProxy request = AxiHttpProxy.Get($"{WebSiteApi}/NesRomList?Page={page}&PageSize={pageSize}"); + yield return request.SendWebRequest; + if (!request.downloadHandler.isDone) + { + callback.Invoke(null); + yield break; + } + /* UnityWebRequest request = UnityWebRequest.Get($"{WebSiteApi}/NesRomList?Page={page}&PageSize={pageSize}"); yield return request.SendWebRequest(); @@ -48,13 +66,23 @@ namespace AxibugEmuOnline.Client callback.Invoke(null); yield break; } - + */ var resp = JsonUtility.FromJson(request.downloadHandler.text); callback.Invoke(resp); } public IEnumerator GetNesRomInfo(int RomID, Action callback) { + + AxiHttpProxy.SendWebRequestProxy request = AxiHttpProxy.Get($"{WebSiteApi}/RomInfo?PType={PlatformType.Nes}&RomID={RomID}"); + yield return request.SendWebRequest; + if (!request.downloadHandler.isDone) + { + callback.Invoke(null); + yield break; + } + + /* UnityWebRequest request = UnityWebRequest.Get($"{WebSiteApi}/RomInfo?PType={PlatformType.Nes}&RomID={RomID}"); yield return request.SendWebRequest(); @@ -62,7 +90,7 @@ namespace AxibugEmuOnline.Client { callback.Invoke(null); yield break; - } + }*/ var resp = JsonUtility.FromJson(request.downloadHandler.text); callback.Invoke(resp); diff --git a/AxibugEmuOnline.Client/Assets/Script/AppMain/Manager/RomLib/RomFile.cs b/AxibugEmuOnline.Client/Assets/Script/AppMain/Manager/RomLib/RomFile.cs index 49c85cd..e64a895 100644 --- a/AxibugEmuOnline.Client/Assets/Script/AppMain/Manager/RomLib/RomFile.cs +++ b/AxibugEmuOnline.Client/Assets/Script/AppMain/Manager/RomLib/RomFile.cs @@ -1,8 +1,9 @@ -using AxibugEmuOnline.Client.ClientCore; +using AxibugEmuOnline.Client.ClientCore; using ICSharpCode.SharpZipLib.Zip; using System; using System.Collections; using System.IO; +using UnityEngine; using UnityEngine.Networking; namespace AxibugEmuOnline.Client @@ -12,7 +13,8 @@ namespace AxibugEmuOnline.Client private HttpAPI.Resp_RomInfo webData; private bool hasLocalFile; private EnumPlatform platform; - private UnityWebRequest downloadRequest; + //private UnityWebRequest downloadRequest; + private AxiHttpProxy.SendDownLoadProxy downloadRequest; public bool IsUserRom { get; private set; } @@ -24,9 +26,14 @@ namespace AxibugEmuOnline.Client /// 指示该Rom文件是否已下载完毕 public bool RomReady => hasLocalFile; + ///// 指示是否正在下载Rom文件 + //public bool IsDownloading => downloadRequest != null && downloadRequest.result == UnityWebRequest.Result.InProgress; + //public float Progress => IsDownloading ? downloadRequest.downloadProgress : 0; + /// 指示是否正在下载Rom文件 - public bool IsDownloading => downloadRequest != null && downloadRequest.result == UnityWebRequest.Result.InProgress; - public float Progress => IsDownloading ? downloadRequest.downloadProgress : 0; + public bool IsDownloading => downloadRequest != null && !downloadRequest.downloadHandler.isDone; + public float Progress => IsDownloading ? downloadRequest.downloadHandler.DownLoadPr : 0; + public EnumPlatform Platform => platform; /// 指示该Rom信息是否已填充 @@ -114,21 +121,42 @@ namespace AxibugEmuOnline.Client private IEnumerator DownloadRemoteRom(Action callback) { - downloadRequest = UnityWebRequest.Get($"{App.httpAPI.WebHost}/{webData.url}"); - yield return downloadRequest.SendWebRequest(); + downloadRequest = AxiHttpProxy.GetDownLoad($"{App.httpAPI.WebHost}/{webData.url}"); + + while (!downloadRequest.downloadHandler.isDone) + { + yield return null; + Debug.Log($"下载进度:{downloadRequest.downloadHandler.DownLoadPr} ->{downloadRequest.downloadHandler.loadedLenght}/{downloadRequest.downloadHandler.NeedloadedLenght}"); + } + AxiHttpProxy.ShowAxiHttpDebugInfo(downloadRequest.downloadHandler); var request = downloadRequest; downloadRequest = null; - if (request.result != UnityWebRequest.Result.Success) - { - callback(null); - } - else - { - callback(request.downloadHandler.data); - } - } + if (request.downloadHandler.Err != null) + { + callback(null); + } + else + { + callback(request.downloadHandler.data); + } + + //downloadRequest = UnityWebRequest.Get($"{App.httpAPI.WebHost}/{webData.url}"); + //yield return downloadRequest.SendWebRequest(); + + //var request = downloadRequest; + //downloadRequest = null; + + //if (request.result != UnityWebRequest.Result.Success) + //{ + // callback(null); + //} + //else + //{ + // callback(request.downloadHandler.data); + //} + } public void SetWebData(HttpAPI.Resp_RomInfo resp_RomInfo) {