Best way to catch a HTTP Timeout from the ClientBuilder

Posted by onnodehaan on 06-Jul-2019 21:52

Hi Guys

I'm using the Clientbuilder for a while now. For speed I've set it up based on one of the examples online

Now I'm wondering, if I where to change the example below and insert a timeout

Example
oResponse = ClientBuilder:Build():Client:Execute(oRequest).

Would become
oResponse = ClientBuilder:Build():Client:SetRequestTimeout(10):Execute(oRequest).

What would be the best way to capture the timeout? 

Will the clientbuilder throw an error? Or will it return an error statuscode?

Couldn't find this in the documentation, so I thought I'd just ask before logging a supprt case.

REgards, Onno

-------- UPDATE EXAMPLE ------

BLOCK-LEVEL ON ERROR UNDO, THROW.

USING OpenEdge.Core.String.
USING OpenEdge.Net.HTTP.ClientBuilder.
USING OpenEdge.Net.HTTP.IHttpRequest.
USING OpenEdge.Net.HTTP.IHttpResponse.
USING OpenEdge.Net.HTTP.RequestBuilder.
USING Progress.Json.ObjectModel.JsonObject. 

DEFINE VARIABLE httpUrl AS CHARACTER NO-UNDO.
DEFINE VARIABLE oRequest AS IHttpRequest NO-UNDO.
DEFINE VARIABLE oResponse AS IHttpResponse NO-UNDO.
DEFINE VARIABLE oRequestBody AS String NO-UNDO.
DEFINE VARIABLE oJsonEntity AS JsonObject NO-UNDO.
DEFINE VARIABLE JsonString AS LONGCHAR NO-UNDO.

SESSION:DEBUG-ALERT = TRUE.
httpUrl = "www.googleapis.com/.../token".

oRequestBody = new String('client_id=your_client_id&client_secret=your_client_secret&refresh_token=your_refresh_token&grant_type=refresh_token').

oRequest = RequestBuilder:Post("www.googleapis.com/.../token", oRequestBody)
:ContentType('application/x-www-form-urlencoded')
:AcceptJson()
:Request.

oResponse = ClientBuilder:Build():Client:SetRequestTimeout(10):Execute(oRequest).

MESSAGE
oResponse:StatusCode SKIP
oResponse:StatusReason SKIP
VIEW-AS ALERT-BOX.

oJsonEntity = CAST(oResponse:Entity, JsonObject).
oJsonEntity:Write(JsonString, TRUE).

MESSAGE STRING(JsonString)
VIEW-AS ALERT-BOX.

Posted by Peter Judge on 16-Jul-2019 13:46

Hey Onno,
 
There HTTP library listens for timeouts and throws an error if one happens.  An OpenEdge.Net.HTTP.HttpRequestError is thrown (which is an AppError).
 
See  github.com/.../ABLSocketLibrary.cls
 
The ClientSocket used by the HTTP library publishes a number of events; you could subscribe to them from the code that instantiates the HTTP client.
   
    /** Event fired when a chunk of data is received from the socket */
    define public event DataReceived signature void (input poSender as ClientSocket,
                                                     input poEventArgs as SocketReadEventArgs).
   
    /** Fired when a read times out (optional based on a timeout) */
    define public event ReadTimeout signature void (input poSender as ClientSocket,
                                                    input poEventArgs as SocketReadEventArgs).
   
    /** Fired when a read is terminated for an reason (socket disconnect, no data etc) */
    define public event ReadTerminated signature void (input poSender as ClientSocket,
                                                       input poEventArgs as SocketReadEventArgs).
   
 
