ljjh#True

ops/2024/10/30 22:39:52/

// background.js

// 使用 Map 存储拦截的请求数据,键为 interceptionId
const interceptedRequests = new Map();

// 使用 Map 存储 requestId 到 interceptionId 的映射
const requestIdToInterceptionIdMap = new Map();

// 存储已附加调试器的标签页ID
const debuggedTabs = new Set();

/**
 * 生成唯一的 uniqueRequestId
 */
function generateRequestId() {
  return `req_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
}

/**
 * 监听浏览器标签页更新,自动附加调试器
 */
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  if (changeInfo.status === 'complete' && !debuggedTabs.has(tabId)) {
    // 仅附加到 http 和 https 协议的标签页
    if (/^https?:\/\//.test(tab.url)) {
      attachDebugger(tabId);
    }
  }
});

/**
 * 监听新标签页创建,自动附加调试器
 */
chrome.tabs.onCreated.addListener((tab) => {
  if (/^https?:\/\//.test(tab.url)) {
    attachDebugger(tab.id);
  }
});

/**
 * 附加调试器到指定标签页
 */
function attachDebugger(tabId) {
  try {
    chrome.debugger.attach({ tabId: tabId }, '1.3', () => {
      if (chrome.runtime.lastError) {
        console.error(`Debugger attach failed: ${chrome.runtime.lastError.message}`);
        return;
      }
      debuggedTabs.add(tabId);
      console.log(`Debugger attached to tab ${tabId}`);

      // 启用网络域
      chrome.debugger.sendCommand({ tabId: tabId }, 'Network.enable');

      // 设置请求捕获的过滤器,拦截 URL 包含 '/build' 的所有类型请求
      chrome.debugger.sendCommand({ tabId: tabId }, 'Network.setRequestInterception', {
        patterns: [
          { urlPattern: '*://*/*/build*', interceptionStage: 'Request' }
        ]
      }, (response) => {
        if (chrome.runtime.lastError) {
          console.error(`Failed to set request interception: ${chrome.runtime.lastError.message}`);
        } else {
          console.log(`Request interception set for tab ${tabId}`);
        }
      });
    });
  } catch (error) {
    console.error(`Failed to attach debugger to tab ${tabId}:`, error);
  }
}

/**
 * 监听调试器的网络事件
 */
chrome.debugger.onEvent.addListener((source, method, params) => {
  if (method === 'Network.requestIntercepted') {
    handleRequestIntercepted(source.tabId, params);
  }
  if (method === 'Network.responseReceived') {
    handleResponseReceived(source.tabId, params);
  }
  // 监听加载完成事件
  if (method === 'Network.loadingFinished') {
    handleLoadingFinished(source.tabId, params);
  }
});

/**
 * 处理 Network.requestIntercepted 事件
 */
function handleRequestIntercepted(tabId, params) {
  const { interceptionId, requestId, request, resourceType } = params;
  const { url, method, headers, postData } = request;

  // 检查是否成功获取到 url
  if (!url) {
    console.warn(`[background.js] 拦截ID ${interceptionId} 的请求缺少 URL 信息`);
    // 继续拦截的请求,避免阻塞
    chrome.debugger.sendCommand({ tabId: tabId }, 'Network.continueInterceptedRequest', { interceptionId }, (response) => {
      if (chrome.runtime.lastError) {
        console.error(`Failed to continue request ${interceptionId}: ${chrome.runtime.lastError.message}`);
      } else {
        console.log(`Continued request ${interceptionId}`);
      }
    });
    return;
  }

  // 仅处理 URL 包含 '/build' 的请求
  if (!url.includes('/build')) {
    // 继续拦截的请求,避免阻塞
    chrome.debugger.sendCommand({ tabId: tabId }, 'Network.continueInterceptedRequest', { interceptionId }, (response) => {
      if (chrome.runtime.lastError) {
        console.error(`Failed to continue request ${interceptionId}: ${chrome.runtime.lastError.message}`);
      } else {
        console.log(`Continued request ${interceptionId}`);
      }
    });
    return;
  }

  console.log(`存储请求信息 ${JSON.stringify({
    url: url,
    method: method,
    headers: headers,
    payload: postData || null,
    type: resourceType,
    responseContent: null
  })}`)

  // 存储请求信息
  interceptedRequests.set(interceptionId, {
    url: url,
    method: method,
    headers: headers,
    payload: postData || null,
    type: resourceType,
    responseContent: null
  });

  // 存储 requestId 到 interceptionId 的映射
  requestIdToInterceptionIdMap.set(requestId, interceptionId);

  console.log(`[background.js] 捕获到请求 - 拦截ID: ${interceptionId}, 类型: ${resourceType}, 方法: ${method}, URL: ${url}`);

  // 继续拦截的请求,确保请求不会被阻塞
  chrome.debugger.sendCommand({ tabId: tabId }, 'Network.continueInterceptedRequest', { interceptionId }, (response) => {
    if (chrome.runtime.lastError) {
      console.error(`Failed to continue request ${interceptionId}: ${chrome.runtime.lastError.message}`);
    } else {
      console.log(`Continued request ${interceptionId}`);
    }
  });
}

/**
 * 处理 Network.responseReceived 事件
 */
function handleResponseReceived(tabId, params) {
  const { requestId, response, type } = params;

  // 检查是否有对应的 interceptionId
  if (!requestIdToInterceptionIdMap.has(requestId)) {
    return;
  }

  const interceptionId = requestIdToInterceptionIdMap.get(requestId);
  const requestInfo = interceptedRequests.get(interceptionId);

  if (!requestInfo) {
    return;
  }

  // 更新请求类型(有时 response.type 可能更准确)
  requestInfo.type = type;

  console.log(`[background.js] 捕获到响应 - 类型: ${type}, URL: ${response.url}`);
}

/**
 * 处理 Network.loadingFinished 事件
 */
function handleLoadingFinished(tabId, params) {
  const { requestId } = params;

  // 检查是否有对应的 interceptionId
  if (!requestIdToInterceptionIdMap.has(requestId)) {
    return;
  }

  const interceptionId = requestIdToInterceptionIdMap.get(requestId);
  requestIdToInterceptionIdMap.delete(requestId);

  const requestInfo = interceptedRequests.get(interceptionId);

  if (!requestInfo) {
    return;
  }

  // 发送命令获取响应内容
  chrome.debugger.sendCommand({ tabId: tabId }, 'Network.getResponseBody', { requestId }, (bodyResponse) => {
    if (chrome.runtime.lastError) {
      console.error(`Failed to get response body for requestId ${requestId}: ${chrome.runtime.lastError.message}`);
      interceptedRequests.delete(interceptionId);
      return;
    }

    const { body, base64Encoded } = bodyResponse;
    const responseContent = base64Encoded ? atob(body) : body;

    console.log(`[background.js] 捕获到响应内容 - 方法: ${requestInfo.method}, URL: ${requestInfo.url}, 内容长度: ${responseContent.length}`);

    // 生成唯一的 uniqueRequestId
    const uniqueRequestId = generateRequestId();

    // 存储完整的请求和响应信息
    console.log(`存储完整的请求和响应信息: ${JSON.stringify({
      requestId: uniqueRequestId,
      url: requestInfo.url,
      method: requestInfo.method,
      payload: requestInfo.payload,
      headers: requestInfo.headers,
      responseContent: responseContent,
      type: requestInfo.type
    })}`);

    interceptedRequests.set(uniqueRequestId, {
      requestId: uniqueRequestId,
      url: requestInfo.url,
      method: requestInfo.method,
      payload: requestInfo.payload,
      headers: requestInfo.headers,
      responseContent: responseContent,
      type: requestInfo.type
    });

    console.log(`[background.js] 请求已存储,uniqueRequestId: ${uniqueRequestId}`);

    // 移除原始的 interceptionId
    interceptedRequests.delete(interceptionId);
  });
}

/**
 * 监听来自弹出页面的消息
 */
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === "getDocumentRequests") {
    // 将 Map 转换为数组进行传输
    const data = Array.from(interceptedRequests.values());
    sendResponse({ data: data });
  } else if (request.action === "clearDocumentRequests") {
    interceptedRequests.clear();
    requestIdToInterceptionIdMap.clear();
    sendResponse({ data: "清除成功" });
    console.log("interceptedRequests 和 requestIdToInterceptionIdMap 已清除");
  }
});

/**
 * 在扩展卸载时,移除所有附加的调试器
 */
chrome.runtime.onSuspend.addListener(() => {
  debuggedTabs.forEach(tabId => {
    chrome.debugger.detach({ tabId: tabId }, () => {
      console.log(`Debugger detached from tab ${tabId}`);
    });
  });
});
 


http://www.ppmy.cn/ops/129714.html

相关文章

Spring Authorization Server基于Spring Session的前后端分离实现

本章实现的效果 授权码模式下使用前后端分离的登录页面和授权确认页面。设备码模式(Device Flow)下使用前后端分离的登录页面、授权确认页面、用户码(user_code)验证页面和用户码(user_code)验证成功页面。 分析 在一年多以前我出了两篇前后端分离的文章,分别是《S…

像素、分辨率、PPI(像素密度)、帧率的概念

文章目录 前言一、像素1、定义2、像素点也不是越多越好 二、分辨率1、定义 三、PPI(像素密度)1、定义2、计算公式3、视网膜屏幕 四、帧率1、帧 (Frame)2、帧数 (Frames)3、帧率 (Frame Rate)4、FPS (Frames Per Second)5、赫兹 五、其他1、英寸2、为何显示器尺寸以英寸命名 总结…

vue 项目中无 router 文件夹,如何安装路由

两种情况: 第一种:创建项目的时候有勾选 router,但是没有生成 router 文件夹; 解决方法: 首先,查看 package.json 中有没有 router 的依赖,如下图: 自行在 src 目录下添加 router…

HarmonyOS开发5.0 net 启动界面设置

第一步、创建我们界面 第二步, 在EntryAbility中配置启动页面,在entry/src/main/ets/entryability/EntryAbility.ets中配置启动页面 配置如下 至此大功告成

react文档阅读笔记

文章目录 一、基础入门1. 创建声明一个组件2. 使用组件3. JSX标签语法4. 给组件添加样式5.在JSX中使用JS代码6.在JS中使用JSX7.内联样式8.条件渲染9.列表渲染10.事件处理11.更新页面(状态记录)12.react的hook函数13.组件间的数据共享 二、React哲学1.将U…

高级java每日一道面试题-2024年10月24日-JVM篇-说一下JVM有哪些垃圾回收器?

如果有遗漏,评论区告诉我进行补充 面试官: 说一下JVM有哪些垃圾回收器? 我回答: 1. Serial收集器 特点:Serial收集器是最古老、最稳定的收集器,它使用单个线程进行垃圾收集工作。在进行垃圾回收时,它会暂停所有用户线程,即St…

EasyExcel自定义下拉注解的三种实现方式

文章目录 一、简介二、关键组件1、ExcelSelected注解2、ExcelDynamicSelect接口(仅用于方式二)3、ExcelSelectedResolve类4、SelectedSheetWriteHandler类 三、实际应用总结 一、简介 在使用EasyExcel设置下拉数据时,每次都要创建一个SheetWr…

CSS复习2

CSS所有样式表都可以在CSS Reference查到。 一、利用阴影制作三角形 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"…