如何在Indy SSL中设置ConnectTimeout/ReadTimeout

4
如何在使用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相同的代码,结果符合预期。

1个回答

5
您无需在IOHandler本身上手动设置ConnectTimeout和ReadTimeout,只需在客户端组件(在此情况下为TIdHTTP)上进行设置。TIdTCPClient.Connect()会为您分配IOHandler的值。
当底层套接字连接到服务器之前,任何SSL/TLS会话创建时,ConnectTimeout将应用,因此无论是否使用SSL/TLS都是如此。
ReadTimeout适用于Indy尝试从IOHandler的内部连接读取字节时。当不使用SSL/TLS时,它直接进入套接字,因此当套接字上没有字节到达时会超时。但是,在使用SSL/TLS时,Indy使用OpenSSL的旧版SSL_...() API,而不是较新的BIO_...() API,这意味着OpenSSL正在代表Indy进行其自己的套接字读取和缓冲,并且因此当OpenSSL未提供任何解密的应用程序字节时,Indy会超时。
TIdSSLIOHandlerSocketOpenSSL在Windows与其他平台上操作的一个差异是,在仅限Windows Vista+的平台上,TIdSSLIOHandlerSocketOpenSSL通过IOHandler的Binding.SetSockOpt()方法将ReadTimeout应用于底层套接字的SO_RCVTIMEO和SO_SNDTIMEO超时,作为对Windows上的OpenSSL错误的一种解决方法。对于其他平台,Indy当前不设置这两个套接字超时。
手动设置这些超时的好地方是在IOHandler的OnBeforeConnect事件中,在该事件触发时,套接字已连接到服务器并且尚未创建任何SSL/TLS会话。

我怎样可以在OnBeforeConnect中手动设置它呢?我试过了OnBeforeConnect事件: var time : timeval; begin time.tv_sec := 5000; time.tv_usec := 0; fpsetsockopt(ASender.Binding.Handle, Id_SOL_SOCKET, Id_SO_RCVTIMEO, @time, SizeOf(time)); fpsetsockopt(ASender.Binding.Handle, Id_SOL_SOCKET, Id_SO_SNDTIMEO, @time, SizeOf(time)); end;``` 但是没有任何改变。 - RepeatUntil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接