使用SSL时如何在Indy中设置ConnectTimeout / ReadTimeout?
MCVE:
program mcve; uses {$IFDEF UNIX}{$IFDEF UseCThreads} cthreads, {$ENDIF}{$ENDIF}SysUtils, IdHTTP, IdSSLOpenSSL, DateUtils; var HTTP : TIdHTTP; SSL : TIdSSLIOHandlerSocketOpenSSL; Started : TDateTime; begin HTTP := TIdHTTP.Create(); try HTTP.ReadTimeout := 1000; HTTP.ConnectTimeout := 2000; SSL := TIdSSLIOHandlerSocketOpenSSL.Create(HTTP); SSL.ConnectTimeout := HTTP.ConnectTimeout; SSL.ReadTimeout := HTTP.ReadTimeout; SSL.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2]; HTTP.IOHandler := SSL; Started := Now; try HTTP.Get(ParamStr(1)); except On E: Exception do WriteLn(E.Message); end; Writeln(FormatDateTime('hh:nn:ss', SecondsBetween(Started, Now) / SecsPerDay)); finally HTTP.Free; end; end.
使用http
ConnectTimeout / ReadTimeout时,只有在使用时,此问题才能解决https
:
:~$ ./mcve http://x.x.x.x Read timed out. 00:00:01 <-- Correct.
:~$ ./mcve https://x.x.x.x Socket Error # 0 00:03:38 <-- NOT Correct / More than SSL.ReadTimeout value.
从OPM版本10.6.2.5494安装的Lazarus 2.0.6 Indy。
注意:在Windows上,使用带有Delphi的Indy 10.6.2.5366的相同代码,结果将按预期工作
您无需在IOHandler本身上手动设置ConnectTimeout
and ReadTimeout
,而只需在客户端组件(在本例中为TIdHTTP
)上设置。TIdTCPClient.Connect()
将为您分配值给IOHandler。
在ConnectTimeout
创建任何SSL / TLS会话之前将基础套接字连接到服务器时应用,因此无论您是否使用SSL / TLS,它的操作都相同。
在ReadTimeout
当印地尝试读取从IOHandler的内部连接字节适用。当不使用SSL / TLS时,这意味着它直接进入套接字,从而在没有字节到达套接字时超时。但是,当使用SSL / TLS时,Indy使用OpenSSL的旧版SSL_...()
API,而不是其较新的BIO_...()
API,这意味着OpenSSL代表Indy进行自己的套接字读取和缓冲,因此当OpenSSL不提供任何解密的应用程序字节时,Indy超时。
TIdSSLIOHandlerSocketOpenSSL
在Windows与其他平台上的操作方式的一个区别是仅在Windows Vista +上,TIdSSLIOHandlerSocketOpenSSL
它确实通过IOHandler的方法ReadTimeout
将套接字应用于基础套接字SO_RCVTIMEO
和SO_SNDTIMEO
超时Binding.SetSockOpt()
,作为Windows上OpenSSL错误的解决方法。对于其他平台,Indy当前未设置这两个套接字超时。
手动设置这些超时的一个好地方是在IOHandler的OnBeforeConnect
事件中,该事件在套接字连接到服务器之后以及创建任何SSL / TLS会话之前触发。