TIdHTTPProxyServer 引发 "Unknown Protocol" RSHTTPUnknownProtocol 异常

TIdHTTPProxyServer raising "Unknown Protocol" RSHTTPUnknownProtocol exception

我正在使用 Delphi 10.3 Rio 重新编码旧的 Delphi XE 程序。它使用 TIdHTTPProxyServer Indy 组件监听 127.0.0.1:80.

  with IdHTTPProxyServer.Bindings.Add do begin

    IP := '127.0.0.1';
    Port := 80;

  end;

  IdHTTPProxyServer.Active := True;

为了测试,我在主机文件中添加了 127.0.0.1 localtest123.com 和 127.0.0.1 www.localtest123.com 并禁用了 DNS 缓存服务。然后在多个浏览器中我请求 http://localtest123.com/ and http://www.localtest123.com/。使用 OutputDebugString() 我可以看到已接受连接,但随后引发 "Unknown Protocol" 错误。

我在 IdHTTPProxyServer.pas 中调试了 TIdHTTPProxyServer.CommandPassThrough 过程中的异常。似乎 LURI.Protocol 是一个空字符串,这就是引发 RSHTTPUnknownProtocol 的原因。

  LContext := TIdHTTPProxyServerContext(ASender.Context);
  LContext.FCommand := ASender.CommandHandler.Command; //<-'GET'
  LContext.FTarget := ASender.Params.Strings[0]; //<-'/'

  LContext.FOutboundClient := TIdTCPClient.Create(nil);
  try
    LURI := TIdURI.Create(LContext.Target); //<-'/'
    try
      TIdTCPClient(LContext.FOutboundClient).Host := LURI.Host; //<-''

      if LURI.Port <> '' then begin //<-''
        TIdTCPClient(LContext.FOutboundClient).Port := IndyStrToInt(LURI.Port, 80);
      end
      else if TextIsSame(LURI.Protocol, 'http') then begin //<-''    {do not localize}
        TIdTCPClient(LContext.FOutboundClient).Port := IdPORT_HTTP;
      end
      else if TextIsSame(LURI.Protocol, 'https') then begin //<-'' {do not localize}
        TIdTCPClient(LContext.FOutboundClient).Port := IdPORT_https;
      end else begin
        raise EIdException.Create(RSHTTPUnknownProtocol);
      end;

我可能遗漏了一些东西,但是 TIdHTTPProxyServer 不需要太多代码就可以工作,所以我不得不寻求有关此异常的帮助。提前致谢!

您不能只重定向 HOSTS 文件中的域并期望事情神奇地起作用。这不是代理的工作方式。

您必须明确配置 Web 浏览器以通过 HTTP 代理发出 HTTP 请求,以便它们格式化代理可以理解的正确请求。将 HTTP 请求直接发送到目标 Web 服务器的处理方式不同于通过代理发送相同的 HTTP 请求。

您收到异常是因为浏览器请求未正确定位您的代理。

例如,当浏览器直接向目标 Web 服务器发送 HTTP GET 请求时,它会直接连接到该服务器,然后发送如下所示的请求:

GET /path HTTP/1.1
Host: server.com

但是,当它通过 HTTP 代理发送相同的请求时,它会连接到代理并发送看起来更像这样的请求:

GET http://server.com/path HTTP/1.1

您的浏览器请求中缺少 GET 行中的额外路径信息,因为您没有将浏览器配置为代理,因此当 TIdHTTPProxyServer 试图确定信息时出现异常它需要连接到目标 Web 服务器并将当前请求转发给它。

这是 HTTP 工作原理的根本,也是 TIdHTTPProxyServer 设计工作原理的基础。

当涉及到 HTTPS 时,事情会稍微复杂一些,但我暂时不考虑这个细节,因为它与您关于异常的问题无关。

更新:在评论中,您说:

In the XE version it never raised an exception when checking for the protocol which would still work today because I manually set the host and port in DoHTTPBeforeCommand.

在那个旧版本中,没有引发异常,因为TIdHTTPProxyServer还没有检查协议来区分 HTTP 和 HTTPS。当收到不是专门针对您的代理的请求时,您可以手动填写缺失的信息。这就是为什么事情以前对你有用。

在更高版本中,TIdHTTPProxyServer 已更新以在请求中未明确指定端口时区分 HTTP 和 HTTPS,因此默认端口是根据请求的协议设置的。该检查发生在 DoHTTPBeforeCommand() 被调用之前。

要恢复旧行为,您必须更改 TIdHTTPProxyServer 的源代码以将异常的引发延迟到 DoHTTPBeforeCommand() returns 之后,因此您有一个有机会再次填写缺失值。

如果您 file a feature request 这样做,我可能会考虑将其添加到 Indy 的官方代码中。