DataSnap migration from Delphi 7 to XE6 - delphi

Sorry again for my english,
I already migrating client and the server and everything worked well,
until I noticed that Remote Data Module of the server is not working what is expected. When I run query from a client that requires more time, the other Remote Data Modules remain on hold (including and the main thred). It behaves like thread model is tmSingle. I looked around and tried everything I found with no success. Even more strange is that where I registered the new server (builded with XE6), the old one (builded with D7) started giving the same symptoms.
When installing the new server on machine that is alredy running the old one I use
xxxxx.exe /unregserver (for the old one) and
xxxxx.exe /regserver (for the new one), and then is noticeable the problem. Even if I unregister the new server, and register the old one the problem stays.
The client and the server communicate via DataSnap Socket (the client with TSocketConnection and TConnectionBroker).
Here is some info for the server
Server_TLB
unit Server_TLB;
uses Windows, ActiveX, Classes, Graphics, Midas, StdVCL, Variants;
const
LIBID_Server: TGUID = '{xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}';
IID_IrdmServer: TGUID = '{xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}';
CLASS_rdmServer: TGUID = '{xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}';
IrdmServer = interface;
IrdmServerDisp = dispinterface;
rdmServer = IrdmServer;
IrdmServer = interface(IAppServer)
['{xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}']
........... methods..........
IrdmServerDisp = dispinterface
['{xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}']
........... methods..........
CordmServer = class
class function Create: IrdmServer;
class function CreateRemote(const MachineName: string): IrdmServer;
implementation
uses ComObj;
class function CordmServer.Create: IrdmServer;
begin
Result := CreateComObject(CLASS_rdmServer) as IrdmServer;
end;
class function CordmServer.CreateRemote(const MachineName: string): IrdmServer;
begin
Result := CreateRemoteComObject(MachineName, CLASS_rdmServer) as IrdmServer;
end;
end.
..initialisation of the RDM
initialization
TComponentFactory.Create(ComServer, TrdmServer,
Class_rdmServer, ciMultiInstance, tmFree);
..the sequence of creation
Forms.Application.ShowMainForm := False;
Forms.Application.Initialize;
Forms.Application.CreateForm(TdmServer, dmServer);
Forms.Application.CreateForm(TfMain, fMain);
windows.PostMessage(fMain.Handle, MSG_INITIALIZE, 0, 0);
Forms.Application.Run;
with standard SocketDispatcher and standart SConnect
Also alredy tryed with setting key
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}\InprocServer32]
"ThreadingModel"="Free" or "Both"
I miss something else?
Thank you for the help!

In this situation I would create a simple DataSnap client / server application with the correct threading model, test it, and then compare the auto-generated server module code with the code in your migrated project.
This way, you can detect the critical differences, adjust the code to be the equivalent in the test project server module.

I suggest you to check:
Security on a Midas / DataSnap server: http://qc.embarcadero.com/wc/qcmain.aspx?d=8814
Also, the socket server is deprecated and has some drawbacks:
It bypasses DCOM security. Everything is run in the security context of the user which runs the server (if it is localsystem, it has the most powerful privileges - beware of that)
Connection is not encrypted unless you write a module for it, which lacks basic securee key exchange features. DCOM can encrypt and authenticate packets (although its encryption is today not really strong) - you just need to configure it in dcomcnfg.
It doesn't support 64 bit values and other types, see http://qc.embarcadero.com/wc/qcmain.aspx?d=69924

Related

Bind delphi SOAP client to a specific local port/range

I am consuming a web service with Delphi SOAP library (using THTTPRIO). One of the customers has strict networking policy requiring specific ports to be used on both sides of connection. So I need the web service client to use a specific local port or small local port range to make the connections to the server. Is there a way to do it (either through programming or using a Windows setting)? I am using Delphi 10.4.
I am not sure if I get the question right, but I think it would be as simple as specifying a port number as part of the URL property of a THTTPRIO object as below.
// Example of AUrl: 'http://COMPNAMEORIPADD:9878'
function SoapClientClass.CreateSoapClient(AUrl: string; AInterfaceName: string): THTTPRIO;
begin
result := THTTPRIO.Create(nil);
result.URL := AUrl + AInterfaceName;
end;

indy ssl delphi server

