Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
SimpleHTTPServer in C#
// MIT License - Copyright (c) 2016 Can Güney Aksakalli
// https://aksakalli.github.io/2014/02/24/simple-http-server-with-csparp.html
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Threading;
using System.Diagnostics;
class SimpleHTTPServer
{
private readonly string[] _indexFiles = {
"index.html",
"index.htm",
"default.html",
"default.htm"
};
private static IDictionary<string, string> _mimeTypeMappings = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase) {
#region extension to MIME type list
{".asf", "video/x-ms-asf"},
{".asx", "video/x-ms-asf"},
{".avi", "video/x-msvideo"},
{".bin", "application/octet-stream"},
{".cco", "application/x-cocoa"},
{".crt", "application/x-x509-ca-cert"},
{".css", "text/css"},
{".deb", "application/octet-stream"},
{".der", "application/x-x509-ca-cert"},
{".dll", "application/octet-stream"},
{".dmg", "application/octet-stream"},
{".ear", "application/java-archive"},
{".eot", "application/octet-stream"},
{".exe", "application/octet-stream"},
{".flv", "video/x-flv"},
{".gif", "image/gif"},
{".hqx", "application/mac-binhex40"},
{".htc", "text/x-component"},
{".htm", "text/html"},
{".html", "text/html"},
{".ico", "image/x-icon"},
{".img", "application/octet-stream"},
{".iso", "application/octet-stream"},
{".jar", "application/java-archive"},
{".jardiff", "application/x-java-archive-diff"},
{".jng", "image/x-jng"},
{".jnlp", "application/x-java-jnlp-file"},
{".jpeg", "image/jpeg"},
{".jpg", "image/jpeg"},
{".js", "application/x-javascript"},
{".mml", "text/mathml"},
{".mng", "video/x-mng"},
{".mov", "video/quicktime"},
{".mp3", "audio/mpeg"},
{".mpeg", "video/mpeg"},
{".mpg", "video/mpeg"},
{".msi", "application/octet-stream"},
{".msm", "application/octet-stream"},
{".msp", "application/octet-stream"},
{".pdb", "application/x-pilot"},
{".pdf", "application/pdf"},
{".pem", "application/x-x509-ca-cert"},
{".pl", "application/x-perl"},
{".pm", "application/x-perl"},
{".png", "image/png"},
{".prc", "application/x-pilot"},
{".ra", "audio/x-realaudio"},
{".rar", "application/x-rar-compressed"},
{".rpm", "application/x-redhat-package-manager"},
{".rss", "text/xml"},
{".run", "application/x-makeself"},
{".sea", "application/x-sea"},
{".shtml", "text/html"},
{".sit", "application/x-stuffit"},
{".swf", "application/x-shockwave-flash"},
{".tcl", "application/x-tcl"},
{".tk", "application/x-tcl"},
{".txt", "text/plain"},
{".war", "application/java-archive"},
{".wbmp", "image/vnd.wap.wbmp"},
{".wmv", "video/x-ms-wmv"},
{".xml", "text/xml"},
{".xpi", "application/x-xpinstall"},
{".zip", "application/zip"},
#endregion
};
private Thread _serverThread;
private string _rootDirectory;
private HttpListener _listener;
private int _port;
public int Port
{
get { return _port; }
private set { }
}
/// <summary>
/// Construct server with given port.
/// </summary>
/// <param name="path">Directory path to serve.</param>
/// <param name="port">Port of the server.</param>
public SimpleHTTPServer(string path, int port)
{
this.Initialize(path, port);
}
/// <summary>
/// Construct server with suitable port.
/// </summary>
/// <param name="path">Directory path to serve.</param>
public SimpleHTTPServer(string path)
{
//get an empty port
TcpListener l = new TcpListener(IPAddress.Loopback, 0);
l.Start();
int port = ((IPEndPoint)l.LocalEndpoint).Port;
l.Stop();
this.Initialize(path, port);
}
/// <summary>
/// Stop server and dispose all functions.
/// </summary>
public void Stop()
{
_serverThread.Abort();
_listener.Stop();
}
private void Listen()
{
_listener = new HttpListener();
_listener.Prefixes.Add("http://*:" + _port.ToString() + "/");
_listener.Start();
while (true)
{
try
{
HttpListenerContext context = _listener.GetContext();
Process(context);
}
catch (Exception ex)
{
}
}
}
private void Process(HttpListenerContext context)
{
string filename = context.Request.Url.AbsolutePath;
Console.WriteLine(filename);
filename = filename.Substring(1);
if (string.IsNullOrEmpty(filename))
{
foreach (string indexFile in _indexFiles)
{
if (File.Exists(Path.Combine(_rootDirectory, indexFile)))
{
filename = indexFile;
break;
}
}
}
filename = Path.Combine(_rootDirectory, filename);
if (File.Exists(filename))
{
try
{
Stream input = new FileStream(filename, FileMode.Open);
//Adding permanent http response headers
string mime;
context.Response.ContentType = _mimeTypeMappings.TryGetValue(Path.GetExtension(filename), out mime) ? mime : "application/octet-stream";
context.Response.ContentLength64 = input.Length;
context.Response.AddHeader("Date", DateTime.Now.ToString("r"));
context.Response.AddHeader("Last-Modified", System.IO.File.GetLastWriteTime(filename).ToString("r"));
byte[] buffer = new byte[1024 * 16];
int nbytes;
while ((nbytes = input.Read(buffer, 0, buffer.Length)) > 0)
context.Response.OutputStream.Write(buffer, 0, nbytes);
input.Close();
context.Response.StatusCode = (int)HttpStatusCode.OK;
context.Response.OutputStream.Flush();
}
catch (Exception ex)
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
}
}
else
{
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
}
context.Response.OutputStream.Close();
}
private void Initialize(string path, int port)
{
this._rootDirectory = path;
this._port = port;
_serverThread = new Thread(this.Listen);
_serverThread.Start();
}
}
Owner

