Issues with OpenEdge.Net.HTTP

Posted by dbeavon on 28-Nov-2017 12:56

I'm trying to execute a simple REST call via the OpenEdge.Net.HTTP library classes.  See code example below.

BLOCK-LEVEL ON ERROR UNDO, THROW.



   /* ********************************************************************* */
   /* Call rest                                                             */
   /* ********************************************************************* */
USING  OpenEdge.Net.*.
USING OpenEdge.Net.UriSchemeEnum.
USING OpenEdge.Net.HTTP.*.
USING Progress.Json.ObjectModel.*.

   /* ********************************************************************* */
   /* Call rest                                                             */
   /* ********************************************************************* */
   DEFINE VARIABLE v_Client AS OpenEdge.Net.HTTP.IHttpClient NO-UNDO .
   DEFINE VARIABLE v_Request AS OpenEdge.Net.HTTP.IHttpRequest NO-UNDO .
   DEFINE VARIABLE v_Response AS OpenEdge.Net.HTTP.IHttpResponse NO-UNDO .
   DEFINE VARIABLE v_ReturnOffset AS INTEGER NO-UNDO INIT ?.
   
   
         MESSAGE "HERE {&FILE-NAME} {&LINE-NUMBER} START". PAUSE.
      
   /* ********************************************************************* */
   /* Call rest                                                             */
   /* ********************************************************************* */
define variable oURI as URI no-undo.
oURI = new URI("http", "xyz.com", 8889).
oURI:Path = "GetTimeZoneOffsetFromHpuxName/EST5EDT".

v_Request = NEW OpenEdge.Net.HTTP.HttpRequest("GET", oURI).
/*v_Request:Method = "GET".*/
MESSAGE "HERE {&FILE-NAME} {&LINE-NUMBER} FINISH (METHOD NOT SHOWN): " v_Request:Method. PAUSE.
 

 

This is only the beginning part, but I'm not even able to get this much to work right.  At the point "METHOD NOT SHOWN", the method on the request is blank whereas it should be "GET".  If I uncomment the line right before it, I do see that the method appears as "GET".  I can't understand why the constructor isn't working properly.  

My troubles are happening with 11.6 and 11.7.  Is this OpenEdge.Net.HTTP stuff open source?  The docs that I found our pretty basic and aren't helping me much. https://documentation.progress.com/output/oehttpclient/116/

I think the source code would help troubleshoot these types of issues a bit more.  

I am able to use the "fluent builders" to get things working.  So far those haven't failed me. Here is an example of that...

v_Request = RequestBuilder:Get("xxx:8889/.../EST5EDT")
:AcceptJson()
:Request.

But at a high level, I'm trying to use these classes to run a simple GET operation against a REST service in 5 ms.  Unfortunately as-of now I cannot do better than 50-100 ms.  I turned up 4GLTrace logging to level 3 and I get several thousand lines of output, most of which is related to the fluent builders that I'm trying to optimize away (without any luck).  Here is the proc stats for a simple REST request.

Procedure call statistics:		12:02:47

Caller           Callee         Load Size   Calls  Rd Bytes    Reread      Time
<top>            OpenEdge.ApplicationServer    6157       1         0         0         0

...

 



