I am trying to port some code from a Windows form application to WP8, and have run into some issues regarding asynchronous calls.
The basic idea is to do some UAG authentication. In the Windows form code, I do a GET on the portal homepage and wait for the cookies. I then pass these cookies into a POST request to the validation URL the UAG server. It all works fine in the form, since all the steps are sequential and synchronous.
Now, when I started porting this to WP8, first thing I noticed was that GetResponse() wasn't available, instead I had to use BeginGetResponse(), which is asynchronous and calls a callback function. This is no good for me, since I need to ensure this step finishes before I do the POST
My Windows form code looks like this (taken from http://usingnat.net/sharepoint/2011/2/23/how-to-programmatically-authenticate-to-uag-protected-sharep.html):
private void Connect()
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(this.Url);
request.CookieContainer = new CookieContainer();
request.UserAgent = this.UserAgent;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
//Get the UAG generated cookies from the response
this.Cookies = response.Cookies;
}
}
private void ValidateCredentials()
{
//Some code to construct the headers goes here...
HttpWebRequest postRequest = (HttpWebRequest)WebRequest.Create(this.ValidationUrl);
postRequest.ContentType = "application/x-www-form-urlencoded";
postRequest.CookieContainer = new CookieContainer();
foreach (Cookie cookie in this.Cookies)
{
postRequest.CookieContainer.Add(cookie);
}
postRequest.Method = "POST";
postRequest.AllowAutoRedirect = true;
using (Stream newStream = postRequest.GetRequestStream())
{
newStream.Write(data, 0, data.Length);
}
using (HttpWebResponse response = (HttpWebResponse)postRequest.GetResponse())
{
this.Cookies = response.Cookies;
}
public CookieCollection Authenticate()
{
this.Connect();
this.ValidateCredentials();
return this.Cookies;
}
The thing is this code relies on synchronous operation (first call Connect(), then ValidateCredentials() ), and it seems WP8 does not support that for Web requests. I could combine the two functions into one, but that won't solve my problem fully since later on this needs to be expanded to access resources behind the UAG, so it would need a modular design.
Is there a way to "force" synchronization?
Thanks
You can still continue your steps in the call back function using the asynchronous model. Or you can use the new HttpClient which can be used with the await keyword so you can program your stuff in a synchronous way.
You can get HttpClient through nuget
install-package Microsoft.Net.Http
Related
I am trying to use MassTransit for Request/Response communication through Azure service bus queue. Sender is an Azure WebApp, Consumer is a windows service installed at on-premise machine.
Everything works fine when it is about small volumes of messages. However as soon as I start sending more than ~20 msg/sec i see severe(1-2 sec) delays in responses from consumer. My telemetry tells me that delay is happening at point when consumer needs to grab messages from queue.
One strange, but I think important part of behavior: I can see that with current load amount of unread messages in queue is on avg constant and its 25. If I send 2x more messages, than I see on avg 50messages in queue. With delays on consumption side i would expect queue to GROW, but it is constant, so it is definitely something inside code that throttles the connection.
Quick info:
There are no problems with hardware on the machine. CPU/Mem not high.
I tried playing with the UseConcurrencyLimit, MaxConcurrentCalls, PrefetchCount configs on consuner side. It did not help
My solution code of sender and consumer are next to classic examples.
Consumer: .Net framework 4.7.2 and MassTransit.Azure.ServiceBus.Core 5.5.2
Here's my listener class with all business logic removed:
public class QueueListener
{
private IBusControl Bus { get; set; }
public QueueListener()
{
Bus = MassTransit.Bus.Factory.CreateUsingAzureServiceBus(serviceBusFactoryConfigurator =>
{
var host = serviceBusFactoryConfigurator.Host(SettingsHelper.AzureServiceBusConnectionString,
(config) =>
{
config.OperationTimeout = TimeSpan.FromSeconds(60);
config.TransportType = TransportType.AmqpWebSockets;
});
serviceBusFactoryConfigurator.ReceiveEndpoint(host, SettingsHelper.CouponQueryQueueName, e =>
{
e.Handler<JToken>(HandleMessage);
e.UseConcurrencyLimit(16);
e.MaxConcurrentCalls = 16;
e.PrefetchCount = 32;
});
serviceBusFactoryConfigurator.EnableBatchedOperations = true;
serviceBusFactoryConfigurator.DefaultMessageTimeToLive = TimeSpan.FromSeconds(60);
});
}
private async Task HandleMessage(ConsumeContext context)
{
await Task.Delay(800);
if (context.ExpirationTime > SystemDateTime.Now)
{
await context.RespondAsync(new CouponUsedList { CouponsUsed = new List<CouponCurrentUsed>() });
}
}
public Task LaunchAsync()
{
return Bus.StartAsync();
}
public Task StopAsync()
{
return Bus.StopAsync();
}
}
Seems that here, once again, it was just missing one config. All code that you write inside ReceiveEndpoint configurates the consumer listener queue and configurations that you provide in CreateUsingAzureServiceBus are configurations for a consumer response queue.
All I needed was to add one line inside consumer configuration. Without this config all prefetched messages are handled gradually
e.EnableBatchedOperations = true;
I wrote a program that includes an embedded web browser that loads a website which have a changing part (the part changes about 2 times a week and it have no regular timing pattern) that I want to search for a particular part in the opened webpage source code after refreshing the webpage in a specified time interval.
I found many things similar to my question but this is what I want and those questions doesn't have:
search embedded webpage source (they searching the webpage without embedding, and I had to embed it because I had to login before I see the particular page)
so this is the procedure I'm trying to do:
1- open a website in embedded web browser
2- after user logged in, with a press of button in program, it hides the embedded
web browser and start to refresh the page in a time interval (like
every minute) and search if the particular code changed in the source of
that opened webpage
any other/better Ideas appreciated
thanks
Many years ago I wrote an app to reintegrate forum posts from several pages into one and I struggled with the login issue too and thought it was only possible using an embedded browser. As it turns out, it's possible to use System.Net in .NET to handle web pages that need a login as you can pull the cookies out and keep them on hand. I would suggest you do that and move away from the embedded browser.
Unfortunately I wrote the code in C# originally, but as it's .NET and is mostly classes-based, it shouldn't be too difficult to port over.
The Basic Principle
Find out what information is included in the POST when you login, which you can do in Chrome with developer mode on (F12). Convert that to a byteArray, POST it to the page, store the cookies and make another call with the cookie data later on. You will need a class variable to hold the cookies.
Code:
private void Login()
{
byte[] byteArray = Encoding.UTF8.GetBytes("username=" + username + "&password=" + password + "&autologin=on&login=Log+in"); // Found by investigation
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("yourURL");
request.AllowAutoRedirect = false;
request.CookieContainer = new CookieContainer();
request.Method = "POST";
request.ContentLength = byteArray.Length;
request.ContentType = "application/x-www-form-urlencoded";
Stream dataStream = request.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
WebResponse response = request.GetResponse();
if (((HttpWebResponse)response).StatusCode == HttpStatusCode.Found)
{
// Well done, your login has been accepted
loginDone = true;
cookies = request.CookieContainer;
}
else
{
// If at first you don't succeed...
}
response.Close();
}
private string GetResponseHTML(string url)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.AllowAutoRedirect = false;
// Add cookies from Login()
request.CookieContainer = cookies;
request.ContentType = "application/x-www-form-urlencoded";
WebResponse response = request.GetResponse();
string sResponse = "";
StreamReader reader = null;
if (((HttpWebResponse)response).StatusCode == HttpStatusCode.OK)
{
reader = new StreamReader(response.GetResponseStream());
sResponse = reader.ReadToEnd();
reader.Close();
}
response.Close();
return sResponse;
}
Hope that helps.
I had to change to C# and I found what I was looking for:
string webPageSource = webBrowser1.DocumentText;
That gave me the source of web page opened in webBrowser1 control.
I'm using HttpClient from WP8 and do a Post request. I know the call may take long time as I'm actually simulating slow network scenarios. Therefore I set the HttpClient.Timeout accordingly to 5 minutes.
However, I get a Timeout at around 60s. I believe the Timeout is not working.
I believe there is an issue with this for WP as stated in this question:
HttpClient Portable returns 404 notfound on WP8.
They use a workaround but that does not applies to my scenario. I do actually want to wait for long time.
My questions:
1) Is it a bug/issue of HttpClient for WP8 or I'm not setting it properly?
2) Do you think of a workaround still using HttpClient?
I've read that maybe HttpWebRequest is an option. However, I believe HttpClient should be ideal for this 'simple' scenario.
My code is simple:
private static async Task<HttpResponseMessage> PostAsync(Uri serverUri, HttpContent httpContent)
{
var client = new HttpClient();
client.Timeout = TimeSpan.FromMinutes(5);
return await client.PostAsync(serverUri, httpContent).ConfigureAwait(false);
}
The server receives the request and while is processing it, the client aborts.
UPDATE: The HttpResponseMessage returned by HttpClient.PostAsyn is this "{StatusCode: 404, ReasonPhrase: '', Version: 0.0, Content: System.Net.Http.StreamContent, Headers: { Content-Length: 0 }}". As I said, the server is found and is receiving the data and processing it.
After some search and some tests I've came to the conclusion that the problem is Windows Phone itself and that it has a 60 seconds timeout (irrespective of the HttpClient) and that cannot be changed to my knowledge. See http://social.msdn.microsoft.com/Forums/en-US/faf00a04-8a2e-4a64-b1c1-74c52cf685d3/httpwebrequest-60-seconds-timeout.
As I'm programming the server as well, I will try the advice by Darin Rousseau in the link provided above, specifically to send an OK and then do some more processing.
UPDATE: The problem seems to be the Windows Phone emulator as stated here:
http://social.msdn.microsoft.com/forums/wpapps/en-us/6c114ae9-4dc1-4e1f-afb2-a6b9004bf0c6/httpclient-doesnt-work-on-windows-phone?forum=wpdevelop. In my experience the tcp connection times-out if it doesn't hear anything for 60s.
Therefore my solution is to use the Http header characters as a way of keep alive. The first line Http header response always starts with HTTP/1.0. So I send the characters one by one with a delay <60s between them. Of course, if the response gets ready, everything that is left is sent right away. This buys some time, for instance if using a delay of 50s per 9 character we get about 450s.
This is a project for my degree so I wouldn't recommend it for production.
By the way, I also tried with other characters instead the sub string of the header, for instance space character, but that results in a http protocol violation.
This is the main part of the code:
private const string Header1 = #"HTTP/1.0 ";
private int _keepAliveCounter = 0;
private readonly object _sendingLock = new object();
private bool _keepAliveDone = true;
private void StartKeepAlive()
{
Task.Run(() => KeepAlive());
}
/// <summary>
/// Keeps the connection alive sending the first characters of the http response with an interval.
/// This is a hack for Windows Phone 8 that need reponses within 60s interval.
/// </summary>
private void KeepAlive()
{
try
{
_keepAliveDone = false;
_keepAliveCounter = 0;
while (!_keepAliveDone && _keepAliveCounter < Header1.Length)
{
Task.Delay(TimeSpan.FromSeconds(50)).Wait();
lock (_sendingLock)
{
if (!_keepAliveDone)
{
var sw = new StreamWriter(OutputStream);
sw.Write(Header1[_keepAliveCounter]);
Console.Out.WriteLine("Wrote keep alive char '{0}'", Header1[_keepAliveCounter]);
_keepAliveCounter++;
sw.Flush();
}
}
}
_keepAliveCounter = 0;
_keepAliveDone = true;
}
catch (Exception e)
{
// log the exception
Console.Out.WriteLine("Error while sending keepalive: " + e.Message);
}
}
Then, the actual processing happens in a different thread.
Comments and critics are appreciated.
It is possible that you are hitting the timeout of the network stream. You can change this by doing,
var handler = new WebRequestHandler();
handler.ReadWriteTimeout= 5 * 60 * 1000;
var client = new HttpClient(handler);
client.Timeout = TimeSpan.FromMinutes(5);
return await client.PostAsync(serverUri, httpContent).ConfigureAwait(false);
The default on the desktop OS is already 5mins. However, it is possible that on Windows Phone it has been reduced by default.
Can someone show me or tell some example how to unregister from notification hub in windows phone 8. I tried on this way but it doesn't work.
public void registerForNotifications(string[] tags)
{
var channel = HttpNotificationChannel.Find("xxx");
if (channel == null)
{
channel = new HttpNotificationChannel("xxx");
channel.Open();
channel.BindToShellToast();
}
string[] tagsToSubscribeTo = tags;
channel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(async (o, args) =>
{
var hub = new NotificationHub("xxx", "xxx");
await hub.RegisterNativeAsync(args.ChannelUri.ToString(), tagsToSubscribeTo);
});
}
public async void unregisterFromNotifications()
{
var channel = HttpNotificationChannel.Find("xxx");
var hub = new NotificationHub("xxx", "xxx");
await hub.UnregisterAllAsync(channel.ChannelUri.ToString());
}
You didn't say what "it didn't work" means. Did you get an error message? Did it report success but actually fail? In your questions, it really helps more if you share those things. But I'll take a stab at this anyway.
I suspect that you might be using the DefaultListenSharedAccessSignature endpoint from your Windows Phone 8 app.
According to http://msdn.microsoft.com/en-us/library/dn495373.aspx, the Listen access level grants permission to:
Create/Update registration.
Read registration.
Read all registrations for a handle.
Delete registration.
Reading that last one, I wonder if the UnregisterAllAsync method might require a higher access level to delete all registrations, rather than just one.
But rather than use the DefaultFullSharedAccessSignature endpoint, I would rather just try the UnregisterAsync method instead of UnregisterAllAsync.
Disclaimer: I have not tried this out. It may not help at all.
I'm using a web server to control devices in the house with a microcontroller running .netMF (netduino plus 2). The code below writes a simple html page to a device that connects to the microcontroller over the internet.
while (true)
{
Socket clientSocket = listenerSocket.Accept();
bool dataReady = clientSocket.Poll(5000000, SelectMode.SelectRead);
if (dataReady && clientSocket.Available > 0)
{
byte[] buffer = new byte[clientSocket.Available];
int bytesRead = clientSocket.Receive(buffer);
string request =
new string(System.Text.Encoding.UTF8.GetChars(buffer));
if (request.IndexOf("ON") >= 0)
{
outD7.Write(true);
}
else if (request.IndexOf("OFF") >= 0)
{
outD7.Write(false);
}
string statusText = "Light is " + (outD7.Read() ? "ON" : "OFF") + ".";
string response = WebPage.startHTML(statusText, ip);
clientSocket.Send(System.Text.Encoding.UTF8.GetBytes(response));
}
clientSocket.Close();
}
public static string startHTML(string ledStatus, string ip)
{
string code = "<html><head><title>Netduino Home Automation</title></head><body> <div class=\"status\"><p>" + ledStatus + " </p></div> <div class=\"switch\"><p>On</p><p>Off</p></div></body></html>";
return code;
}
This works great, so I wrote a full jquery mobile website to use instead of the simple html. This website is stored on the SD card of the device and using the code below, should write the full website in place of the simple html above.
However, my problem is the netduino only writes the single HTML page to the browser, with none of the JS/CSS style files that are referenced in the HTML. How can I make sure the browser reads all of these files, as a full website?
The code I wrote to read the website from the SD is:
private static string getWebsite()
{
try
{
using (StreamReader reader = new StreamReader(#"\SD\index.html"))
{
text = reader.ReadToEnd();
}
}
catch (Exception e)
{
throw new Exception("Failed to read " + e.Message);
}
return text;
}
I replaced string code = " etc bit with
string code = getWebsite();
How can I make sure the browser reads all of these files, as a full website?
Isn't it already? Use an HTTP debugging tool like Fiddler. As I read from your code, your listenerSocket is supposed to listen on port 80. Your browser will first retrieve the results of the getWebsite call and parse the HTML.
Then it'll fire more requests, as it finds CSS and JS references in your HTML (none shown). These requests will, as far as we can see from your code, again receive the results of the getWebsite call.
You'll need to parse the incoming HTTP request to see what resource is being requested. It'll become a lot easier if the .NET implementation you run supports the HttpListener class (and it seems to).