aksakalli commented Feb 24, 2014

If you need to run a quick web server and you don't want to mess with setting up IIS or something then this simple class allows you to serve your static files on .Net Client Profile. It is a simple class that uses HttpListener Class and acts like SimpleHTTPServer of Python. It serves given path on specified port or auto assigned empty port.

Create server with auto assigned port:

string myFolder = @"C:\folderpath\to\serve";

//create server with auto assigned port
SimpleHTTPServer myServer = new SimpleHTTPServer(myFolder);

Create server with specified port:

//create server with given port
SimpleHTTPServer myServer = new SimpleHTTPServer(myFolder, 8084);

Now it is running.

Console.WriteLine("Server is running on this port: " + myServer.Port.ToString());

Stop method should be called before exit.

//stop server
myServer.Stop();

Check the blog post for detailed explanation

Nice. One thing I noticed: you have to set the OK status for the response before you flush the output stream or you get a server internal error. (Line 192)

Sotam commented Nov 5, 2015

Thanks for the awesome code! I've changed a single line, otherwise I couldn't stop the application/thread.

Find this:

_serverThread = new Thread(this.Listen);
_serverThread.Start();

And add between:

_serverThread.IsBackground = true;

Not really sure if thats necessary, however it fixed my problem of not being able to close the application.

Cool, thanks a lot! How can I wait until HTTPListener is really started to show MessageBox with text like "HTTP server was succesfully started on port 80."?

I sometimes get the following error:

System.InvalidOperationException: Cannot be changed after headers are sent.
  at System.Net.HttpListenerResponse.set_StatusCode (Int32 value)

Moving the line

    context.Response.StatusCode = (int)HttpStatusCode.OK;

to the top when the headers are set fixes this, but I'm not sure if that's the right way...

Owner

aksakalli commented Mar 5, 2016

@ddoubleday , @raffomania you are right, I updated it. Thanks for your feedback.

@nobodyguy it is a synchronous method, you can show the port in the second line after SimpleHTTPServer myServer = new SimpleHTTPServer(myFolder, 8084);

