uROMessage over HTTP transport (rel. 1517)

Hi, I have discovered another message-aware issue…

started with release 1517, there are some modifications in base TROMessage class… these new instance variables:

..  
    {$IFDEF DELPHI10UP}{$REGION 'http settings'}{$ENDIF}
    fhttp_CanUseContentEncoding: Boolean;
    fhttp_ContentType: String;
    function Http_Headers: TStringList;
..

It’s quite easy to guess purpose of these fields… to collect and adjust parameters for messages marshalled over HTTP transports. Probably, variables are handled well, like in the TROSoapMessage.Initialize method…

  if (fSoapAction = '') and (not fActionIsCustom) then 
    fSoapAction := Format('urn:%s-%s#%s', [LibraryName, anInterfaceName, aMessageName]);
    Http_Headers.Values['SOAPAction'] := '"'+fSoapAction +'"';
  end;

  if xsoSoap12 in SerializationOptions then
    fhttp_ContentType := 'application/soap+xml; charset=utf-8; action=' + fSoapAction
  else
    fhttp_ContentType :=  DataFormatXml;

…but… as I can search over all RO source files… there is NO SINGLE PLACE where these parameters are applied to related HTTP transport object… no single call for uROHTTPTools.SetHTTPInfo() found over whole library. As a result of this, Content-Type param of outgoing HTTP response is always set to default

Content-Type: text/html; charset=ISO-8859-1

It is invalid for XML formatted responses, typically SOAP responses, where

Content-Type: text/xml; charset=utf-8

…is expected.

It seems that there is something forgotten in this concept, something planned but finally unfinished…

Regards from Prague, Jan

Hi,

I cannot reproduce this.
Content-Type is set in the TROMessage.ApplyAttributes2_Transport method.

can you create a simple testcase that reproduces this issue, pls?

…just to be sure… Im talking about server-side message handling, when server outgoing response is assembled. On the client side, there is point in source code where IROMessage.ApplyAttributes2_Transport method is called… mean this:

uROTransportChannel.TROTransportChannel.Dispatch()

…thus, HTTP related variables are applied to HTTP transport object… but, ONLY for client outgoing requests, say again.

Hi,

Still cannot reproduce.

lets see the Mega Demo sample:

procedure TMegaDemoService_Invoker.Invoke_EchoPerson(const __Instance: IInterface; const __Message: IROMessage; const __Transport: IROTransport; out __oResponseOptions: TROResponseOptions);
..
  __Message.ApplyAttributes2_Transport(__Transport); ///<<<

This is fragment of my version MegaDemoLibrary_Invk.pas:

procedure TMegaDemoService_Invoker.Invoke_EchoPerson(const __Instance: IInterface; const __Message: IROMessage; const __Transport: IROTransport; out __oResponseOptions: TROResponseOptions);
var
  l_aPerson: MegaDemoLibrary_Intf.TPerson;
  l_anotherPerson: MegaDemoLibrary_Intf.TPerson;
  __lObjectDisposer: TROObjectDisposer;
  __lintf: MegaDemoLibrary_Intf.IMegaDemoService;
begin
  CheckRoles(__Instance, GetDefaultServiceRoles());
  __Message.SetAttributes(__Transport, ['EA_Model'], ['C:\Dev\ROSDK3\Tests\MegaDemo\NewLibrary.eap']);
  l_aPerson := nil;
  l_anotherPerson := nil;
  try
    if not Supports(__Instance, IMegaDemoService, __lintf) then begin
      raise EIntfCastError.Create('Critical error in TMegaDemoService_Invoker.Invoke_EchoPerson: __Instance does not support MegaDemoService interface');
    end;

    __Message.Read('aPerson', System.TypeInfo(MegaDemoLibrary_Intf.TPerson), l_aPerson, []);

    __lintf.EchoPerson(l_aPerson, l_anotherPerson);

    __Message.InitializeResponseMessage(__Transport, 'MegaDemoLibrary', 'MegaDemoService', 'EchoPersonResponse');
    __Message.Write('anotherPerson', System.TypeInfo(MegaDemoLibrary_Intf.TPerson), l_anotherPerson, []);
    __Message.Finalize();
    __Message.UnsetAttributes(__Transport);

  finally
    __lintf := nil;
    __lObjectDisposer := TROObjectDisposer.Create(__Instance);
    try
      __lObjectDisposer.Add(l_aPerson);
      __lObjectDisposer.Add(l_anotherPerson);
    finally
      __lObjectDisposer.Free();
    end;
  end;
end;

No occurence of “ApplyAttributes2_Transport” found… just as all other “Invoke_xxx” methods in this unit.

Additionaly, I’d check content of “…\CodeGen\uRODLToPascalInvk.pas” unit… the same… no single mention about using of “ApplyAttributes2_Transport” method…

Ok, I’ll prepare simple testcase, to reproduce and present this issue…

Regards from Prague, Jan

Hi,

try to recompile project. it will be added.

it isn’t updated for ages because we have switched to Codegen4 from Codegen1 a long time ago

Ok, I’ll try… does it means that CODEGEN1 is obsolete and unmaintained… from what time? Was this fact published and underlined somewhere?

If I can consider… starting with r.1517 there is functional discrepancy between code, generated using CODEGEN1 and CODEGEN2… resulting to invalid/different code behavior in CODEGEN1 units?