The code at github.com/.../set_options.p  shows how you can use a ClientSocket object of your choosing (from which you'd subscribe to the events).
 
Note that if you go this route,
- you have to be careful of holding on too long, and preventing GC of the socket
- I'm not sure of the program flow, since an error is thrown (as opposed to added to the EventArgs object); I'd have a catch block in place.
 
hth,
-- peter
 
 
 

All Replies

Posted by onnodehaan on 12-Jul-2019 21:44

Hi guys

Anyone any thoughts on this?

Posted by dbeavon on 12-Jul-2019 22:10

Are you able to create a timeout scenario and test your code?  Maybe you can just change the host name in the url to something that doesn't exist.

I'm fairly certain you will catch an error from that code if anything goes wrong.  OO programming and SEH error handling work well together, and I'd guess that all these new API's would throw errors by convention.  It is much better for API's to follow a standard pattern than come up with their own strange and unique error -publication strategies (just thinking about ugly API's like OS-COPY give me a cold chill).

Are you already familiar with structured error handling (SEH)?  If not I would highly recommend that you read the "Error Handling" document from beginning to end.

community.progress.com/.../2911.openedge-11-7-product-documentation

The most important parts in there are related to "Structured Error Handling".  This is a similar error-handling pattern to what is found in other languages like Java and C#.

I suspect that if you put a do/catch block around all that code, you will probably be able to CATCH a Progress.Lang.Error (or more specifically a class that derives from it).   See. documentation.progress.com/.../index.html

Have you tried that?  The OpenEdge.Net.HTTP stuff is open source so you can go look for yourself to see if there are lots of UNDO, THROW statements in the case of error conditions.  This will be your biggest clue.  Here is a link to another conversation about that API.  Peter Judge seems to be the expert, if not the author:

community.progress.com/.../36251

Posted by Peter Judge on 16-Jul-2019 13:46

Hey Onno,
 
There HTTP library listens for timeouts and throws an error if one happens.  An OpenEdge.Net.HTTP.HttpRequestError is thrown (which is an AppError).
 
 
The ClientSocket used by the HTTP library publishes a number of events; you could subscribe to them from the code that instantiates the HTTP client.
   
    /** Event fired when a chunk of data is received from the socket */
    define public event DataReceived signature void (input poSender as ClientSocket,
                                                     input poEventArgs as SocketReadEventArgs).
   
    /** Fired when a read times out (optional based on a timeout) */
    define public event ReadTimeout signature void (input poSender as ClientSocket,
                                                    input poEventArgs as SocketReadEventArgs).
   
    /** Fired when a read is terminated for an reason (socket disconnect, no data etc) */
    define public event ReadTerminated signature void (input poSender as ClientSocket,
                                                       input poEventArgs as SocketReadEventArgs).
   
 
The code at github.com/.../set_options.p  shows how you can use a ClientSocket object of your choosing (from which you'd subscribe to the events).
 
Note that if you go this route,
- you have to be careful of holding on too long, and preventing GC of the socket
- I'm not sure of the program flow, since an error is thrown (as opposed to added to the EventArgs object); I'd have a catch block in place.
 
hth,
-- peter
 
 
 

Posted by onnodehaan on 17-Jul-2019 17:00

Hi Peter,

The reason I'm looking into this, is becauase once in a while we see the HttpClient hang on "Connect". The appserver busy, but seems stuck in the Connect method.

I'm not sure how to proceed. Setting a timeout might not work. And a stop-after is perhaps also ignored, because it seems that something is blocking inside the Connect method.
The strange thing is, that this happens seamingly at random and not very often. We already ruled out the REST-service that we are calling, that's working fine.

Any suggestions?

Posted by Peter Judge on 17-Jul-2019 19:20

Hey Onno,
 
There is an option on the socket CONNECT to specify a timeout. There's an example coded at  github.com/.../set_connect_timeout.p  .
 
If I remember right, when this times out then the ReadTimeout event is fired (and you get the HttpRequestError).
 
I can't find a bug for adding this to the ClientSocket that the HTTP Client uses; you may want to log a bug asking for this connect timeout.
 
-- pteer
 

Posted by Ken McIntosh on 17-Jul-2019 19:41

This was also created as Progress knowledgebase article:

000096511 - How to set the Connection Timeout for a request using OpenEdge HttpClient?

knowledgebase.progress.com/.../How-to-set-the-Connection-Timeout-for-a-request-using-OpenEdge-HttpClient

Posted by Peter Judge on 17-Jul-2019 20:20

Oh cool, thanks Ken.
 

Posted by onnodehaan on 17-Jul-2019 22:45

Hi Peter, Ken

I get an error when I try to compile the ConnectCSCP class from the samples. It can be fixed by commenting out the line below.

Why is that? Did something change in the parent class? I'm currently on 11.7

   constructor public ConnectCSCP ( input pScheme as character, input pHost as character, input pPort as integer ):

//        super (input pScheme, input pHost, input pPort).

   end constructor.

Posted by Torben on 23-Jul-2019 07:48

You need 11.7.2 or newer for the sample to work!

Posted by onnodehaan on 23-Jul-2019 09:09

Hi Torben,

Does that mean tha tI can't set a -connectTimeOut using the HTTP Client when a customer is using 11.7.1 for example?

Posted by Torben on 23-Jul-2019 13:51

Don't know.

I'm just see that constructor for ClientSocketConnectionParameters with needed signature was added in OE 11.7.2!

super (input pScheme, input pHost, input pPort).

Posted by onnodehaan on 09-Aug-2019 09:18

I got it to work by using peters example combined with a stop-after block to catch the hanging “connect”

This thread is closed