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.”
现在,有趣的部分来了:
- 如果根本不使用代理(即不设置 FHttp.ProxyParams 设置)- 一切正常。
- 如果没有设置任何 POST 参数(即空 FParams),但仍然 使用代理 - 一切正常。
- 最奇怪的:如果我正在逐步调试 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:443
的 CONNECT
请求,然后发送 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 BEFORE 在 ARequest.UseProxy
为 [ 的情况下,本地 LUseConnectVerb
变量设置为 not Connected
=58=](还有 ctProxy
)。这样,第二个 POST
请求将断开与代理的连接,并使用新的 CONNECT
请求 re-connect。
我正在尝试使用 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.”
现在,有趣的部分来了:
- 如果根本不使用代理(即不设置 FHttp.ProxyParams 设置)- 一切正常。
- 如果没有设置任何 POST 参数(即空 FParams),但仍然 使用代理 - 一切正常。
- 最奇怪的:如果我正在逐步调试 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:443
的 CONNECT
请求,然后发送 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 BEFORE 在ARequest.UseProxy
为 [ 的情况下,本地LUseConnectVerb
变量设置为not Connected
=58=](还有ctProxy
)。这样,第二个POST
请求将断开与代理的连接,并使用新的CONNECT
请求 re-connect。