Thursday, February 22, 2007 3:06 AM bart

HttpListener for dummies: a simple "HTTP Request Reflector"

Probably you did know already that Windows XP SP2, Windows Server 2003 and Windows Vista have a component called http.sys to handle HTTP requests.

Basically, http.sys is a kernel-mode listener that has intrinsic knowledge of HTTP. Different parties can register with this listener to have requests forwarded to them. Typical examples nowadays include IIS 6 and SQL Server 2005 (for Web Service functionality). The result? An increase throughput (kernel-mode caching), a more reliable architecture and a decreased footprint on the system (no need to install IIS to do - for example - SQLXML kind of stuff since SQL Server can hook directly into http.sys).

Enough theory. Did you know your managed code application can take advantage of all this goodness right away? Discover System.Net.HttpListener right away. This sample shows the basic concepts:

1 using System; 2 using System.Collections; 3 using System.Collections.Specialized; 4 using System.IO; 5 using System.Net; 6 using System.Reflection; 7 using System.Text; 8 using System.Threading; 9 10 class Program 11 { 12 static void Main() 13 { 14 HttpListener listener = new HttpListener(); 15 listener.Prefixes.Add("http://*:8080/"); 16 listener.Start(); 17 Console.WriteLine("Listening..."); 18 for(;;) 19 { 20 HttpListenerContext ctx = listener.GetContext(); 21 new Thread(new Worker(ctx).ProcessRequest).Start(); 22 } 23 } 24 25 class Worker 26 { 27 private HttpListenerContext context; 28 29 public Worker(HttpListenerContext context) 30 { 31 this.context = context; 32 } 33 34 public void ProcessRequest() 35 { 36 string msg = context.Request.HttpMethod + " " + context.Request.Url; 37 Console.WriteLine(msg); 38 39 StringBuilder sb = new StringBuilder(); 40 sb.Append("<html><body><h1>" + msg + "</h1>"); 41 DumpRequest(context.Request, sb); 42 sb.Append("</body></html>"); 43 44 byte[] b = Encoding.UTF8.GetBytes(sb.ToString()); 45 context.Response.ContentLength64 = b.Length; 46 context.Response.OutputStream.Write(b, 0, b.Length); 47 context.Response.OutputStream.Close(); 48 } 49 50 private void DumpRequest(HttpListenerRequest request, StringBuilder sb) 51 { 52 DumpObject(request, sb); 53 } 54 55 private void DumpObject(object o, StringBuilder sb) 56 { 57 DumpObject(o, sb, true); 58 } 59 60 private void DumpObject(object o, StringBuilder sb, bool ulli) 61 { 62 if (ulli) 63 sb.Append("<ul>"); 64 65 if (o is string || o is int || o is long || o is double) 66 { 67 if(ulli) 68 sb.Append("<li>"); 69 70 sb.Append(o.ToString()); 71 72 if(ulli) 73 sb.Append("</li>"); 74 } 75 else 76 { 77 Type t = o.GetType(); 78 foreach (PropertyInfo p in t.GetProperties(BindingFlags.Public | BindingFlags.Instance)) 79 { 80 sb.Append("<li><b>" + p.Name + ":</b> "); 81 object val = null; 82 83 try 84 { 85 val = p.GetValue(o, null); 86 } 87 catch {} 88 89 if (val is string || val is int || val is long || val is double) 90 sb.Append(val); 91 else 92 93 if (val != null) 94 { 95 Array arr = val as Array; 96 if (arr == null) 97 { 98 NameValueCollection nv = val as NameValueCollection; 99 if (nv == null) 100 { 101 IEnumerable ie = val as IEnumerable; 102 if (ie == null) 103 sb.Append(val.ToString()); 104 else 105 foreach (object oo in ie) 106 DumpObject(oo, sb); 107 } 108 else 109 { 110 sb.Append("<ul>"); 111 foreach (string key in nv.AllKeys) 112 { 113 sb.AppendFormat("<li>{0} = ", key); 114 DumpObject(nv[key],sb,false); 115 sb.Append("</li>"); 116 } 117 sb.Append("</ul>"); 118 } 119 } 120 else 121 foreach (object oo in arr) 122 DumpObject(oo, sb); 123 } 124 else 125 { 126 sb.Append("<i>null</i>"); 127 } 128 sb.Append("</li>"); 129 } 130 } 131 if (ulli) 132 sb.Append("</ul>"); 133 } 134 } 135 }

