Я использую ASP.NET IHttpAsyncHandler для асинхронного перенаправления Long Polling HTTP Requsets на другой URL. Он отлично работает с.NET 4.5 (Windows 7,8). Но не работает с Mono (Mono JIT-компилятор версии 3.2.8 (Debian 3.2.8 + dfsg-4ubuntu1), Ubuntu 14.04). После завершения запроса.BeginGetResponse AsyncCallback не вызывает 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);
}
}
Я не знаю причины этого странного beahaviour. Я полагаю, что это поведение является ошибкой класса HttpWebRequest в среде Mono, но я не уверен. Могут ли быть какие-либо обходные пути этой проблемы?
Мы обнаружили некоторое обходное решение проблемы с помощью 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);
}
}