Contact   •   Products   •   Search

Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs

Add a Web Server to your .NET 2.0 app with a few lines of code


As I’m still looking around for solutions to create some sort of easy mechanism of capturing all HTTP requests generated from IE, I ran across the HttpListener class in .NET 2.0. HttpListener is essentially a Web Server class, that’s super easy to use.

 

So figuring that an HTTP Proxy is really more or a less glorified HTTP server with a forwarding mechanism I played around with this a bit today and it seems to work in easily capturing output. Here’s a simple wrapper I threw together earlier to make accessing the server a little easier yet. With this code you can simply add a this class to a form and either implement ReceiveWebRequest event or else subclass this class and and override the ProcessRequest method.

 

Here’s the wrapper class in C# code:

 

using System;

using System.Text;

using System.Net;

using System.IO;

using Westwind.Tools;

 

namespace Westwind.InternetTools

{

    public delegate void delReceiveWebRequest(HttpListenerContext Context);

 

    /// <summary>

    /// Wrapper class for the HTTPListener to allow easier access to the

    /// server, for start and stop management and event routing of the actual

    /// inbound requests.

    /// </summary>

    public class HttpServer

    {

 

        protected HttpListener Listener;

        protected bool IsStarted = false;

 

        public event delReceiveWebRequest ReceiveWebRequest;

 

        public HttpServer()

        {

        }

 

        /// <summary>

        /// Starts the Web Service

        /// </summary>

        /// <param name="UrlBase">

        /// A Uri that acts as the base that the server is listening on.

        /// Format should be: http://127.0.0.1:8080/ or http://127.0.0.1:8080/somevirtual/

        /// Note: the trailing backslash is required! For more info see the

        /// HttpListener.Prefixes property on MSDN.

        /// </param>

        public void Start(string UrlBase)

        {

            // *** Already running - just leave it in place

            if (this.IsStarted)

                return;

 

            if (this.Listener == null)

            {

                this.Listener = new HttpListener();

            }

 

            this.Listener.Prefixes.Add(UrlBase);

 

            this.IsStarted = true;

            this.Listener.Start();

 

            IAsyncResult result = this.Listener.BeginGetContext( new AsyncCallback(WebRequestCallback), this.Listener );

        }

 

        /// <summary>

        /// Shut down the Web Service

        /// </summary>

        public void Stop()

        {

            if (Listener != null)

            {

                this.Listener.Close();

                this.Listener = null;

                this.IsStarted = false;

            }

        }

 

 

        protected void WebRequestCallback(IAsyncResult result)

        {

            if (this.Listener == null)

                return;

 

            // Get out the context object

            HttpListenerContext context = this.Listener.EndGetContext(result);

 

            // *** Immediately set up the next context

            this.Listener.BeginGetContext(new AsyncCallback(WebRequestCallback), this.Listener);

 

            if (this.ReceiveWebRequest != null)

                this.ReceiveWebRequest(context);

 

            this.ProcessRequest(context);

        }

 

        /// <summary>

        /// Overridable method that can be used to implement a custom hnandler

        /// </summary>

        /// <param name="Context"></param>

        protected virtual void ProcessRequest(HttpListenerContext Context)

        {

        }

 

    }

}

 

To try this out throw a textbox and a couple buttons on the form, name the textbox txtUrl and btnStart, btnStop.

 

public partial class Form1 : Form

{

    public HttpServer Server = null;

 

    public Form1()

    {

        InitializeComponent();

        Server = new HttpProxyServer();

    }

 

    private void btnStart_Click(object sender, EventArgs e)

    {

        this.Server.Start(this.txtUrl.Text);

    }

 

    private void btnStop_Click(object sender, EventArgs e)

    {

        this.Server.Stop();

    }

}

 

In my case I’m testing out the behavior leading up to acting like a proxy, so I created a subclass called HttpProxy Server like this (which right now is merely echoing back the headers):

 

public class HttpProxyServer : HttpServer

{

    protected override void ProcessRequest(System.Net.HttpListenerContext Context)

    {

        HttpListenerRequest Request = Context.Request;

        HttpListenerResponse Response = Context.Response;

 

        StringBuilder sb = new StringBuilder();

 

        sb.AppendLine(Request.HttpMethod + " " + Request.RawUrl + " Http/" + Request.ProtocolVersion.ToString());

 

        if (Request.UrlReferrer != null)

            sb.AppendLine("Referer: " + Request.UrlReferrer);

 

        if (Request.UserAgent != null)

            sb.AppendLine("User-Agent: " + Request.UserAgent);

 

        for (int x = 0; x < Request.Headers.Count; x++)

        {

            sb.AppendLine(Request.Headers.Keys[x] + ":" + " " + Request.Headers[x]);

        }

 

        sb.AppendLine();

 

        string Output = "<html><body><h1>Hello world</h1>Time is: " + DateTime.Now.ToString() +

            "<pre>" + sb.ToString() +  "</pre>";

 

        byte[] bOutput = System.Text.Encoding.UTF8.GetBytes(Output);

 

        Response.ContentType = "text/html";

        Response.ContentLength64 = bOutput.Length;

 

        Stream OutputStream = Response.OutputStream;

        OutputStream.Write(bOutput, 0, bOutput.Length);

        OutputStream.Close();

    }

 

}

 

If you run this thing it will service HTTP requests by simply echoing back the input headers and all so fancy HelloWorld message. Yeah, cheesy I know and woefully incomplete even for what it does in terms of request capturing, but it’s really nice to be able to do all of this with just a few lines of code. Obviously doing something useful in response of the HTTP request might get more complex quickly. Also, realize this is not meant as a scalable server – this wrapper uses a single async to fire a request and although it starts up a new context quite quickly, for a more scalable solution you probably need to set up a Thread Pool. Then again if you stick this sort of thing into your application I expect you’re not looking for an ultra-scalable mechanism but merely for a quick way to pass request/response messages back and forth.

 

I can think of a number of uses that I will have for this from easy machine to machine communication that can now literally be handled with just a few lines of code on each end for the data passing end at least. Note though that this class requires Windows XP SP 2 or Windows Server 2003 (or Vista I presume) to work. I haven't checked exactly what the dependencies are, but it definitely works without IIS running. The docs don't mention much so I assume it must work without other OS requirements activated.

 

Beyond that there’s my proxy/request capturing problem <g> which led me here in the first place. With this I can easily capture the HTTP input response, capture the request data I need and then fire up an HTTPWebRequest to actually forward the request to the original request Url (specified in the hostname), capturing the output then writing it back to the client. Now that’s a lot of overhead, but it sure is lots easier then getting bogged down in HTTP protocol details like continue headers, chunked responses and the keep alive management etc. that make using Raw Sockets for this not as easy as it may seem. In fact several of the Proxy Server Samples I looked at fell apart as soon as the browser would send a non-sequential request header…

 

 

Make Donation


Feedback for this Post

 
# re: Add a Web Server to your .NET 2.0 app with a few lines of code
by dominick December 04, 2005 @ 9:39pm
Hi Rick,

you forgot to mention that this code will only work as admin (or are you running as admin ?? :))

http://www.leastprivilege.com/HttpCfgACLHelper.aspx

cheers
dominick
# re: Add a Web Server to your .NET 2.0 app with a few lines of code
by Rick Strahl December 05, 2005 @ 12:04am
Thanks for posting that dominick. I guess you're becoming my security conscience around here <g>...

And yes, I run as admin unfortunately. I have too many applications (and most of them not mine <g>) that require admin rights, plus there's IIS admin where I spend much of my time during the day. It's not worth going through the pain of running with less privililege only to be running everything using RunAs...

# re: Add a Web Server to your .NET 2.0 app with a few lines of code
by Peter Bromberg December 05, 2005 @ 3:04am
Yay, Rick!

Now that's using yer header.
# .NET 2.0での簡易Webサーバの作成
by どっとねっとふぁんBlog December 06, 2005 @ 8:59pm
Add a Web Server to your&nbsp; .NET 2.0&nbsp; app with a few lines of code
.NET Framework 2.0??????HttpLisetner????????????Web????????????????MSDN?????????????????????????????????????...
# re: Add a Web Server to your .NET 2.0 app with a few lines of code
by Sadiq May 16, 2006 @ 2:25am
Hi Rick,
I am making a proxy like yours one but I want that after catching the user request from browser, it should display the actual contents of the page requested in the user's browser. I used HTTPWebRequest and HTTPWebListener classes but it gives lots of errors, especially when there are images on the page. Can you give some idea.
My mail is chormara1@yahoo.com

Thanks

# re: Add a Web Server to your .NET 2.0 app with a few lines of code
by cDima July 18, 2006 @ 9:23am
Man, this is a cool class.

I'm thinking of using this for a front-end to a portal, exposed to a LAN of 100 users.

Could you tell me more about scalability of this approach? Lets say there are 4-6 database queries to generate one page. And there are at most 6 people using the page at a time. Will it die? :)

email: sadakov@gmail.com
# re: Add a Web Server to your .NET 2.0 app with a few lines of code
by Krishna August 10, 2006 @ 6:47am
Sorry If I'm asking anything stupid.
Why we need this at all, when we have IIS6.0 / IIS7.0 will all the security feature in.
# re: Add a Web Server to your .NET 2.0 app with a few lines of code
by Rick Strahl August 10, 2006 @ 12:29pm
For a server application I would agree 100%, but there are client and P2P sceanrios where this come in quite handy. IIS7 is a big deal to have running on a client machine and this avoids having to use it - although you still need extended rights to host the server.

# How can I capture all HTTP traffic on a specific port using code? - Rick Strahl
by Rick Strahl October 05, 2006 @ 5:12pm
I'm trying to figure out how to capture all HTTP traffic on a specific port, so it can be played back at some later point. So today I'm picking your brain... maybe.
# DotNetSlackers: Add a Web Server to your .NET 2.0 app with a few lines of code
by DotNetSlackers Latest ASP.NET News October 09, 2006 @ 11:12pm
# Article: A low-level look at the ASP.NET Architecture - Rick Strahl's Web Log
by Rick Strahl's Web Log October 10, 2006 @ 10:11am
ASP.NET is a powerful platform for building Web applications, that provides a tremendous amount of flexibility and power for building just about any kind of Web application. Most people are familiar only with the high level frameworks like WebForms and WebServices which sit at the very top level of the ASP.NET hierarchy. This article looks at how Web requests flow through the ASP.NET framework from a very low level perspective, from Web Server, through ISAPI all the way up the request handler an
# re: Add a Web Server to your .NET 2.0 app with a few lines of code
by Kunal December 19, 2006 @ 8:21pm
Hey Rick,

The article is cool ! However, can you be more clear about ReceiveWebRequest function ?
What would you do in that function ? Can you share a code snippet ?

Thanks,
Kunal (koolkunal@gmail.com)
# Google Groups: microsoft.public.dotnet.framework
by microsoft.public.dotnet.framework Google Group December 21, 2006 @ 9:31am
# TagWorld :: fannyadams - .net
by Tagworld: fannyadams - Posts January 07, 2007 @ 2:51am
# re: Add a Web Server to your .NET 2.0 app with a few lines of code
by OpticTygre December 06, 2007 @ 5:40pm
One thing - Because the Listener.EndGetContext is blocking code - just in another thread - if you tried to stop this server, the main application would stop, but one background thread would remain open, still waiting for a connection. To truly stop the HttpListener, you should call Listener.Abort.
# re: Add a Web Server to your .NET 2.0 app with a few lines of code
by zmrcic March 21, 2008 @ 8:49am
can I use simple desktop application?
# re: Add a Web Server to your .NET 2.0 app with a few lines of code
by Derek Smyth October 03, 2008 @ 6:45am
Hi Rick, just wanted to say great site filled with great information. Excellent work mate!
# re: Add a Web Server to your .NET 2.0 app with a few lines of code
by Vito Botta February 05, 2009 @ 5:17pm
I have found inspiration countless times on this blog but - my bad - I haven't yet spent a minute to write a comment. May I take this opportunity to thank you for one of the most useful websites I have ever found on these subjects!
# re: Add a Web Server to your .NET 2.0 app with a few lines of code
by Sanket March 02, 2010 @ 3:10am
Thanks, You made my day :)
# re: Add a Web Server to your .NET 2.0 app with a few lines of code
by Alex November 16, 2010 @ 12:42am
Hi, your solution does not work: try to put Thread.Sleep inside processrequest function and check it with simple load test.
# re: Add a Web Server to your .NET 2.0 app with a few lines of code
by Etienne March 07, 2011 @ 3:41pm
Please note that if you use Thread.Sleep you will end up with odd behavior. If you want to do some load testing, make sure to offload the Thread.sleep to a new Thread in the ThreadPool.
# re: Add a Web Server to your .NET 2.0 app with a few lines of code
by Ralph Krausse September 06, 2012 @ 12:10pm
Can this be used/configured to support external pages? If a machine didn't have IIS installed and I wanted to use this to host a site. Can that work?

thx
Ralph
 


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2014