Regards from Prague, Jan

Hi,

Codegen4 was created 5 years ago. We have switched to it in that time. Codegen1 isn’t updated for such time too.
it wasn’t removed 5 years ago for maintaining generation of unit in Lazarus for non-windows platforms.
I’ve logged an issue (#19142) for removing Codegen1 from installation for preventing such misunderstanding.

if you are using cmd line compilation, you can [re]generate units with rodl2code.exe.
also you can use the rocg4_helper program. I’ve already posted it at this forum.
it’s a simple wrapper for the uROCodegen4Helper.pas unit that allows to generate files too and used by our IDE packages for generating files inside Delphi IDE.

you can see RODL/Codegen changes at Commits · remobjects/ROCodeGen · GitHub

Ok, thanks for explanation… I’ll re-generate all our RODL’s using CODEGEN4.

Btw, we are still using classic, old-fashioned server design, based on RODLs… CodeFirst concept is not suitable for us, for many reasons (strict separation between library definition and implementing code, mainly…)… is there a certainty that RODL-based server design will be preserved and still maintained in future?

Regards from Prague, Jan

Hi,

The RODL-based servers will be maintained in the future.
We have no plans for cancelling them.

Good to hear from you, thanks. Btw, have you any knowledge or estimate which server design concept is preferred by customers… RODL vs CodeFirst? I must say that strict separation between library definition and implementing code is - in common meaning - more usable, better to maintain… in compliance with classical time proven concepts like TLB, IDL… and RODL, of course.

Regards from Prague, Jan

Hi,

It doesn’t have any influence to be honest. both types of servers work equally.
one group of users like RODL-based servers, other group - CodeFirst-based servers.

Initial developing can be more easier in Service Builder, because codegen will create automatically _impl for you from template.
Note: Service Builder allows to select some options that are used for generation of Delphi units.
Note2: RODL-based server can be converted to CodeFirst-based server in any time.

from maintaining sight:

  • RODL-based server generates _Intf at compiling stage. in CodeFirst-based server you should start server and generate _Intf from started server.

Hello, Evgeny

I just tried to generate CODEGEN source files for MegaDemo project, using Service Builder (r.1517, of course)… and there is some strange thing I cannot quite understand:

occurrence of these lines

__Message.SetAttributes(__Transport, ['EA_Model'], ['C:\Dev\ROSDK3\Tests\MegaDemo\NewLibrary.eap']);
__Message.ApplyAttributes2_Transport(__Transport);

…inside of Service_Invoker.Invoke_xxx methods depends on library custom attributes, defined in RODL. Say other way - if there is at least one custom library attribute defined in RODL, generated code contains lines for setting transport parameters, like this:

If there is no library attribute defined, these lines are missing in code… and no transport parameters are set.

What’s the point… why library atributtes are important/necessary to include these lines into the generated code?

Thanks, regards from Prague

Jan

Hi,

I’ll check this in sources

Hi,

fixed

Hm… regardless of anything said here up to now, regardless of using CODEGEN4 instead CODEGEN1… the problem applying transport parameters adjusted in message STILL REMAINS in release 1517.

See this… snapshot of SoapUI, capturing SOAP call against my server… where simple SOAP service “Server_CustomAction” with one method “Execute” is implemented, just as echo… passing back incoming parameters:

As You can see, value of string parameter is interpreted as corrupted. Inspecting response as raw HTTP confirms, that “Content-Type” is set to “´text/html; charset=ISO-8859-1” instead of righ “text/xml;charset=UTF-8” value.

Let’s come on… see auto-generated invoker code for this call:

  • stage 1, RED - yep… parameters are applied from message to transport…

  • stage 2, GREEN - call is finished, response message is going to be assembled… and… RIGHT HERE, inside of __Message.InitializeResponseMessage, HTTP parameters for HTTP response (!) are adjusted and set. In releases before 1517, HTTP parameters were set directly to transport, inside message code, using SetHTTPInfo… it worked well. Now… just internal message variables are set, with no impact to transport.

  • stage 3, BLUE - this is the place, where transport parameters must be applied again… otherwise, HTTP transport parameters are set to default values.

Hope issue is described enough and well…

Regards from Prague, Jan

Logged as bugs://D19145.

bugs://D19145 was closed as fixed.

Hello,

just additional notice… as I observed, behavior is a bit different in dependence on component platform on which server is built. Using IndyHTTPServer, behavior is worst… “Content-Type” value is never set to transport, default value “text/html; charset=ISO-xxx” is always applied. This issue is - by my opinion - caused by implementation of IROHTTPTransport.GetContentType method in class uROIndyHTTPServer.TIndyHTTPTransport…

function TIndyHTTPTransport.GetContentType: string;
begin
  result := fResponseInfo.ContentType;
end;

Im pretty sure, that value must be fetched from fRequestInfo instead of fResponseInfo.

In case of TROWinHTTPServer, TROSynapseHTTPServer, behavior is a bit better… HTTP parameters, applied at beginning of “_Invoker.Invoke_xxx” code using ApplyAttributes2_Transport are preserved during whole execution… but, if there is need to change/add some parameters inside response message preparation, those will be never applied to transport… and this is common conceptual behavior.

Regards from Prague, Jan