Relaying a request in asp.net (Forwarding a request)

36,601

Solution 1

Actually, something like this worked well

HttpRequest original = context.Request;
HttpWebRequest newRequest = (HttpWebRequest)WebRequest.Create(newUrl);

newRequest .ContentType = original.ContentType;
newRequest .Method = original.HttpMethod;
newRequest .UserAgent = original.UserAgent;

byte[] originalStream = ReadToByteArray(original.InputStream, 1024);

Stream reqStream = newRequest .GetRequestStream();
reqStream.Write(originalStream, 0, originalStream.Length);
reqStream.Close();


newRequest .GetResponse();

edit: ReadToByteArray method just makes a byte array from the stream

Solution 2

I have an extension method on HttpResponseBase to copy an incoming request to an outgoing request.

Usage:

    var externalRequest = (HttpWebRequest)WebRequest.Create("http://stackoverflow.com");
    this.Request.CopyTo(externalRequest);
    var externalResponse = (HttpWebResponse)externalRequest.GetResponse();

Source:

/// <summary>
/// Copies all headers and content (except the URL) from an incoming to an outgoing
/// request.
/// </summary>
/// <param name="source">The request to copy from</param>
/// <param name="destination">The request to copy to</param>
public static void CopyTo(this HttpRequestBase source, HttpWebRequest destination)
{
    destination.Method = source.HttpMethod;

    // Copy unrestricted headers (including cookies, if any)
    foreach (var headerKey in source.Headers.AllKeys)
    {
        switch (headerKey)
        {
            case "Connection":
            case "Content-Length":
            case "Date":
            case "Expect":
            case "Host":
            case "If-Modified-Since":
            case "Range":
            case "Transfer-Encoding":
            case "Proxy-Connection":
                // Let IIS handle these
                break;

            case "Accept":
            case "Content-Type":
            case "Referer":
            case "User-Agent":
                // Restricted - copied below
                break;

            default:
                destination.Headers[headerKey] = source.Headers[headerKey];
                break;
        }
    }

    // Copy restricted headers
    if (source.AcceptTypes.Any())
    {
        destination.Accept = string.Join(",", source.AcceptTypes);
    }
    destination.ContentType = source.ContentType;
    destination.Referer = source.UrlReferrer.AbsoluteUri;
    destination.UserAgent = source.UserAgent;

    // Copy content (if content body is allowed)
    if (source.HttpMethod != "GET"
        && source.HttpMethod != "HEAD"
        && source.ContentLength > 0)
    {
        var destinationStream = destination.GetRequestStream();
        source.InputStream.CopyTo(destinationStream);
        destinationStream.Close();
    }
}

Solution 3

Here's some good relay code in VB.NET using MVC.

GLOBAL.ASAX.VB

Public Class MvcApplication
    Inherits System.Web.HttpApplication

    Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
        routes.MapRoute("Default", "{*s}", New With {.controller = "Home", .action = "Index"})
    End Sub

    Sub Application_Start()
        RegisterRoutes(RouteTable.Routes)
    End Sub
End Class

HomeController.vb

Option Explicit On
Option Strict On

Imports System.Net

<HandleError()> _
Public Class HomeController
    Inherits System.Web.Mvc.Controller

    Function Index(ByVal s As String) As ActionResult
        Server.ScriptTimeout = 60 * 60
        If Request.QueryString.ToString <> "" Then s = s + "?" + Request.QueryString.ToString
        Dim req As HttpWebRequest = CType(WebRequest.Create("http://stackoverflow.com/" + s), HttpWebRequest)
        req.AllowAutoRedirect = False
        req.Method = Request.HttpMethod
        req.Accept = Request.Headers("Accept")
        req.Referer = Request.Headers("Referer")
        req.UserAgent = Request.UserAgent
        For Each h In Request.Headers.AllKeys
            If Not (New String() {"Connection", "Accept", "Host", "User-Agent", "Referer"}).Contains(h) Then
                req.Headers.Add(h, Request.Headers(h))
            End If
        Next
        If Request.HttpMethod <> "GET" Then
            Using st = req.GetRequestStream
                StreamCopy(Request.InputStream, st)
            End Using
        End If
        Dim resp As WebResponse = Nothing
        Try
            Try
                resp = req.GetResponse()
            Catch ex As WebException
                resp = ex.Response
            End Try

            If resp IsNot Nothing Then
                Response.StatusCode = CType(resp, HttpWebResponse).StatusCode
                For Each h In resp.Headers.AllKeys
                    If Not (New String() {"Content-Type"}).Contains(h) Then
                        Response.AddHeader(h, resp.Headers(h))
                    End If
                Next
                Response.ContentType = resp.ContentType

                Using st = resp.GetResponseStream
                    StreamCopy(st, Response.OutputStream)
                End Using
            End If
        Finally
            If resp IsNot Nothing Then resp.Close()
        End Try
        Return Nothing
    End Function
    Sub StreamCopy(ByVal input As IO.Stream, ByVal output As IO.Stream)
        Dim buf(0 To 16383) As Byte
        Using br = New IO.BinaryReader(input)
            Using bw = New IO.BinaryWriter(output)
                Do
                    Dim rb = br.Read(buf, 0, buf.Length)
                    If rb = 0 Then Exit Do
                    bw.Write(buf, 0, rb)
                Loop
            End Using
        End Using
    End Sub