I use Delphi 10.1 Berlin and Indy 10.6.2, 32bit on a Windows Server 2012.
I implemented a server based on TIdHTTPServer. Works like a charm for many years.
Now my customer wants the traffic secured. SSL is an area I have little knowledge of.
There are several helpful pointers on the web that have helped me make CA certificate and key files with OpenSSL. And from several examples I've put together the code below, using TIdServerIOHandlerSSLOpenSSL.
The cert/key files are in the exe directory and so are the OpenSSL dlls ssleay32 and libeay32.
The server responds to http://localhost:8080 but there is no response when addressing it via https://localhost. It behaves as if the TIdServerIOHandlerSSLOpenSSL is not there at all. (The IOHandler does read the cert/key files and it complains when I remove the OpenSSL DLLs). It is as if I've forgotten to throw a switch somewhere.
The analysis of Windows Network Diagnostics (in IEdge) is 'The device or resource (localhost) is not set up to accept connections on port "https".'
I tried to log a message via the OnConnect event, but that stage is never reached with HTTPS.
I have run out of ideas, and can not find relevant suggestions on the web.
Here is my code (the components are all declared in code):
procedure TServerForm.FormCreate(Sender: TObject);
var
ServerSSLIOHandler: TIdServerIOHandlerSSLOpenSSL;
rootdir : string;
begin
inherited;
rootdir:=ExtractFilePath(Application.ExeName);
ServerSSLIOHandler:=TIdServerIOHandlerSSLOpenSSL.Create(self);
ServerSSLIOhandler.SSLOptions.RootCertFile:=rootdir+'ca.cert.pem';
ServerSSLIOhandler.SSLOptions.CertFile:=rootdir+'localhost.cert.pem';
ServerSSLIOhandler.SSLOptions.KeyFile:=rootdir+'localhost.key.pem';
ServerSSLIOhandler.SSLOptions.Method:=sslvSSLv23;
ServerSSLIOhandler.SSLOptions.Mode:=sslmServer;
ServerSSLIOhandler.OnGetPassword:=NIL;
ServerSSLIOhandler.OnVerifyPeer:=OnVerifyPeer;
HTTPServer:=TIdHTTPServer.Create(self);
HTTPServer.IOhandler:=ServerSSLIOHandler;
HTTPserver.Bindings.Add.Port:=443;
HTTPserver.Bindings.Add.Port:=8080;
HTTPServer.Active:=True;
HTTPServer.AutoStartSession:=True;
HTTPServer.SessionTimeOut:=1200000;
HTTPserver.OnQuerySSLPort:=OnQuerySSLPort;
HTTPServer.OnCommandGet:=HTTPServerCommandGet;
...
end;
procedure TServerForm.OnQuerySSLPort(APort: Word; var VUseSSL: Boolean);
// This will not be called when the request is a HTTPS request
// It facilitates the use of the server for testing via HTTP://localhost:8080 (i.e. without SSL)
begin
VUseSSL := (APort<>8080);
end;
function TServerForm.OnVerifyPeer(Certificate: TIdX509; AOk: Boolean; ADepth, AError: Integer): Boolean;
begin
result:=AOk;
end;
Thank you. Remy's remark about OnConnect and his suggestion to use netstat did the trick. I.e. it lead me to discover that the problem was elsewhere. In the past I had to move away from port 80 because it became in use by a Windows service. From then on I specified a port number (8080) in an ini file and acted as follows.
Code:
prt:=parameters.ReadInteger('settings','port',80);
if prt<>HTTPserver.DefaultPort
then begin HTTPserver.Active:=false;
HTTPserver.Bindings.Clear;
HTTPserver.DefaultPort:=prt;
HTTPserver.Active:=true;
end;
Since this piece of code was still there, obviously only the specified port (8080) was active. netstat revealed that immediately.
Will you believe that I am very happy with your quick response!

DataSnap Limits Inbound Request to 16k