app0000          OpenEdge.Core.Util   48408       1         0         0         0
app0000          OpenEdge.Net.HTTP    1303       2    354278         0         4
ClientBuilder    OpenEdge.Core.Util   52706       3     52706         0         0
ClientBuilder    OpenEdge.Net.HTTP   25197       4     27342         0         0
BuilderRegistry  OpenEdge.Core      10530       1     70124         0         1
Assert           OpenEdge.Core.Assertion   52706       1         0         0         0
DefaultHttpClientBuilder OpenEdge.Net.HTTP    4472       2    118041         0         2
ClientBuilder    OpenEdge.Net.HTTP.Lib    3239       1    104398         0         1
ClientLibraryBuilder OpenEdge.Core.Util   23877       2         0         0         0
ClientLibraryBuilder OpenEdge.Net.HTTP    3079       2         0         0         0
ClientLibraryBuilder OpenEdge.Net.HTTP.Lib   25197       1         0         0         0
ClientLibraryBuilder OpenEdge.Net.HTTP.Lib.ABLSockets    2111       1     17609         0         2
ABLSocketLibraryBuilder OpenEdge.Net.ServerConnection   49849       2         0         0         0
ABLSocketLibraryBuilder OpenEdge.Net.HTTP.Lib.ABLSockets   26444       1     26444         0         0
HttpClient       OpenEdge.Net.HTTP    1284       4     18928         0         2
RequestBuilder   OpenEdge.Net        4329       1      4329         0         0
URI              OpenEdge.Net       39945       1     80601         0         0
URI              OpenEdge.Core.Collections   39945       2         0         0         0
RequestBuilder   OpenEdge.Core.Util   23877       2         0         0         0
RequestBuilder   OpenEdge.Net.HTTP    7645       4     28876         0         0
DefaultRequestBuilder OpenEdge.Net.HTTP   39819       2         0         0         1
HttpMessage      OpenEdge.Core.Collections   39945       4         0         0         0
HttpMessage      OpenEdge.Net.HTTP   19520       3         0         0         0
HttpRequest      OpenEdge.Net.HTTP    6778       1     71020         0        10
HttpHeaderBuilder OpenEdge.Core.Util   23877       9         0         0         0
HttpHeaderBuilder OpenEdge.Net.HTTP   62435      26     90129         0        20
DefaultHeaderBuilder OpenEdge.Net.HTTP   34209      10         0         0         0
HttpMessage      OpenEdge.Net.HTTP.Filter.Writer    9915       1     25683         0         3
SetHeaderMessageWriterBuilder OpenEdge.Core.Util   23877       1         0         0         0
SetHeaderMessageWriterBuilder OpenEdge.Net.HTTP    3079       1         0         0         0
ResponseBuilder  OpenEdge.Core.Util   23877       2         0         0         0
ResponseBuilder  OpenEdge.Net.HTTP    3079       3         0         0         1
DefaultResponseBuilder OpenEdge.Net.HTTP   39819       2         0         0         0
HttpClient       OpenEdge.Core.System   19309       2         0         0         0
ABLSocketLibrary OpenEdge.Core      10132       1     83487         0         0
ByteBucket       OpenEdge.Core       5771       5     21898         0         1
ABLSocketLibrary OpenEdge.Net.HTTP.Filter.Writer   11002       2     58602         0         1
RequestWriterBuilder OpenEdge.Core.Util   48408       2         0         0         0
RequestWriterBuilder OpenEdge.Net.HTTP    2321       2         0         0         0
RequestWriterBuilder OpenEdge.Net.HTTP.Filter.Writer   16127       1         0         0         0
RequestWriterBuilder OpenEdge.Net.HTTP.Filter.Payload   13736       2         0         0         0
ClientSocket     OpenEdge.Core       7367       2         0         0         0
ClientSocket     sockethelper        2729       1      2729         0         0
ByteBucket       OpenEdge.Core.Util    5726       1         0         0         0
ABLSocketLibrary OpenEdge.Net.HTTP.Filter.Payload   37210       1         0         0         0
ClientSocket     OpenEdge.Net.ServerConnection    5771       1         0         0         0
Memptr           OpenEdge.Core       9091       1      9091         0         0
EntityWriterBuilder OpenEdge.Net.HTTP.Filter.Writer    1107       2    102109         0         0
EntityWriterRegistry OpenEdge.Core.Util   23877       1         0         0         0
EntityWriterRegistry OpenEdge.Net.HTTP    3079       1         0         0         0
MessageWriterBuilder OpenEdge.Core.Util   23877       2         0         0         0
MessageWriterBuilder OpenEdge.Net.HTTP    3079       2         0         0         0
MessageWriterBuilder OpenEdge.Net.HTTP.Filter.Writer   26444       2         0         0         2
DefaultMessageWriterBuilder OpenEdge.Net.HTTP.Filter.Payload   13736       2         0         0         0
HttpClient       OpenEdge.Net.HTTP.Filter.Writer   16365       1     33649         0         4
StatusCodeWriterBuilder OpenEdge.Core.Util   23877       1         0         0         0
StatusCodeWriterBuilder OpenEdge.Net.HTTP    3079       1         0         0         0

