C#:修改Owin响应流导致AccessViolationException [英] C#: modifying Owin response stream causes AccessViolationException
问题描述
我正在尝试使用某些自定义的Owin中间件在特定情况下修改(在这种情况下,完全替换)响应流.
I'm attempting to use some custom Owin middleware to modify (in this case, completely replace) the response stream in specific circumstances.
任何时候只要我打出确实触发我的中间件来替换响应的调用,一切都会正常进行.仅当我致电我的中间件未对其进行更改时,才会出现此问题.此外,当替换为不的API调用返回手动创建的HttpResponseMessage对象时,我只能出现错误.
Anytime I make a call that does trigger my middleware to replace the response, everything works properly. The problem only occurs when I make a call that my middleware does not make changes to. Additionally, I have only been able to get the error to occur when the API call that is not being replaced is returning a manually created HttpResponseMessage object.
例如,调用此API:
public class testController : ApiController
{
public HttpResponseMessage Get()
{
return Request.CreateResponse(HttpStatusCode.OK,new { message = "It worked." });
}
}
工作正常,但此类:
public class testController : ApiController
{
public HttpResponseMessage Get()
{
HttpResponseMessage m = Request.CreateResponse();
m.StatusCode = HttpStatusCode.OK;
m.Content = new StringContent("It worked.", System.Text.Encoding.UTF8, "text/plain");
return m;
}
}
导致发生错误. (在两种情况下,均会调用http://localhost:<port>/test
.)
causes the error to occur. (In both cases, http://localhost:<port>/test
is being called.)
该错误导致以下任一情况:
The error causes either of the following:
- 导致iisexpress.exe(如果在实际的IIS中运行则为w3wp.exe)因访问冲突而崩溃.
-
抛出一个
AccessViolationException
,Visual Studio会捕获该AccessViolationException
,但是它无法执行任何操作,因为它发生在外部代码中.当Visual Studio确实捕获到异常时,我看到:
- Causes iisexpress.exe (or w3wp.exe if running in actual IIS) to crash with an Access Violation.
Throws an
AccessViolationException
that Visual Studio catches but is unable to do anything with as it occurs in external code. When Visual Studio does catch the exception, I see:
An unhandled exception of type 'System.AccessViolationException' occurred in System.Web.dll
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
很明显,如果我不启用中间件,则根本没有问题.同样,我只能导致手动创建并返回HttpResponseMessage对象时发生该问题,如第二类所示.
Obviously if I do not enable my middleware I do not have the issue at all. Also I have only been able to cause the issue to occur when manually creating and returning an HttpResponseMessage object as shown in the second class.
这是我的中间件类.它的当前设置是,只要有人请求端点/replace
,无论管道中是否有任何其他操作,它都将替换掉整个响应流.
Here is my middleware class. Its currently set to simply replace the entire response stream any time someone requests the endpoint /replace
regardless of if anything else in the pipeline has done anything to it.
using Microsoft.Owin;
using Owin;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using Newtonsoft.Json;
using AppFunc = System.Func<
System.Collections.Generic.IDictionary<string, object>,
System.Threading.Tasks.Task
>;
namespace TestOwinAPI
{
public class ResponseChangeMiddleware
{
AppFunc _next;
public ResponseChangeMiddleware(AppFunc next, ResponseChangeMiddlewareOptions opts)
{
_next = next;
}
public async Task Invoke(IDictionary<string,object> env)
{
var ctx = new OwinContext(env);
// create a new memory stream which will replace the default output stream
using (var ms = new MemoryStream())
{
// hold on to a reference to the actual output stream for later use
var outStream = ctx.Response.Body;
// reassign the context's output stream to be our memory stream
ctx.Response.Body = ms;
Debug.WriteLine(" <- " + ctx.Request.Path);
// allow the rest of the middleware to do its job
await _next(env);
// Now the request is on the way out.
if (ctx.Request.Path.ToString() == "/replace")
{
// Now write new response.
string json = JsonConvert.SerializeObject(new { response = "true", message = "This response will replace anything that the rest of the API might have created!" });
byte[] jsonBytes = System.Text.Encoding.UTF8.GetBytes(json);
// clear everything else that anything might have put in the output stream
ms.SetLength(0);
// write the new data
ms.Write(jsonBytes, 0, jsonBytes.Length);
// set parameters on the response object
ctx.Response.StatusCode = 200;
ctx.Response.ContentLength = jsonBytes.Length;
ctx.Response.ContentType = "application/json";
}
// In all cases finally write the memory stream's contents back to the actual response stream
ms.Seek(0, SeekOrigin.Begin);
await ms.CopyToAsync(outStream);
}
}
}
public static class AppBuilderExtender
{
public static void UseResponseChangeMiddleware(this IAppBuilder app, ResponseChangeMiddlewareOptions options = null )
{
if (options == null)
options = new ResponseChangeMiddlewareOptions();
app.Use<ResponseChangeMiddleware>(options);
}
}
public class ResponseChangeMiddlewareOptions
{
}
}
我做的很明显-整夜进行RAM测试(一切都很好),然后尝试在另一个系统上(它也在那里发生).
I've done the obvious - a full night of RAM testing (all good), and trying on another system (it occurred there too).
此外,该错误也不是始终如一的-大约发生一半的时间.换句话说,通常我可以通过一个或两个成功的请求,但是最终会出现错误.
Additionally, the error is not consistent - it occurs about half the time. In other words, often I can get one or two successful requests through, but eventually, the error occurs.
最后,如果我在中间件中的内存流复制之前在程序中放置一个断点,然后缓慢地逐步执行代码,则永远不会发生该错误.这向我表明我必须遇到某种竞争状况,这与我正在使用MemoryStreams玩游戏有关.
Finally, if I put a breakpoint in my program right before the memory stream copy in my middleware, and slowly step through the code, the error never occurs. This indicates to me that I must be hitting some kind of race condition, and it has to be related to the fact that I'm playing with MemoryStreams.
有什么想法吗?
推荐答案
哦,我的
我不确定是否要更改此权限是正确的事情,但这肯定可以解决问题:
I'm not sure if changing this is the right thing to do, but it definitely fixed the problem:
await ms.CopyToAsync(outStream);
到
ms.CopyTo(outStream);
我唯一的猜测是,应用程序以某种方式在异步调用完成复制之前关闭了MemoryStream,这是有道理的.
My only guess is that somehow the app was closing the MemoryStream before the async call had completed copying it, which would make sense.
这篇关于C#:修改Owin响应流导致AccessViolationException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!