I built a DataSnap server with Delphi XE2 that implements TDSHTTPService. When the inbound request comes in, TIdIOHandler.InitComponent is called in a thread before execution is handed to the method called in TServerMethods. I do not have any Indy components in the server, so DataSnap is using Indy 10 under-the-hood.
.InitComponent() sets the IO handler's max line length to a hard-coded value (FMaxLineLength := IdMaxLineLengthDefault;), which is 16384. I can't find a way to increase the value. I even tried copying the IdIOHandler Unit to the project folder and changing the constant value. But it still picks up the IdIOHandler.dcu from the Indy 10 build, and ignores the copied file in my project folder. I also tried adding a TIdIOHandlerStream component to the server project and setting its MaxLineLength to no avail.
Plan A = Properly set the MaxLineLength value in the DataSnap server.
Plan B = Somehow compile a modified IdIOHandler.pas file into my project.
Are either of these possible? I've been working on this for hours and can't find anything similar in all my searching, and can't seem to make any headway by experimenting.
After recompiling all Indy packages in Delphi XE3, having changed the IdMaxLineLengthDefault constant to 512 * 1024, and working as expected after that, I began searching for the simplest solution to this problem. So, I found out this an easy workaround for this limit.
You can implement a procedure for the OnContextCreated event of the TIdHTTPWebBrokerBridge object used in the main unit of the DataSnap REST Server project. In that event, the AContext object is received, which is created for each request to the DataSnap server. So, in the code for this procedure you just have to override the default value for this property as follows:
procedure TForm1.FormCreate(Sender: TObject);
begin
FServer := TIdHTTPWebBrokerBridge.Create(Self);
{Here you assign the new procedure for this event}
FServer.OnContextCreated:= OnContextCreated;
end;
procedure TForm1.OnContextCreated(AContext: TIdContext);
begin
AContext.Connection.IOHandler.MaxLineLength:= 512*1024 {or whatever value you need);
end;
Short of removing the Delphi XE2 install of Indy 10 and downloading the source, tweaking the constant values and compiling / maintaining my own build forever going forward..., I solve the issue.
I created an additional method in the DataSnap server so that I could create a record in the database with a call to the first method, and then incrementally stream the rest of the data by passing it to the second method 16k at a time -- buffering it in the DataSnap server until all parts are received. Then I update the record in the database with the fully buffered value from the DataSnap server.
Maybe not the most effective solution, but it works and it will scale as needed.

How can I make ADO database connections in 'TISAPIApplication` before processing incoming requests?

