Search code examples
delphidelphi-xe5

Error downloading protocol with https : // - Delphi


I wonder how do I download files with protocol https: // in Delphi . I'm using idhttp , but when I click to download it returns an error code :

-1

After clicking the button, a few seconds it terminates the application.

uses: IdHTTP, IdSSLOpenSSL;

IdHTTP1 := TIdHTTP.create(nil);
 try
            IdHTTP1.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);  
            IdHTTP1.Request.Accept := 'text/html, */*';
            IdHTTP1.Request.UserAgent := 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0';
            IdHTTP1.Request.ContentType := 'application/x-www-form-urlencoded';
            IdHTTP1.HandleRedirects := True;
            IdHTTP1.get(FNomeArq,MS);
            Ms.Seek(0,soFromBeginning);
            header := IdHTTP1.Response.ContentType;
 except on E : EIdHTTPProtocolException do begin
             showmessage(intToStr(IdHTTP1.ResponseCode));
            end;
            on E: EIdSocketError do begin
                showmessage(intToStr(IdHTTP1.ResponseCode));
            end;
            on E: Exception do begin
              showmessage(intToStr(IdHTTP1.ResponseCode));
            end;
 end;

enter image description here


Solution

  • First, your error handling is wrong. You are displaying only the TIdHTTP.ResponseCode property value and nothing else. The only exception where the ResponseCode will have a meaningful value is EIdHTTPProtocolException (and even then, you should get the value from the EIdHTTPProtocolException.ErrorCode property). The ResponseCode is not likely to be populated on any other exception, which is clearly the case in your situation. Since you are going to the trouble of catching different types of exceptions, you should display error messages that are meaningful to those particular types, eg:

    except
      on E: EIdHTTPProtocolException do begin
        ShowMessage('HTTP request failed'#13 + IntToStr(E.ErrorCode) + ' ' + E.Message);
      end;
      on E: EIdOpenSSLAPISSLError do
      begin
        ShowMessage('OpenSSL API failure'#13 + E.ClassName + #13'Result Code: ' + IntToStr(E.RetCode) + #13'Error Code: ' + IntToStr(E.ErrorCode));
      end;
      on E: EIdOpenSSLAPICryptoError do
      begin
        ShowMessage('OpenSSL crypto failure'#13 + E.ClassName + #13'Error Code: ' + IntToStr(E.ErrorCode));
      end;
      on E: EIdOpenSSLError do
      begin
        ShowMessage('Unknown OpenSSL error'#13 + E.ClassName + #13 + E.Message);
      end;
      on E: EIdSocketError do begin
        ShowMessage('Socket error'#13 + E.ClassName + #13'Error Code: ' + IntToStr(E.LastError) + ' ' + E.Message);
      end;
      on E: Exception do begin
        ShowMessage('Unexpected error'#13 + E.ClassName + #13 + E.Message);
      end;
    end;
    

    Second, if you are running your app on a desktop system (Windows or OSX), you are leaking your TIdSSLIOHandlerSocketOpenSSL object, as you are not assigning an Owner to it, and TIdHTTP does not take ownership of it. If you are running your app on a mobile system instead (iOS or Android), your code will crash, because there is no reference to keep the TIdSSLIOHandlerSocketOpenSSL object alive after it is created. The TIdTCPConnection.IOHandler and TIdTCPConnection.Socket properties use weak referencing on those platforms, so the object's reference count will not be incremented, and it will end up being destroyed prematurely. If you don't assign an Owner, you need to free the object explicitly, such as in a try/finally like Zam's answer demonstrates.

    Alternatively, if you are using an up-to-date version of Indy, you can take advantage of a new HTTPS feature that was added to TIdHTTP earlier this year so you don't have to create the IOHandler object explicitly:

    New HTTPS functionality for TIdHTTP

    Third, regarding the actual exception you are getting, EIdOSSLCouldNotLoadSSLLibrary means exactly what its name suggests. The OpenSSL libraries could not be loaded. This means either the DLLs (libeay32.dll and ssleay32.dll) could not be found, or they do not export everything that Indy looks for. You can call the WhichFailedToLoad() function in the IdSSLOpenSSLHeaders unit to find out why OpenSSL could not be loaded:

    except
      //...
      on E: EIdOSSLCouldNotLoadSSLLibrary do
      begin
        ShowMessage('Could not load OpenSSL library'#13'Failed to load: ' + WhichFailedToLoad);
      end;
      //...
    end;
    

    Make sure you have a compatible version of the OpenSSL DLLs installed, either in your application's folder, in a folder on the OS search path, or in a folder that you specify to Indy via its IdOpenSSLSetLibPath() function in the IdSSLOpenSSLHeaders unit. Typically, you should strive to always use the latest OpenSSL version that is available (which, at the time of this writing, is 1.0.2d).