I am using RestSharp (a REST client for .NET) in my Windows Phone 8 app, but I think my question also applies using HttpWebRequest or any other ways of running web requests.
I am trying to find a way to automatically resubmit the requests when app is resumed from dormant state. This is only from dormant and not from the tombstone state.
The idea I had was to create a wrapper object which subscribes to the Deactivated event before starting the request and rerunning the request in case it received the event.
I assume that since the deactivated event was received, the request failed.
public class RestClientEx
{
bool wasDeactivated = false;
public async Task<T> ExecuteTaskAsync<T>(RestClient client, RestRequest request) where T : new()
{
var phoneApplicationService = App.Current.ApplicationLifetimeObjects.OfType<PhoneApplicationService>().First();
phoneApplicationService.Deactivated += phoneApplicationService_Deactivated;
var t = await client.ExecuteTaskAsync<T>(request);
if (this.wasDeactivated)
{
// resubmit request
this.wasDeactivated = false;
t = await this.ExecuteTaskAsync<T>(client, request);
}
return t;
}
void phoneApplicationService_Deactivated(object sender, DeactivatedEventArgs e)
{
(sender as PhoneApplicationService).Deactivated -= phoneApplicationService_Deactivated;
this.wasDeactivated = true;
}
}
My question is, is there another way to achieve this?
Is it OK what I am doing?
Related
I have vert.x app which is consuming api REST over json but intermittently I am seeing exception with reason "Connection was closed". Below are my details -
please share your inputs if anything wrong in the configuration. may be creating scheduler or instantiating httpclient ?
on a different note is it advisable to use same http client to call more than 1 different api's on the same host and port ?
Vert.x Version: 3.5.0
import io.vertx.core.http.HttpClient;
private static Scheduler scheduler =
Schedulers.from(Executors.newFixedThreadPool(8));
// http client instantiated at the time of verticle startup
HttpClient httpclient = vertx.createHttpClient(getHttpClientOptions());
public static HttpClientOptions getHttpClientOptions() {
return new HttpClientOptions()
.setKeepAlive(true)
.setMaxPoolSize(100)
.setPipelining(true)
.setDefaultHost(xxxx.xxxx.com)
.setDefaultPort(8084)
.setSsl(true);
}
// invoke api call
public static Single<Response> invokePOSTServiceAsync(String reqBodyStr, String endpointURI) throws Exception {
try{
return Single.create((SingleEmitter<Response> emitter) -> {
HttpClientRequest request = httpClient.post(endpointURI);
request.putHeader("Content-type","application/json")
request.exceptionHandler(error -> {
LOG.error("ExceptionHandler "+error.getMessage());
emitter.onError(new Throwable(" Failure"));
})
.handler(response -> {
int statusCode = response.statusCode();
if (statusCode == 200) {
response.bodyHandler(body -> {
StringBuilder responseData = new StringBuilder();
responseData.append(body);
emitter.onSuccess(new Response(statusCode,responseData.toString(),"","",null));
});
} else {
emitter.onError(new Throwable(" Failure"));
}
})
.putHeader(HttpHeaders.CONTENT_LENGTH, reqBodyStr.length() + "")
.setTimeout(6000)
.write(reqBodyStr)
.end();
}).subscribeOn(scheduler);
}catch(Exception exe){
exe.printStackTrace();
return null;
}
}
My guess is that this is not related to the client. Either your server is being overloaded, or your network is unreliable. If you're consuming service which doesn't belong to you, you also may get throttled, and that's the reason you're seeing this.
In any case, you need to circumvent those problems, as the network is unreliable anyway. Make your POST requests idempotent and introduce retries.
I am new to Server Sent Events but not to Spring.
Have made a controller which gets triggered from a button on the UI which initiates SSEEmitter and passed that to another thread which in loop sends message to UI after each 4 seconds.
SO far i am running a loop of 10 which sleeps for 4 seconds each but suddenly around iteration of 6 or 7th loop, I get exception "Exception in thread "Thread-4" java.lang.IllegalStateException: ResponseBodyEmitter is already set complete"..
Hence, event source again re-establishes the connection i.e. calls the controller method again which certainly i do not want.
I am here trying a simple thing.. User subscribes by clicking on the button..
Server send response 10 or 20 whatever times to the browser. And as far as I think this is what SSE created for.
Code below.:
#RequestMapping("/subscribe")
public SseEmitter subscribe() {
SseEmitter sseEmitter = new SseEmitter();
try {
sseEmitter.send("Dapinder");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Runnable r = new AnotherThread(sseEmitter);
new Thread(r).start();
return sseEmitter;
}
public class AnotherThread implements Runnable {
private SseEmitter sseEmitter;
public AnotherThread(SseEmitter sseEmitter) {
super();
this.sseEmitter = sseEmitter;
}
#Override
public void run() {
SseEventBuilder builder = SseEmitter.event();
builder.name("dapEvent");
for (int i = 0; i < 10; i++) {
builder.data("This is the data: " + i +" time.");
try {
//sseEmitter.send(builder);
sseEmitter.send("Data: "+i);
//sseEmitters.get(1L).send("Hello");
} catch (IOException e) {
e.printStackTrace();
}
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
sseEmitter.complete();
}
public SseEmitter getSseEmitter() {
return sseEmitter;
}
public void setSseEmitter(SseEmitter sseEmitter) {
this.sseEmitter = sseEmitter;
}
}
function start() {
var eventSource = new EventSource("http://localhost:8080/HTML5SSE/springSSE/subscribe"); // /springSSE/connect
eventSource.onmessage = function(event) {
document.getElementById('foo').innerHTML = event.data;
};
}
<button onclick="start()">Subscribe</button>
Your builder is not being used; you create and configure a builder, but then you send a plain message with 'sseEmitter.send' directly. Try this:
sseEmitter.send(SseEmitter.event().name("dapEvent").data("This " + i +" time.");
One more thing: Why do you call the send method already in the subscribe method? At this point in time, the SseEmitter has not been returned. Is this message coming through to the client?
Here is an excellent article explaining SSE from the JavaScript perspective (not Spring). You will see here that you can cancel the event stream from the client by calling close on the stream. Combine this with an event listener, and you should have what you need:
var source = new EventSource('...');
source.addEventListener('error', function(e) {
if (e.currentTarget.readyState == EventSource.CLOSED) {
// Connection was closed.
} else {
// Close it yourself
source.close();
}
});
source.addEventListener('message', function(e) {
console.log(e.data);
});
Note: The article says e.readyState, however I think this is wrong. The received object e is an Event. You need to get the EventSource object from it like this: e.currentTarget.
You need to use the second constructor of SseEmitter which takes a Long timeout argument. Please refer the code below -
#RequestMapping("/subscribe")
public SseEmitter subscribe() {
SseEmitter sseEmitter = new SseEmitter(Long.MAX_VALUE) // for maximum timeout
Below is the copy of Java-doc of this constructor -
/**
* Create a SseEmitter with a custom timeout value.
* <p>By default not set in which case the default configured in the MVC
* Java Config or the MVC namespace is used, or if that's not set, then the
* timeout depends on the default of the underlying server.
* #param timeout timeout value in milliseconds
* #since 4.2.2
*/
I think the default timeout of SSE connection in Tomcat is 40 seconds. Not sure though.
I have an app running on Windows Phone 8.1 which calls a URL via InAppBrowser plugin. This URL is supposed to ask for the user certificate stored on a virtual smartcard on the phone.
When I call the URL via Internet Explorer, I am asked for my PIN to unlock the virtual smartcard but in the InAppBrowser, this doesn't work. No PIN prompt, nothing.
Iterating through the Certificates yielded from
IReadOnlyList<Certificate> certStores = await CertificateStores.FindAllAsync();
I can see the certificate at app runtime but InAppBrowser doesn't seem to query for them. Do I have to copy its reference to another certificate store or is InAppBrowser not capable of establishing SSL with user certificates ?
The issue is with the webview component, x-ms-webview to be more precisely. InAppBrowser plugin uses this component internally.
Found a workaround mentioned here, it kinda sounds like a security issue tbh so this could get fixed in the future but here are more details on said workaround:
Make a request to the URL which is supposed to trigger virtual smartcard unlock to access the user certificate, but with the HttpClient at native level (C#)
I've created another Windows Runtime Component in my solution which does a simple POST to the url I want to access from InAppBrowser later on.
While setting up the Windows.Web.Http.HttpClient, I fetch the user certificate from the smartcard and set it as HttpBaseProtocolFilter.ClientCertificate.
public sealed class SSLHelper
{
private static String errorMessage = "";
private static String statusMessage = "";
public static IAsyncOperation<Boolean> establishSSLConnection(String url)
{
return connect(url).AsAsyncOperation<Boolean>();
}
public static String getErrorMessage()
{
return SSLHelper.errorMessage;
}
public static String getStatusMessage()
{
return SSLHelper.statusMessage;
}
private static async Task<Boolean> connect(String urlString)
{
Certificate clientCert = await getCertificateAsync();
HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter();
filter.ClientCertificate = clientCert;
HttpClient client = new HttpClient(filter);
try
{
System.Uri url = new System.Uri(urlString);
HttpResponseMessage response = await client.PostAsync(url, new HttpStringContent(""));
response.EnsureSuccessStatusCode();
SSLHelper.statusMessage = response.StatusCode.ToString();
return true;
}
catch (Exception e)
{
SSLHelper.errorMessage = e.ToString();
return false;
}
}
private static async Task<Certificate> getCertificateAsync()
{
CertificateQuery query = new CertificateQuery();
query.IssuerName = "Sample Issuer";
IReadOnlyList<Certificate> certStores = await CertificateStores.FindAllAsync(query);
return certStores.FirstOrDefault<Certificate>();
}
}
Make that code return as a promise on Javascript level and once it resolves, start the code which uses InAppBrowser to access the secure URL again. The native request causes the PIN prompt for virtual smartcard access, once you have entered the correct PIN, InAppBrowser / WebView can magically establish the connection.
I have a webrequest to get a xml.That works great but when i press F12(lock screen) while the the server is requested by my app...I got a WebException.
I use a taskCompeltionSource object...Here is my code
public async Task<String> Query(DataRequestParam dataRequestParam)
{
_dataRequestParam = dataRequestParam;
try
{
Result = "";
Result = await myDownloadString(dataRequestParam);
}
catch (WebException we)//ERROR IS CAUGHT HERE
{
throw new WebException(we.Message);
}
catch (Exception ex)
{
throw new MyException(ex.Message);
}
return Result;
}
public static Task<string> myDownloadString(DataRequestParam dataRequestParam)
{
var tcs = new TaskCompletionSource<string>();
var web = new WebClient();
if (!string.IsNullOrEmpty(dataRequestParam.AuthentificationLogin))
{
System.Net.NetworkCredential account = new NetworkCredential(dataRequestParam.AuthentificationLogin, dataRequestParam.AuthentificationPassword);
web.Credentials = account;
}
web.DownloadStringCompleted += (s, e) =>
{
if (e.Error != null) tcs.TrySetException(e.Error);
else if (e.Cancelled) tcs.TrySetCanceled();
else tcs.TrySetResult(e.Result);
};
web.DownloadStringAsync(dataRequestParam.TargetUri);
return tcs.Task;
}
If you haven't disabled ApplisationIdleDetection, your process is stopped while entering Lock screen - thus you probably get the exception - like I've said in comment. Disabling will solve this issue, but you must be aware of few things:
you will still get the exception when hitting Start Button (or other case putting your app to dormant state). In this case your app is stopped and there is no way to prevent this behaviour.
you must fulfill certification requirements when disabling App Idle Detection - point 6.3
if you want to download files in the Background (lock screen, after closing/leaving app) then you can think of Background Transfers
I'm a little bit new to all of these technologies so I'll try to be as clear as I can.
I'm writing a windows phone app that sends data in string format to a server:
public class sendDataControl
{
private string response = "";
public void sendToServer(string FullSTR)
{
try
{
WebClient webClient = new WebClient();
Uri uri = new Uri("http://pricequeryserver.azurewebsites.net/api/ReceiptDataService/?incomingdata=");
webClient.UploadStringAsync(uri,FullSTR);
webClient.UploadStringCompleted += new UploadStringCompletedEventHandler(webClient_UploadStringCompleted);
}
catch (Exception ex)
...
...
}
}
void webClient_UploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
{
if (e.Error != null)
{
responseXml=e.Error.Message;
MessageBox.Show(responseXml);
return;
}
else
{
responseXml = e.Result;
}
}
}
The server is an MVC4, basic, with api controller I added, that needs to get the data sent from the mobile.
As a test I'm just getting back a string that I send:
public class ReceiptDataServiceController : ApiController
{
private ReceiptContext db = new ReceiptContext();
...
...
public string GetDataFromMobile(string IncomingData)
{
return IncomingData;
}
}
While running the application I get an error via responseXml:
"The remote server returned an error: NotFound".
The server returns the right answer from all kinds of browsers, while on IIS and on the azure but not from the mobile emulator.
Any suggestions?
If you take a look at the documentation for UploadStringAsync overload you are using, you will notice that it sends data using POST method. While in your controller you have only implemented GET. And for your
You have to use other overload of UploadStringAsync, which lets you specify the HTTP VERB to use. And you must specify GET. Your client code should be converted to:
webClient.UploadStringAsync(uri,"GET", FullSTR);
And the best solution for simple GET operations like your is to actually use DownloadStringAsync:
var fullUri = new Uri("http://pricequeryserver.azurewebsites.net/api/ReceiptDataService/?incomingdata=" + FullStr);
webClient.DownloadStringAsync(fullUri);
Anyway, your question has nothing to do with Windows Azure, thus the removed tag.