Delphi TIdHTTPProxyServer提升“;未知协议“;RSHTTPUnknownProtocol异常

Delphi TIdHTTPProxyServer提升“;未知协议“;RSHTTPUnknownProtocol异常,delphi,indy,indy10,delphi-10.3-rio,Delphi,Indy,Indy10,Delphi 10.3 Rio,我正在用Delphi10.3Rio重新编写一个旧的DelphiXE程序。它使用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

我正在用Delphi10.3Rio重新编写一个旧的DelphiXE程序。它使用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缓存服务。然后在多个浏览器中,我请求和。使用OutputDebugString()可以看到已接受的连接,但随后会引发“未知协议”错误

我在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;
LContext:=TIdHTTPProxyServerContext(ASender.Context);

LContext.FCommand:=ASender.CommandHandler.Command// 你不能仅仅重定向
主机
文件中的域,然后期望事情神奇地工作。代理不是这样工作的

必须显式配置web浏览器以通过HTTP代理发出HTTP请求,以便它们格式化代理能够理解的正确请求。直接向目标web服务器发送HTTP请求的处理方式与通过代理发送相同的HTTP请求的处理方式不同

您将获得异常,因为浏览器请求没有正确地针对您的代理

例如,当浏览器将HTTP
GET
请求直接发送到目标web服务器时,它将直接连接到该服务器,然后发送一个类似以下内容的请求:

GET /path HTTP/1.1
Host: server.com
但是,当它通过HTTP代理发送相同的请求时,它会连接到代理并发送一个看起来更像这样的请求:

GET http://server.com/path HTTP/1.1
浏览器请求中缺少
GET
行中的额外路径信息,因为您没有将浏览器配置为代理,因此
TIdHTTPProxyServer
尝试确定连接到目标web服务器并将当前请求转发给它所需的信息时出现异常

这从根本上决定了HTTP的工作方式,以及
TIdHTTPProxyServer
的设计工作方式

当涉及到HTTPS时,事情会有点复杂,但我现在不考虑这个细节,因为它与您关于异常的问题无关

更新:在评论中,您说:

在XE版本中,它在检查协议时从未引发异常,因为我在DoHTTPBeforeCommand中手动设置了主机和端口,该协议今天仍然有效

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

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

要恢复旧的行为,您必须更改
TIdHTTPProxyServer
的源代码,将异常的引发延迟到
DoHTTPBeforeCommand()
返回之后,这样您就有机会再次填充缺少的值


如果你这样做,我可以考虑把它添加到Indy的官方代码中。 好的,当我在Delphi XE中编写这个程序时,TIdHTTPProxyServer的行为不同。我需要监视一个域并在请求某些文件时替换不同的文件,它只需很少的代码就可以很好地工作,这非常令人惊讶。看起来你几年前重写了commandpass,但现在对我的程序不起作用。我应该研究TIdMappedPortTCP还是TIdHTTPServer并自己处理所有事情?我无法将浏览器设置为使用代理,因为这些GET请求也将来自不同的应用程序,因此我选择了主机文件方式。谢谢非常感谢您的详细解释,雷米!看起来TIdHTTPProxyServer现在是一个真正的代理服务器。实际上,我正在重新考虑我的项目,在不修改hosts文件的情况下使用它,这有两个优点。如果没有,我将编写自己的TIdCmdTCPServer,或者只使用TIdHTTPServer。谢谢你的帮助,Indy是一个很棒的套接字库,这就是为什么我仍然使用Delphi@Jack“当我在Delphi XE中制作这个程序时,TIdHTTPProxyServer的表现不同”——不,它没有。“看起来你在几年前重写了CommandPassThrough”-内部实现多年来一直在改变,但HTTP代理的语义没有改变
TIdHTTPProxyServer
始终要求客户端在请求行中指定绝对URL,即使它并不总是验证URL的指定协议。Web浏览器不会请求绝对URL,除非他们知道自己是通过HTTP代理连接的。@Jack“我应该查看TIdMappedPortTCP或TIdHTTPServer并自己处理所有事情吗?”-根据您的描述,我不得不说是的。我有一个8年以上的程序,每天都在使用,XE中的TIdHTTPProxyServer的行为与今天的TIdHTTPProxyServer不同。在XE版本中,它在检查协议时从未引发异常,因为我在DoHTTPBeforeCommand中手动设置了主机和端口,该协议今天仍然有效。因此,TIdHTTPProxyServer现在的行为必须有所不同。再次感谢你的帮助!