I'm a bit discouraged that my HttpClient approach in OE won't ever run in less than 5 ms.  Does anyone have experience making REST calls from ABL?

 

Posted by Peter Judge on 28-Nov-2017 14:22

You should call Initialize() after calling the constructor. This makes sure some of the internal structures are set appropriately (header collections etc).
 
It’s not doc’ed that you should do this.
 
Every time you see a type that implements the OpenEdge.Core.ISupportInitialize interface you should call Initialize() after the constructor.
 
 
 

All Replies

Posted by Peter Judge on 28-Nov-2017 14:22

You should call Initialize() after calling the constructor. This makes sure some of the internal structures are set appropriately (header collections etc).
 
It’s not doc’ed that you should do this.
 
Every time you see a type that implements the OpenEdge.Core.ISupportInitialize interface you should call Initialize() after the constructor.
 
 
 

Posted by dbeavon on 28-Nov-2017 15:05

OK, Thanks for the feedback.  I figured something like that was going on, which is why I was hoping to take a peek at the source.  The API is fluent (which I really like, so long as it is fast).  But of course one big problem with ABL is that every single method call has a measurable performance penalty.  

Any chance of open-sourcing this stuff?  I'd love to be able to see what the fluent "builders" do, so that I can pick-and-choose only the portions that I need....  It takes every trick in the book to squeeze performance out of ABL.  

The Microsoft.Net-based approach described in the following KB ended up getting me down to about 3 ms round-trips on my REST client calls.  (I will be using this as an optimization for WIN32 deployments). knowledgebase.progress.com/.../Is-there-an-HTTP-client-for-OpenEdge

I see a growing number of pl/OO libraries in recent versions of OpenEdge.  How does Progress choose when to compile an API like this down to machine code (instead of leaving it as r-code)?  I think there are some relatively new things they've added (eg DOM/SAX for XML) that are fast because they are built into the AVM itself.  I don't see why HTTP stuff can't be done the same way...

Posted by Peter Judge on 28-Nov-2017 15:14
Posted by Peter Judge on 28-Nov-2017 16:22

If you want to optimise for Win32/64 you should look into creating your own implementation of an IHttpClientLibrary; that way you can keep the public/client-dev interfaces as-is and just swap out the implementations.

There are a couple of places where the HTTP client is slow/inefficient - one big one is in how we deal with the HTTP message bodies: since the ABL does not (yet) have a natively dynamically-expandable MEMPTR structure, this gets done in ABL which is quite a bit slower than it'd be in the AVM. That said, in 12.0 (for which there's a new ESAP "coming soon") we changed the implementation of the ABL ByteBucket to use a native structure (a  Progress.IO.MemporyOutputStream). That will be faster, although I've not tested yet exactly how much faster.

Posted by marian.edu on 29-Nov-2017 00:32

Yes yes, the famous `initialization pattern`… there should be somewhere a factory that takes care of calling that `initialize` method for you or it might use the `lazy initialization` to be nice or just throw you a `non initialized` error right in your face when you try to use it without knowing how to do it :)


But I do feel your pain Peter, documentation is by far the hardest part and no fun at all :(
 
Marian Edu

Acorn IT 
+40 740 036 212

Posted by Peter Judge on 29-Nov-2017 07:44

The builder _does_ take care of the initialisation, in fairness.
 
And I hate having a “not init’ed” variable -  it smells to me - but maybe that’s what’s needed in cases like these :)

This thread is closed