6

I have an ASP.NET site that uses a third-party reporting component. This component is misbehaving by throwing a NullReferenceException whenever the client browser is not specifying a User-Agent in the request headers.

It's basically an odd scenario that I'm just trying to come up with a workaround for. I do not know who/what client is not specifying a User-Agent, which seems like bad form IMO, but we have to deal with the exceptions it is generating. I have logged a support ticket with the third-party regarding the bug in their reporting component, but I have my doubts about how fruitful that route is going to be. So my thought was just to detect when the User-Agent is blank and default it to something just to appease the reporting component. However, I can't seem to change anything in the Request.Headers collection. I get the following exception:

Operation is not supported on this platform.

I'm starting to believe I'm not going to be able to do this. I understand why ASP.NET wouldn't allow this, but I haven't come up with any other workaround.

Update: At penfold's suggestion, I tried to add the User-Agent to the Request.Headers collection using an HttpModule. This got it added to the Headers collection, but did nothing to update the Request.UserAgent property, which is what is causing the reporting component to fail. I've been looking through .NET Reflector to determine how that property is set so that I can update it, but I haven't come up with anything yet (there isn't just a private field that drives the property that I can find).

Community
  • 1
  • 1
glancep
  • 356
  • 5
  • 18
  • Can you provide the code where you're attempting to change the HttpWebRequest headers? – CAbbott Mar 05 '13 at 21:58
  • No, I'm not using HttpWebRequest--I'm trying to change the headers of the incoming request (HttpRequest object). Ex: Request.Headers.Set("User-Agent", "some user agent") – glancep Mar 05 '13 at 22:12

3 Answers3

3

Recently I also facing similar problem same as you. I overcome the problem of Request.UserAgent by using a mock HttpWorkerRequest.

(Assuming you already solve the agent string in Request.Headers with custom HttpModule)

Here is the sample code:

Friend Class MockedRequestWorker
    Inherits HttpWorkerRequest

    Private ReadOnly _BaseHttpWorkerRequest As HttpWorkerRequest
    Private ReadOnly _UserAgent As String

    Friend Sub New(ByVal base As HttpWorkerRequest, 
        ByVal UserAgent As String)
        _BaseHttpWorkerRequest = base
        _UserAgent = UserAgent
    End Sub

    Public Overrides Sub EndOfRequest()
        _BaseHttpWorkerRequest.EndOfRequest()
    End Sub

    Public Overrides Sub FlushResponse(ByVal finalFlush As Boolean)
        _BaseHttpWorkerRequest.FlushResponse(finalFlush)
    End Sub

    'Note: remember to override all other virtual functions by direct invoke functions
    'from _BaseHttpWorkerRequest, except the following function

    Public Overrides Function GetKnownRequestHeader(ByVal index As Integer) As String

        'if user is requesting the user agent value, we return the 
        'override user agent string
        If index = HttpWorkerRequest.HeaderUserAgent Then
            Return _UserAgent
        End If

        Return _BaseHttpWorkerRequest.GetKnownRequestHeader(index)
    End Function

End Class

then, in your custom HttpApplication.BeginRequest handler, do this

Private Sub BeginRequestHandler(ByVal sender As Object, ByVal e As EventArgs)

    Dim request As HttpRequest = HttpRequest.Current.Request
    Dim HttpRequest_wrField As FieldInfo = GetType(HttpRequest).GetField("_wr", BindingFlags.Instance Or BindingFlags.NonPublic)

    Dim ua As String = "your agent string here"
    Dim _wr As HttpWorkerRequest = HttpRequest_wrField.GetValue(request)
    Dim mock As New MockedRequestWorker(_wr, ua)

    'Replace the internal field with our mocked instance
    HttpRequest_wrField.SetValue(request, mock)

End Sub

Note: this method still does not replace the user agent value in ServerVariables, but it should able to solve what you need(and my problem too)

Hope this help :)

terryfkjc
  • 191
  • 1
  • 4
  • That ended up being not worth the effort in my particular case, but impressive work. Thanks for sharing! – glancep Jan 24 '14 at 17:43
3
protected void Application_BeginRequest(Object sender, EventArgs e) {
    const string ua = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)";
    Request.Headers["User-Agent"] = ua;
    var httpWorkerRequestField = Request.GetType().GetField("_wr", BindingFlags.Instance | BindingFlags.NonPublic);
    if (httpWorkerRequestField != null) {
        var httpWorkerRequest = httpWorkerRequestField.GetValue(Request);
        var knownRequestHeadersField = httpWorkerRequest.GetType().GetField("_knownRequestHeaders", BindingFlags.Instance | BindingFlags.NonPublic);
        if (knownRequestHeadersField != null) {
            string[] knownRequestHeaders = (string[])knownRequestHeadersField.GetValue(httpWorkerRequest);
            knownRequestHeaders[39] = ua;
        }
    }
}
MKB
  • 7,587
  • 9
  • 45
  • 71
alabala
  • 35
  • 1
2

I think the best way of handling this is to use a http module that will check the header and inject the user agent if necessary.

As you have found out you cannot use the set method on the Headers object. Instead you will have to inject the user agent string into the header via protected properties that can be accessed through reflection as outlined in the answer to this question.

UPDATE

Unfortunately Request.UserAgent doesn't use the information held in Request.Headers, instead it calls the method GetKnownRequestHeader in HttpWorkerRequest. This is an abstract class and from looking at the decompiled library code the actual implementation varies depending on the hosting environment. Therefore I cannot see a way to replace the user agent string in a reliable manner via reflection. You could roll your own WorkerRequest class but for the amount of effort I don't think the payoff would be worth it.

Sorry to be negative but I think its just not possible to set the user agent within the web application in a simple manner. Your best option at the moment would be to perform a pre-check for a user agent, and if the request doesn't have one return a browser not supported error message.

You could also investigate injecting something earlier on, i.e. in IIS or at your proxy server if you use one.

Also I would recommend that this issue is reported to SAP. I know they are actively working on the Viewer at the moment and who knows they might fix it and maybe even add support for Opera!

Community
  • 1
  • 1
mickfold
  • 2,003
  • 1
  • 14
  • 20
  • Thanks. That seems to get the value into the Headers collection, but the Request.UserAgent property does not reflect the new value (still null/nothing) and Crystal is still bombing out. Will upvote for the helpful response, but any idea how to force the header-based properties to reload from the Headers collection? – glancep Mar 05 '13 at 23:10
  • Actually, I bet my problem was that I did not implement as an HTTP module. I just built that code into my page for testing. Hmm... I'll have to see if it will work as an HTTP module, as I'm betting that will occur earlier in the life cycle. If I can get it to work that way, I'll accept. Thanks again! – glancep Mar 05 '13 at 23:15
  • The crystal web plugin uses a http handler to handle report generation so putting the injection code on the aspx page is far to late in the lifecycle. For your purposes the only way to amend the request headers is to create a module that intercepts BeginRequest and injects the user agent at that point. – mickfold Mar 06 '13 at 08:38
  • I implemented it as an HttpModule in the BeginRequest event... but still no dice. User-Agent is being added to the Headers collection, but Request.UserAgent is still null and Crystal is still choking on it. Is there another event that would execute prior to the setting of the Request properties? Thanks again for your help... – glancep Mar 06 '13 at 20:18
  • Thanks for updating. You've been very helpful. I ended up taking your suggestion and implementing a "browser not supported" message to avoid the exception. FWIW, I did notify SAP and their response was that they didn't support browsers that don't specify a user agent, and "This is not something we would fix."!! Go figure... – glancep Mar 08 '13 at 14:10