Ignore the naive approach to threading in Main and the quick-n-dirty dumb DumpObject thing at the end of the sample and just focus on the real stuff in ProcessRequest. HttpListenerContext has not more than three properties that allow you to deal with the request, the response and the user. The first two are illustrated. I could have created a simple "Hello World" kind of thing, but decided to make it a little more complex by means of a "HTTP Request Reflector". Although there are better approaches to reflect against an object tree (e.g. the object dumper in the LINQ CTPs - which does a similar thing compared to my DumpObject method), the result looks pretty good (click to enlarge):

Line 15 in the code snippet above is a pretty interesting one since it's crucial to the whole sample. What it does is the registration of a so-called prefix with the http.sys infrastructure. Essentially, this tells the kernel mode listener to forward all requests that match the specified pattern (http://*:8080/) to this listener. Other patterns that you could use include a hostname (http://bart-pc:8080/), the https protocol instead of http, other port numbers of course, virtual directories (try to substitute http://*:8080/ in the sample above with http://*:8080/reflector/ and make a request to the original url and the new url including the "vdir"), FQDNs, etc. Notice the prefixes should end with /.

Some nice exercises:

  • Combine this with the goodness of Cassini (http://blogs.msdn.com/dmitryr/default.aspx search for Cassini).
  • Make a proxy server (and avoid endless request loops to the same proxy over and over again - the IE proxy setting is used by some BCL classes such as WebRequest)

A few notes

If you receive an "Access is denied" HttpListenerException message, check that your command prompt (or whatever shell) is elevated as Administrator (for Vista users):

On Windows Vista, use netsh to inspect the registered http.sys listeners. An approach is shown below:

Windows Server 2003 users can use the httpcfg.exe tool.

Enjoy (Cache-Control = public)!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under:

Comments

# Bock auf einen eigenen Webserver?

Wednesday, February 28, 2007 8:09 AM by TheUndeadable entwickelt

Ein Zwanzigzeiler reicht: http.sys macht's möglich.

# Bock auf einen eigenen Webserver?

Wednesday, February 28, 2007 8:09 AM by TheUndeadable entwickelt

Ein Zwanzigzeiler reicht: http.sys macht's möglich.

# How to create a simple proxy in C#? ??? HTMLCoderHelper.com

Pingback from  How to create a simple proxy in C#? ??? HTMLCoderHelper.com

# A Minimal Http Server in C# &laquo; Angel &#8220;Java&#8221; Lopez on Blog

Pingback from  A Minimal Http Server in C# &laquo; Angel &#8220;Java&#8221; Lopez on Blog

# Un Servidor Http Mínimo en C#

Saturday, February 05, 2011 11:16 AM by Angel "Java" Lopez

Hace dos meses, escribí un post implementando un servidor HTTP mínimo en Java: Un Servidor Http Mínimo

# How to create a simple proxy in C#? | Question and answer

Wednesday, August 01, 2012 2:11 AM by How to create a simple proxy in C#? | Question and answer

Pingback from  How to create a simple proxy in C#? | Question and answer

# How to create a simple proxy in C#? | Question and answer

Pingback from  How to create a simple proxy in C#? | Question and answer

# HttpListener for dummies: a simple &quot;HTTP Request Reflector&quot; - B# .NET Blog | ASP.NET samples | Scoop.it

Pingback from  HttpListener for dummies: a simple &quot;HTTP Request Reflector&quot; - B# .NET Blog | ASP.NET samples | Scoop.it

# Simple HTTP Listener example &laquo; Mike&#039;s Dev Blog

Friday, January 18, 2013 6:17 AM by Simple HTTP Listener example « Mike's Dev Blog

Pingback from  Simple HTTP Listener example &laquo; Mike&#039;s Dev Blog

# How to create a simple proxy in C#? - Tech Forum Network

Pingback from  How to create a simple proxy in C#? - Tech Forum Network

# How to create a simple proxy in C#? | Ask &amp; Answers

Tuesday, October 29, 2013 4:18 AM by How to create a simple proxy in C#? | Ask & Answers

Pingback from  How to create a simple proxy in C#? | Ask &amp; Answers

# How to create a simple proxy in C#? | Technology &amp; Programming

Pingback from  How to create a simple proxy in C#? | Technology &amp; Programming