[英]POST with Indy + SSL + Proxy
I'm trying to do a POST request through proxy using https.我正在尝试使用 https 通过代理发出 POST 请求。 Code looks like:代码如下:
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);
Proxy server is Squid.代理服务器是 Squid。
Code generates error "Socket Error # 10054 Connection reset by peer."代码生成错误“Socket Error # 10054 Connection reset by peer。”
Now, the interesting part comes:现在,有趣的部分来了:
POST parameters are not sent properly for some reason? POST 参数由于某种原因没有正确发送?
And why step 3 is happening?为什么会发生第 3 步?
Indy is up to date, just pulled from repository Indy 是最新的,刚刚从存储库中提取
UPDATE更新
After intercepting TIdHTTP calls (thanks Remy ) there is a little bit more clarity.拦截 TIdHTTP 调用(感谢Remy )后,清晰度会更高一些。 ( failing log , working log ). ( 失败日志, 工作日志)。
Short version: when doing debug, Indy does 3 CONNECT + POST + DISCONNECT requests (because there are redirection on the service I believe) and it works.简短版本:在进行调试时,Indy 会执行 3 个 CONNECT + POST + DISCONNECT 请求(因为我相信服务上有重定向)并且它可以工作。
When running test without debug - CONNECT + DISCONNECT + POST - and it fails obviously (ie POST is executed without CONNECT in front).在没有调试的情况下运行测试时 - CONNECT + DISCONNECT + POST - 它显然失败了(即在前面没有 CONNECT 的情况下执行 POST)。 See attached log files for details.有关详细信息,请参阅附加的日志文件。
You have found some logic bugs in TIdHTTP
that need to be fixed.您在TIdHTTP
中发现了一些需要修复的逻辑错误。 I have opened a new ticket for that:我为此开了一张新票:
#315: Bugs in TIdHTTP proxy handling #315:TIdHTTP 代理处理中的错误
Here is what I see happening in your "failing" scenario:这是我在您的“失败”场景中看到的情况:
TIdHTTP
connects to the proxy, sends a CONNECT
request that successfully connects to my.service.com:443
, then sends a POST
request (using HTTP 1.0 rather than HTTP 1.1 a ). TIdHTTP
连接代理,发送CONNECT
请求成功连接到my.service.com:443
,然后发送POST
请求(使用 HTTP 1.0 而不是Z293C9EA246FF9985DC6F62A1650)
a) to send a POST
request with HTTP 1.1, you have to set the TIdHTTP.ProtocolVersion
property to pv1_1
, AND enable the hoKeepOrigProtocol
flag in the TIdHTTP.HTTPOptions
property. a) 要使用 HTTP 1.1 发送POST
请求,您必须将TIdHTTP.ProtocolVersion
属性设置为pv1_1
,并在TIdHTTP.HTTPOptions
属性中启用hoKeepOrigProtocol
标志。 Otherwise, TIdHTTP.Post()
forces the ProtocolVersion
to pv1_0
.否则, TIdHTTP.Post()
将ProtocolVersion
强制为pv1_0
。
The HTTP server replies with a 302 Found
response redirecting to a different URL, including a Keep-Alive
header indicating the server will close the connection if a new request is not sent in the next 5 seconds. HTTP 服务器回复302 Found
响应重定向到不同的 URL,包括Keep-Alive
header,如果在下一个请求中未发送新连接,则表明服务器将关闭。
When TIdHTTP
is done processing the POST
response, it knows it is going to re-send the same request to a new URL.当TIdHTTP
完成对POST
响应的处理后,它知道它将向新的 URL 重新发送相同的请求。 On the next loop iteration, it sees that the target server is the same, and the proxy is still connected, and so the connection is not closed, and the code that would have sent a new CONNECT
request is skipped.在下一次循环迭代中,它看到目标服务器是相同的,并且代理仍然连接,因此连接没有关闭,并且会发送新的CONNECT
请求的代码被跳过。
Just before the POST
request is sent, the Response.KeepAlive
property is checked to know whether or not to close the socket connection anyway.就在发送POST
请求之前,会检查Response.KeepAlive
属性以确定是否要关闭套接字连接。 The KeepAlive
property getter sees the ProtocolVersion
property is pv1_0
and that there is no Proxy-Connection: keep-alive
header present in the response (even though there is a Connection: keep-alive
header), so it returns False, and then the socket connection is closed. KeepAlive
属性 getter 看到ProtocolVersion
属性是pv1_0
并且没有Proxy-Connection: keep-alive
header 出现在响应中(即使有一个Connection: keep-alive
标头),所以它返回 False,然后是套接字连接关闭。
TIdHTTP
then re-connects to the proxy again, but does not send a new CONNECT
request before sending the POST
request. TIdHTTP
然后再次重新连接到代理,但在发送POST
请求之前不会发送新的CONNECT
请求。 The proxy does not know what to do with the POST
, so it fails the request with a 400 Bad Request
response.代理不知道如何处理POST
,因此它以400 Bad Request
响应使请求失败。
Here is what I see happening in your "working" scenario:这是我在您的“工作”场景中看到的情况:
Everything is the same as above, up to the point where the 1st POST
request is processed.一切都与上面相同,直到处理第一个POST
请求。 Then there is a delay of roughly 16 seconds (likely since you are stepping through code) - more than the 5-second Keep-Alive
delay allows - so the HTTP server closes its connection with the proxy, which then closes its connection to TIdHTTP
.然后会有大约 16 秒的延迟(可能是因为您正在单步执行代码) - 超过 5 秒的Keep-Alive
延迟允许 - 所以 HTTP 服务器关闭与代理的连接,然后关闭与TIdHTTP
的连接。
By the time TIdHTTP
is ready to send the 2nd POST
request, it knows it has been disconnected from the proxy, so it re-connects to the proxy, sends a new CONNECT
request, and then sends the POST
request.当TIdHTTP
准备发送第二个POST
请求时,它知道它已经与代理断开连接,所以它重新连接到代理,发送一个新的CONNECT
请求,然后发送POST
请求。
Until I can fix the bugs properly, try the following:在我可以正确修复错误之前,请尝试以下操作:
enable the hoKeepOrigProtocol
flag in the TIdHTTP.HTTPOptions
property to allow TIdHTTP.Post()
to use HTTP 1.1.在TIdHTTP.HTTPOptions
属性中启用hoKeepOrigProtocol
标志以允许TIdHTTP.Post()
使用 HTTP 1.1。 That in itself may fix the issue with the connection being closed unnecessarily before sending the 2nd POST
request to the redirected URL.这本身可能会解决在将第二个POST
请求发送到重定向的 URL 之前不必要地关闭连接的问题。
if that doesn't solve the issue, try editing IdHTTP.pas
yourself and recompile Indy, to update the TIdCustomHTTP.ConnectToHost()
method to force a Disconnect()
if the Response.KeepAlive
property is False BEFORE the local LUseConnectVerb
variable is set to not Connected
in the case where ARequest.UseProxy
is ctSSLProxy
(and ctProxy
, too).如果这不能解决问题,请尝试自己编辑IdHTTP.pas
并重新编译 Indy,以更新TIdCustomHTTP.ConnectToHost()
方法以在本地LUseConnectVerb
变量设置为之前Response.KeepAlive
属性为 False 时强制Disconnect()
在ARequest.UseProxy
是ctSSLProxy
(和ctProxy
也是)的情况下not Connected
。 That way, the 2nd POST
request will disconnect from the proxy and re-connect with a new CONNECT
request.这样,第二个POST
请求将与代理断开连接,并使用新的CONNECT
请求重新连接。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.