@Sotam have you tried to call myServer.Stop(); before closing your app?

should use "using" to close file stream if execption occurs.

not a big deal just : using(Stream input = new FileStream(filename, FileMode.Open)), will call dispose method

nice, thanks

I'm getting an error: An unhandled exception of type 'System.Net.HttpListenerException' occurred in System.dll Access is denied

Please help!
P.S Otherwise great class and I look forward to using it in my application!

This need to be run under administrator to open a localhost port

I have a HTML page where we need to access a .xml and a .json file. However, they aren't loaded by the web server. Any ideas on why this isn't working??

Great snippet!

hadzim commented Oct 21, 2016

Take care of Path.Combine, if the second parameter is absolute path, it returns second parameter. So when I put something like: http://localhost/C:/somefile.txt this implementation streams content of C:/somefile.txt without taking care, if this path is under specified directory or not!!!

Joohae commented Nov 2, 2016

Thank you for sharing the code, it helps me a lot.

I got ProtocolViolationException on context.Response.OutputStream.Write(buffer, 0, nbytes); when I tried to download files with old version of wget.

I found following article:
ProtocolViolationException when writing on response NetworkStream in c#

According to the article, we could get rid of the exception by not to send content when client requested header.
I have updated line 187 ~ 190 as followed:

if (context.Request.HttpMethod != "HEAD") 
{
   byte[] buffer = new byte[1024 * 16];
   int nbytes;
   while ((nbytes = input.Read(buffer, 0, buffer.Length)) > 0)
       context.Response.OutputStream.Write(buffer, 0, nbytes);
}

@Hulubulu123, find line with _listener.Prefixes.Add("http://*:" + _port.ToString() + "/");
and change it to _listener.Prefixes.Add("http://127.0.0.1:" + _port.ToString() + "/");

wqweto commented Feb 12, 2017

https://gist.github.com/wqweto/f698cbe9ac8bfdbb3f300b0c4563f7e2 - the same with regex routing implementation in 174 LOC of C#. Includes directory browsing "middleware" as a sample extension method.

What about HTTPS-support?

zezba9000 commented Jun 18, 2017

Fixed memory leaks, Mono / Linux / macOS support issues and threading bugs.

https://gist.github.com/zezba9000/04054e3128e6af413e5bc8002489b2fe

Euler29 commented Jun 27, 2017

An unhandled exception of type 'System.Net.HttpListenerException' occurred in System.dll

Additional information:Access Denied.

I tried it run as be Administrator.I gave Safety Wall Permissions.But it didn't work.

muzack2 commented Jul 7, 2017

@Euler29, same problem, tried everything,

Anyone please?

@Euler29 @muzack2

what you need to do is run your command prompt as an administrator. Then, use netsh http add urlacl url=http://+:80/ user=DOMAIN\user
Substitute 80 to whatever port you are using, DOMAIN to the name of your computer, and the second user to your windows user name.

Also change the asterisk on Line 138 to a + sign. You should be able to now run the server w/o an exception then go to localhost:port/path in your browser and it will work.

San3131 commented Sep 15, 2017

I would like to run my server in 10.10.10.10 on default port 80 (10.10.10.10) or (10.10.10.10:80). But this line is throwing an exception. httpListener.Prefixes.Add("http://10.10.10.10:" + "/"); How do I handle this.Also how do i connect with my server from my mobile at same time..

hunterbeanland commented Nov 16, 2017

Fixes needed:
Add {".mp4", "video/mpeg"}, to the MIME list.
To serve file with spaces, in Proceess, decode the URL: System.Web.HttpUtility.UrlDecode(filename.Substring(1));
You must use _listener.Prefixes.Add("http://127.0.0.1: or localhost to avoid Access Denied issues.

roschler commented Jan 3, 2018

If you don't know what to use for the "user=DOMAIN\user" parameter shown in the netsh command given by @therealpappy, you can use "everyone" instead:

netsh http add urlacl url=http://+:80/ user=everyone

Note, everyone is language dependent so Spanish is "todos", German is 'alles", etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment