news 2026/9/8 23:30:36

Puppeteer HTTPResponse.remoteAddress() 深度解析:读取远端服务器 IP 与端口

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Puppeteer HTTPResponse.remoteAddress() 深度解析:读取远端服务器 IP 与端口

Puppeteer HTTPResponse.remoteAddress() 深度解析:读取远端服务器 IP 与端口

【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer

本文围绕 Puppeteer(JavaScript API for Chrome and Firefox)中HTTPResponse.remoteAddress()方法展开,讲解其方法签名、RemoteAddress返回结构、CDP 与 WebDriver BiDi 双协议下的实现差异,并结合仓库源码与测试用例给出可直接运行的监控与调试实战示例。读完本文,你将掌握如何从一次真实网络响应中取得与远端服务器建立连接的 IP 地址与端口号,并理解其在代理、重定向、缓存等边界场景下的取值规律。

一、方法概览:它返回什么

HTTPResponse.remoteAddress()的定义极为简洁——"The IP address and port number used to connect to the remote server"(用于连接远端服务器的 IP 地址与端口号)。需要特别强调的是,它描述的是当前响应实际与之建立 TCP 连接的远端对端地址,而不是 URL 中域名解析出的逻辑地址;当请求经由 HTTP 代理转发时,它反映的是与代理服务器之间连接的远端地址。

方法签名

class HTTPResponse { abstract remoteAddress(): RemoteAddress; }

该方法的 API 说明位于 puppeteer.httpresponse.remoteaddress.md,它是抽象基类HTTPResponse上的抽象方法,返回类型为RemoteAddress。其实际子类会根据底层自动化协议(CDP 或 WebDriver BiDi)给出不同的实现与数据来源,下文第三节与第四节会分别拆解。

RemoteAddress 返回结构

export interface RemoteAddress { ip?: string; port?: number; }

接口定义位于 packages/puppeteer-core/src/api/HTTPResponse.ts,对应官方文档 puppeteer.remoteaddress.md。两个字段均为可选(optional)

  • ip:远端服务器的 IP 地址字符串,可能是 IPv4(如127.0.0.1)或带括号的 IPv6(如[::1])。
  • port:远端服务器的端口号数字。

字段可选的原因有二:其一,部分协议(如 WebDriver BiDi)当前并不暴露该信息,实现只能返回占位值;其二,某些响应(如内存缓存命中)可能不具备完整的连接元数据。因此,消费方代码应对缺失字段做容错处理,不要假定ipport必然存在。

二、在哪里获取 HTTPResponse 对象

remoteAddress()HTTPResponse实例上的方法,因此获取响应对象是使用前提。常用途径有三:

  1. 导航返回page.goto()page.reload()等导航方法会 resolve 为导航产生的主响应。
