Long response interval cause exception or hang

Apr 9, 2013 at 3:14 PM
Edited Apr 9, 2013 at 5:27 PM
I observed some strange behaviour for long running response.

First of all I am talking about one connection that lasts until server end closes the socket.

If server side sends response continuously in short spawn of time between data it works fine (few seconds between data).

If server side sends data in interval of 60s between data then two things happen:
1) either it hangs on get_line().get(), ([added] it doesn't hang like immediately but for me on fourth iteration)
2) I put a Sleep(30000) in my loop (to retrieve the response) when get_line returns 0. This cause an exception (_M_pTask->->CancelWithException())

I hope there is a workaround.
Coordinator
Apr 9, 2013 at 4:32 PM
HI c2c,

I will take a look at this and try reproducing, I'll get back to you. Thanks for reporting the issue.

Steve
Apr 9, 2013 at 7:01 PM
Edited Apr 9, 2013 at 7:01 PM
To easily reproduce do this (very simple):

1) Download http://www.lightstreamer.com/download (Lightstreamer_Allegro-Presto-Vivace_5_1_1_Colosseo_20130305.zip)
2) unzip to use. <unzip path>\conf\lightstreamer_conf.xml, change <default_keepalive_millis randomize="N">5000</default_keepalive_millis> to 60000
and <max_keepalive_millis>60000</max_keepalive_millis>
3) from command prompt launch the demo server <unzip location>\bin\windows\start_LS_as_Application.bat
4) from another command prompt you can test this using curl.exe (curl --data "LS_adapter_set=MONITOR" http://localhost:8080/lightstreamer/create_session.txt)

Using code you would have this:
http_client client(U("http://localhost:8080");
string_t body = U("LS_adapter_set=MONITOR");
string_t path = U("/lightstreamer/create_session.txt");
string_t form_type = U("application/x-www-form-urlencoded");

client.request(methods::POST, path, body, form_type).then([=](http_response response)
{
    size_t read = 0;
    stringstreambuf data;
    string ss;
    bool done = false;

    while(!done)
   {
       read = response.body().read_line(data).get();
       ss = data.collection();
   }
}).wait();
I can confirm it's more like _CancelWithException() than hang with above code as a test.

Call stack looks like this
c2csdk.exe!`pplx::details::_PPLTaskHandle<unsigned char,pplx::task<web::http::http_response>::_ContinuationTaskHandle<web::http::http_response,void,<lambda_43af509f4670ca29a13ee9e2657788c5>,std::integral_constant<bool,0>,pplx::details::_TypeSelectorNoAsync>,pplx::details::_ContinuationTaskHandleBase>::operator()'::`1'::catch$2() Line 1488    C++
c2csdk.exe!_CallSettingFrame() Line 51  Unknown
c2csdk.exe!__CxxCallCatchBlock(_EXCEPTION_RECORD * pExcept) Line 1264   C++
ntdll.dll!RcConsolidateFrames()    Unknown
c2csdk.exe!pplx::details::_PPLTaskHandle<unsigned char,pplx::task<web::http::http_response>::_ContinuationTaskHandle<web::http::http_response,void,<lambda_43af509f4670ca29a13ee9e2657788c5>,std::integral_constant<bool,0>,pplx::details::_TypeSelectorNoAsync>,pplx::details::_ContinuationTaskHandleBase>::operator()() Line 1472    C++
c2csdk.exe!pplx::details::_UnrealizedChore::_InvokeBridge<pplx::details::_PPLTaskHandle<unsigned char,pplx::task<web::http::http_response>::_ContinuationTaskHandle<web::http::http_response,void,<lambda_43af509f4670ca29a13ee9e2657788c5>,std::integral_constant<bool,0>,pplx::details::_TypeSelectorNoAsync>,pplx::details::_ContinuationTaskHandleBase> >(void * _PContext) Line 321    C++
Coordinator
Apr 10, 2013 at 12:01 AM
Hi c2c,

Ok, I took a deeper look into this. I didn't try reproducing with Lightstreamer, because I might have to get permission to use it with the license at Microsoft. I did however create a simple HTTP server in that does exactly why you describe. For each response the server repeatedly writes one line of data, using chunked transfer encoding, and then sleeps for 60 seconds. Using the default constructed http_client this will result in a http_exception with message "The operation timed out". By default our http_client uses a timeout of 30 seconds, however this is easily configurable. Can you try changing your construction of the http_client to be the following, increasing to 90 seconds:
http_client_config config;
config.set_timeout(utility::seconds(90));
http_client client(U("http://localhost:8080"), config);
I'm a little confused by your callstack above it looks like parts are missing, but does this fix your problem?

Thanks,
Steve
Apr 10, 2013 at 12:39 AM
That works great!
Coordinator
Apr 10, 2013 at 12:42 AM
Glad to here the problem is solved. We figured 30 seconds was a good default, but obviously there are scenarios where it needs to be extended.
Oct 9, 2013 at 8:01 PM
May I know how to handle this timeout exception without get() or wait()? I didn't see a way to handle it with then().
Yes, I can set the timeout value to a very large one but it would be great I can still get a notification.
Oct 9, 2013 at 10:09 PM
I believe you can't specify the timeout after http_client is being declared. So you would do what's mentioned above. You can't really re-purpose http_client over its lifetime (discussed in another post) so you will just have to recreate http_client if you need different timeout.
http_client_config config;
config.set_timeout(utility::seconds(90));
http_client client(U("http://localhost:8080"), config);
Oct 10, 2013 at 12:48 AM
What I mean is to handle the timeout exception.
If the server didn't response with 90 seconds and it will throw a timeout exception which can't be caught outside.
For example, the code below can't catch timeout exception without wait() or get() to hold the process.
try {
    http_client_config config;
    config.set_timeout(utility::seconds(90));
    http_client client(U("http://localhost:8080"), config);
    client.request(methods::GET).then([](http_response response) {
        try {
            // Do something
        }
        catch (...) {
            // No timeout exception go here
        }
    });
}
catch(...) {
    // No timeout exception go here
}

// Do something else without waiting for response.
Coordinator
Oct 10, 2013 at 1:02 AM
Hi garywang1,

You can add a task based continuation at the end of the chain and handle all errors and exceptions there.
Section "Handling errors in a task chain" in this msdn doc has more details.
Example:
    client.request(methods::GET).then([](http_response response) {
        // Do something.
    }).then([](pplx::task<void> t)
    {
        try
        {
            t.get(); // This gets the results of the task. 
        }
        catch(std::exception ex) // or http_exception
        {
            // Print the timeout exception details.
        }
    });
Hope this answers your question.
Oct 10, 2013 at 1:20 AM
Thanks, this is what I want to know.