Saturday, May 30, 2009

Intercepting a HTTP call to modify its behaviour

Say you have some application that uses a HTTP call to get hold of some XML data. It could be a web service but might also be an ASHX generic handler, or any kind of server-side HTTP handling code. Say you want to change the behaviour of that code in some way but you don’t have access to the source code but can change the URL that the calling application uses. What to do?

This was my problem to solve. Actually I didn’t realise it was a problem until I thought of the solution, then thought it would be an exceedingly useful thing to do, and may well be useful in other scenarios. The basic idea is to create a generic handler that will replace the original handler and in the simplest case just pass the request onto the original handler and return the response from the original handler. The configuration of the calling app needs to be updated to use the new handler.

There may be other solutions, such as adding a HTTP module to the original handler, if it happens to be an ASP.NET application. This solution may impact performance since two HTTP calls will be made rather than just one, but I still quite like it.

Here’s the code for the ProcessRequest method, that obviously needs updating to make any changes to the response returned by the original handler. It also shows how to deal with POST data, if the call you’re intercepting is a POST call.

    public void ProcessRequest (HttpContext context) 
    {
      context.Response.ContentType = "text/xml";
      
      // create outgoing HTTP request
      HttpWebRequest req = (HttpWebRequest)WebRequest.Create(
        ConfigurationManager.AppSettings["messageHandlerUrl"]);
      req.Method = "POST";
      req.KeepAlive = false;
      req.ContentType = context.Request.ContentType;

      // get POST data from incoming request
      string parameters;
      using (StreamReader postReader = new StreamReader(context.Request.InputStream))
      {
        parameters = postReader.ReadToEnd();
      }

      // add POST data to outgoing request
      using (Stream stream = req.GetRequestStream())
      using (StreamWriter streamWriter = new StreamWriter(stream))
      {
        streamWriter.Write(parameters);
        streamWriter.Close();
      }
      
      // get response
      using (WebResponse resp = req.GetResponse())
      using (Stream respStream = resp.GetResponseStream())
      using (StreamReader reader = new StreamReader(respStream))
      {
        string response = reader.ReadToEnd();
        context.Response.Write(response);
      }
    }

No comments: