FBro浏览器响应拦截
概述
FBro提供了强大的HTTP响应拦截功能,允许开发者在浏览器接收到服务器响应时进行实时处理。通过资源过滤器机制,您可以监控、修改或保存HTTP响应数据,实现数据抓取、内容过滤、缓存管理等高级功能。
核心概念
响应拦截流程
- GetResourceResponseFilter - 在BrowserEvent中判断是否需要拦截请求
- FBroResponseFilterEvent - 创建响应过滤器回调来处理数据
- 数据流处理 - 通过Filter方法实时处理响应数据流
- 数据存储 - 在End方法中完成最终数据处理
关键组件
| 组件 | 作用 | 文件位置 |
|---|---|---|
| GetResourceResponseFilter | 过滤器创建和配置 | BrowserEvent.cs |
| FBroResponseFilterEvent | 响应数据处理回调 | callback.cs |
| Filter方法 | 数据流实时处理 | 继承实现 |
| Start/End方法 | 生命周期管理 | 继承实现 |
基础用法
1. 设置资源过滤器
csharp
/// <summary>
/// 在BrowserEvent.cs中实现GetResourceResponseFilter方法
/// </summary>
/// <param name="browser">浏览器实例</param>
/// <param name="frame">框架实例</param>
/// <param name="request">HTTP请求对象</param>
/// <param name="response">HTTP响应对象</param>
/// <returns>响应过滤器实例</returns>
public override IFBroSharpResponseFilter GetResourceResponseFilter(
IFBroSharpBrowser browser,
IFBroSharpFrame frame,
IFBroSharpRequest request,
IFBroSharpResponse response)
{
try
{
string url = request.GetURL();
string method = request.GetMethod();
// 判断是否需要拦截此请求
if (ShouldInterceptRequest(url, method))
{
// 创建响应过滤器
var filter = FBroSharp.CreateResponseFilter();
// 创建回调处理器
var filterCallback = new FBroResponseFilterEvent();
filterCallback.url = url;
filterCallback.method = method;
filterCallback.requestHeaders = request.GetHeaderMap();
// 设置回调
filter.SetCallback(filterCallback);
Console.WriteLine($"已设置响应拦截器: {method} {url}");
return filter;
}
return null; // 不拦截
}
catch (Exception ex)
{
Console.WriteLine($"设置响应过滤器失败: {ex.Message}");
return null;
}
}
/// <summary>
/// 判断是否应该拦截请求
/// </summary>
/// <param name="url">请求URL</param>
/// <param name="method">请求方法</param>
/// <returns>是否拦截</returns>
private bool ShouldInterceptRequest(string url, string method)
{
// 基础拦截规则示例
if (string.IsNullOrEmpty(url)) return false;
// 拦截API请求
if (url.Contains("/api/") && method == "GET")
return true;
// 拦截JSON响应
if (url.EndsWith(".json"))
return true;
// 拦截特定域名
if (url.Contains("example.com"))
return true;
return false;
}2. 基础响应过滤器实现
csharp
/// <summary>
/// 基础响应过滤器事件处理器
/// </summary>
public class FBroResponseFilterEvent : FBroSharpResponseFilterEvent
{
/// <summary>
/// 当前处理的资源地址
/// </summary>
public string url = "";
/// <summary>
/// HTTP请求方法
/// </summary>
public string method = "";
/// <summary>
/// 请求头信息
/// </summary>
public Dictionary<string, string> requestHeaders;
/// <summary>
/// 用于存放全部响应数据
/// </summary>
private MemoryStream responseStream;
/// <summary>
/// 二进制写入器
/// </summary>
private BinaryWriter writer;
/// <summary>
/// 响应开始时间
/// </summary>
private DateTime startTime;
/// <summary>
/// 初始化过滤器
/// </summary>
/// <param name="flag">标志参数</param>
/// <returns>是否开始拦截</returns>
public override bool InitFilter(long flag)
{
try
{
Console.WriteLine($"初始化响应过滤器: {method} {url}");
return true; // 返回true开始拦截
}
catch (Exception ex)
{
Console.WriteLine($"初始化过滤器失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 开始处理响应数据
/// </summary>
/// <param name="flag">标志参数</param>
public override void Start(long flag)
{
try
{
responseStream = new MemoryStream();
writer = new BinaryWriter(responseStream);
startTime = DateTime.Now;
Console.WriteLine($"开始拦截响应数据: {url}");
}
catch (Exception ex)
{
Console.WriteLine($"开始处理响应失败: {ex.Message}");
}
}
/// <summary>
/// 处理响应数据流
/// </summary>
/// <param name="flag">标志参数</param>
/// <param name="dataIn">输入数据流</param>
/// <param name="dataInRead">实际读取的字节数</param>
/// <param name="dataOut">输出数据流</param>
/// <param name="dataOutWritten">实际写入的字节数</param>
/// <returns>过滤器状态</returns>
public override FBroSharpResponseFilterStatus Filter(
long flag,
Stream dataIn,
out long dataInRead,
Stream dataOut,
out long dataOutWritten)
{
dataInRead = 0;
dataOutWritten = 0;
try
{
if (dataIn == null)
{
return FBroSharpResponseFilterStatus.RESPONSE_FILTER_DONE;
}
// 计算可读取的数据量
dataInRead = Math.Min(dataIn.Length, dataOut.Length);
dataOutWritten = dataInRead;
var readBytes = new byte[dataInRead];
dataIn.Read(readBytes, 0, readBytes.Length);
dataOut.Write(readBytes, 0, readBytes.Length);
// 将数据写入内存流保存
if (writer != null)
{
writer.Write(readBytes);
}
// 如果还有更多数据需要读取
if (dataInRead < dataIn.Length)
{
return FBroSharpResponseFilterStatus.RESPONSE_FILTER_NEED_MORE_DATA;
}
return FBroSharpResponseFilterStatus.RESPONSE_FILTER_DONE;
}
catch (Exception ex)
{
Console.WriteLine($"处理响应数据失败: {ex.Message}");
return FBroSharpResponseFilterStatus.RESPONSE_FILTER_ERROR;
}
}
/// <summary>
/// 完成响应数据处理
/// </summary>
/// <param name="flag">标志参数</param>
public override void End(long flag)
{
try
{
if (responseStream == null) return;
byte[] data = responseStream.ToArray();
var duration = DateTime.Now - startTime;
Console.WriteLine($"响应拦截完成: {url}, 数据大小: {data.Length} 字节, 耗时: {duration.TotalMilliseconds}ms");
if (data != null && data.Length > 0)
{
// 处理响应数据
ProcessResponseData(data, url, method);
}
}
catch (Exception ex)
{
Console.WriteLine($"完成响应处理失败: {ex.Message}");
}
finally
{
// 清理资源
CleanupResources();
}
}
/// <summary>
/// 处理响应数据
/// </summary>
/// <param name="data">响应数据</param>
/// <param name="url">请求URL</param>
/// <param name="method">请求方法</param>
private void ProcessResponseData(byte[] data, string url, string method)
{
try
{
// 保存到文件
SaveResponseToFile(data, url);
// 如果是文本数据,可以进行内容分析
if (IsTextContent(data))
{
string content = System.Text.Encoding.UTF8.GetString(data);
AnalyzeTextContent(content, url);
}
}
catch (Exception ex)
{
Console.WriteLine($"处理响应数据失败: {ex.Message}");
}
}
/// <summary>
/// 保存响应数据到文件
/// </summary>
/// <param name="data">响应数据</param>
/// <param name="url">请求URL</param>
private void SaveResponseToFile(byte[] data, string url)
{
try
{
string path = Path.Combine(Directory.GetCurrentDirectory(), "temp");
// 生成安全的文件名
string filename = GenerateSafeFileName(url);
// 确保目录存在
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
string fullPath = Path.Combine(path, filename);
// 写入文件
File.WriteAllBytes(fullPath, data);
Console.WriteLine($"响应数据已保存到: {fullPath}");
}
catch (Exception ex)
{
Console.WriteLine($"保存响应数据失败: {ex.Message}");
}
}
/// <summary>
/// 生成安全的文件名
/// </summary>
/// <param name="url">原始URL</param>
/// <returns>安全的文件名</returns>
private string GenerateSafeFileName(string url)
{
if (string.IsNullOrEmpty(url))
return $"response_{DateTime.Now:yyyyMMddHHmmss}.dat";
// 限制长度
if (url.Length > 120)
url = url.Substring(0, 120);
// 移除查询参数
int queryIndex = url.IndexOf("?");
if (queryIndex > 0)
url = url.Substring(0, queryIndex);
// 替换不安全的字符
string filename = url.Replace("/", "%")
.Replace(":", ";")
.Replace("*", "_")
.Replace("?", "_")
.Replace("\"", "_")
.Replace("<", "_")
.Replace(">", "_")
.Replace("|", "_");
// 添加时间戳避免重复
string timestamp = DateTime.Now.ToString("_yyyyMMddHHmmss");
return filename + timestamp;
}
/// <summary>
/// 判断是否为文本内容
/// </summary>
/// <param name="data">数据字节</param>
/// <returns>是否为文本</returns>
private bool IsTextContent(byte[] data)
{
if (data == null || data.Length == 0) return false;
// 简单的文本检测:检查前100字节是否都是可打印字符
int checkLength = Math.Min(data.Length, 100);
for (int i = 0; i < checkLength; i++)
{
byte b = data[i];
// 非ASCII字符或控制字符(除了常见的换行、制表符等)
if (b < 32 && b != 9 && b != 10 && b != 13)
return false;
}
return true;
}
/// <summary>
/// 分析文本内容
/// </summary>
/// <param name="content">文本内容</param>
/// <param name="url">请求URL</param>
private void AnalyzeTextContent(string content, string url)
{
try
{
Console.WriteLine($"文本内容分析 - URL: {url}, 长度: {content.Length} 字符");
// JSON数据分析
if (content.TrimStart().StartsWith("{") || content.TrimStart().StartsWith("["))
{
Console.WriteLine("检测到JSON格式数据");
// 这里可以添加JSON解析和分析逻辑
}
// XML数据分析
if (content.TrimStart().StartsWith("<"))
{
Console.WriteLine("检测到XML/HTML格式数据");
// 这里可以添加XML解析和分析逻辑
}
}
catch (Exception ex)
{
Console.WriteLine($"分析文本内容失败: {ex.Message}");
}
}
/// <summary>
/// 清理资源
/// </summary>
private void CleanupResources()
{
try
{
writer?.Dispose();
responseStream?.Dispose();
writer = null;
responseStream = null;
}
catch (Exception ex)
{
Console.WriteLine($"清理资源失败: {ex.Message}");
}
}
}高级用法
智能响应拦截管理器
csharp
/// <summary>
/// 智能响应拦截管理器
/// </summary>
public class ResponseInterceptManager
{
private readonly Dictionary<string, InterceptRule> _interceptRules;
private readonly List<InterceptedResponse> _interceptedResponses;
private readonly object _lock = new object();
public ResponseInterceptManager()
{
_interceptRules = new Dictionary<string, InterceptRule>();
_interceptedResponses = new List<InterceptedResponse>();
// 初始化默认规则
InitializeDefaultRules();
}
/// <summary>
/// 添加拦截规则
/// </summary>
/// <param name="name">规则名称</param>
/// <param name="rule">拦截规则</param>
public void AddInterceptRule(string name, InterceptRule rule)
{
lock (_lock)
{
_interceptRules[name] = rule;
Console.WriteLine($"已添加拦截规则: {name}");
}
}
/// <summary>
/// 检查是否应该拦截请求
/// </summary>
/// <param name="url">请求URL</param>
/// <param name="method">请求方法</param>
/// <param name="headers">请求头</param>
/// <returns>匹配的规则名称,如果不拦截则返回null</returns>
public string ShouldIntercept(string url, string method, Dictionary<string, string> headers)
{
lock (_lock)
{
foreach (var kvp in _interceptRules)
{
if (kvp.Value.Matches(url, method, headers))
{
return kvp.Key;
}
}
return null;
}
}
/// <summary>
/// 记录拦截的响应
/// </summary>
/// <param name="response">拦截的响应</param>
public void RecordInterceptedResponse(InterceptedResponse response)
{
lock (_lock)
{
_interceptedResponses.Add(response);
// 限制存储数量,避免内存溢出
if (_interceptedResponses.Count > 1000)
{
_interceptedResponses.RemoveAt(0);
}
Console.WriteLine($"已记录拦截响应: {response.Url}, 总计: {_interceptedResponses.Count}");
}
}
/// <summary>
/// 获取拦截统计信息
/// </summary>
/// <returns>统计信息</returns>
public InterceptStatistics GetStatistics()
{
lock (_lock)
{
var stats = new InterceptStatistics
{
TotalIntercepted = _interceptedResponses.Count,
RuleCount = _interceptRules.Count,
TotalDataSize = _interceptedResponses.Sum(r => r.DataSize),
AverageResponseTime = _interceptedResponses.Count > 0
? _interceptedResponses.Average(r => r.ProcessingTime.TotalMilliseconds)
: 0
};
return stats;
}
}
/// <summary>
/// 初始化默认拦截规则
/// </summary>
private void InitializeDefaultRules()
{
// API请求规则
AddInterceptRule("API_REQUESTS", new InterceptRule
{
UrlPattern = @"/api/.*",
Methods = new[] { "GET", "POST", "PUT", "DELETE" },
ContentTypes = new[] { "application/json", "application/xml" }
});
// 静态资源规则
AddInterceptRule("STATIC_RESOURCES", new InterceptRule
{
UrlPattern = @"\.(json|xml|csv)$",
Methods = new[] { "GET" }
});
// 特定域名规则
AddInterceptRule("TARGET_DOMAIN", new InterceptRule
{
UrlPattern = @"https?://target\.example\.com/.*",
Methods = new[] { "GET", "POST" }
});
}
}
/// <summary>
/// 拦截规则定义
/// </summary>
public class InterceptRule
{
public string UrlPattern { get; set; }
public string[] Methods { get; set; }
public string[] ContentTypes { get; set; }
public Dictionary<string, string> RequiredHeaders { get; set; }
public long MaxSize { get; set; } = long.MaxValue;
/// <summary>
/// 检查请求是否匹配此规则
/// </summary>
public bool Matches(string url, string method, Dictionary<string, string> headers)
{
try
{
// URL模式匹配
if (!string.IsNullOrEmpty(UrlPattern))
{
if (!System.Text.RegularExpressions.Regex.IsMatch(url, UrlPattern,
System.Text.RegularExpressions.RegexOptions.IgnoreCase))
return false;
}
// 方法匹配
if (Methods != null && Methods.Length > 0)
{
if (!Methods.Contains(method, StringComparer.OrdinalIgnoreCase))
return false;
}
// 必需的头部匹配
if (RequiredHeaders != null)
{
foreach (var requiredHeader in RequiredHeaders)
{
if (!headers.ContainsKey(requiredHeader.Key) ||
headers[requiredHeader.Key] != requiredHeader.Value)
return false;
}
}
return true;
}
catch (Exception ex)
{
Console.WriteLine($"规则匹配失败: {ex.Message}");
return false;
}
}
}
/// <summary>
/// 拦截的响应记录
/// </summary>
public class InterceptedResponse
{
public string Url { get; set; }
public string Method { get; set; }
public DateTime Timestamp { get; set; }
public long DataSize { get; set; }
public TimeSpan ProcessingTime { get; set; }
public string ContentType { get; set; }
public string RuleName { get; set; }
public string FilePath { get; set; }
}
/// <summary>
/// 拦截统计信息
/// </summary>
public class InterceptStatistics
{
public int TotalIntercepted { get; set; }
public int RuleCount { get; set; }
public long TotalDataSize { get; set; }
public double AverageResponseTime { get; set; }
public override string ToString()
{
return $"总拦截: {TotalIntercepted}, 规则数: {RuleCount}, " +
$"总数据量: {TotalDataSize / 1024}KB, 平均响应时间: {AverageResponseTime:F2}ms";
}
}高级响应过滤器
csharp
/// <summary>
/// 高级响应过滤器事件处理器
/// </summary>
public class AdvancedResponseFilterEvent : FBroSharpResponseFilterEvent
{
private readonly ResponseInterceptManager _manager;
private readonly string _ruleName;
private readonly InterceptedResponse _response;
private MemoryStream _responseStream;
private BinaryWriter _writer;
public AdvancedResponseFilterEvent(ResponseInterceptManager manager, string ruleName,
string url, string method)
{
_manager = manager;
_ruleName = ruleName;
_response = new InterceptedResponse
{
Url = url,
Method = method,
Timestamp = DateTime.Now,
RuleName = ruleName
};
}
public override bool InitFilter(long flag)
{
Console.WriteLine($"初始化高级过滤器: {_response.Method} {_response.Url} (规则: {_ruleName})");
return true;
}
public override void Start(long flag)
{
_responseStream = new MemoryStream();
_writer = new BinaryWriter(_responseStream);
_response.Timestamp = DateTime.Now;
}
public override FBroSharpResponseFilterStatus Filter(long flag, Stream dataIn,
out long dataInRead, Stream dataOut, out long dataOutWritten)
{
// 使用基础过滤逻辑
return base.Filter(flag, dataIn, out dataInRead, dataOut, out dataOutWritten);
}
public override void End(long flag)
{
try
{
_response.ProcessingTime = DateTime.Now - _response.Timestamp;
if (_responseStream != null)
{
byte[] data = _responseStream.ToArray();
_response.DataSize = data.Length;
if (data.Length > 0)
{
// 保存数据并记录文件路径
_response.FilePath = SaveAdvancedResponseData(data);
}
}
// 记录到管理器
_manager.RecordInterceptedResponse(_response);
}
finally
{
_writer?.Dispose();
_responseStream?.Dispose();
}
}
private string SaveAdvancedResponseData(byte[] data)
{
// 实现高级数据保存逻辑
// 返回保存的文件路径
return "";
}
}使用场景和最佳实践
场景1:API数据抓取
csharp
/// <summary>
/// API数据抓取场景
/// </summary>
public void SetupApiDataExtraction()
{
var manager = new ResponseInterceptManager();
// 添加API抓取规则
manager.AddInterceptRule("API_DATA", new InterceptRule
{
UrlPattern = @"https://api\.example\.com/.*",
Methods = new[] { "GET" },
ContentTypes = new[] { "application/json" }
});
}场景2:内容监控
csharp
/// <summary>
/// 内容监控场景
/// </summary>
public void SetupContentMonitoring()
{
// 监控特定关键词的响应
var rule = new InterceptRule
{
UrlPattern = @".*",
Methods = new[] { "GET", "POST" }
// 可以添加内容过滤逻辑
};
}注意事项
1. 性能考虑
- 避免拦截过多请求,会影响浏览器性能
- 及时清理内存流,防止内存泄漏
- 设置合理的数据大小限制
2. 错误处理
- 在所有回调方法中添加异常处理
- 确保资源正确释放
- 记录详细的错误日志
3. 数据安全
- 注意敏感数据的处理和存储
- 实现适当的数据加密机制
- 遵守相关的数据保护法规
通过这些高级功能和最佳实践,您可以构建强大而可靠的HTTP响应拦截系统。