Mono: IHttpAsyncHandler doesn't call EndProcessRequest - c#

I use ASP.NET IHttpAsyncHandler for async redirect Long Polling HTTP Requsets to other URL. It works perfectly with .NET 4.5 (Windows 7,8). But doesn't work with Mono (Mono JIT compiler version 3.2.8 (Debian 3.2.8+dfsg-4ubuntu1), Ubuntu 14.04). After completing request.BeginGetResponse AsyncCallback doesn't call EndProcessRequest.
public bool IsReusable { get { return true; } }
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
var request = (HttpWebRequest)HttpWebRequest.Create("http://www.google.com/");
request.Method = context.Request.HttpMethod;
request.UserAgent = context.Request.UserAgent;
request.Accept = string.Join(",", context.Request.AcceptTypes);
if (!string.IsNullOrEmpty(context.Request.Headers["Accept-Encoding"]))
{
request.Headers["Accept-Encoding"] = context.Request.Headers["Accept-Encoding"];
}
request.ContentType = context.Request.ContentType;
request.ContentLength = context.Request.ContentLength;
using (var stream = request.GetRequestStream())
{
CopyStream(context.Request.InputStream, stream);
}
return request.BeginGetResponse(cb, new object[] { context, request });
}
public void EndProcessRequest(IAsyncResult result)
{
// EndProcessRequest never called
var context = (HttpContext)((object[])result.AsyncState)[0];
var request = (HttpWebRequest)((object[])result.AsyncState)[1];
using (var response = request.EndGetResponse(result))
{
context.Response.ContentType = response.ContentType;
foreach (string h in response.Headers)
{
context.Response.AppendHeader(h, response.Headers[h]);
}
using (var stream = response.GetResponseStream())
{
CopyStream(stream, context.Response.OutputStream);
}
response.Close();
context.Response.Flush();
}
}
private void CopyStream(Stream from, Stream to)
{
var buffer = new byte[1024];
while (true)
{
var read = from.Read(buffer, 0, buffer.Length);
if (read == 0) break;
to.Write(buffer, 0, read);
}
}
I don't know reason of this strange beahaviour. I suppose this behavior is bug of HttpWebRequest class in Mono framework but I am not sure. May be are there any workarounds of this problem?

