为什么在配置 postman 或 K6 等 http 客户端时只代理顶级请求 参考代理服务器代码

问题描述

目标

  • 运行集成测试,但将子请求存根到 API 代码本身中发生的特定域
  • 理想情况下,通过 docker-compose 执行此操作,因为我希望能够在 Circle CI 或 Jenkins 中运行此操作

尝试

我正在使用 here 概述的 mockttp 库来创建代理服务器,该服务器允许所有流量原封不动地通过,但一个特定域除外,我将在该域中获得一组响应。

代理服务器、API 和 K6 服务使用 docker-compose 启动,并在 K6 服务中设置 HTTP(S)_PROXY 变量。

问题

直接请求会被代理,但由这些请求发起的请求不会被代理。例如,如果我对尝试存根的特定域提出请求,代理就会完成它的工作。但该域不是直接调用的,它是 API 代码的一部分,代理在此级别没有任何作用。

如果我启动代理服务器并将我的笔记本电脑配置为使用代理服务器,一切正常。但是,如果我将 Postman 或 K6 配置为使用代理服务器,则不会。

docker-compose.yml

version: '3.7'
services:
  service:
    build:
      context: ./service
    container_name: service
    ports:
      - '3000:3000'
    environment:
      - NODE_TLS_REJECT_UNAUTHORIZED=0
  proxy:
    build:
      context: ./
    container_name: proxy
    ports:
      - '8002:8002'
    environment: 
      - BASE_URL=service:3000
  k6:
    image: loadimpact/k6:latest
    container_name: k6
    depends_on: 
      - service
      - proxy
    volumes: 
      - ./service/test/:/perf
    environment: 
      - HTTPS_PROXY=https://proxy:8002
      - HTTP_PROXY=http://proxy:8002
      - BASE_URL=http://service:3000/foo
      - NODE_TLS_REJECT_UNAUTHORIZED=0
      - K6_HTTP_DEBUG=true
    entrypoint: ['k6','run','--insecure-skip-tls-verify','/perf/test.js']

我确定我遗漏了一些明显的东西,但网络不是我的强项。我的理解是,如果我告诉某事使用代理,所有流量都应该通过代理。我发现如果我在计算机的设置中设置代理,情况确实如此。但是如果我通过配置 K6 甚至 Postman 来尝试这个,行为就会不同。

我做错了什么?

参考代理服务器代码

(async () => {
  const server = await require('mockttp').getLocal();
  server.enableDebug();
  await server.anyRequest().forHost('hostiwanttostub').always().thenReply(201);
  await server.anyRequest().always().thenForwardTo(process.env.BASE_URL);
  await server.start(8002);
  // Print out the server details:
  console.log(`Server running on port ${server.port}`);
})(); // (Run in an async wrapper so we can use top-level await everywhere)

解决方法

首先,在不深入研究 mockttp 代码的情况下,您可能会尝试指定服务协议,即。 http 或 https:

docker-compose.yml

  ...
  proxy:
    environment: 
      - BASE_URL=http://service:3000

其次,您可能希望确保 K6 和 Postman 实际使用 HTTP_PROXY 和 HTTPS_PROXY 环境变量 - 他们可能使用自己的代码,可能没有考虑到这些,并且可能需要通过某些方式配置为使用代理其他配置文件或选项。

如果有任何 HTTP 压缩选项,或替代协议,例如 HTTP/2 或 Websockets,您可能想要禁用它们 - 如果没有提到它在 mockttp 中实现 - 或者您可能想要使用久经考验的代理服务器,例如 squid 或 nginx - 它们都有详细的文档和支持。

代理非常简单——你可以简单地实现你自己的代理,只需打开一个套接字,读取纯文本 HTTP/1.1 标头,匹配“^Host:”行,然后决定重写或添加什么标头以及打开套接字的位置以发送您从中收到的所有内容 - 然后简单地在两个套接字之间中继信息。范围请求和压缩只会让事情变得更加复杂,这需要一些额外的工作。

如果您不确定发生了什么,您可以简单地使用 netcatsocat 之类的实用程序来查看发生了什么:

 $  nc -l -p 8082

将侦听端口 8082 - 然后您可以向它发出请求,它会回显可能有帮助的标头。您只需使用一个简单的 curl http://127.0.0.1:8082 即可对其进行测试。

或者你可以使用 socat 坐在你的代理前面:

$ socat -v TCP-LISTEN:8083 TCP4:myproxy:8082

查看 socat 手册页 - 它也可以用作代理。 -x 执行十六进制转储。您还可以使用 tcpdumptsharkwireshark,这可能会有所启发 - 或者看到您使用的是 Javascript,只需检查调试器中发生的事情。

您还可以检查一件事:

     await server.anyRequest().forHost('http://hostiwanttostub').always().thenReply(201);

同样,不确定mockttp 是如何实现的,但它可能需要您指定协议。有可能(尽管不太可能)您的初始请求是通过 http 传递的,随后的请求是通过 https 传递的 - 并且只有一个被拦截。

Mockttp 的生活目的似乎是作为一个易于设置的 HTTPS 代理 - 还要检查 K6 或 Postman 盲目接受自签名证书的设置,是否已启用 - 或者您是否在他们的证书链中安装了 mockttp 证书 - 他们可能会看到伪造的证书,这可能会改变他们的行为。