Skip to content

Instantly share code, notes, and snippets.

Embed
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();
}
}
@aksakalli

This comment has been minimized.

Copy link
Owner Author

@aksakalli 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

@ddoubleday

This comment has been minimized.

Copy link

@ddoubleday ddoubleday commented Oct 2, 2015

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

This comment has been minimized.

Copy link

@Sotam 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.

@nobodyguy

This comment has been minimized.

Copy link

@nobodyguy nobodyguy commented Jan 6, 2016

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."?

@raffomania

This comment has been minimized.

Copy link

@raffomania raffomania commented Feb 24, 2016

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...

@aksakalli

This comment has been minimized.

Copy link
Owner Author

@aksakalli 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?

@dacrovinunghi

This comment has been minimized.

Copy link

@dacrovinunghi dacrovinunghi commented Mar 30, 2016

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

@dacrovinunghi

This comment has been minimized.

Copy link

@dacrovinunghi dacrovinunghi commented Mar 30, 2016

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

@DanijelBumbar

This comment has been minimized.

Copy link

@DanijelBumbar DanijelBumbar commented Apr 3, 2016

nice, thanks

@Hulubulu123

This comment has been minimized.

Copy link

@Hulubulu123 Hulubulu123 commented Jun 4, 2016

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!

@Anime4000

This comment has been minimized.

Copy link

@Anime4000 Anime4000 commented Jun 25, 2016

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

@mdawsonuk

This comment has been minimized.

Copy link

@mdawsonuk mdawsonuk commented Jul 6, 2016

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??

@SnelleJelle

This comment has been minimized.

Copy link

@SnelleJelle SnelleJelle commented Sep 17, 2016

Great snippet!

@hadzim

This comment has been minimized.

Copy link

@hadzim 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

This comment has been minimized.

Copy link

@Joohae 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);
}
@nij4t

This comment has been minimized.

Copy link

@nij4t nij4t commented Feb 2, 2017

@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

This comment has been minimized.

Copy link

@wqweto 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.

@Geograph-us

This comment has been minimized.

Copy link

@Geograph-us Geograph-us commented Apr 15, 2017

What about HTTPS-support?

@zezba9000

This comment has been minimized.

Copy link

@zezba9000 zezba9000 commented Jun 18, 2017

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

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

@Euler29

This comment has been minimized.

Copy link

@Euler29 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

This comment has been minimized.

Copy link

@muzack2 muzack2 commented Jul 7, 2017

@Euler29, same problem, tried everything,

Anyone please?

@therealpappy

This comment has been minimized.

Copy link

@therealpappy therealpappy commented Jul 13, 2017

@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

This comment has been minimized.

Copy link

@San3131 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

This comment has been minimized.

Copy link

@hunterbeanland 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

This comment has been minimized.

Copy link

@roschler 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.

@VBWebprofi

This comment has been minimized.

Copy link

@VBWebprofi VBWebprofi commented Feb 9, 2018

It's a nice implementation, one point I would generally change - getting of the MIME-type. This should be done each time or on start at/from Registry. Extensions there registered and MIME type is available most times from value Content Type (with space between both words). There you can add also other MIME types or provide a static functionality at your class to add new custom MIME types to this you find on the Registry. A dynamic solution would be to lookup at Registry on demand and store found in static dictionary. All other with no registered MIME type should become application/octet-stream.

@movAX13h

This comment has been minimized.

Copy link

@movAX13h movAX13h commented Jun 2, 2018

if you want to make sure videos (larger files) are working properly:
context.Response.SendChunked = input.Length > 1024 * 16;

@movAX13h

This comment has been minimized.

Copy link

@movAX13h movAX13h commented Jun 8, 2018

Note that this solution does not work if your website needs more than 1 concurrent request like it does if it streams a video and the user clicks a link for example. The request following the click will not be processed because the server is busy serving the video. To solve this problem it is necessary to process requests decoupled from the listener thread. Here it gets quite complicated. It has been done, for example: https://github.com/JamesDunne/aardwolf