We found some workaround of the problem by using ThreadPool.QueueUserWorkItem:
public bool IsReusable { get { return true; }}
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
return new AsynchOperation(cb, context, extraData).Start();
}
public void EndProcessRequest(IAsyncResult result) { }
public void ProcessRequest(HttpContext context) { }
private class AsynchOperation : IAsyncResult
{
private AsyncCallback cb;
private HttpContext context;
public WaitHandle AsyncWaitHandle { get { return null; } }
public object AsyncState { get; private set; }
public bool IsCompleted { get; private set; }
public bool CompletedSynchronously { get { return false; } }
public AsynchOperation(AsyncCallback callback, HttpContext context, object state)
{
cb = callback;
this.context = context;
AsyncState = state;
IsCompleted = false;
}
public IAsyncResult Start()
{
ThreadPool.QueueUserWorkItem(AsyncWork, null);
return this;
}
private void AsyncWork(object _)
{
var request = (HttpWebRequest)WebRequest.Create(boshUri);
request.Method = context.Request.HttpMethod;
// copy headers & body
request.UserAgent = context.Request.UserAgent;
request.Accept = string.Join(",", context.Request.AcceptTypes);
if (!string.IsNullOrEmpty(context.Request.Headers["Accept-Encoding"]))
{
request.Headers["Accept-Encoding"] = context.Request.Headers["Accept-Encoding"];
}
request.ContentType = context.Request.ContentType;
request.ContentLength = context.Request.ContentLength;
using (var stream = request.GetRequestStream())
{
CopyStream(context.Request.InputStream, stream);
}
request.BeginGetResponse(EndGetResponse, Tuple.Create(context, request));
}
private void EndGetResponse(IAsyncResult ar)
{
var data = (Tuple<HttpContext, HttpWebRequest>)ar.AsyncState;
var context = data.Item1;
var request = data.Item2;
try
{
using (var response = request.EndGetResponse(ar))
{
context.Response.ContentType = response.ContentType;
// copy headers & body
foreach (string h in response.Headers)
{
context.Response.AppendHeader(h, response.Headers[h]);
}
using (var stream = response.GetResponseStream())
{
CopyStream(stream, context.Response.OutputStream);
}
context.Response.Flush();
}
}
catch (Exception err)
{
if (err is IOException || err.InnerException is IOException)
{
// ignore
}
else
{
LogManager.GetLogger("ASC.Web.BOSH").Error(err);
}
}
finally
{
IsCompleted = true;
cb(this);
}
}

Related

Azure App Service- GZip Compression in Request

With this code (without the request compression part) I'm able to get gzip compressed content from Azure App Service (Xamarin.Froms App with offline sync). But when i try to gzip the request http-content i get a "Bad Request".
Any ideas? Is it possible to gzip the request content with Azure App Service?
namespace XXX.XXX.XXX.XXX.XXX
{
public class HttpGZipClientHandler : System.Net.Http.HttpClientHandler
{
long time = 0;
private long _downloadedBytesFromServer;
private long _downloadedProcessedBytes;
private long _intendedUploadedBytesToServer;
private long _uploadedBytesToServer;
private long _additionalTimeOverhead = 0;
public override bool SupportsAutomaticDecompression { get { return true; } }
public long DownloadedBytesFromServer { get { return _downloadedBytesFromServer; } }
public long DownloadedProcessedBytes { get { return _downloadedProcessedBytes; } }
public long IntendedUploadedBytesToServer { get { return _intendedUploadedBytesToServer; } }
public long UploadedBytesToServer { get { return _uploadedBytesToServer; } }
public long AdditionalTimeOverhead { get { return _additionalTimeOverhead; } }
public void ResetStatistics()
{
_downloadedBytesFromServer = 0;
_downloadedProcessedBytes = 0;
_intendedUploadedBytesToServer = 0;
_uploadedBytesToServer = 0;
_additionalTimeOverhead = 0;
}
protected override async Task<System.Net.Http.HttpResponseMessage> SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
//Save content headers before compressing
System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>> savedContentHeaders = new Dictionary<string, IEnumerable<string>>();
foreach (System.Collections.Generic.KeyValuePair<string, System.Collections.Generic.IEnumerable<string>> keyValue in request.Content.Headers)
{
savedContentHeaders.Add(keyValue.Key, keyValue.Value);
}
//Compress request content
System.Diagnostics.Stopwatch sp1 = new System.Diagnostics.Stopwatch();
sp1.Start();
_intendedUploadedBytesToServer += request.Content.Headers.ContentLength.HasValue ? request.Content.Headers.ContentLength.Value : 0;
await request.Content.LoadIntoBufferAsync().ConfigureAwait(false);
request.Content = new HttpGZipContent(await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false), System.IO.Compression.CompressionMode.Compress);
byte[] uploadedBytes = await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
_uploadedBytesToServer += uploadedBytes.Length;
sp1.Stop();
_additionalTimeOverhead += sp1.ElapsedMilliseconds;
//Set headers
foreach (System.Collections.Generic.KeyValuePair<string, System.Collections.Generic.IEnumerable<string>> keyValue in savedContentHeaders)
{
request.Content.Headers.Add(keyValue.Key, keyValue.Value);
}
request.Headers.AcceptEncoding.Add(new System.Net.Http.Headers.StringWithQualityHeaderValue("gzip"));
request.Content.Headers.Add("Content-Encoding", "gzip");
//Execute request
System.Net.Http.HttpResponseMessage response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
_downloadedBytesFromServer += response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : 0;
//Decompress response content
if (response.Content.Headers.ContentEncoding.Contains("gzip"))
{
System.Diagnostics.Stopwatch sp2 = new System.Diagnostics.Stopwatch();
sp2.Start();
await response.Content.LoadIntoBufferAsync().ConfigureAwait(false);
response.Content = new HttpGZipContent(await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false), System.IO.Compression.CompressionMode.Decompress);
byte[] processedBytes = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
_downloadedProcessedBytes += processedBytes.Length;
sp2.Stop();
_additionalTimeOverhead += sp2.ElapsedMilliseconds;
}
else
_downloadedProcessedBytes += response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : 0;
return response;
}
}
internal sealed class HttpGZipContent : System.Net.Http.HttpContent
{
private readonly byte[] _content;
private readonly System.IO.Compression.CompressionMode _compressionMode;
public HttpGZipContent(byte[] content, System.IO.Compression.CompressionMode compressionMode)
{
_compressionMode = compressionMode;
_content = content;
}
protected override async System.Threading.Tasks.Task SerializeToStreamAsync(System.IO.Stream stream, System.Net.TransportContext context)
{
if (_compressionMode == System.IO.Compression.CompressionMode.Compress)
{
using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(_content.Length))
{
using (System.IO.Compression.GZipStream zipStream = new System.IO.Compression.GZipStream(memoryStream, System.IO.Compression.CompressionMode.Compress))
{
zipStream.Write(_content, 0, _content.Length);
zipStream.Flush();
}
byte[] compressed = memoryStream.ToArray();
System.IO.MemoryStream copyStream = new System.IO.MemoryStream(compressed);
await copyStream.CopyToAsync(stream).ConfigureAwait(false);
}
}
else
{
using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(_content, 0, _content.Length))
{
using (System.IO.Compression.GZipStream zipStream = new System.IO.Compression.GZipStream(memoryStream, System.IO.Compression.CompressionMode.Decompress))
{
await zipStream.CopyToAsync(stream).ConfigureAwait(false);
}
}
}
}
protected override bool TryComputeLength(out long length)
{
length = _content.Length;
return true;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
}
}
Based on my understanding, you need to implement the request decompression for your mobile app back-end. If you are using the C# backend, you could create your custom ActionFilterAttribute as follows:
public class RequestDeCompressFilter : ActionFilterAttribute
{
public override async Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
var request = actionContext.Request;
if (request.Content.Headers.ContentEncoding.Contains("GZIP"))
{
await request.Content.LoadIntoBufferAsync().ConfigureAwait(false);
request.Content = new HttpGZipContent(await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false), System.IO.Compression.CompressionMode.Decompress);
}
//TODO: compress the response, you could follow http://www.intstrings.com/ramivemula/articles/jumpstart-47-gzipdeflate-compression-in-asp-net-mvc-application/
await base.OnActionExecutingAsync(actionContext, cancellationToken);
}
public override Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
{
//you could also compress the response here
return base.OnActionExecutedAsync(actionExecutedContext, cancellationToken);
}
}
Then, mark your action as follows:
[RequestDeCompressFilter]
public async Task<IHttpActionResult> PostMessage(Message item)
Also, you could follow HTTP Message Handlers in ASP.NET Web API to implement your HTTP message handler.

