C#:修改Owin响应流导致AccessViolationException [英] C#: modifying Owin response stream causes AccessViolationException

查看:102
本文介绍了C#:修改Owin响应流导致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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