A very comfortable alternative solution (if you dont mind >200 dlls) which works in all cases is using Microsoft.AspNetCore (does not require IIS): https://www.meziantou.net/2017/05/02/starting-a-http-file-server-from-the-file-explorer-using-net-core

@ibrahimKafein

This comment has been minimized.

Copy link

@ibrahimKafein ibrahimKafein commented Jun 11, 2018

Its simple and clear. Thanks... But how to resize request header length? How can I increase?

@ricardo85x

This comment has been minimized.

Copy link

@ricardo85x ricardo85x commented Jun 28, 2018

Is there a way to run this without administrator privileges?

@xdvarpunen

This comment has been minimized.

Copy link

@xdvarpunen xdvarpunen commented Jul 11, 2018

@ricardo85x Use these links:

@rodrigovitachi

This comment has been minimized.

Copy link

@rodrigovitachi rodrigovitachi commented Jul 14, 2018

Any idea why JavaScript files are not working? Trying to load a html template with JQuery and other simple JS files, but they won't run.

@george-lemish

This comment has been minimized.

Copy link

@george-lemish george-lemish commented Jul 21, 2018

What about https support?

@smchughinfo

This comment has been minimized.

Copy link

@smchughinfo smchughinfo commented Oct 30, 2018

This works for what I need. Thanks a lot.

@ruicaramalho

This comment has been minimized.

Copy link

@ruicaramalho ruicaramalho commented Nov 10, 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.

Just do http add urlacl url=http://+:80/ sddl=D:(A;;GX;;;S-1-1-0)

sddl=D:(A;;GX;;;S-1-1-0) = "everyone" in all languages

@sjml

This comment has been minimized.

Copy link

@sjml sjml commented Jan 29, 2019

I was trying this and noticed it was having trouble serving up multiple requests at a time. Turns out this line was throwing exceptions occasionally for modifying the status code after headers were sent. I moved it up before anything was send to the output stream and things started working more smoothly, but I wonder why it works sometimes if that's really as problematic as it looks.

@atuladhar

This comment has been minimized.

Copy link

@atuladhar atuladhar commented Feb 22, 2019

Hi, after creating a local server using the code above, I need to open a local file from that server. I have used the following code:

string myFolder = "D:\flower.jpg";
SimpleHTTPServer myServer = new SimpleHTTPServer(myFolder, 8084);

Now, when I try to access the link: http://localhost:8084/, the page isn't loaded. What needs to be done so that I can access the file in my path from localhost?

@prakis

This comment has been minimized.

Copy link

@prakis prakis commented Feb 28, 2019

string myFolder = "D:\flower.jpg";
SimpleHTTPServer myServer = new SimpleHTTPServer(myFolder, 8084);

Your 'myFolder' variable is a file('..flower.jpg') not a folder.

@kartoonist435

This comment has been minimized.

Copy link

@kartoonist435 kartoonist435 commented Mar 11, 2019

Hi all I'm using
public void StartServer() { myServer = new SimpleHTTPServer(Path.Combine(Application.streamingAssetsPath, "PDF")); Application.OpenURL("http:/localhost:" + myServer.Port + "/" + FirstIndexPath); }
from this link https://answers.unity.com/questions/1245582/create-a-simple-https-server-on-the-streaming-asset.html

and I can't get anything to work. I keep getting localhost page can't be found. I've tried using html files pdfs and jpegs and nothing works. I tried using a folder on the my android to house the pdfs at file:/// thinking it was a streaming asset Unity issue but that didn't help either. Any help would be appreciated.

@ulasaksan

This comment has been minimized.

Copy link

@ulasaksan ulasaksan commented Mar 12, 2019

Hello, while using with .mp4 or .mpeg , video link starts to download mode, but i want it not download in browser, want it stream, can you show how to do it?

