Search code examples
delphidownloadindy

How to download file with Indy. Error "iohandler value is not valid"


I had this function that worked for HTTP. Now, with the wide spread of HTTPS it didn't work anymore. I was getting an "IOHandler value is not valid" error.

So, I created an SSLIOHandler object as shown below, but now I get a new error:

Exception class EIdOSSLUnderlyingCryptoError with message 'Error connecting with SSL. error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version'

How do I setup the SSLIOHandler object properly?

{ Only suitable for small files because the whole download will be saved to RAM before writing it to disk. }
function DownloadFile(CONST SourceURL, DestFileName: string; OUT ErrorMsg: String): Boolean;
VAR
  Indy: TIDHTTP;
  Stream: TFileStream;
  IDAntiFreeze: TIDAntiFreeze;
  IOHandler: TIdSSLIOHandlerSocketOpenSSL;
begin
  Indy:= TIDHTTP.Create(NIL);
  IOHandler:= TIdSSLIOHandlerSocketOpenSSL.Create(NIL);
  IDAntiFreeze:= TIDAntiFreeze.Create(NIL);
  Stream:= TFileStream.Create(DestFileName, fmCreate);
  TRY
    IOHandler.Destination := SourceURL;
    IOHandler.Host := SourceURL;
    IOHandler.MaxLineAction := maException;
    //IOHandler.Port := 587;
    //IOHandler.DefaultPort := 0;
    //IOHandler.OnStatusInfo := SSLIOHandlerStatusInfo;

    Indy.ConnectTimeout          := 0;                   { Indy default value: zero }
    Indy.ReadTimeout             := -1;                  { Indy default value: -1 }
    Indy.HandleRedirects         := TRUE;                { http://stackoverflow.com/questions/4549809/indy-idhttp-how-to-handle-page-redirects }
    Indy.AllowCookies            := FALSE;
    Indy.Request.UserAgent       := 'Mozilla/4.0';
    Indy.Request.Connection      := 'Keep-Alive';
    Indy.Request.ProxyConnection := 'Keep-Alive';
    Indy.Request.CacheControl    := 'no-cache';
    Indy.IOHandler:= IOHandler;

    // Indy.OnWork:= HttpWork;

    TRY
      Indy.Get(SourceURL, Stream);
      Result:= TRUE;
    EXCEPT
      On E: Exception DO
        begin
          Result:= FALSE;
          ErrorMsg := E.Message + ' (' + IntToStr(Indy.ResponseCode) + ')';
        end;
    END;
  FINALLY
    FreeAndNil(Stream);
    FreeAndNil(IDAntiFreeze);
    FreeAndNil(IOHandler);
    FreeAndNil(Indy);
  END;
end;

PS: The libeay32.dll and ssleay32.dll are present in the app's folder.


Solution

  • You are not configuring the TIdSSLIOHandlerSocketOpenSSL's TLS settings in any way. By default, it enables only TLS 1.0 (known issue: https://github.com/IndySockets/Indy/issues/181), but chances are that the website in question requires TLS 1.1+ instead.

    Try adding this:

    IOHandler.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2];
    

    Also, just on a side note, you DO NOT need to configure the IOHandler's Destination, Host, or Port properties at all. TIdHTTP will handle that internally for you.