4

How i can set ConnectTimeout/ReadTimeout in Indy when using SSL ?

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.

When using http ConnectTimeout/ReadTimeout work fine the issue only when using https see below:

:~$ ./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.

Lazarus 2.0.6 Indy installed from OPM version 10.6.2.5494.

Note: On Windows same code using Delphi with shipped Indy 10.6.2.5366, The results works as expected

RepeatUntil
  • 2,272
  • 4
  • 32
  • 57

1 Answers1

5

You don't need to manually set the ConnectTimeout and ReadTimeout on the IOHandler itself, only on the client component (in this case, TIdHTTP) . TIdTCPClient.Connect() will assign the values to the IOHandler for you.

The ConnectTimeout applies when the underlying socket is being connected to the server, before any SSL/TLS session is created, so it operates the same whether you use SSL/TLS or not.

The ReadTimeout applies when Indy attempts to read bytes from the IOHandler's internal connection. When not using SSL/TLS, that means it goes straight to the socket, and thus times out when no bytes arrive on the socket. But when using SSL/TLS, Indy uses OpenSSL's legacy SSL_...() APIs, not its newer BIO_...() APIs, which means OpenSSL is doing its own socket reading and buffering on Indy's behalf, and thus Indy times out when OpenSSL does not provide any decrypted application bytes.

One difference in how TIdSSLIOHandlerSocketOpenSSL operates on Windows vs other platforms is that on Windows Vista+ only, TIdSSLIOHandlerSocketOpenSSL does apply the ReadTimeout to the underlying socket's SO_RCVTIMEO and SO_SNDTIMEO timeouts via the IOHandler's Binding.SetSockOpt() method, as a workaround to an OpenSSL bug on Windows. For other platforms, Indy does not currently set those two socket timeouts.

A good place to set those timeouts manually would be in the IOHandler's OnBeforeConnect event, which is fired after the socket is connected to the server and before any SSL/TLS session is created.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • How i can set it manually in OnBeforeConnect? i tried this on the OnBeforeConnect event `uses unixtype, sockets;` `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;` but nothing changes. – RepeatUntil Nov 10 '19 at 10:58