@Reza805

This comment has been minimized.

Copy link

@Reza805 Reza805 commented Apr 13, 2019

hi , how to get list of files in the directory?

@aksakalli

This comment has been minimized.

Copy link
Owner Author

@aksakalli aksakalli commented Apr 24, 2019

@Reza805

for listing the files in the current directory as json, you can add a code block after line 159 like:

if (filename=="getfiles"){
  files = System.IO.Directory.GetFiles(_rootDirectory, @"*", SearchOption.TopDirectoryOnly);
  var serializer = new JavaScriptSerializer();
  var jsonContent = serializer.Serialize(files);
  context.Response.Write(jsonContent);
  context.Response.StatusCode = (int)HttpStatusCode.OK;
  return;
}

also add this to the top

using System.Web.Script.Serialization;

Ps. I didn't run this snippet but it should work.

@syuki3

This comment has been minimized.

Copy link

@syuki3 syuki3 commented May 15, 2019

@aksakalli Thanks for the project. I just wanted to report a potential bug and ask if anybody has encountered it. I've found that the HTTPServer cannot seem to host files that are "Read only".

@mikybart

This comment has been minimized.

Copy link

@mikybart mikybart commented May 21, 2019

I have tested your Http Server, why to run my application I have to leave it as an administrator?

@mikybart

This comment has been minimized.

Copy link

@mikybart mikybart commented May 21, 2019

I asked the wrong question:

why must run my application with your http server only as an administrator?

@alxbrn

This comment has been minimized.

Copy link

@alxbrn alxbrn commented Jun 5, 2019

I know this is a very old gist and might not be active anymore, but does anyone know how to serve mp4 media files? Not to download but to play within the browser window.

{".mp4","video/mp4"},

I added this to the MIME type list to support mp4 files, but when i then request a file with mp4 format i get a GET http://localhost:8084/video.mp4 500 (Internal Server Error) with no further information of what the issue is.

My updated process method

private void Process(HttpListenerContext context)
        {
            string filename = WebUtility.UrlDecode(context.Request.Url.AbsolutePath.Substring(1));
            Console.WriteLine(filename);

            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);

                    context.Response.ContentType = _mimeTypeMappings.TryGetValue(Path.GetExtension(filename), out string mime) ? mime : "application/octet-stream";
                    context.Response.ContentLength64 = input.Length;

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

                    context.Response.StatusCode = (int)HttpStatusCode.OK;
                    context.Response.OutputStream.Flush();
                }
                catch
                {
                    context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                }
            }
            else
            {
                context.Response.StatusCode = (int)HttpStatusCode.NotFound;
            }

            context.Response.OutputStream.Close();
        }
if (filename=="getfiles"){
  files = System.IO.Directory.GetFiles(_rootDirectory, @"*", SearchOption.TopDirectoryOnly);
  var serializer = new JavaScriptSerializer();
  var jsonContent = serializer.Serialize(files);
  context.Response.Write(jsonContent);
  context.Response.StatusCode = (int)HttpStatusCode.OK;
  return;
}

Ps. I didn't run this snippet but it should work.

This snippet does not work, HTTPListenerResponse does not contain a method called Write, 'files' has no variable declared, and consider using JsonConvert.Serialize(files) instead of JavaScripSerializer as it's not included in the latest version .NET framework.

My version of get all files (somewhat the same).

if (filename == "*")
            {
                try
                {
                    string responseString = JsonConvert.SerializeObject(Directory.GetFiles(_rootDirectory, @"*.*", SearchOption.AllDirectories));
                    byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
                    context.Response.ContentLength64 = buffer.Length;
                    Stream output = context.Response.OutputStream;
                    output.Write(buffer, 0, buffer.Length);

                    context.Response.StatusCode = (int)HttpStatusCode.OK;
                    context.Response.OutputStream.Flush();
                }
                catch
                {
                    context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                }
            }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.