12

This seems like a simple operation.

We have a need in our development environment (running on XP/IIS 5) to add some headers into each HttpRequest arriving at our application. (This is to simulate a production environment that we don't have available in dev). At first blush, this seemed like a simple HttpModule, along the lines of:

public class Dev_Sim: IHttpModule
{
    public void Init(HttpApplication app)
    {
        app.BeginRequest += delegate { app.Context.Request.Headers.Add("UserName", "XYZZY"); };
    }

    public void Dispose(){}
}

But on trying to do that, I find that the Headers collection of the Request is read-only, and the Add method fails with an OperationNotSupported exception.

Spending a couple hours researching this on Google, I've come up with no easy answer to what should be a relatively straight-forward problem.

Does anyone have any pointers?

Steven V
  • 16,357
  • 3
  • 63
  • 76
Dave Hanna
  • 2,491
  • 3
  • 32
  • 52
  • There is the ability to modify the response stream. Do you have something specific you can get a handle on and replace? – brumScouse Nov 03 '10 at 20:10
  • I don't want to modify the response. I need to modify the request, and send it on to the rest of the chain, ultimately ending in my MVC application. When it gets to my app, my app must be able to see the headers that I'm trying to insert in this module. – Dave Hanna Nov 03 '10 at 21:32

2 Answers2

18

Okay, with the assistance of a co-worker and some experimentation, I found that this can be done with the assistance of some protected properties and methods accessed through reflection:

var headers = app.Context.Request.Headers;
Type hdr = headers.GetType();
PropertyInfo ro = hdr.GetProperty("IsReadOnly", 
    BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy);
// Remove the ReadOnly property
ro.SetValue(headers, false, null);
// Invoke the protected InvalidateCachedArrays method 
hdr.InvokeMember("InvalidateCachedArrays", 
    BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, 
    null, headers, null);
// Now invoke the protected "BaseAdd" method of the base class to add the
// headers you need. The header content needs to be an ArrayList or the
// the web application will choke on it.
hdr.InvokeMember("BaseAdd", 
    BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, 
    null, headers, 
    new object[] { "CustomHeaderKey", new ArrayList {"CustomHeaderContent"}} );
// repeat BaseAdd invocation for any other headers to be added
// Then set the collection back to ReadOnly
ro.SetValue(headers, true, null);

This works for me, at least.

Dave Hanna
  • 2,491
  • 3
  • 32
  • 52
  • We have a winner! – Steve Jansen Jan 06 '16 at 03:00
  • This is so wrong. I used it to add parameters to the request (they are also read-only). No need to make it the value an ArrayList in that case, though - just a string. +1 – iandisme Oct 27 '16 at 14:35
  • 1
    Awesome! I used this to create an extension method for when I needed to add a header to a HttpRequest for my tests. https://gist.github.com/mrstebo/81288a96f55c0b74f6c5b37ee17533a6 – mrstebo Apr 12 '17 at 10:56
-1

You can add to the Header this way. This is a way to add credential information to the request before it enter the authentication sequence.

string cred = "UN:PW";
System.Web.HttpContext.Current.Request.Headers.Add("Authorization", "Basic " +Convert.ToBase64String(Encoding.ASCII.GetBytes(cred)));
kapex
  • 28,903
  • 6
  • 107
  • 121
mons0160
  • 49
  • 5
  • 5
    I'm not certain, and, since it's a year and a half later, I don't still have a test environment to try it, but I believe you'll find that Headers.Add will fail with and OperationNotSupported exception as it did in my original example. I may be wrong... – Dave Hanna Feb 22 '12 at 16:13
  • 1
    I tried this method and did get the same OperationNotSupported exception Dave Hanna's Solution worked for me – Franklin May 23 '16 at 15:48