Skip to content

FBro浏览器响应拦截

概述

FBro提供了强大的HTTP响应拦截功能,允许开发者在浏览器接收到服务器响应时进行实时处理。通过资源过滤器机制,您可以监控、修改或保存HTTP响应数据,实现数据抓取、内容过滤、缓存管理等高级功能。

核心概念

响应拦截流程

  1. GetResourceResponseFilter - 在BrowserEvent中判断是否需要拦截请求
  2. FBroResponseFilterEvent - 创建响应过滤器回调来处理数据
  3. 数据流处理 - 通过Filter方法实时处理响应数据流
  4. 数据存储 - 在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响应拦截系统。

如果文档对您有帮助,欢迎 请喝咖啡 ☕ | 软件发布 | 源码购买