RTC Forums

Subscription => Support => Topic started by: Henk vd Boogaard on May 22, 2013, 07:18:16 PM



Title: sending a XML-file back with a TRctHTTPserver
Post by: Henk vd Boogaard on May 22, 2013, 07:18:16 PM
Hello,
Using the examples 1 and 2 I managed to make a webserver that receives a XML-file.
I can read the file and make an answer also in a  XML-file.
I madk the XML-file in a TXMLdocument.
How can I send this XML-file (in the TXMLDocument) as an answer?

Another question:
How can I test the webserver in FireFox or in MS-explorer?
Besides the URL I have also include the XML-file that must be processed.

Thanks in advance,
Henk van den Boogaard


Title: Re: sending a XML-file back with a TRctHTTPserver
Post by: D.Tkalcec (RTC) on May 22, 2013, 07:34:06 PM
Sender:TRtcConnection object (sent to every RTC event) has Write and WriteEx methods, which are used for sending the content body (XML document). You will need your XML content in a String (Write) or a dynamic byte array (WriteEx) in order to send it out, so check if the class you are using (TXMLDocument?) has a method which returns your XML document in a String, or an array of bytes. One thing you obviously can't do, is send an object out.

PS. You could also write the XML document into a file and then send the file out by using the Read_File function (rtcInfo.pas unit), but ... if you are creating temporary files for sending, make sure the file does not already exist (you don't want to write to the same file over and over again if your Server has to work with more than one Client (especially if it is running with MultiThreaded=True).

Best Regards,
Danijel Tkalcec


Title: help with XML and transition to RTC
Post by: Henk vd Boogaard on September 02, 2018, 12:50:37 PM
Hello,
I have an old program (windows service) that listens.
I if get an notify message that a pay has made and that the information can be fetched.
It then puts the information in a NexusDB database.

However the old program does not function anymore due to old SSL functions.

I will try to rewrite the program with the use of RTC and Streamsec2 and if possible no XMLdocument.

But I have not the knowledge to finsch it.

I hope someone can help me.
At the end is the old program with Indy.

This is so far I have got:
----------------------------------------------------
unit Work;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, rtcDataSrv, rtcSystem, rtcInfo, rtcConn, rtcHttpSrv, Vcl.StdCtrls, rtcDataCli, rtcHttpCli;

type
  TForm10 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Label1: TLabel;
    Ontvangen: TLabel;
    Edit1: TEdit;
    Button2: TButton;
    Server: TRtcHttpServer;
    RtcDataProvider1: TRtcDataProvider;
    RtcHttpClient1: TRtcHttpClient;
    RtcDataRequest1: TRtcDataRequest;
    procedure Button1Click(Sender: TObject);
    procedure RtcDataProvider1CheckRequest(Sender: TRtcConnection);
    procedure RtcDataProvider1DataReceived(Sender: TRtcConnection);
    procedure Button2Click(Sender: TObject);
    procedure RtcDataRequest1BeginRequest(Sender: TRtcConnection);
    procedure RtcDataRequest1DataReceived(Sender: TRtcConnection);
  private
    { Private declarations }
    TransactionID_str: rtcString;
    SiteURL, SitePort: rtcString;
    DoDebug: boolean;
  public
    { Public declarations }
  end;

var
  Form10: TForm10;

implementation

{$R *.dfm}

procedure TForm10.Button1Click(Sender: TObject);
begin
  Server.ServerPort := Edit1.Text;
  Server.Listen();
  Button2.Enabled := true;
  Button1.Enabled := false;
  DoDebug := true;
end;

procedure TForm10.Button2Click(Sender: TObject);
begin
  Server.StopListen;
  Button2.Enabled := false;
  Button1.Enabled := true;;
end;

procedure TForm10.RtcDataProvider1CheckRequest(Sender: TRtcConnection);
begin
  with Sender as TRtcDataServer do
    Memo1.Lines.Add(Request.FileName);
  with Sender as TRtcDataServer do
    if UpperCase(Request.FileName) = '/MSP' then
      Accept;

end;

procedure TForm10.RtcDataProvider1DataReceived(Sender: TRtcConnection);
begin
  with Sender as TRtcDataServer do
  begin
    if Request.Complete then
    begin
      if DoDebug then
        Memo1.Lines.Clear;
      TransactionID_str := Request.Query['transactionid'];
      if DoDebug then
        Memo1.Lines.Add(TransactionID_str);
      // now get the information
      RtcHttpClient1.ServerAddr := SiteURL;
      RtcHttpClient1.ServerPort := SitePort;
      with RtcDataRequest1 do
      begin
        Request.Method := 'GET';
        Request.FileName := '/' + TransactionID_str;
        // HERE THE XML?? HOW???
        //WITH REQUEST.QUERY??
        //WHAT BY 'Status ua' ??
        Post; // Post the request
      end;

    end;
  end;
end;

procedure TForm10.RtcDataRequest1BeginRequest(Sender: TRtcConnection);
begin
  with TRtcDataClient(Sender) do
  begin // make sure our request starts with "/"
    if Copy(Request.FileName, 1, 1) <> '/' then
      Request.FileName := '/' + Request.FileName;
    // define the "HOST" header
    if Request.Host = '' then
      Request.Host := ServerAddr;
    if DoDebug then
      Memo1.Text := 'Requesting "' + Request.FileName + '" from "' + ServerAddr + '".';
    // send request header out
    WriteHeader;
  end;

end;

procedure TForm10.RtcDataRequest1DataReceived(Sender: TRtcConnection);
begin
  with TRtcDataClient(Sender) do
  begin
    if Response.Started then
    begin { Executed only once per request,
        when we start receiving the response. }
      // Clear the info we wrote here in our "OnBeginRequest"
      // HERE COMES THE XML BACK?? HOW TO HANDLE THIS?
      //ALSO WITH REQUEST.QUERY??
      if DoDebug then
      begin
        Memo1.Clear;
        Memo1.Lines.Add('Status code: ' + IntToStr(Response.StatusCode));
        Memo1.Lines.Add('Status text:' + Response.StatusText);
        Memo1.Lines.Add('ALL Headers:');
        Memo1.Lines.Add(Response.HeaderText);
        Memo1.Lines.Add('Content Length:');
        Memo1.Lines.Add(IntToStr(Response.ContentLength));
        Memo1.Lines.Add('Content body:');
        Memo1.Lines.Add('START >');
      end; { Could be executed more than once,  depending on the content size }
      // add content received now.
      Memo1.Text := Memo1.Text + Read;
      if Response.Done then
      begin { Executed only once per request, when we have just received it all. }
        Memo1.Lines.Add('< END');
      end;
    end; //DoDebug
  end;
end;

end.
-------------------------------------------------

======================the old program===========================

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs,
  IdBaseComponent, IdComponent, IdCustomTCPServer, IdCustomHTTPServer,
  IdHTTPServer,
  Variants, Forms, xmldom, XMLIntf, msxmldom, XMLDoc, IdAntiFreezeBase, IdAntiFreeze,
  IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack, IdSSL, IdSSLOpenSSL,
  IdTCPConnection, IdTCPClient, IdHTTP, IdServerIOHandler,
  IniFiles, IdContext, IdHeaderList, ActiveX, DB, nxdb, nxsdServerEngine,
  nxreRemoteServerEngine, nxllComponent, nxllTransport, nxptBasePooledTransport,
  nxtwWinsockTransport, Xml.omnixmldom;

type
  TService1 = class(TService)
    IdHTTPServer1: TIdHTTPServer;
    IdServerIOHandlerSSLOpenSSL1: TIdServerIOHandlerSSLOpenSSL;
    IdHTTP1: TIdHTTP;
    IdSSLIOHandlerSocketOpenSSL1: TIdSSLIOHandlerSocketOpenSSL;
    IdAntiFreeze1: TIdAntiFreeze;
    XMLDoc1: TXMLDocument;
    XMLDoc2: TXMLDocument;
    nxWinsockTransport1: TnxWinsockTransport;
    nxRemoteServerEngine1: TnxRemoteServerEngine;
    nxSession1: TnxSession;
    nxDatabase1: TnxDatabase;
    DS_algpar: TDataSource;
    nxT_algpar: TnxTable;
    DS_IDealRecords: TDataSource;
    nxT_IDealRecords: TnxTable;

    procedure ServiceStart(Sender: TService; var Started: Boolean);
    procedure IdHTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
    procedure IdHTTPServer1CreatePostStream(AContext: TIdContext; AHeaders: TIdHeaderList; var VPostStream: TStream);
    procedure ServiceStop(Sender: TService; var Stopped: Boolean);
  private
    { Private declarations }
  protected
    function DoStop: Boolean; override;
    function DoPause: Boolean; override;
    function DoContinue: Boolean; override;
    procedure DoInterrogate; override;
    procedure DoShutdown; override;
  public
    constructor Create(AOwner: TComponent); override;
    function GetServiceController: TServiceController; override;
    { Public declarations }
  end;

var
  Service1: TService1;
  AppsPad: string;
  XML_Dir: string;
  LogAanUit: string;

implementation

uses
  CodeSiteLogging;
{$R *.DFM}

procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  Service1.Controller(CtrlCode);
end;

constructor TService1.Create(AOwner: TComponent);
var
  Ini: TInifile;
  DB_naam: string;
  DB_Alias: string;
  DB_Poort: integer;
  ServerPoort: Word;
begin
  inherited;
  CoInitializeEx(nil, 2);
  CodeSite.Clear;
  DisplayName := 'AVN Service';

  // setup:
  AppsPad := ExtractFilePath(Application.ExeName); // BS geen JclSysInfo meer nodig...
  CodeSite.Send('AppsPad = ' + AppsPad);
  Ini := TInifile.Create(AppsPad + 'AVN_SERVICE_DB_Settings.ini');
  try
    DB_naam := Ini.ReadString('ServerInfo', 'Server', 'NEXUSDB');
    DB_Poort := Ini.ReadInteger('Transport', 'Poort', 16000); // 16000 voor gewoon 17000 voor secure
    DB_Alias := Ini.ReadString('Alias', 'Aliasnaam', 'AVN');
    ServerPoort := Ini.ReadInteger('Com', 'ServerPoort', 22333);
    XML_Dir := Ini.ReadString('FILES', 'XML_Dir', 'C:\TEMP\');
    LogAanUit := Ini.ReadString('LOG', 'LogAanUit', 'AAN');
    CodeSite.Send('1a');
  finally
    Ini.Free;
    CodeSite.Send('1b');
    CodeSite.Send('DB ' + DB_naam);
    CodeSite.Send('Poort ' + IntToStr(DB_Poort));
    CodeSite.Send('Alias ' + DB_Alias);
  end;
  Application.ProcessMessages;

  // setup de database connectie:
  // INDIEN ALS SERVICE DAN DE INI-FILE IN DE MAP WINDOWS/SYSTEM32 ZETTEN
  nxWinsockTransport1.Active := false;
  CodeSite.Send('1c');
  nxWinsockTransport1.ServerName := DB_naam;
  CodeSite.Send('1d');
  nxDatabase1.AliasPath := '';
  CodeSite.Send('1e');
  nxDatabase1.AliasName := DB_Alias;
  CodeSite.Send('1f');
  nxWinsockTransport1.Port := DB_Poort;
  CodeSite.Send('1g');
  nxWinsockTransport1.Active := true;
  CodeSite.Send('1h');
  Application.ProcessMessages;
  if nxWinsockTransport1.Connected then
  begin
    CodeSite.Send('Database geopend');
    nxRemoteServerEngine1.Active := true;
    nxSession1.Active := true;
    nxDatabase1.Active := true;
    CodeSite.Send('Bestanden geopend');
    // einde setup
    nxT_algpar.Open;
    nxT_IDealRecords.Open;
    Application.ProcessMessages;
    IdServerIOHandlerSSLOpenSSL1.SSLOptions.CertFile := nxT_algpar.FieldByName('IDeal_SSL_Cert_file').AsString;
    // '83.161.204.179.cer';
    IdServerIOHandlerSSLOpenSSL1.SSLOptions.KeyFile := nxT_algpar.FieldByName('IDeal_SSL_Cert_key').AsString;
    // '83.161.204.179.key';
    IdServerIOHandlerSSLOpenSSL1.SSLOptions.Method := sslvTLSv1_2;
    IdServerIOHandlerSSLOpenSSL1.SSLOptions.Mode := sslmBoth;
    IdServerIOHandlerSSLOpenSSL1.SSLOptions.VerifyMode := [];
    IdServerIOHandlerSSLOpenSSL1.SSLOptions.VerifyDepth := 0;
    IdSSLIOHandlerSocketOpenSSL1.SSLOptions.CertFile := nxT_algpar.FieldByName('IDeal_SSL_Cert_file').AsString;
    // '83.161.204.179.cer';
    IdSSLIOHandlerSocketOpenSSL1.SSLOptions.KeyFile := nxT_algpar.FieldByName('IDeal_SSL_Cert_key').AsString;
    // '83.161.204.179.key';
    IdSSLIOHandlerSocketOpenSSL1.SSLOptions.Method := sslvTLSv1_2;
    IdSSLIOHandlerSocketOpenSSL1.SSLOptions.Mode := sslmBoth;
    IdSSLIOHandlerSocketOpenSSL1.SSLOptions.VerifyMode := [];
    IdSSLIOHandlerSocketOpenSSL1.SSLOptions.VerifyDepth := 0;
    Application.ProcessMessages;
  end
  else
    CodeSite.Send('fout geen connectie');

  IdHTTPServer1.DefaultPort := ServerPoort;
  CodeSite.Send(IntToStr(ServerPoort));
  // IdHTTPServer1.Active := true; -- BS: Dat doen we pas in de ServiceStart zelf!
  OnStart := ServiceStart;
end;

function TService1.DoContinue: Boolean;
begin
  CodeSite.Send('Service Continue');
  try
    IdHTTPServer1.Active := true;
  except
    on E: Exception do
      CodeSite.SendException(E);
  end;
  Result := inherited;
end;

procedure TService1.DoInterrogate;
begin
  inherited;
end;

function TService1.DoPause: Boolean;
begin
  CodeSite.Send('Service Pause');
  try
    IdHTTPServer1.Active := false;
  except
    on E: Exception do
      CodeSite.SendException(E);
  end;
  Result := inherited;
end;

procedure TService1.DoShutdown;
begin
  CodeSite.Send('Service Shutdown');
  try
    IdHTTPServer1.Active := false;
  except
    on E: Exception do
      CodeSite.SendException(E);
  end;
  inherited;
end;

function TService1.DoStop: Boolean;
begin
  CodeSite.Send('Service Stop');
  try
    IdHTTPServer1.Active := false;
  except
    on E: Exception do
      CodeSite.SendException(E);
  end;
  Result := inherited;
end;

function TService1.GetServiceController: TServiceController;
begin
  Result := ServiceController;
end;

procedure TService1.IdHTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo;
  AResponseInfo: TIdHTTPResponseInfo);
var
  HtmlResult: String;
  Request: String;
  IDealRecNr: integer;
  Exportfilenaam: string;
  iChild, iNodeEwallet, iNodeStatus, iNode_Merchant, iNodeTransaction: IXMLNode;
  Resulttekst, Statustekst: string;
  Strm: TStringStream;

begin
  CodeSite.EnterMethod('command get');
  CoInitialize(nil);
  Request := Uppercase(ARequestInfo.Document);
  HtmlResult := ARequestInfo.Params.Values['transactionid'];
  if HtmlResult <> '' then
  begin
    try
      CodeSite.Send('IDealRecNr = ' + HtmlResult);
      CodeSite.Send('record ontvangen');
      IDealRecNr := StrToInt(HtmlResult);
      // nu in database Huurovereenkomst controleren - indien OK dan VerstuurHTML:
      // maak XML-file:

      { if not DirectoryExists(XML_Dir) then
        CreateDir(XML_Dir);
        Exportfilenaam := XML_Dir + 'IDeal_' + IntToStr(IDealRecNr) + '.XML';
        dit alleen voor testen anders de volgende regel: }

      Exportfilenaam := 'IDeal_' + IntToStr(IDealRecNr) + '.XML';
      CodeSite.Send('Exportfilename = ' + Exportfilenaam);
      XMLDoc1.Active := false;
      XMLDoc1.XML.Text := '';
      XMLDoc1.Active := true;
      XMLDoc1.Encoding := 'UTF-8';
      XMLDoc1.AddChild('Pricat', 'http://www.ean.nl');
      XMLDoc1.DocumentElement := XMLDoc1.CreateNode('status');
      XMLDoc1.DocumentElement.Attributes['ua'] := 'Mozilla/3.0 (compatible; Indy Library)';

      // merchant
      iNode_Merchant := XMLDoc1.DocumentElement.AddChild('merchant');
      iChild := iNode_Merchant.AddChild('account');
      iChild.Text := nxT_algpar.FieldByName('IDeal_account').AsString;
      iChild := iNode_Merchant.AddChild('site_id');
      iChild.Text := nxT_algpar.FieldByName('IDeal_site_id').AsString;
      iChild := iNode_Merchant.AddChild('site_secure_code');
      iChild.Text := nxT_algpar.FieldByName('IDeal_site_secure_id').AsString;

      // transaction
      iNodeTransaction := XMLDoc1.DocumentElement.AddChild('transaction');
      iChild := iNodeTransaction.AddChild('id');
      iChild.Text := IntToStr(IDealRecNr);
      // bestand maken:
      XMLDoc1.SaveToFile(Exportfilenaam);

      CodeSite.Send('XMLDoc1 saved to file');

      // versturen:
      Strm := TStringStream.Create;
      try
        // alleen voor debug anders regel hieronder Strm.LoadFromFile(Exportfilenaam);
        XMLDoc1.SaveToStream(Strm);
        IdHTTP1.Request.ContentType := 'text/xml';
        XMLDoc2.LoadFromXML(IdHTTP1.Post(nxT_algpar.FieldByName('IDeal_Site_URL').AsString, Strm));
        Resulttekst := XMLDoc2.DocumentElement.Attributes['result'];
        iNodeEwallet := XMLDoc2.DocumentElement.ChildNodes.FindNode('ewallet');
        iNodeStatus := iNodeEwallet.ChildNodes.FindNode('status');
        if Assigned(iNodeStatus) then
          Statustekst := iNodeStatus.Text;
        CodeSite.Send('Status = ' + Statustekst);
        // database bijwerken:
        if nxT_IDealRecords.FindKey([IDealRecNr]) then   //hier volgend in v.1.1.0 gewijzigd omdat de status niet juist werd vertaald:
        begin
          if Uppercase(Resulttekst) = 'OK' then
          begin
            nxT_IDealRecords.Edit;
            nxT_IDealRecords.FieldByName('StatusOntvangen').AsBoolean := true;
            nxT_IDealRecords.FieldByName('Status').AsString := Statustekst;
            if Uppercase(Statustekst) = 'COMPLETED' then
              nxT_IDealRecords.FieldByName('BetalingOK').AsBoolean := true;
            nxT_IDealRecords.Post;
          end
          else
          begin
            nxT_IDealRecords.Edit;
            nxT_IDealRecords.FieldByName('StatusOntvangen').AsBoolean := false;
            nxT_IDealRecords.FieldByName('Status').AsString := Statustekst;
             nxT_IDealRecords.FieldByName('Foutkode').AsString := Resulttekst;
            nxT_IDealRecords.Post;
          end;

        end
        else
          CodeSite.Send('Kan IDealrecord niet vinden');
        // bewaren als laatste zodat eerst de database wordt bijgewerkt
        // alleen voor debug:   XMLDoc2.SaveToFile(XML_Dir + 'result' + IntToStr(IDealRecNr) + '.xml');
      finally
        Strm.Free;
      end;

    except
      on E: Exception do
        CodeSite.SendException('fout(en) bij communicatie', E);
    end;

  end;
  CodeSite.ExitMethod('command get');
end;

procedure TService1.IdHTTPServer1CreatePostStream(AContext: TIdContext; AHeaders: TIdHeaderList; var VPostStream: TStream);
begin
  VPostStream := TMemoryStream.Create;
end;

procedure TService1.ServiceStart(Sender: TService; var Started: Boolean);
begin
  CodeSite.Send('Service Start');
  CoInitialize(nil);
  try
    IdHTTPServer1.Active := true;
    Started := true;
  except
    on E: Exception do
      CodeSite.SendException(E);
  end;
end;

procedure TService1.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
  CoUninitialize
end;

end.
===================================

Greetings
Henk van den Boogaard


Title: Re: help with XML and transition to RTC
Post by: Henk vd Boogaard on September 03, 2018, 12:28:13 PM
Hello Danijel,

After I get a TransactionID from the client I must sent a XML to get back an XML with the information I need.
I was thinking to get that with a RtcHttpClient.


Henk


Title: Re: help with XML and transition to RTC
Post by: Henk vd Boogaard on September 03, 2018, 12:35:55 PM
I was thinking that a RtcDataRequest needs a RtcHttpClient


Title: Re: help with XML and transition to RTC
Post by: D.Tkalcec (RTC) on September 03, 2018, 01:08:20 PM
If your Server can remain single-threaded (RtcHttpServer.MultiThreaded=FALSE), you can use a single RtcHttpClient component to send out requests to external Servers and get the data, but ... please keep in mind that doing this also means that your Server is ONLY capable of handling one request from your Clients at a time. If another Client sends a request to your Server while your Server is fetching data from the external Server, it will have to wait until the Server exits the "OnDataReceived" event where it's been waiting for a response from the external Server.

So far, your code looks OK. Now, you need to implement the "OnDataReceived" event for your "RtcDataReuqest" component, because that's where you will get the response from your external Server after you post the request with the "RtcDataRequest" and "RtcHttpClient1" component. Since your Server is single-threaded and you are only using one RtcHttpClient component, you can also use one private variable to store the response which you get from the external Server.

You will also need to make your client connection BLOCKING for this to work, which can either be done by setting the "useProxy", "useWinHttp" or "useBlocking" property to TRUE on your RtcHttpClient1 component (not sure if you did that already, since I don't see your Form). If you do that, you don't have to worry about RtcHttpClient1 or RtcDataRequest component events triggered asynchronously while you are waiting for a response from the Client.

Since you are using a single RtcHttpClient component and sending requests to a custom Server each time, also make sure to call Disconnect after you read the response, so you won't end up sending all the future requests to the same external Server.

Let me know if you need any more help.


Title: Re: help with XML and transition to RTC
Post by: Henk vd Boogaard on September 03, 2018, 01:30:29 PM
I have not made thinks clear to you so I try again.
I have a website where a customer can login to extent the hire of a car.
After he as filled in the necessary information he get redirected to MultisafePay (a firm that handles a payment by Ideal.
When the payment has made MultisafePay sent out a message (notification) to this program that there has been a mutation on the hire (a payment has been made or it did not succeed).
This program gets the notification and must the fetch the payment information by MultisafePay and process the information in the database.

My big question is how can process the information I get in a XML and how can I sent information to ask to get the payment information back in a XML.
In sort how can I handle the I get XML’s and I sent without using XMLDocument.
And also one tag of the outgoing XML is Attributes['ua']
(XMLDoc1.DocumentElement.Attributes['ua'] := 'Mozilla/3.0 (compatible; Indy Library)';)
What must I put in when using RTC?

Thanks for helping me.


Title: Re: help with XML and transition to RTC
Post by: D.Tkalcec (RTC) on September 03, 2018, 01:35:48 PM
Since RTC does NOT include components for XML processing (you simply send out raw HTTP/S data and get raw HTTP/S data back), even if you are using RTC for communication, you still need something else to handle the XML. Is there a problem with the XML code you've used with Indy, or why are you looking for a solution which would NOT use that part of your old code?


Title: Re: help with XML and transition to RTC
Post by: Henk vd Boogaard on September 03, 2018, 02:02:14 PM
I thought RTC could handle XML’s, my mistake and that was easier.
Then I go use the old code again.
Still I have two questions:
1.   How van I sent / receice the XML’s I made / got.
2.   What must I put in the Attributes['ua'] tag

Henk


Title: Re: help with XML and transition to RTC
Post by: D.Tkalcec (RTC) on September 03, 2018, 02:14:47 PM
1. Using RtcHttpClient and RtcDataRequest components, you can send any kind of HTTP request and receive a HTTP response. Check this topic from the RTC Classroom for a step-by-step guide on using the RTC SDK to send a request and receive a response from a Server:
https://rtc.teppi.net/realthinclient-sdk-demo-6-client-to-get-a-file-from-any-web-server/

2. I'm not sure what you mean, but looking at your code, it seems like the Attributes['ua'] from your example is just a name of the Client and only has informative functionality for the receiving Server, so ... you can either set the same as you've already used with Indy, or replace the "Indy Library" part of that text with "RTC" and see what happens ;)


Title: Re: help with XML and transition to RTC
Post by: Henk vd Boogaard on September 03, 2018, 02:17:31 PM
I will give it a try and give you the results.

Thanks,
Henk


Title: Re: help with XML and transition to RTC
Post by: D.Tkalcec (RTC) on September 03, 2018, 02:20:19 PM
OK. Just keep in mind that your code needs to use "WaitForCompletion" after calling "Post" and "Connect" on the "RtcHttpClient" component to wait for the response (emulate blocking behavior), and ... you need to set "useProxy", "useWinHttp" or "useBlocking" property to TRUE on the "RtcHttpClient" component to make the communication blocking. Otherwise, your calls to "Post" and "Connect" will return immediately (before you get any data from the Server) and your request call would then be handled asynchronously (afterwards).

Also, if you have to communicate with different Servers for each request, make sure to call "Disconnect" after you get the data, to avoid problems with changing Servers (ServerAddr : ServerPort). On the other hand, if the Server is always the same and you expect to be sending more requests there, there's no need for a "Disconnect".



Title: Re: sending a XML-file back with a TRctHTTPserver
Post by: Henk vd Boogaard on September 04, 2018, 03:56:05 PM
Hi Danijel,

Will you take a short look if my code should work like this?

Also, the RtcDataRequest should be with HTTPS.
I try to use StreamSec2.
Is there an example how to do that?

here my code so far:
unit U_Work;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, rtcDataSrv, rtcSystem, rtcInfo, rtcConn, rtcHttpSrv, Vcl.StdCtrls, rtcDataCli, rtcHttpCli,
  Data.DB, Vcl.Grids, Vcl.DBGrids, Xml.xmldom, Xml.XMLIntf, Xml.omnixmldom, Xml.XMLDoc, Winapi.ActiveX;

type
  TWork = class(TForm)
    Memo1: TMemo;
    Ontvangen: TLabel;
    Server: TRtcHttpServer;
    RtcDataProvider1: TRtcDataProvider;
    RtcHttpClient1: TRtcHttpClient;
    RtcDataRequest1: TRtcDataRequest;
    DBGrid1: TDBGrid;
    label2: TLabel;
    XMLDoc1: TXMLDocument;
    XMLDoc2: TXMLDocument;
    procedure RtcDataProvider1CheckRequest(Sender: TRtcConnection);
    procedure RtcDataProvider1DataReceived(Sender: TRtcConnection);
    procedure RtcDataRequest1BeginRequest(Sender: TRtcConnection);
    procedure RtcDataRequest1DataReceived(Sender: TRtcConnection);
  private
    { Private declarations }
    TransactionID_str: rtcString;
    StatusText: string;
    IDealRecNr: integer;
    StatusReceived: boolean;
    Exportfilenaam: string;
    iChild, iNodeEwallet, iNodeStatus, iNode_Merchant, iNodeTransaction: IXMLNode;
    // Resulttekst, Statustekst: string;
    Strm: TStringStream;
    XML_string : rtcString;
    XML_response : rtcString;
    procedure FillRecords;

  public
    { Public declarations }
    DoDebug: boolean;
  end;

var
  Work: TWork;

implementation

{$R *.dfm}

uses U_DM1;

procedure TWork.FillRecords;
begin
  try
    if DM1.nxT_IDealRecords.FindKey([IDealRecNr]) then
    begin
      if Uppercase(StatusText) = 'OK' then
      begin
        DM1.nxT_IDealRecords.Edit;
        DM1.nxT_IDealRecords.FieldByName('StatusOntvangen').AsBoolean := true;
        DM1.nxT_IDealRecords.FieldByName('Status').AsString := StatusText;
        if Uppercase(StatusText) = 'COMPLETED' then
          DM1.nxT_IDealRecords.FieldByName('BetalingOK').AsBoolean := true;
        DM1.nxT_IDealRecords.Post;
      end
      else
      begin
        DM1.nxT_IDealRecords.Edit;
        DM1.nxT_IDealRecords.FieldByName('StatusOntvangen').AsBoolean := false;
        DM1.nxT_IDealRecords.FieldByName('Status').AsString := StatusText;
        DM1.nxT_IDealRecords.FieldByName('Foutkode').AsString := StatusText;
        DM1.nxT_IDealRecords.Post;
      end;

    end
    else if DoDebug then
      Memo1.Lines.Add('Kan IDealrecNr niet vinden');
  except
    on E: Exception do
      if DoDebug then
        Memo1.Lines.Add('Fout bij bewerken status in IDealrec. Error: ' + E.Message);
  end;
end;

procedure TWork.RtcDataProvider1CheckRequest(Sender: TRtcConnection);
begin
  with Sender as TRtcDataServer do
    Memo1.Lines.Add(Request.FileName);
  with Sender as TRtcDataServer do
    if Uppercase(Request.FileName) = '/MSP' then
      Accept;

end;

procedure TWork.RtcDataProvider1DataReceived(Sender: TRtcConnection);
begin
  with Sender as TRtcDataServer do
  begin
    if Request.Complete then
    begin
      if DoDebug then
        Memo1.Lines.Clear;
      TransactionID_str := Request.Query['transactionid'];
      if DoDebug then
        Memo1.Lines.Add(TransactionID_str);
      try
        IDealRecNr := StrToInt(TransactionID_str);
      except
        on E: Exception do
          if DoDebug then
            Memo1.Lines.Add('Fout bij vertalen recordnummer van parameter : ' + E.Message);
      end;

      with RtcDataRequest1 do
      begin
        Request.Method := 'GET';
        Request.FileName := '/' + TransactionID_str;
        CoInitialize(nil);
        // nu in database Huurovereenkomst controleren - indien OK dan VerstuurHTML:

        // maak XML-file:
        Exportfilenaam := 'IDeal_' + IntToStr(IDealRecNr) + '.XML';
        XMLDoc1.Active := false;
        XMLDoc1.Xml.Text := '';
        XMLDoc1.Active := true;
        XMLDoc1.Encoding := 'UTF-8';
        XMLDoc1.AddChild('Pricat', 'http://www.ean.nl');
        XMLDoc1.DocumentElement := XMLDoc1.CreateNode('status');
        XMLDoc1.DocumentElement.Attributes['ua'] := 'RTC Library)';

        // merchant
        iNode_Merchant := XMLDoc1.DocumentElement.AddChild('merchant');
        iChild := iNode_Merchant.AddChild('account');
        iChild.Text := DM1.nxT_algpar.FieldByName('IDeal_account').AsString;
        iChild := iNode_Merchant.AddChild('site_id');
        iChild.Text := DM1.nxT_algpar.FieldByName('IDeal_site_id').AsString;
        iChild := iNode_Merchant.AddChild('site_secure_code');
        iChild.Text := DM1.nxT_algpar.FieldByName('IDeal_site_secure_id').AsString;

        // transaction
        iNodeTransaction := XMLDoc1.DocumentElement.AddChild('transaction');
        iChild := iNodeTransaction.AddChild('id');
        iChild.Text := IntToStr(IDealRecNr);
        // bestand maken:
        if DoDebug then
          XMLDoc1.SaveToFile(DM1.XML_Dir + Exportfilenaam);

         XMLDoc1.SaveToXML(XML_string);
        // now get the information
        RtcHttpClient1.ServerAddr := DM1.SiteURL;
        RtcHttpClient1.ServerPort := IntToStr(DM1.SitePort);
        RtcHttpClient1.Write(XML_string);
        Post; // Post the request
        WaitForCompletion;
        XMLDoc1.Active := false;
        XMLDoc2.LoadFromXML(RtcHttpClient1.Response);   // Response is an XML file

        //NOW GET THE INFORMATION FROM xmldOC2


        CoUninitialize;
      end;

    end;
  end;
end;

procedure TWork.RtcDataRequest1BeginRequest(Sender: TRtcConnection);
begin
  with TRtcDataClient(Sender) do
  begin // make sure our request starts with "/"
    if Copy(Request.FileName, 1, 1) <> '/' then
      Request.FileName := '/' + Request.FileName;
    // define the "HOST" header
    if Request.Host = '' then
      Request.Host := ServerAddr;
    if DoDebug then
      Memo1.Text := 'Requeststring "' + Request.FileName + '" from "' + ServerAddr + '".';
    // send request header out
    WriteHeader;
  end;

end;

procedure TWork.RtcDataRequest1DataReceived(Sender: TRtcConnection);
begin
  with TRtcDataClient(Sender) do
  begin
    if Response.Started then
    begin { Executed only once per request,
        when we start receiving the response. }
      // Clear the info we wrote here in our "OnBeginRequest"
      // HERE COMES THE XML BACK?? HOW TO HANDLE THIS?
      // ALSO WITH REQUEST.QUERY??
      if DoDebug then
      begin
        Memo1.Clear;
        Memo1.Lines.Add('Status code: ' + IntToStr(Response.StatusCode));
        Memo1.Lines.Add('Status text:' + Response.StatusText);
        Memo1.Lines.Add('ALL Headers:');
        Memo1.Lines.Add(Response.HeaderText);
        Memo1.Lines.Add('Content Length:');
        Memo1.Lines.Add(IntToStr(Response.ContentLength));
        Memo1.Lines.Add('Content body:');
        Memo1.Lines.Add('START >');
      end; { Could be executed more than once,  depending on the content size }
      // add content received now.
      Memo1.Text := Memo1.Text + Read;
      if Response.Done then
      begin { Executed only once per request, when we have just received it all. }
        Memo1.Lines.Add('< END');
      end;
    end; // DoDebug
  end;
end;

end.

The DMF:

object Work: TWork
  Left = 0
  Top = 0
  Caption = 'AVN_MSP_SERVER'
  ClientHeight = 671
  ClientWidth = 947
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object Ontvangen: TLabel
    Left = 20
    Top = 8
    Width = 72
    Height = 13
    Caption = 'Ontvangen/log'
  end
  object label2: TLabel
    Left = 20
    Top = 352
    Width = 120
    Height = 13
    Caption = 'test of database actief is'
  end
  object Memo1: TMemo
    Left = 146
    Top = 8
    Width = 671
    Height = 297
    ScrollBars = ssBoth
    TabOrder = 0
  end
  object DBGrid1: TDBGrid
    Left = 146
    Top = 352
    Width = 671
    Height = 233
    DataSource = DM1.DS_IDealRecords
    TabOrder = 1
    TitleFont.Charset = DEFAULT_CHARSET
    TitleFont.Color = clWindowText
    TitleFont.Height = -11
    TitleFont.Name = 'Tahoma'
    TitleFont.Style = []
  end
  object Server: TRtcHttpServer
    RestartOn.ListenLost = True
    RestartOn.ListenError = True
    RestartOn.Wait = 10
    Blocking = True
    Left = 848
    Top = 144
  end
  object RtcDataProvider1: TRtcDataProvider
    Server = Server
    OnCheckRequest = RtcDataProvider1CheckRequest
    OnDataReceived = RtcDataProvider1DataReceived
    Left = 848
    Top = 200
  end
  object RtcHttpClient1: TRtcHttpClient
    ReconnectOn.ConnectLost = True
    ReconnectOn.Wait = 5
    Blocking = True
    Left = 848
    Top = 264
  end
  object RtcDataRequest1: TRtcDataRequest
    AutoSyncEvents = True
    Client = RtcHttpClient1
    AutoRepost = 2
    OnBeginRequest = RtcDataRequest1BeginRequest
    OnDataReceived = RtcDataRequest1DataReceived
    Left = 848
    Top = 328
  end
  object XMLDoc1: TXMLDocument
    Left = 848
    Top = 400
    DOMVendorDesc = 'Omni XML'
  end
  object XMLDoc2: TXMLDocument
    Left = 848
    Top = 464
    DOMVendorDesc = 'Omni XML'
  end
end


Title: Re: sending a XML-file back with a TRctHTTPserver
Post by: D.Tkalcec (RTC) on September 05, 2018, 09:05:28 AM
I've just realized that there are two topics from you related to the same question, so I've merged the two topics together now to avoid confusion.


Title: Re: sending a XML-file back with a TRctHTTPserver
Post by: D.Tkalcec (RTC) on September 05, 2018, 09:57:40 AM
I've spotted a few problems with your latest code (related to wrong use of the RTC SDK)...

1. The Write method of the TRtcHttpClient component may ONLY be used from inside an event triggered by that component. In other words, your "RtcHttpClient1.Write(XML_string)" line inside the "RtcDataProvider1DataReceived" event is illegal and would most likely cause you problems.

However, if you already have the complete request headers and the request content body ready for sending and you are confident that the content is small enough to fit into memory without causing you memory problems, then you can use the "PostMethod" method on the "RtcDataRequest" component to have request headers and content body stored for you in temporary buffers and sent out after the "OnBeginRequest" event. This also works if you skip implementing the "OnBeginRequest", provided you also prepare all required headers before calling "PostMethod". In other words, this line replaces your "RtcHttpClient1.Write(XML_string);" and "Post" calls in the "RtcDataProvider1DataReceived", but it is entirely legal and does what you intended (provided you are using RTC SDK v9) ...

RtcDataRequest1.PostMethod('',XML_string);

2. You've set Request.Method='GET', but since you are posting a request with content body, I have a feeling that this is actually a "POST" request, so you might need to change that to Request.Method='POST' in your code, or simply remove that Request.Method assignment line from your code and use this line instead of the one I've recommended above (point 1) ...

RtcDataRequest1.PostMethod('POST',XML_string);

3. You are storing the response you get from the external Server inside the Memo.Text, which is fine for debugging but bad for production. You should change this to store the response content in a variable, so it doesn't get messed up with Memo component formatting.

In other words, instead of doing this inside your "RtcDataRequest1DataReceived" event ->

 // add content received now.
      Memo1.Text := Memo1.Text + Read;

You should declare a variable like "RecvContent:RtcString",
then you can do this inside the "RtcDataRequest1DataReceived" event ->

 // add content received now.
      RecvContent:=Sender.Read;
      Memo1.Text := RecvContent;

This makes your response content body accessible inside the "RtcDataProvider1DataReceived" event, after the "WaitForCompletion" call, so you can do this to access the XML string received as part of the response content body ->

XMLDoc2.LoadFromXML(RecvContent);   // Response is an XML file

Let me know if you have any other question.


Title: Re: sending a XML-file back with a TRctHTTPserver
Post by: Henk vd Boogaard on September 05, 2018, 11:01:39 AM
First, I indeed posted my reply in the wrong place.
I was looking there for you answer for my xml questions, but that program you advised me to work with a rtcFunction and that program works fine.
However now I must use xml and I am struggling again.
I apricate very much that you help so good.
===========================
I altered the code as you suggested and made also the last additions.
I post here the last bit.
Only I get an error on:
RtcDataRequest1.PostMethod('POST', XML_string);
// <--- GIVES A COMPILE ERROR 'THERE IS NO OVERLOADED VERSION OF THIS'

Also can you point me to a good example to implement StreamSec2 for the RtcDataRequest for that must be in HTTPS.

Thanks
Henk
====================
procedure TWork.RtcDataProvider1DataReceived(Sender: TRtcConnection);
var
  RecvContent, ResultText, StatusText: rtcString;
begin
  with Sender as TRtcDataServer do
  begin
    if Request.Complete then
    begin
      RecvContent := Sender.Read;
      TransactionID_str := Request.Query['transactionid'];
      if DoDebug then
        Memo1.Text := Memo1.Text + READ;
      Memo1.Lines.Add('ID=' + TransactionID_str);
      try
        IDealRecNr := StrToInt(TransactionID_str);
      except
        on E: Exception do
          if DoDebug then
            Memo1.Lines.Add('Fout bij vertalen recordnummer van parameter : ' + E.Message);
      end;

      with RtcDataRequest1 do
      begin
        Request.FileName := '/' + TransactionID_str;
        CoInitialize(nil);
        // nu in database Huurovereenkomst controleren - indien OK dan VerstuurHTML:

        // maak XML-file:
        Exportfilenaam := 'IDeal_' + IntToStr(IDealRecNr) + '.XML';
        XMLDoc1.Active := false;
        XMLDoc1.Xml.Text := '';
        XMLDoc1.Active := true;
        XMLDoc1.Encoding := 'UTF-8';
        XMLDoc1.AddChild('Pricat', 'http://www.ean.nl');
        XMLDoc1.DocumentElement := XMLDoc1.CreateNode('status');
        XMLDoc1.DocumentElement.Attributes['ua'] := 'RTC Library)';

        // merchant
        iNode_Merchant := XMLDoc1.DocumentElement.AddChild('merchant');
        iChild := iNode_Merchant.AddChild('account');
        iChild.Text := DM1.nxT_algpar.FieldByName('IDeal_account').AsString;
        iChild := iNode_Merchant.AddChild('site_id');
        iChild.Text := DM1.nxT_algpar.FieldByName('IDeal_site_id').AsString;
        iChild := iNode_Merchant.AddChild('site_secure_code');
        iChild.Text := DM1.nxT_algpar.FieldByName('IDeal_site_secure_id').AsString;

        // transaction
        iNodeTransaction := XMLDoc1.DocumentElement.AddChild('transaction');
        iChild := iNodeTransaction.AddChild('id');
        iChild.Text := IntToStr(IDealRecNr);
        // bestand maken:
        if DoDebug then
          XMLDoc1.SaveToFile(DM1.XML_Dir + Exportfilenaam);

        XMLDoc1.SaveToXML(XML_string);
        // now get the information
        RtcHttpClient1.ServerAddr := DM1.SiteURL;
        RtcHttpClient1.ServerPort := IntToStr(DM1.SitePort);
        // Post the request with 'POST' :
        RtcDataRequest1.PostMethod('POST', XML_string); // <--- GIVES A COMPILE ERROR 'THERE IS NO OVERLOADED VERSION OF THIS'
        WaitForCompletion;

        XMLDoc1.Active := false;
        XMLDoc2.Active := false;
        XMLDoc2.LoadFromXML(RecvContent); // Response is an XML file
        XMLDoc2.Active := true;
        // now get the information from XMLdoc2:
        ResultText := XMLDoc2.DocumentElement.Attributes['result'];
        iNodeEwallet := XMLDoc2.DocumentElement.ChildNodes.FindNode('ewallet');
        iNodeStatus := iNodeEwallet.ChildNodes.FindNode('status');
        if Assigned(iNodeStatus) then
          StatusText := iNodeStatus.Text;
        // database bijwerken:
        if DM1.nxT_IDealRecords.FindKey([IDealRecNr]) then
        begin
          if Uppercase(ResultText) = 'OK' then
          begin
            DM1.nxT_IDealRecords.Edit;
            DM1.nxT_IDealRecords.FieldByName('StatusOntvangen').AsBoolean := true;
            DM1.nxT_IDealRecords.FieldByName('Status').AsString := StatusText;
            if Uppercase(StatusText) = 'COMPLETED' then
              DM1.nxT_IDealRecords.FieldByName('BetalingOK').AsBoolean := true;
            DM1.nxT_IDealRecords.Post;
          end
          else
          begin
            DM1.nxT_IDealRecords.Edit;
            DM1.nxT_IDealRecords.FieldByName('StatusOntvangen').AsBoolean := false;
            DM1.nxT_IDealRecords.FieldByName('Status').AsString := StatusText;
            DM1.nxT_IDealRecords.FieldByName('Foutkode').AsString := ResultText;
            DM1.nxT_IDealRecords.Post;
          end;

        end
        else if DoDebug then
          Memo1.Lines.Add('Kan IDealrecord niet vinden');
        // bewaren als laatste zodat eerst de database wordt bijgewerkt
        if DoDebug then
          XMLDoc2.SaveToFile(DM1.XML_Dir + DateTimeToStr(now) + 'result' + IntToStr(IDealRecNr) + '.xml');
        CoUninitialize;
      end;
    end;
  end;
end;



Title: Re: sending a XML-file back with a TRctHTTPserver
Post by: D.Tkalcec (RTC) on September 05, 2018, 11:18:30 AM
Oops  :-[ Sorry, my mistake. You need the "Write" method on the "RtcDataRequest" component (not on the "RtcHttpClient" component) to prepare the request content body which you want to send, then you can use "PostMethod" to post your request without the need to implement the "OnBeginRequest" and "OnDataSent" events for sending the request headers and/or content out.

In other words, you need ...

RtcDataRequest1.Write(XML_string);
RtcDataRequest1.PostMethod('POST');

As for your StreamSec question, please contact the StreamSec component vendor with ANY questions you may have about using their components.


Title: Re: sending a XML-file back with a TRctHTTPserver
Post by: D.Tkalcec (RTC) on September 05, 2018, 11:30:23 AM
I also see that you've misunderstood my intention with the "RecvContent" variable. It has to be a global variable or a variable of your main class (not local to any event) and you need to use the "Sender.Read" method inside the "OnDataReiceved" event of your RtcDataRequest component if you want the response from your external Server (received by your RtcDataRequest component, which is attached to your RtcHttpClient companent).

So ... what you did now by calling "RecvContent:=Read" inside your "RtcDataProviderDataReceived" event gives you the request content which your Server has received from your inbound Client and NOT the response content body sent to you from your external Server.

On one side, you have your Server communicating with Clients through "RtcHttpServer" and the linked "RtcDataProvider" component (using events attached to these components), while on the other side you have the same process communicating with another external Server by using the "RtcHttpClient" and "RtcDataRequest" (using events on these components). These are two completely separate communication channels, each with its own request and response data, so need to watch out and sure you do NOT mix them up! It's a HUGE difference where and on which components you call "Read" and "Write"!!!


Title: Re: sending a XML-file back with a TRctHTTPserver
Post by: Henk vd Boogaard on September 10, 2018, 11:33:17 AM
Hi Danijel

Are you sure that sending with:
RtcDataRequest1.Write(XML_string);
RtcDataRequest1.PostMethod('POST');
is working within the procedure RtcDataProvider1DataReceived(Sender: TRtcConnection);?

It seems there is nothing sent out.

Can I email you a link so you can download my program for testing?


Henk van den Boogaard


Title: Re: sending a XML-file back with a TRctHTTPserver
Post by: D.Tkalcec (RTC) on September 10, 2018, 01:03:42 PM
Remove the "WriteHeader" call from your "RtcDataRequest1BeginRequest" event. That call makes it impossible to send out the content you've prepared with the "RtcDataRequest1.Write(XML_string)".

Why? Because you are NOT setting the "Request.ContentLength", which HAS TO BE SET manually before calling "WriteHeader". If you just call "WriteHeader" but have NOT set the "Request.ContentLength" before, you will end up sending a request with NO CONTENT BODY.


Title: Re: sending a XML-file back with a TRctHTTPserver
Post by: Henk vd Boogaard on September 10, 2018, 04:15:42 PM
Hi Danijel,

How can I see/get the complete message before it is being POST?
So I can check it of it is what MSP want.


Henk


Title: Re: sending a XML-file back with a TRctHTTPserver
Post by: D.Tkalcec (RTC) on September 10, 2018, 04:46:25 PM
Normally, whatever you prepare will be sent out, so there are no events or methods on RTC components which would allow you to double-check what was prepared before it actually goes out, but ... you should be able to use
external Network Traffic monitoring tools like Wireshark to check what is REALLY being sent and received:
https://www.wireshark.org/


Title: Re: sending a XML-file back with a TRctHTTPserver
Post by: Henk vd Boogaard on September 11, 2018, 02:02:04 PM
Hi Danijel

I have the program working.
So I am very happy you and Henrick (StreamSec) helped me.
Only one problem is now that on my pc is works fine but on the server (Windows Server 2016) it seems that the outgoing port 443 is blocked.
But they are investigate that.

Thanks
Henk van den Boogaard


Title: Re: sending a XML-file back with a TRctHTTPserver
Post by: D.Tkalcec (RTC) on September 11, 2018, 06:39:03 PM
Glad to know it's working. Thanks for your feedback.