WP8: HttpRequest works only the first time

Monkey Forums/Monkey Bug Reports/WP8: HttpRequest works only the first time

Xaron(Posted 2015) [#1]
On Windows Phone 8 the HttpRequest works for me only the first time I call it. With that I mean that the IOnHttpRequestComplete callback is only called the first time, but not for the next requests.

I've digged a bit into it and actually

STDMETHODIMP CXMLHTTPRequest2Callback::OnResponseReceived( __RPC__in_opt IXMLHTTPRequest2 *pXHR,__RPC__in_opt ISequentialStream *pStream )

is called correctly even the second time but somehow the callback on the Monkey site isn't fired again. Mark could you please take a look into this?


SLotman(Posted 2015) [#2]
I didn't test it recently, but my Escape From Alcatraz uses it, and it works without problems... is your problem with POST or GET?


Xaron(Posted 2015) [#3]
I use GET. And I do nothing special, works great for iOS and Android but only once for WP8. I tried with Monkey v79e btw.


Xaron(Posted 2015) [#4]
Here's a small example to reproduce this behaviour (v85a):



Works fine for desktop but works only once for Windows Phone 8. Slotman, could you try this as well please?


Xaron(Posted 2015) [#5]
Mark could you please at least run my example and tell me if you can reproduce that issue?


Xaron(Posted 2015) [#6]
I start to tackle it down.

The second time Send ist called, the OnRequestCompleted Method is called immediately after the send, setting _isRunning to false. Followed by that comes the callback start command which sets _isRunning to true and your UpdateASync runs forever waiting for the request to be finished which is already finished...


Xaron(Posted 2015) [#7]
Ok, fix is:

In httprequest.winrt.cpp change:
void BBHttpRequest::Send(){

	if( FAILED( _req->Send( 0,0 ) ) ){
	//	bbPrint( "Send failed" );
	}
	
	_cb->Start();
}


to:
void BBHttpRequest::Send(){

	_cb->Start();

	if( FAILED( _req->Send( 0,0 ) ) ){
	//	bbPrint( "Send failed" );
	}	
}


and change:

void BBHttpRequest::SendText( String text,String encoding ){

	_data=Microsoft::WRL::Details::Make<CXMLHTTPRequestData>();
	
	int length=_data->SetText( text );
	
	if( FAILED( _req->Send( _data.Get(),length ) ) ){
	//	bbPrint( "Send failed" );
	}

	_cb->Start();
}


to:

void BBHttpRequest::SendText( String text,String encoding ){

	_data=Microsoft::WRL::Details::Make<CXMLHTTPRequestData>();
	
	int length=_data->SetText( text );
	
	_cb->Start();

	if( FAILED( _req->Send( _data.Get(),length ) ) ){
	//	bbPrint( "Send failed" );
	}
}


Mark, I know you're pretty busy with your Monkey 2 stuff atm, but actually it would be nice to get at least a response from you regarding bug reports, at least if you can reproduce it when there is a repro. ;)


marksibly(Posted 2015) [#8]
After upgrading to windows10, I haven't been able to get winrt development working again. I think this is mainly because the upgrade made a huge mess of my existing msvc installs.

I will do a clean reinstall of windows 10 soon and try again.


marksibly(Posted 2015) [#9]
Okay, I can confirm this, and that your fix fixes it here too. I will add the fix to the next update.


Xaron(Posted 2015) [#10]
Thanks! :) And sorry for the harsh words.


Xaron(Posted 2015) [#11]
I have to reopen this. Looks like there is the problem that this stuff works on a cached base, which is quite annoying in case the content changes. ;)

Mark, could you please take (again) a look at it? I've attached a fix for this.

Problem:
It looks like IXMLHTTPRequest2 is caching downloaded content regardless of the headers I add to the request. I have tried "Cache-Control: no-cache", "Cache Control: no-store", and "Pragma: no-cache". None of them are honored by the API, it still retrieves downloaded content from the cache instead of from the server.

Workaround for this:

Open httprequest.winrt.cpp and replace

void BBHttpRequest::Send(){
  _cb->Start();
  if( FAILED( _req->Send( 0,0 ) ) ){
  //	bbPrint( "Send failed" );
  _cb->Failed();
  }
}


with:
void BBHttpRequest::Send(){
  _cb->Start();
  _req->SetRequestHeader(L"If-Modified-Since", L"Sat, 01 Jan 2000 00:00:01 GMT");
  if( FAILED( _req->Send( 0,0 ) ) ){
  //	bbPrint( "Send failed" );
  _cb->Failed();
  }
}


Background:
https://social.msdn.microsoft.com/Forums/en-US/dadbea2e-85d4-4af0-9283-2f91e5b7cc2c/ixmlhttprequest2-and-caching-is-broken?forum=winappswithnativecode

You can force XHR to retrieve the latest content by setting the "If-Modified-Since" HTTP header in the request and set a time in the past.

If you have control over the server response, you could send back an Expires HTTP response header with a value 0 or a date in the past. That should make XHR retrieve the latest response for you.

You are only required to do one of the above, changing both the client and server side code is not necessary.

The client side code could be changed to something like this:

xhr->Open(...)

xhr->SetRequestHeader(L"If-Modified-Since", L"Sat, 01 Jan 2000 00:00:01 GMT");

xhr->Send(...)

For changing the server side behavior if your server side code is based on ASP.net you could change your response header like this:

Response.Headers.Add("Expires", "0")

Thanks,

Prashant.



marksibly(Posted 2015) [#12]
Wow, ugly stuff!

I assumes this means that *nothing* gets cached? Well, as long as it only affects WinRT I guess it's OK.


Xaron(Posted 2015) [#13]
Hell ya. Microsoft messed this up completely. This stuff is broken by design. They won't never fix it officially.

Actually at the moment *everything* is cached. So no matter what comes from the server you will *always* get the cached version which is odd. Only an app restart helps.
With the approach above *nothing* gets cached. Not nice, but at least it will always get the correct set of data from the server.


Danilo(Posted 2015) [#14]
@Xaron:
Why not make the behavior optional, if a choice is available on a specific target? Could be a property or optional parameter.
void BBHttpRequest::Send(bool cached = true){
  _cb->Start();
  if (cached == false)
    _req->SetRequestHeader(L"If-Modified-Since", L"Sat, 01 Jan 2000 00:00:01 GMT");
  if( FAILED( _req->Send( 0,0 ) ) ){
  //	bbPrint( "Send failed" );
  _cb->Failed();
  }
}

For targets where this choice is not available, the optional parameter/property is just ignored.


Xaron(Posted 2015) [#15]
Yes Danilo, that would be the best. So you should write @Mark, not @Xaron. ;)


Danilo(Posted 2015) [#16]
@Xaron:
It's just a general thought. By using some smart (optional) flags/properties, you can give more power to different platforms,
instead only using the lowest common denominator of all platforms.


Xaron(Posted 2015) [#17]
Well in that particular case the only affected platform is winrt so a flag won't make a difference. And the way it is implemented now it simply doesn't work.