End Class

Solution 4

HttpContext includes the Request property, which in turn contains the Headers collection. It should be all the information you need.

Share:
36,601
El Che
Author by

El Che

.NET and Java developer

Updated on July 09, 2022

Comments

  • El Che
    El Che over 1 year

    I have a web application that communicates between two different web applications (one receiver and one sender, the sender communicates with my application, and my application communicates with both).

    A regular scenario is that the sender sends a HttpRequest to my application, and I receive it in an HttpHandler. This in turn sends the HttpContext to some businesslogic to do some plumbing.

    After my business classes are finished storing data (some logging etc), I want to relay the same request with all the headers, form data etc to the receiver application. This must be sent from the class, and not the HttpHandler.

    The question is really - how can I take a HttpContext object, and forward/relay the exact same request only modifying the URL from http://myserver.com/ to http://receiver.com.

    Any code examples in preferable c# would be great!

  • El Che
    El Che almost 15 years
    HttpWebRequest relayingRequest = (HttpWebRequest)WebRequest.Create(relayUrl); relayingRequest.Headers.Add(context.Request.Headers); This does not work - I also need params, httpmethod etc. The request should be identical...
  • John Saunders
    John Saunders almost 15 years
    It's all there, in HttpContext.Request, almost "by definition". Consider that this is how a page or other handler knows what to do. There's a QueryString and Form property to Context.Request as well. Look up the properties, and you'll see.
  • El Che
    El Che almost 15 years
    Yeah I know they're all there, but remember that context.Request is not a System.Net.HttpWebRequest, but a System.Web.HttpRequest. I'm not sure it it's as easy as just looking up the props and getting them into the new request.
  • John Saunders
    John Saunders almost 15 years
    I am sure. Why are you looking for something more complicated? Take a look, try it out, and see.
  • El Che
    El Che almost 15 years
    I'll check more tomorrow. It seems I need to read the inputstream from the original request and write it to the new one. Also I need to copy some of the properties like you say. Weird that there are no method to duplicate a request without this much hassle.
  • John Saunders
    John Saunders almost 15 years
    It's not something people have to do very often. This is something more often done at a lower level of the network infrastructure.
  • El Che
    El Che almost 15 years
    Unable to cast object of type 'System.Web.HttpHeaderCollection' to type 'System.Net.WebHeaderCollection'.
  • Adrian Grigore
    Adrian Grigore about 13 years
    What about request header parameters and cookies?
  • El Che
    El Che about 13 years
    Not needed for my context, but yes that could be done as well
  • diachedelic
    diachedelic over 11 years
    I've generalised this answer below as an extension method to copy all headers & cookies
  • bfi
    bfi over 9 years
    source.InputStream.Position = 0; is needed before source.InputStream.CopyTo
  • Nuno Rodrigues
    Nuno Rodrigues almost 9 years
    Just a small change, for the Headers part is safer and more complete this way: foreach (var headerKey in source.Headers.AllKeys) { if (!WebHeaderCollection.IsRestricted(headerKey)) { target.Headers[headerKey] = source.Headers[headerKey]; } }
  • Donald.Record
    Donald.Record over 8 years
    What's the big difference between this approach and using "Server.Transfer()"?
  • user1069816
    user1069816 over 8 years
    @Donald.Record Server.transfer can work only for sites running on the server in the same app pool; you can't use Server.Transfer to send the user to an external site.
  • johni
    johni over 7 years
    Coping a cookie using the headers like you suggest does not includes the cookie in the forwarded request.
  • vinodh
    vinodh over 7 years
    newRequest .ContentType = original.ContentType; its giving header manipulation security vulnarability issue in scanners.