using CefSharp; using NaiBa.Util.Extension; using System; using System.Collections.Generic; using System.Collections.Specialized; using System.IO; using System.Linq; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading.Tasks; namespace AllBase { public class RequestHandler : IRequestHandler { public List IgnoreList = new List(); public List NotIgnoreList = new List(); public string UserAgent = string.Empty; public List CookieList = new List(); // 留作测试使用 public event Action ResourceLoadComplete; // requestUrl, postData, responseData public event Action ResourceRedirect; // requestUrl, postData, redirectUrl bool IRequestHandler.OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool isRedirect) { return false; } bool IRequestHandler.OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture) { return OnOpenUrlFromTab(browserControl, browser, frame, targetUrl, targetDisposition, userGesture); } protected virtual bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture) { return false; } bool IRequestHandler.OnCertificateError(IWebBrowser browserControl, IBrowser browser, CefErrorCode errorCode, string requestUrl, ISslInfo sslInfo, IRequestCallback callback) { //NOTE: If you do not wish to implement this method returning false is the default behaviour // We also suggest you explicitly Dispose of the callback as it wraps an unmanaged resource. //callback.Dispose(); //return false; //NOTE: When executing the callback in an async fashion need to check to see if it's disposed if (!callback.IsDisposed) { using (callback) { //To allow certificate //callback.Continue(true); //return true; } } return false; } void IRequestHandler.OnPluginCrashed(IWebBrowser browserControl, IBrowser browser, string pluginPath) { // TODO: Add your own code here for handling scenarios where a plugin crashed, for one reason or another. } CefReturnValue IRequestHandler.OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback) { if (NotIgnoreList.Any()) { if (NotIgnoreList.Where(p => request.Url.Contains(p)).Any()) { return CefReturnValue.Continue; } } if (IgnoreList.Any()) { if (IgnoreList.Where(p => request.Url.Contains(p)).Any()) { return CefReturnValue.Cancel; } } NameValueCollection headers = request.Headers; if (!string.IsNullOrEmpty(UserAgent)) { headers["User-Agent"] = UserAgent; } request.Headers = headers; return CefReturnValue.Continue; } bool IRequestHandler.GetAuthCredentials(IWebBrowser browserControl, IBrowser browser, IFrame frame, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback) { //NOTE: If you do not wish to implement this method returning false is the default behaviour // We also suggest you explicitly Dispose of the callback as it wraps an unmanaged resource. callback.Dispose(); return false; } bool IRequestHandler.OnSelectClientCertificate(IWebBrowser browserControl, IBrowser browser, bool isProxy, string host, int port, X509Certificate2Collection certificates, ISelectClientCertificateCallback callback) { //NOTE: If you do not wish to implement this method returning false is the default behaviour // We also suggest you explicitly Dispose of the callback as it wraps an unmanaged resource. return OnSelectClientCertificate(browserControl, browser, isProxy, host, port, certificates, callback); } protected virtual bool OnSelectClientCertificate(IWebBrowser browserControl, IBrowser browser, bool isProxy, string host, int port, X509Certificate2Collection certificates, ISelectClientCertificateCallback callback) { callback.Dispose(); return false; } void IRequestHandler.OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status) { // TODO: Add your own code here for handling scenarios where the Render Process terminated for one reason or another. // browserControl.Load(CefExample.RenderProcessCrashedUrl); } bool IRequestHandler.OnQuotaRequest(IWebBrowser browserControl, IBrowser browser, string originUrl, long newSize, IRequestCallback callback) { //NOTE: If you do not wish to implement this method returning false is the default behaviour // We also suggest you explicitly Dispose of the callback as it wraps an unmanaged resource. //callback.Dispose(); //return false; //NOTE: When executing the callback in an async fashion need to check to see if it's disposed if (!callback.IsDisposed) { using (callback) { //Accept Request to raise Quota //callback.Continue(true); //return true; } } return false; } void IRequestHandler.OnResourceRedirect(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response, ref string newUrl) { if (ResourceRedirect != null) { string postData = GetPostData(request); ResourceRedirect.Invoke(request.Url, postData, newUrl); } } bool IRequestHandler.OnProtocolExecution(IWebBrowser browserControl, IBrowser browser, string url) { return url.StartsWith("mailto"); } void IRequestHandler.OnRenderViewReady(IWebBrowser browserControl, IBrowser browser) { } bool IRequestHandler.OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response) { //NOTE: You cannot modify the response, only the request // You can now access the headers //var headers = response.ResponseHeaders; return false; } IResponseFilter IRequestHandler.GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response) { string cookie = response.ResponseHeaders["Set-Cookie"].ParseToString(); if (!string.IsNullOrEmpty(cookie)) { SetCookie(cookie); } string contentType = response.ResponseHeaders["Content-Type"]; if (string.IsNullOrEmpty(contentType)) { return null; } if (contentType.Contains("application/json") || contentType.Contains("text/javascript")) { if (ResourceLoadComplete != null) { IResponseFilter filter = FilterManager.CreateFilter(request.Identifier.ToString()); return filter; } } return null; } void IRequestHandler.OnResourceLoadComplete(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response, UrlRequestStatus status, long receivedContentLength) { string contentType = response.ResponseHeaders["Content-Type"]; if (string.IsNullOrEmpty(contentType)) { return; } if (contentType.Contains("application/json") || contentType.Contains("text/javascript")) { if (ResourceLoadComplete != null) { JsonFilter jsonFilter = FilterManager.GetFileter(request.Identifier.ToString()) as JsonFilter; if (jsonFilter != null) { UTF8Encoding encoding = new UTF8Encoding(); string responseData = encoding.GetString(jsonFilter.DataAll.ToArray()); string postData = GetPostData(request); ResourceLoadComplete.Invoke(request.Url, postData, responseData); } } } else { return; } } private void SetCookie(string cookie) { string[] commaArr = cookie.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); foreach (string comma in commaArr) { string[] semicolonArr = comma.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); foreach (string semicolon in semicolonArr) { string temp = semicolon.ParseToString().Trim(); if (string.IsNullOrEmpty(temp)) { continue; } if (!temp.Contains("=")) { continue; } if (temp.ToLower().StartsWith("path=") || temp.ToLower().StartsWith("expires=") || temp.ToLower().StartsWith("domain=")) { continue; } string[] keyValue = temp.Split('='); Cookie cookieObj = CookieList.Where(p => p.Name == keyValue[0]).FirstOrDefault(); if (cookieObj == null) { cookieObj = new Cookie { Name = keyValue[0], Value = keyValue[1] }; CookieList.Add(cookieObj); } else { cookieObj.Value = keyValue[1]; } } } } private string GetPostData(IRequest request) { string postData = string.Empty; if (request.PostData != null && request.PostData.Elements.Any()) { UTF8Encoding encoding = new UTF8Encoding(); postData = encoding.GetString(request.PostData.Elements[0].Bytes); postData = GetHtml.UrlDecode(postData); } return postData; } } public class JsonFilter : IResponseFilter { public List DataAll = new List(); public FilterStatus Filter(Stream dataIn, out long dataInRead, Stream dataOut, out long dataOutWritten) { try { if (dataIn == null || dataIn.Length == 0) { dataInRead = 0; dataOutWritten = 0; return FilterStatus.Done; } dataInRead = dataIn.Length; dataOutWritten = Math.Min(dataInRead, dataOut.Length); dataIn.CopyTo(dataOut); dataIn.Seek(0, SeekOrigin.Begin); byte[] bs = new byte[dataIn.Length]; dataIn.Read(bs, 0, bs.Length); DataAll.AddRange(bs); dataInRead = dataIn.Length; dataOutWritten = dataIn.Length; return FilterStatus.NeedMoreData; } catch (Exception ex) { dataInRead = dataIn.Length; dataOutWritten = dataIn.Length; return FilterStatus.Done; } } public bool InitFilter() { return true; } public void Dispose() { } } public class FilterManager { private static Dictionary dataList = new Dictionary(); public static IResponseFilter CreateFilter(string guid) { lock (dataList) { var filter = new JsonFilter(); dataList.Add(guid, filter); return filter; } } public static IResponseFilter GetFileter(string guid) { lock (dataList) { return dataList[guid]; } } } }