TADOConnection is failing to connect in the application initialization section of Delphi ISAPI App (TISAPIApplication):
Application is built with Delphi XE SPI, running Win 7 64/IIS 7.5 and WinServer 2008 RS2 - it cannot connect with ADO in the global ISAPI application context. (Example code is using MS-SQLServer OLEDB - but we also fail using Sybase ASE provider.)
The following code fails when conn.Open is called - TADOConnection.open never returns - ISAPI app hangs in la-la land, no exception raised:
library ISAPIBareBones;
uses
ActiveX,
ADODB,
(...)
var
conn: TADOConnection;
begin
CoInitFlags := COINIT_MULTITHREADED;
Application.Initialize;
coinitialize(nil);
conn := TADOConnection.Create(Application);
conn.ConnectionString := 'Provider=SQLOLEDB.1;xxx';
//Fails here:
try
conn.Open;
except on e:exception do
logException(e)
end;
Application.WebModuleClass := WebModuleClass;
Application.Run;
end.
The same code within a specific request handler (Delphi webAction) runs fine.
We suspect a problem with execution privileges in IIS at the ISAPI application level. But as far as we can tell, the entire IIS application stack from the webServer itself down to the specific virtual directory and the ISAPI dll itself are all running under the same credentials with same execution privileges.
Meanwhile, my workaround has been to initialize the database infrastructure from within an http response call (an ISAPI thread), and then simply check that it's initialized on each subsequent call. This works, but encumbers me with some constraints that I'd prefer not to deal with.
How can I make ADO database connections in a TISAPIApplication instance, before handling incoming requests.
The begin ... end or an initialization part of a dll is the Delphi equivalent to dllmain in C++. So the same restrictions apply including:
Don't call CoInitialize
Don't call COM functions
This implies that you cannot create an ADO connection.
Do you know all the things that happen when you call TADOConnection.Create(Application);?
So what you're trying to do isn't going to work. And even if it did, you should not do it. Here's some better explanations:
http://msdn.microsoft.com/en-us/library/ms682583%28VS.85%29.aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/dn633971(v=vs.85).aspx
http://blogs.msdn.com/b/oldnewthing/archive/2004/01/27/63401.aspx
http://blogs.msdn.com/b/oldnewthing/archive/2004/01/28/63880.aspx
http://blogs.msdn.com/b/oldnewthing/archive/2014/08/21/10551659.aspx
MSDN suggests creating the database connection in GetExtensionVersion. This is how your isapi dll is initialized. It is not just for reporting the extension version. So that is the way to go. Create your own GetExtensionVersion function that initializes your database and then call the previous Delphi function.
library Project1;
uses
Winapi.ActiveX,
System.Win.ComObj,
Web.WebBroker,
Web.Win.ISAPIApp,
Web.Win.ISAPIThreadPool,
Winapi.Isapi2,
Winapi.Windows,
WebModuleUnit1 in 'WebModuleUnit1.pas' {WebModule1: TWebModule};
{$R *.res}
function GetExtensionVersion(var Ver: THSE_VERSION_INFO): BOOL; stdcall;
begin
Result := Web.Win.ISAPIApp.GetExtensionVersion(Ver);
// create your ado connection here
end;
exports
GetExtensionVersion,
HttpExtensionProc,
TerminateExtension;
begin
CoInitFlags := COINIT_MULTITHREADED;
Application.Initialize;
Application.WebModuleClass := WebModuleClass;
Application.Run;
end.
I think the problem is that the code you run runs in the dll's main procedure. This part of the initialization is very restrictive e.g. you may not load any dll nor you are
allowed to call CoInitialize (see http://msdn.microsoft.com/en-us/library/ms678543%28v=vs.85%29.aspx) .
Your ActiveX alls there will cause some troubles which are most likely the reason for your
exception.

delphi 7 http request into string

I want to load a url directly into a string without any data stream,what is the best way, internet open url works but it seems not clear.
I don't want to use any component for reading some short messages
Delphi 6 and later ship with Indy, which has a TIdHTTP client component, eg:
uses
..., IdHTTP;
var
Reply: String;
begin
Reply := IdHTTP1.Get('http://test.com/postaccepter?=msg1=3444&msg2=test');
...
end;
Or:
uses
..., IdHTTP;
var
Reply: TStream;
begin
Reply := TMemoryStream.Create;
try
IdHTTP1.Get('http://test.com/postaccepter?=msg1=3444&msg2=test', Reply);
Reply.Position := 0;
...
finally
Reply.Free;
end;
end;
Depending on your needs.
You can use Synapse, a very light weight library that has a simple function call to get just what your asking for:
uses
classes, httpsend;
var
Response : TStringlist;
begin
if HttpGetText(URL,Response) then
DoSomethingWithResponse(Response.Text);
end;
I would suggest getting the latest copy from SVN, which is more current and contains support for the latest versions of Delphi. There are also simple functions for posting form data, or retrieving binary resources. These are implemented as simple functions and are a great template if you want to do something extra, or that is not directly supported.
You can use our SynCrtSock unit, which is even lighter than Synapse.
See http://synopse.info/fossil/finfo?name=SynCrtSock.pas
It is a self-contained unit (only one dependency with the WinSock unit), and it works from Delphi 6 up to Delphi XE.
You have these two functions available to get your data in one line of code:
/// retrieve the content of a web page, using the HTTP/1.1 protocol and GET method
function HttpGet(const server, port: AnsiString; const url: TSockData): TSockData;
/// send some data to a remote web server, using the HTTP/1.1 protocol and POST method
function HttpPost(const server, port: AnsiString; const url, Data, DataType: TSockData): boolean;
Or you can use textfile-based commands (like readln or writeln) to receive or save data.
TSockData is just a wrapper of RawByteString (under Delphi 2009/2010/XE) or AnsiString (up to Delphi 2007).
If you need also to write a server, you have dedicates classes at hand, resulting in fast processing and low resource consummation (it uses a Thread pool, and is implemented over I/O Completion Ports).
If I'm already using XML in an application (and the MSXML2_TLB), I generally use IXmlHttpRequest to perform http operations. If you open and send the request, you can either use the response data as XML DOM using the ResponseXML, as text using ResponseText or as a data-stream using ResponseStream, see here for an example how to use this in Delphi: http://yoy.be/item.asp?i142

Resources