Validating URLs from Multiple Threads and Updating DataGridView

I'm trying to validate URLs from Multiple threads and update a DataTable.
The validation works fine when a single thread is used
Works Fine--Single Thread
foreach (string url in urllist)
{
Boolean valid = CheckURL(url);
this.Invoke((MethodInvoker)delegate()
{
if (valid)
{
dt.Rows[counter][2] = "Valid";
validcount++;
}
else
{
dt.Rows[counter][2] = statusCode;
invalidcount++;
}
counter++;
});
}
But when i try to do this using multiple threads Some Valid URls are reported as Invalid and vice versa.
Multi-Threads -Not Working
Parallel.ForEach(urllist, ProcessUrl);
private void ProcessUrl(string url)
{
Boolean valid = CheckURL(url);
this.Invoke((MethodInvoker)delegate()
{
if (valid)
{
dt.Rows[counter][2] = "Valid";
validcount++;
}
else
{
dt.Rows[counter][2] = statusCode;
invalidcount++;
}
counter++;
});
}
Associated Method and Class
private Boolean CheckURL(string url)
{
using (MyClient myclient = new MyClient())
{
try
{
myclient.HeadOnly = true;
myclient.Headers.Add(HttpRequestHeader.UserAgent, "My app.");
//fine, no content downloaded
string s1 = myclient.DownloadString(url);
statusCode = null;
return true;
}
catch (WebException error)
{
if (error.Response != null)
{
HttpStatusCode scode = ((HttpWebResponse)error.Response).StatusCode;
if (scode != null)
{
statusCode = scode.ToString();
}
}
else
{
statusCode = "Unknown Error";
}
return false;
}
}
}
class MyClient : WebClient
{
public bool HeadOnly { get; set; }
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest req = base.GetWebRequest(address);
req.Timeout = 10000;
if (HeadOnly && req.Method == "GET")
{
req.Method = "HEAD";
}
return req;
}
}
What i'm i doing wrong ? Please advice..
UPDATE:
How i start the task -->
var ts = new CancellationTokenSource();
CancellationToken ct = ts.Token;
Task.Factory.StartNew(() =>
{
if (nameresfailcount > 10)
{
if (ct.IsCancellationRequested)
{
// another thread decided to cancel
Console.WriteLine("task canceled");
break;
}
}
//stuff
},ct).ContinueWith(task =>
{
_benchmark.Stop();
}

Cant get Async (async await) download of JSON to work

I've been struggling for a while now, but I just can't get this work. I'm trying to download a json string to my Windows Phone 8 application, by using the 'sort of' async await.
I'm using the promising solution of Matthias Shapiro.
HttpExtensions.cs
public static class HttpExtensions
{
public static Task<Stream> GetRequestStreamAsync(this HttpWebRequest request)
{
var taskComplete = new TaskCompletionSource<Stream>();
request.BeginGetRequestStream(ar =>
{
Stream requestStream = request.EndGetRequestStream(ar);
taskComplete.TrySetResult(requestStream);
}, request);
return taskComplete.Task;
}
public static Task<HttpWebResponse> GetResponseAsync(this HttpWebRequest request)
{
var taskComplete = new TaskCompletionSource<HttpWebResponse>();
request.BeginGetResponse(asyncResponse =>
{
try
{
HttpWebRequest responseRequest = (HttpWebRequest)asyncResponse.AsyncState;
HttpWebResponse someResponse = (HttpWebResponse)responseRequest.EndGetResponse(asyncResponse);
taskComplete.TrySetResult(someResponse);
}
catch (WebException webExc)
{
HttpWebResponse failedResponse = (HttpWebResponse)webExc.Response;
taskComplete.TrySetResult(failedResponse);
}
}, request);
return taskComplete.Task;
}
}
public static class HttpMethod
{
public static string Head { get { return "HEAD"; } }
public static string Post { get { return "POST"; } }
public static string Put { get { return "PUT"; } }
public static string Get { get { return "GET"; } }
public static string Delete { get { return "DELETE"; } }
public static string Trace { get { return "TRACE"; } }
public static string Options { get { return "OPTIONS"; } }
public static string Connect { get { return "CONNECT"; } }
public static string Patch { get { return "PATCH"; } }
}
And My MainPageViewModel.cs
protected override void OnActivate()
{
base.OnActivate();
GetSessions();
}
private async void GetSessions()
{
var result = await GetMyData("http://localhost/api/MyData");
}
public async Task<string> GetMyData(string urlToCall)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlToCall);
request.Method = HttpMethod.Get;
HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync();
using (var sr = new StreamReader(response.GetResponseStream()))
{
return sr.ReadToEnd();
}
}
Once it hits the "HttpWebResponse someResponse = (HttpWebResponse)responseRequest.EndGetResponse(asyncResponse);", I'm getting a WebException:
"System.Net.WebException: The remote server returned an error: NotFound"
When I dive a bit deeper, I notice this error isn't the actual error. When I check the "asyncResponse" inside the GetResponseAsync method in the HttpExtensions class I notice the error:
"AsyncWaitHandle = 'asyncResponse.AsyncWaitHandle' threw an exception of type 'System.NotSupportedException'"
I have no idea how to get this work. Is it something I'm doing wrong?
I see the problem now. Since the emulator is using a virtual machine you cannot use localhost because localhost is the phone and not your PC. See this solution: Windows Phone 8 Emulator: Access localhost

