POST 使用 Indy + SSL + 代理

POST with Indy + SSL + Proxy

我正在尝试使用 https 通过代理执行 POST 请求。 代码如下:

  FHttp := TIdHttp.Create(nil);

  FHttp.ProxyParams.ProxyServer := Host;
  FHttp.ProxyParams.ProxyPort := Port;
  FHttp.ProxyParams.ProxyUsername := User;
  FHttp.ProxyParams.ProxyPassword := Password;

  FHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  FHandler.SSLOptions.Method := sslvTLSv1_2;
  FHandler.PassThrough := true;
  FHttp.IOHandler := FHandler;
  FHttp.HandleRedirects := true;
  FHttp.Request.ContentType := 'application/x-www-form-urlencoded';
  FHttp.Request.Connection := 'keep-alive';
  FHttp.Request.ProxyConnection := 'keep-alive';
...

  FParams.Add('username=user');
  FParams.Add('password=pwd');
  FHttp.Post('https://my.service/login', FParams);

代理服务器是 Squid。

代码生成错误“Socket Error #10054 Connection reset by peer.”

现在,有趣的部分来了:

  1. 如果根本不使用代理(即不设置 FHttp.ProxyParams 设置)- 一切正常。
  2. 如果没有设置任何 POST 参数(即空 FParams),但仍然 使用代理 - 一切正常。
  3. 最奇怪的:如果我正在逐步调试 Indy 代码(TIdCustomHTTP.DoRequest 方法)- 上述示例一切正常(代理设置 + 参数)。

POST参数由于某种原因没有正确发送?

为什么要进行第 3 步?

Indy 是最新的,刚从存储库中提取

更新

拦截 TIdHTTP 调用后(感谢 Remy) there is a little bit more clarity. (failing log, working log)。

简短版本:在进行调试时,Indy 执行 3 CONNECT + POST + DISCONNECT 请求(因为我相信服务上有重定向)并且它有效。

当 运行 测试时没有调试 - CONNECT + DISCONNECT + POST - 它显然失败了(即 POST 在前面没有 CONNECT 的情况下执行)。 有关详细信息,请参阅附加的日志文件。

您在 TIdHTTP 中发现了一些需要修复的逻辑错误。我为此开了一张新票:

#315: Bugs in TIdHTTP proxy handling


这是我在您的“失败”场景中看到的情况:

TIdHTTP 连接到代理,发送成功连接到 my.service.com:443CONNECT 请求,然后发送 POST 请求(使用 HTTP 1.0 而不是 HTTP 1.1 a).

a) 要使用 HTTP 1.1 发送 POST 请求,您必须将 TIdHTTP.ProtocolVersion 属性 设置为 pv1_1,并启用hoKeepOrigProtocol 中的标志 TIdHTTP.HTTPOptions 属性。否则,TIdHTTP.Post()ProtocolVersion 强制为 pv1_0

HTTP 服务器回复一个 302 Found 响应重定向到另一个 URL,包括一个 Keep-Alive header 表示如果有新请求服务器将关闭连接在接下来的 5 秒内不会发送。

TIdHTTP 处理完 POST 响应后,它知道它将 re-send 对新 URL 的相同请求。在下一个循环迭代中,它看到目标服务器是相同的,并且代理仍然连接,因此连接没有关闭,并且将跳过发送新的 CONNECT 请求的代码。

就在 POST 请求发送之前,检查 Response.KeepAlive 属性 以了解是否关闭套接字连接。 KeepAlive 属性 getter 看到 ProtocolVersion 属性 是 pv1_0 并且不存在 Proxy-Connection: keep-alive header在响应中(即使有Connection: keep-alive header),所以它returns False,然后关闭套接字连接。

TIdHTTP 然后 re-connects 再次发送给代理,但在发送 POST 请求之前不会发送新的 CONNECT 请求。代理不知道如何处理 POST,因此它以 400 Bad Request 响应使请求失败。


这是我在您的“工作”场景中看到的情况:

一切都和上面一样,直到第一个 POST 请求被处理。然后会有大约 16 秒的延迟(可能是因为您正在单步执行代码)——超过 5 秒 Keep-Alive 延迟允许的延迟——因此 HTTP 服务器关闭与代理的连接,然后关闭与代理的连接TIdHTTP.

TIdHTTP 准备好发送第二个 POST 请求时,它知道它已经与代理断开连接,所以它 re-connects 到代理,发送一个新的CONNECT请求,然后发送POST请求。


在我可以正确修复错误之前,请尝试以下操作:

  • 启用 TIdHTTP.HTTPOptions 属性 中的 hoKeepOrigProtocol 标志以允许 TIdHTTP.Post() 使用 HTTP 1.1。这本身可以解决在将第二个 POST 请求发送到重定向的 URL.

    之前不必要地关闭连接的问题
  • 如果这不能解决问题,请尝试自己编辑 IdHTTP.pas 并重新编译 Indy,更新 TIdCustomHTTP.ConnectToHost() 方法以强制 Disconnect() 如果Response.KeepAlive 属性 为 False BEFOREARequest.UseProxy 为 [ 的情况下,本地 LUseConnectVerb 变量设置为 not Connected =58=](还有 ctProxy)。这样,第二个 POST 请求将断开与代理的连接,并使用新的 CONNECT 请求 re-connect。