Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: REST server with TIdHTTPWebBrokerBridge fails on bcc64 w/o runtime packages



Permlink Replies: 3 - Last Post: Jan 19, 2017 3:22 PM Last Post By: Remy Lebeau (Te...
Arkady Semylio

Posts: 87
Registered: 9/18/15
REST server with TIdHTTPWebBrokerBridge fails on bcc64 w/o runtime packages
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 19, 2017 1:27 AM
Hi,

in order to test the REST DataSnap technology, specifically REST over HTTPS
in a standalone configuration (ie using TIdHTTPWebBrokerBridge), I've made
a couple of projects with Berlin 10.1.1: the client side and the server side.

I noticed a showstopper problem on one specific build configuration, regardless
debug/release settings: it fails when the server side (a normal VCL app) is
compiled as single standalone exe with bcc64 compiler. All other build
configurations works fine (bcc32c single exe, bcc32c exe + runtime pkgs and
bcc64 + runtime pkgs are ok. bcc64 single exe fails when try to load the ssl
certificate when the server starts with the following message:

First chance exception at $000007FEFD55A06D. Exception class EIdOSSLLoadingRootCertError
with message 'Could not load root certificate.
error:00000000:lib(0):func(0):reason(0)'.

Obviously, certificates and settigs for the TIdServerIOHandlerSSLOpenSSL are
ok, because all other build configutions works fine.


// Unit1.h
class TForm1 : public TForm
{
__published:	// IDE-managed Components
    ....
private:	// User declarations
	  std::unique_ptr<TIdHTTPWebBrokerBridge> server_;
 
	  ...
public:		// User declarations
	__fastcall TForm1( TComponent* Owner );
};
 
...
 
// Unit1.cpp
__fastcall TForm1::TForm1( TComponent* Owner )
	: TForm( Owner )
    , server_( std::make_unique<TIdHTTPWebBrokerBridge>( this ) )
{
    String const CertRootPath { _T( "..\\..\\..\\..\\Resources\\SSL\\" ) };
    auto LIOHandleSSL = make_unique<TIdServerIOHandlerSSLOpenSSL>( server_.get() );
    LIOHandleSSL->SSLOptions->CertFile = CertRootPath + _T( "ca.crt" );
    LIOHandleSSL->SSLOptions->RootCertFile = CertRootPath + _T( "caRoot.pem" );
    LIOHandleSSL->SSLOptions->KeyFile = CertRootPath + _T( "ca.key" );
    LIOHandleSSL->OnGetPassword = OnGetSSLPassword;
    server_->IOHandler = LIOHandleSSL.release();
}


I suppose there's a mismatch between Indy's SSL packages and static libs for x64.

Open SSL version is 1.0.2j (openssl-1.0.2j). And, yes, both x86 and x64 dlls are
placed in the appropriate folders in the projects.

Any workaround/suggestion (apart to compile with runtime packages)?

The project is attacched here (it's pretty large 'cause there are the OpenSSL's DLL):

https://forums.embarcadero.com/thread.jspa?messageID=873868&tstart=0#873868

Thanks.

Bye
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: REST server with TIdHTTPWebBrokerBridge fails on bcc64 w/o runtimepackages
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 19, 2017 11:23 AM   in response to: Arkady Semylio in response to: Arkady Semylio
Arkady wrote:

First chance exception at $000007FEFD55A06D. Exception class
EIdOSSLLoadingRootCertError with message 'Could not load root
certificate.
error:00000000:lib(0):func(0):reason(0)'.

That is an OpenSSL error, not a Delphi/C++Builder error.

When the RootCertFile or VerifyDirs property of TIdServerIOHandlerSSLOpenSSL
is not empty, Indy passes those values to OpenSSL's SSL_CTX_load_verify_locations()
function, and if that function fails then EIdOSSLLoadingRootCertError is
thrown. That logic is not dependant on build configurations.

Are you ABSOLUTELY SURE the values you are setting are actually pointing
to the correct folder path in every build configuration? Try using ExpandFileName(_D(
"..
..
..
..
Resources
SSL
")) to verify that the resulting path is
the same in each build configuration.

Since you are using a relative path for the root path, it is possible that
the app's current working directory at startup is different than what you
are expecting. NEVER rely on relative paths, ALWAYS use absolute paths
instead. You can use either TPath::Combine() or ExpandFileName() to build
an absolue path from a relative path.

BTW, a couple of side notes:

1. you should be using the _D() macro instead of the _T() macro for your
System::String literals. _T() only applies to TCHAR, which System::(Unicode)String
does not use at all. _D() applies to System::(Unicode)String.

2. since you are assigning the TIdHTTPWebBrokerBridge object as the Owner
for the TIdServerIOHandlerSSLOpenSSL object, you technically don't need to
wrap the TIdServerIOHandlerSSLOpenSSL object in a std::unique_ptr at all
since the memory is already owned and managed.

Try something more like this:

__fastcall TForm1::TForm1( TComponent* Owner )
    : TForm( Owner ), server_( std::make_unique<TIdHTTPWebBrokerBridge>( 
this ) )
{
    String const ExePath = ParamStr(0); // or Application->ExeName
    String const AppFolder = ExtractFilePath( ExePath );
    String const CertRootPath = TPath::Combine( AppFolder, _D( "..\\..\\..\\..\\Resources\\SSL\\" 
) );
    /* or:
    String const CertRootPath = ExpandFileName( AppFolder + _D( "..\\..\\..\\..\\Resources\\SSL\\" 
) );
    */
 
    auto LIOHandleSSL = new TIdServerIOHandlerSSLOpenSSL( server_.get() );
    LIOHandleSSL->SSLOptions->CertFile = TPath::Combine(CertRootPath, _D( 
"ca.crt" ));
    LIOHandleSSL->SSLOptions->RootCertFile = CertRootPath + _D( "caRoot.pem" 
);
    LIOHandleSSL->SSLOptions->KeyFile = CertRootPath + _D( "ca.key" );
    LIOHandleSSL->OnGetPassword = OnGetSSLPassword;
    server_->IOHandler = LIOHandleSSL;
}


I suppose there's a mismatch between Indy's SSL packages and
static libs for x64.

There is not (besides, Indy does not currently support static linkage of
OpenSSL on any platforms other than iOS), nor is this even a linkage error
to begin wiith.

--
Remy Lebeau (TeamB)
Arkady Semylio

Posts: 87
Registered: 9/18/15
Re: REST server with TIdHTTPWebBrokerBridge fails on bcc64 w/o runtimepackages
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 19, 2017 2:27 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:

Arkady wrote:

First chance exception at $000007FEFD55A06D. Exception class
EIdOSSLLoadingRootCertError with message 'Could not load root
certificate.
error:00000000:lib(0):func(0):reason(0)'.

Are you ABSOLUTELY SURE the values you are setting are actually pointing
to the correct folder path in every build configuration? Try using ExpandFileName(_D(
"..
..
..
..
Resources
SSL
")) to verify that the resulting path is
the same in each build configuration.

I've placed cert files in root (C:\), then I've set the absolute path into
the properties pointing to cert files. Still it works using packages but
it doesn't work linked as standalone exe. It works fine with bcc32c with
any build config.

2. since you are assigning the TIdHTTPWebBrokerBridge object as the Owner
for the TIdServerIOHandlerSSLOpenSSL object, you technically don't need to
wrap the TIdServerIOHandlerSSLOpenSSL object in a std::unique_ptr at all
since the memory is already owned and managed.

The unique_ptr is released (via release()) in the function's
last statement so to obtain the desired exception safety
level. If any exception occur between the creation of
TIdServerIOHandlerSSLOpenSSL and the assignment of the
latter to IdHTTPWebBrokerBridge, it can be still destroyed.

Anyway, thanks for the help.

Bye
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: REST server with TIdHTTPWebBrokerBridge fails on bcc64 w/oruntimepackages
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 19, 2017 3:22 PM   in response to: Arkady Semylio in response to: Arkady Semylio
Hello Arkady,

I've placed cert files in root (C:\), then I've set the absolute path
into the properties pointing to cert files. Still it works using
packages but it doesn't work linked as standalone exe. It works fine
with bcc32c with any build config.

Then you are going to have to do some debugging, because the use or lack
of runtime packages should have no effect whatsoever on how Indy calls OpenSSL
functions.

The unique_ptr is released (via release()) in the function's last
statement so to obtain the desired exception safety level.

Still not technically needed in this example.

You are passing the TIdHTTPWebBrokerBridge object to the TIdServerIOHandlerSSLOpenSSL
constructor, so the Bridge object takes ownership of the IOHandler object
and will destroy it when the Bridge object is itself destroyed. The Bridge
object is itself in a std::unique_ptr, so if an uncaught exception were thrown
in the Form constructor, the Form construction would get canceled, automatically
destroying the std::unique_ptr, which would destroy the Bridge object, which
would destroy the IOHandler object.

In fact, looking at the code again, since the Form object is being set as
the Owner of the Bridge object, technically the Bridge object does not need
to be wrapped in a std::unique_ptr, either:

class TForm1 : public TForm
{
__published:    // IDE-managed Components
....
private:    // User declarations
     TIdHTTPWebBrokerBridge *server_;
     ...
public:        // User declarations
    __fastcall TForm1( TComponent* Owner );
};
 
...
 
__fastcall TForm1::TForm1( TComponent* Owner )
    : TForm( Owner ), server_( new TIdHTTPWebBrokerBridge>( this ) )
{
    ...
    auto LIOHandleSSL = new TIdServerIOHandlerSSLOpenSSL( server_ );
    ...
    server_->IOHandler = LIOHandleSSL;
}


Just because you can use something does not mean you have to use it.

And actually, your original code has some potentially dangerous behavior,
because you are actually assigning 2 owners to the same TIdHTTPWebBrokerBridge
object - the Form object, and the std::unique_ptr object.

Whether the Form object is constructed and destroyed normally, or if the
Form constructor throws an uncaught exception, the std::unique_ptr should
get destructed first, removing the Bridge from the Form's ownership before
the base Form destructor can run to destroy the Bridge again.

If, by some odd miracle, the Form object destroys the IOHandler object before
destroying the std::unique_ptr object (for instance, if someone happened
to call the Form's DestroyComponents() method), the std:::unique_ptr would
crash during its destruction because it is still holding on to a invalid
object pointer.

If any exception occur between the creation of TIdServerIOHandlerSSLOpenSSL
and the assignment of the latter to IdHTTPWebBrokerBridge, it can be still
destroyed.

It would still be destroyed anyway due to the RTL's component ownership semantics,
because the Bridge object is assigned ownership in the IOHandler's constructor.

What you say about ensuring exception safety manually would be true if you
were not specifying any Owners in the constructors. If you are going to
manage the ownership of VCL components yourself, you may as well set their
Owners to NULL instead:

class TForm1 : public TForm
{
__published:    // IDE-managed Components
....
private:    // User declarations
    std::unique_ptr<TIdServerIOHandlerSSLOpenSSL> iohandler_;
    std::unique_ptr<TIdHTTPWebBrokerBridge> server_;
    ...
public:        // User declarations
    __fastcall TForm1( TComponent* Owner );
};
 
...
 
__fastcall TForm1::TForm1( TComponent* Owner )
    : TForm( Owner ),
    iohandler_( make_unique<TIdServerIOHandlerSSLOpenSSL>( nullptr ) ),
    server_( std::make_unique<TIdHTTPWebBrokerBridge>( nullptr ) )
{
    ...
    iohandler_->SSLOptions->CertFile = CertRootPath + _T( "ca.crt" );
    iohandler_->SSLOptions->RootCertFile = CertRootPath + _T( "caRoot.pem" );
    iohandler_->SSLOptions->KeyFile = CertRootPath + _T( "ca.key" );
    iohandler_->OnGetPassword = OnGetSSLPassword;
    server_->IOHandler = iohandler_.get();
}


Otherwise, let the RTL do the work for you.

--
Remy Lebeau (TeamB)
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02