Is there an analog for URLResourceLoader class in nvelocity

I want to provide external resource to VelocityEnngine that located on external server by using url path, in Apache velocity there is URLResourceLoader class but i see that in NVelocity there is only FileRecourceLoader...
Is there a way to provide external (url) resource ExtendedProperty in NVelocity
Thanks in advance.
Implemented the interface by my self:
public class UrlResourceLoader : ResourceLoader
{
protected ArrayList paths;
protected Hashtable templatePaths;
public UrlResourceLoader()
{
templatePaths = new Hashtable();
}
public override void Init(ExtendedProperties configuration)
{
paths = configuration.GetVector("path");
}
public override Stream GetResourceStream(string templateName)
{
lock (this)
{
int size = paths.Count;
if (string.IsNullOrEmpty(templateName))
{
throw;
}
for (int i = 0; i < size; i++)
{
var path = (string) paths[i];
var uri = new Uri(path + templateName);
Stream inputStream = FindTemplate(uri);
if (inputStream != null)
{
SupportClass.PutElement(templatePaths, templateName, path);
return inputStream;
}
}
throw;
}
}
private Stream FindTemplate(Uri requestUri)
{
try
{
var request = (HttpWebRequest)WebRequest.Create(requestUri);
request.Method = "GET";
var response = (HttpWebResponse) request.GetResponse();
if (HttpStatusCode.OK != response.StatusCode)
{
throw;
}
return response.GetResponseStream();
}
catch (Exception ex)
{
throw;
}
}
public override bool IsSourceModified(Resource resource)
{
var path = (string)templatePaths[resource.Name];
var uri = new Uri(path + resource.Name);
var request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "GET";
using (var response = (HttpWebResponse)request.GetResponse())
{
return response.LastModified.Ticks != resource.LastModified;
}
}
public override long GetLastModified(Resource resource)
{
var path = (string)templatePaths[resource.Name];
var uri = new Uri(path + resource.Name);
var request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "GET";
using (var response = (HttpWebResponse)request.GetResponse())
{
return response.LastModified.Ticks;
}
}
}

