我正在尝试使用 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。”
现在,有趣的部分来了:
POST 参数由于某种原因没有正确发送?
为什么会发生第 3 步?
Indy 是最新的,刚刚从存储库中提取
更新
拦截 TIdHTTP 调用后(感谢 Remy )有一点更清晰。 (failing log、working log)。
简短版本:在进行调试时,Indy 会执行 3 个 CONNECT + POST + DISCONNECT 请求(因为我相信服务上有重定向)并且它可以工作。
在没有调试的情况下运行测试时 - CONNECT + DISCONNECT + POST - 它显然失败了(即在前面没有 CONNECT 的情况下执行 POST)。
有关详细信息,请参阅附加的日志文件。
最佳答案
您在 TIdHTTP
中发现了一些逻辑错误。需要修复。我为此开了一张新票:
#315: Bugs in TIdHTTP proxy handling
这是我在您的“失败”场景中看到的情况:TIdHTTP
连接到代理,发送 CONNECT
成功连接到 my.service.com:443
的请求,然后发送 POST
请求(使用 HTTP 1.0 而不是 HTTP 1.1 a)。
a) 发送 POST
使用 HTTP 1.1 请求,您必须设置 TIdHTTP.ProtocolVersion
属性(property)给 pv1_1
, 并启用 hoKeepOrigProtocol
TIdHTTP.HTTPOptions
中的标志属性(property)。否则,TIdHTTP.Post()
强制ProtocolVersion
至pv1_0
.
HTTP 服务器回复 302 Found
重定向到不同 URL 的响应,包括 Keep-Alive
header 表示如果在接下来的 5 秒内没有发送新请求,服务器将关闭连接。
当TIdHTTP
处理完 POST
响应,它知道它将重新发送相同的请求到一个新的 URL。在下一次循环迭代中,它看到目标服务器是相同的,并且代理仍然连接,因此连接没有关闭,并且会发送新的 CONNECT
的代码请求被跳过。
就在 POST
之前请求已发送,Response.KeepAlive
检查属性以确定是否关闭套接字连接。 KeepAlive
属性 getter 看到 ProtocolVersion
属性是 pv1_0
并且没有Proxy-Connection: keep-alive
响应中存在 header (即使有 Connection: keep-alive
header ),因此它返回 False,然后关闭套接字连接。TIdHTTP
然后再次重新连接到代理,但不发送新的 CONNECT
发送 POST
之前的请求要求。代理不知道如何处理 POST
,因此请求失败并返回 400 Bad Request
回复。
这是我在您的“工作”场景中看到的情况:
一切都和上面一样,直到第一个 POST
请求被处理。然后会有大约 16 秒的延迟(可能是因为您正在单步执行代码) - 超过 5 秒 Keep-Alive
延迟允许 - 所以 HTTP 服务器关闭它与代理的连接,然后关闭它到 TIdHTTP
的连接.
到时候TIdHTTP
准备发送第二个POST
请求,它知道它已经与代理断开连接,所以它重新连接到代理,发送一个新的CONNECT
请求,然后发送 POST
要求。
在我可以正确修复错误之前,请尝试以下操作:
hoKeepOrigProtocol
TIdHTTP.HTTPOptions
中的标志允许的属性TIdHTTP.Post()
使用 HTTP 1.1。这本身可能会解决在发送第二个 POST
之前不必要地关闭连接的问题。请求重定向的 URL。IdHTTP.pas
自己重新编译 Indy,以更新 TIdCustomHTTP.ConnectToHost()
强制 Disconnect()
的方法如果 Response.KeepAlive
属性在本地 LUseConnectVerb
之前为 False变量设置为 not Connected
在 ARequest.UseProxy
的情况下是 ctSSLProxy
(和 ctProxy
也是)。这样,第二个 POST
请求将断开与代理的连接并使用新的 CONNECT
重新连接要求。关于delphi - 使用 Indy + SSL + 代理发布,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62836672/