Files
KJD/AllBase/RequestHandler.cs
T
2025-06-29 19:38:31 +08:00

341 lines
13 KiB
C#

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<string> IgnoreList = new List<string>();
public List<string> NotIgnoreList = new List<string>();
public string UserAgent = string.Empty;
public List<Cookie> CookieList = new List<Cookie>(); // 留作测试使用
public event Action<string, string, string> ResourceLoadComplete; // requestUrl, postData, responseData
public event Action<string, string, string> 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<byte> DataAll = new List<byte>();
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<string, IResponseFilter> dataList = new Dictionary<string, IResponseFilter>();
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];
}
}
}
}