Trouble sending file through IHttpAsyncHandler

I'm using a IHttpHandler to call a webservice and return the resulting byte[] to the client as a downloaded file attachment. This works fine, but when I tried changing the IHttpHandler to a IHttpAsyncHandler, the file download dialog shows, but the file does not start/finish downloading. What am I doing wrong?
<%# WebHandler Language="C#" Class="PreviewPDF" %>
using System;
using System.Web;
public class PreviewPDF : IHttpAsyncHandler
{
public void ProcessRequest(HttpContext context)
{
}
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
{
string data = "some data";
using (WebService.RequestService service = new WebService.RequestService())
{
AsyncCallback callback = new AsyncCallback(EndProcessRequest);
return service.BeginGetFile(data, callback, context);
}
}
public void EndProcessRequest(IAsyncResult result)
{
HttpContext context = result.AsyncState as HttpContext;
byte[] wsoutput;
using (WebService.RequestService service = new WebService.RequestService())
{
wsoutput = service.EndGetFile(result);
}
context.Response.ContentType = "application/octet-stream";
context.Response.ContentEncoding = System.Text.Encoding.Unicode;
context.Response.AddHeader("Content-Disposition", "attachment; filename=attachment.pdf");
using (System.IO.MemoryStream ms = new System.IO.MemoryStream(wsoutput))
{
ms.WriteTo(context.Response.OutputStream);
}
context.Response.Flush();
}
public bool IsReusable {
get {
return false;
}
}
}
Few remarks about your code:
You need to call EndGetFile on the same service instance on which you called BeginGetFile
You need to pass cb as the AsyncCallBack instead of EndProcessRequest
Here's the code with these remarks taken into account:
private class State
{
public HttpContext Context { get; set; }
public RequestService Service { get; set; }
}
public void ProcessRequest(HttpContext context)
{
throw new NotImplementedException();
}
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
{
// Don't use using block or it will dispose the service before you can call EndGetFile
var state = new State
{
Service = new RequestService(),
Context = context
};
// Pass cb here and not EndProcessRequest
return state.Service.BeginGetFile(cb, state);
}
public void EndProcessRequest(IAsyncResult result)
{
State state = result.AsyncState as State;
// Be carefull as this may throw: it is best to put it in a try/finally block
// so that you dispose properly of the service
byte[] buffer = state.Service.EndGetFile(result);
state.Service.Dispose();
state.Context.Response.ContentType = "application/octet-stream";
state.Context.Response.AddHeader("Content-Disposition", "attachment; filename=attachment.pdf");
// Write directly into the output stream, and don't call Flush
state.Context.Response.OutputStream.Write(buffer, 0, buffer.Length);
}
public bool IsReusable
{
get { return false; }
}

Categories