I converted a synchronous HTTP Server (using .NET’s HttpListener) to asynchronous following this example. It works, but for every request I issue from Google Chrome, be it hitting enter at the address bar or pressing F5, I get two requests. The first one is perfectly handled, and the response is rendered fine in the browser.
However, the second request, which shouldn’t even appear, has an empty query string (my server is a console application so I see all this happening using Console.WriteLine).
None of this happened when the server was synchronous, so I must have messed something up when moving to the async model.
Here’s the code:
using System;
using System.Diagnostics;
using System.Net;
namespace MyWebServer{
internal static class MyHTTPServer {
private static int mPortNumber = 7091;
private static HttpListener mListener;
private static MyCustomHttpHandler mHttpHandler;
//Omitted some auxiliary methods to print stuff to Console,
//like WriteError and WriteRequestHeaderInformation
public static void Main( String[] pArg ) {
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper;
HttpHandler = new MyCustomHttpHandler();
mPortNumber = Convert.ToInt32( pArg[0] );
Console.WriteLine( "Starting the HttpListener on port:{0}", mPortNumber );
InitialiseListener();
Console.WriteLine( "Listener started on port: {0}, waiting for requests.", mPortNumber );
Console.WriteLine( "Listening. Press Enter to stop." );
Console.ReadLine();
if( mListener.IsListening )
mListener.Stop();
}
private static void InitialiseListener() {
try {
mListener = new HttpListener {
AuthenticationSchemes = AuthenticationSchemes.Basic
};
string prefix = string.Format( "http://+:{0}/", mPortNumber );
mListener.Prefixes.Add( prefix );
mListener.AuthenticationSchemes = AuthenticationSchemes.Anonymous;
mListener.Start();
mListener.BeginGetContext( RequestReceived, null );
} catch( Exception ex ) {
WriteError( ex.Message );
}
}
private static void RequestReceived( IAsyncResult result ) {
var timer = new Stopwatch();
Console.WriteLine( "---Request Received----" );
timer.Reset();
timer.Start();
//Retrieve context; on error, print message and wait for another request
HttpListenerContext context = null;
try {
context = mListener.EndGetContext( result );
} catch( HttpListenerException e ) {
WriteError( e.ToString() );
if( mListener.IsListening )
mListener.BeginGetContext( RequestReceived, null );
return;
}
//Process request and send response
mListener.BeginGetContext( RequestReceived, null );
try {
WriteRequestHeaderInformation( context );
CreateResponseDocument( context );
} catch( Exception ex ) {
WriteError( ex.Message );
}
Console.WriteLine( "----Request processed in {0} milliseconds ----", timer.ElapsedMilliseconds );
}
private static void CreateResponseDocument( HttpListenerContext pHttpListenerContext ) {
try {
byte[] htmlOutput = HttpHandler.ProcessRequest( pHttpListenerContext.Request.Url.LocalPath.Replace( "/", "" ), pHttpListenerContext.Request.Url.Query.Replace( "?", "" ) );
if( htmlOutput != null && pHttpListenerContext.Response.OutputStream.CanWrite ) {
pHttpListenerContext.Response.Headers.Add( "Access-Control-Allow-Origin", "*" );
//pHttpListenerContext.Response.ContentType = "image/jpg";
pHttpListenerContext.Response.ContentType = "text/plain";
pHttpListenerContext.Response.OutputStream.Write( htmlOutput, 0, htmlOutput.Length );
}
pHttpListenerContext.Response.Close();
} catch( Exception ex ) {
WriteError( ex.Message );
}
}
}
}
It seems that this happens only when the response is just a bunch of bytes (representing a jpeg image in this case). If instead of entering the http request directly in the address bar I open an HTML page like this…
… one and only one request is received by my server. (I anonymized my port and parameters; where it says queryParametersGoHere, I actually had
?param1=value1¶m2=value2, etc.