import puppeteer from 'puppeteer'; const browser = await puppeteer.launch(); const page = await browser.newPage(); const response = await page.goto('https://example.com'); if (response) { console.log('远端地址:', response.remoteAddress()); // 输出示例:{ ip: '93.184.215.14', port: 443 } }

注意:goto()返回的response可能为null——例如跳转到about:blank、data: URL,或发生错误页导航时,没有可用的 HTTP 响应对象。

  1. 监听事件:订阅page.on('response')可以拿到页面发出的每一个子资源(图片、脚本、样式表、XHR/Fetch 等)对应的HTTPResponse,这是做"全站连接审计"的入口。
page.on('response', response => { const {ip, port} = response.remoteAddress(); console.log(`${response.url()} -> ${ip}:${port}`); });
  1. 按条件等待:配合page.waitForResponse()精确捕获某个请求的响应。
const response = await page.waitForResponse(res => res.url().includes('/api/data') ); console.log(response.remoteAddress());

此外,HTTPResponse.request()返回发起该响应的HTTPRequest(见 puppeteer.httpresponse.request.md),结合request().redirectChain()可以逐跳追溯重定向历史中每一跳的远端地址(详见第六节示例)。

三、CDP 实现:数据从哪来

在 Chrome/Chromium(CDP 协议)路径下,响应对象由CdpHTTPResponse实现,文件位于 packages/puppeteer-core/src/cdp/HTTPResponse.ts。构造函数接收 DevTools Protocol 的Network.Response负载(对应 CDP 的Network.responseReceived事件),并从中取出连接元数据:

this.#remoteAddress = { ip: responsePayload.remoteIPAddress, port: responsePayload.remotePort, };

remoteIPAddressremotePort是 CDPNetwork.Response结构体中的字段,由浏览器网络栈在收到响应时填充。remoteAddress()的 getter 只是直接返回这个缓存的快照:

override remoteAddress(): RemoteAddress { return this.#remoteAddress; }

也就是说,该地址在构造响应对象那一刻就已固定,之后不会随连接状态变化而更新,属于一次性快照数据。这一点与securityDetails()(返回 TLS/HTTPS 安全连接详情,见 puppeteer.httpresponse.securitydetails.md)互补:前者回答"连到哪台服务器",后者回答"连接是否加密、证书归属"。

在 packages/puppeteer-core/src/cdp/NetworkManager.ts 附近可以找到CdpHTTPResponse被组装的位置,它接收来自 CDP 事件流的响应负载与 extraInfo 事件;单测 packages/puppeteer-core/src/cdp/HTTPResponse.test.ts 中构造CdpHTTPResponse时亦使用remoteIPAddress: '127.0.0.1'remotePort: 80这类负载字段,印证了数据源结构。

四、WebDriver BiDi 实现:协议缺口的占位值

Firefox 及使用 WebDriver BiDi 协议连接的浏览器走BidiHTTPResponse实现,文件位于 packages/puppeteer-core/src/bidi/HTTPResponse.ts。由于 WebDriver BiDi 标准目前没有提供远端地址的等价字段,该实现对remoteAddress()返回固定的占位值:

@invokeAtMostOnceForArguments override remoteAddress(): RemoteAddress { return { ip: '', port: -1, }; }

这里有两个值得注意的细节:

  1. 占位值约定ip为空字符串、port-1,语义上即"未知/不可用"。跨浏览器编写代码时,应通过判断port === -1 || !ip来识别 BiDi 场景下的无效数据,而不是直接信任返回值。
  2. 协议差距在测试中被固化:仓库的 test/TestExpectations.json 中明确登记了一条针对webDriverBiDi参数组合的期望:
{ "testIdPattern": "[network.test] network Response.remoteAddress *", "platforms": ["darwin", "linux", "win32"], "parameters": ["webDriverBiDi"], "expectations": ["FAIL"], "comment": "Bidi does not have equivalent field" }

注释直接写明"Bidi does not have equivalent field"——即该用例在 BiDi 模式下被标记为预期失败(FAIL),这正是协议能力差异在官方测试矩阵中的显式表达。

因此,若你的自动化脚本需要强依赖远端地址信息(例如做安全审计、代理校验、合规取证),当前应优先使用 Chrome/Chromium + CDP 的组合;如需覆盖 Firefox,则应把该能力视作"尽力而为"。

五、真实调用链与测试用例佐证

官方集成测试位于 test/src/network.test.ts 的Response.remoteAddress分组,两个用例直接验证了上文描述的语义:

用例一:基础取值(should work)

const response = (await page.goto(server.EMPTY_PAGE))!; const remoteAddress = response.remoteAddress(); // Either IPv6 or IPv4, depending on environment. expect( remoteAddress.ip!.includes('::1') || remoteAddress.ip === '127.0.0.1', ).toBe(true); expect(remoteAddress.port).toBe(server.PORT);

测试注释揭示了关键约束:返回的 IP 格式取决于环境——在启用 IPv6 回环的环境下为::1,否则为 IPv4 的127.0.0.1;而端口必须精确等于本地测试服务器的监听端口。这提醒我们在断言远端地址时不要硬编码某一种 IP 字面量,而应允许 IPv4/IPv6 两种形态(判断时可以用includes/正则等宽松方式)。

用例二:重定向场景(should support redirects)

server.setRedirect('/foo.html', '/empty.html'); const response = (await page.goto(FOO_URL))!; const redirectChain = response.request().redirectChain(); expect(redirectChain).toHaveLength(1); expect(redirectChain[0]!.response()!.remoteAddress().port).toBe(server.PORT);

该用例表明:重定向链上每一跳的中间响应都各自持有独立的HTTPResponse(通过request().redirectChain()获取),并且每一跳都可以单独调用remoteAddress()查询其远端地址。

六、实战:连接审计与边界防护

场景一:监测页面实际连接了哪些主机

下面把监听、导航与地址提取组合起来,实现一个"连接目的地审计器",可用于发现页面是否偷偷连向了预期之外的服务器:

import puppeteer from 'puppeteer'; const browser = await puppeteer.launch(); const page = await browser.newPage(); const destinations = new Map<string, string>(); page.on('response', response => { const {ip, port} = response.remoteAddress(); if (ip) { // 同一 URL 可能命中缓存/多次请求,用 URL 作为键覆盖记录 destinations.set(response.url(), `${ip}:${port}`); } }); await page.goto('https://example.com', {waitUntil: 'networkidle0'}); for (const [url, addr] of destinations) { console.log(`${url} -> ${addr}`); } await browser.close();

场景二:识别来自代理的真实对端

remoteAddress()反映的是 TCP 连接的真实对端。若 Chrome 通过--proxy-server走代理访问目标站,返回的ip:port通常是代理服务器而非目标站地址;对比 URL 主机名与返回 IP 是否一致,即可判断流量是否经过代理转发。

场景三:区分缓存命中与网络请求

HTTPResponse还提供了fromCache()(是否来自浏览器磁盘/内存缓存)与fromServiceWorker()(是否由 Service Worker 响应)等方法。将remoteAddress()与这两者结合可以判断:凡是真正走了网络连接的响应,才具备有意义的远端地址;而缓存或 Service Worker 直接返回的响应,其连接元数据往往不完整或已无实际意义,解读时应降低对remoteAddress()的权重。

场景四:跨浏览器适配写法

由于 BiDi 返回{ip: '', port: -1},推荐封装一个统一访问函数:

function describeRemoteAddress(remoteAddress: {ip?: string; port?: number}) { if (!remoteAddress.ip || remoteAddress.port === -1) { return 'unavailable (protocol does not expose remote address)'; } return `${remoteAddress.ip}:${remoteAddress.port}`; }

七、易混淆点与边界小结

  • 与 DNS 结果的差别remoteAddress()返回连接对端地址,若启用代理或负载均衡,可能与 URL 域名直接解析出的 IP 不一致,这是正常现象。
  • IPv4 / IPv6 形态差异:同一站点在不同网络环境下可能返回1.2.3.4[::1]等不同格式,断言与日志输出需兼容。
  • 重定向链逐跳可取:每一跳都有独立的HTTPResponse,可借助request().redirectChain()分别读取,见上文测试用例二。
  • 跨协议可用性不同:CDP 提供真实地址;WebDriver BiDi 返回占位值(ip: ''port: -1),且官方测试矩阵中该能力在 BiDi 参数下被标记为预期失败。
  • securityDetails()互补:需要 TLS 证书信息(签发者、有效期、SAN 等)时调用securityDetails();HTTP 明文场景下该方法返回null

总结

HTTPResponse.remoteAddress()是一把轻量但信息量很大的"连接探针":在 CDP 路径下,它基于Network.ResponseremoteIPAddress/remotePort字段返回 TCP 连接真实对端;在 WebDriver BiDi 路径下受协议限制仅返回占位值。理解其数据来源与协议差异后,你可以将它用于请求来源审计、代理行为验证、缓存判定辅助以及重定向链分析,与HTTPResponse家族的url()headers()securityDetails()fromCache()等方法配合,即可构建完整的响应级网络观测视图。如需查看更多相关 API 定义,可继续阅读 HTTPResponse 类总览 与 RemoteAddress 接口。

【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 23:30:26

res-downloader 使用教程:捕获视频资源并处理加密视频

res-downloader 使用教程&#xff1a;捕获视频资源并处理加密视频 【免费下载链接】res-downloader 视频号、小程序、抖音、快手、小红书、直播流、m3u8、酷狗、QQ音乐等常见网络资源下载! 项目地址: https://gitcode.com/GitHub_Trending/re/res-downloader res-downlo…

作者头像 李华
网站建设 2026/9/8 23:30:10

PROFIBUS DP编码器GSD文件导入与通讯故障排查指南

简介&#xff1a;帝尔编码器TR CMV582M-00022的GSD文件包&#xff0c;面向工业自动化现场调试与PLC编程工程师&#xff0c;解决该型号编码器在PROFINET网络中的设备描述与组态导入问题。压缩包共9个文件&#xff0c;包含5个XML格式的GSDML描述文件&#xff0c;覆盖V2.32至V2.35…

作者头像 李华
网站建设 2026/9/8 23:29:43

离线 IP 定位 10 微秒级响应:ip2region 多语言实战与选型

离线 IP 定位 10 微秒级响应&#xff1a;ip2region 多语言实战与选型 【免费下载链接】ip2region Ip2region is an offline IP-to-Region localization library and IP data management framework with both IPv4 and IPv6 supports, 10-microsecond level query efficiency, x…

作者头像 李华
网站建设 2026/9/8 23:28:42

GitLab自托管实战:从Docker部署到CI/CD高频操作与避坑

1. GitLab是谁&#xff0c;为什么值得折腾先聊一个看着有点傻、但几乎每天都会有人问的问题&#xff1a;GitLab 到底是干嘛的&#xff1f;它本质上就是一套可以自己部署的代码托管平台&#xff0c;跟 GitHub、Gitee 做的事差不多——管理代码仓库、处理合并请求、跑 CI/CD 流水…

作者头像 李华
网站建设 2026/9/8 23:27:26

别再手动扒链接:用 res-downloader 捕获下载 6 类网络资源

别再手动扒链接&#xff1a;用 res-downloader 捕获下载 6 类网络资源 【免费下载链接】res-downloader 视频号、小程序、抖音、快手、小红书、直播流、m3u8、酷狗、QQ音乐等常见网络资源下载! 项目地址: https://gitcode.com/GitHub_Trending/re/res-downloader res-do…

作者头像 李华