Skip to content

Instantly share code, notes, and snippets.

@define-private-public
Last active March 9, 2025 22:07
Show Gist options
  • Save define-private-public/d05bc52dd0bed1c4699d49e2737e80e7 to your computer and use it in GitHub Desktop.
Save define-private-public/d05bc52dd0bed1c4699d49e2737e80e7 to your computer and use it in GitHub Desktop.
A Simple HTTP server in C#
// Filename: HttpServer.cs
// Author: Benjamin N. Summerton <define-private-public>
// License: Unlicense (http://unlicense.org/)
using System;
using System.IO;
using System.Text;
using System.Net;
using System.Threading.Tasks;
namespace HttpListenerExample
{
class HttpServer
{
public static HttpListener listener;
public static string url = "http://localhost:8000/";
public static int pageViews = 0;
public static int requestCount = 0;
public static string pageData =
"<!DOCTYPE>" +
"<html>" +
" <head>" +
" <title>HttpListener Example</title>" +
" </head>" +
" <body>" +
" <p>Page Views: {0}</p>" +
" <form method=\"post\" action=\"shutdown\">" +
" <input type=\"submit\" value=\"Shutdown\" {1}>" +
" </form>" +
" </body>" +
"</html>";
public static async Task HandleIncomingConnections()
{
bool runServer = true;
// While a user hasn't visited the `shutdown` url, keep on handling requests
while (runServer)
{
// Will wait here until we hear from a connection
HttpListenerContext ctx = await listener.GetContextAsync();
// Peel out the requests and response objects
HttpListenerRequest req = ctx.Request;
HttpListenerResponse resp = ctx.Response;
// Print out some info about the request
Console.WriteLine("Request #: {0}", ++requestCount);
Console.WriteLine(req.Url.ToString());
Console.WriteLine(req.HttpMethod);
Console.WriteLine(req.UserHostName);
Console.WriteLine(req.UserAgent);
Console.WriteLine();
// If `shutdown` url requested w/ POST, then shutdown the server after serving the page
if ((req.HttpMethod == "POST") && (req.Url.AbsolutePath == "/shutdown"))
{
Console.WriteLine("Shutdown requested");
runServer = false;
}
// Make sure we don't increment the page views counter if `favicon.ico` is requested
if (req.Url.AbsolutePath != "/favicon.ico")
pageViews += 1;
// Write the response info
string disableSubmit = !runServer ? "disabled" : "";
byte[] data = Encoding.UTF8.GetBytes(String.Format(pageData, pageViews, disableSubmit));
resp.ContentType = "text/html";
resp.ContentEncoding = Encoding.UTF8;
resp.ContentLength64 = data.LongLength;
// Write out to the response stream (asynchronously), then close it
await resp.OutputStream.WriteAsync(data, 0, data.Length);
resp.Close();
}
}
public static void Main(string[] args)
{
// Create a Http server and start listening for incoming connections
listener = new HttpListener();
listener.Prefixes.Add(url);
listener.Start();
Console.WriteLine("Listening for connections on {0}", url);
// Handle requests
Task listenTask = HandleIncomingConnections();
listenTask.GetAwaiter().GetResult();
// Close the listener
listener.Close();
}
}
}
@skrepecki
Copy link

hello world

thanks a lot !! ;)

@rzymski
Copy link

rzymski commented Aug 3, 2022

That's what I was looking for. Thank you very much. Good job.

@AlexanderHS
Copy link

Been fooling around with this and noticed some unanswered questions above. Can only tell you what worked for me.

"I want to send a json string to the http server. How can I get the message from the request?"

I went with something like:

string payload = GetRequestPostData(ctx.Request);
Where you define that function like:

string GetRequestPostData(HttpListenerRequest request)
{
    if (!request.HasEntityBody)
    {
        return null;
    }
    using (System.IO.Stream body = request.InputStream)
    {
        using (var reader = new System.IO.StreamReader(body, request.ContentEncoding))
        {
            return reader.ReadToEnd();
        }
    }
}

doesn't work with other Networks on the same Network for example a Phone.
I tried changing "url" to the local IP Address of the Machine Running the code, but this doesn't work for neither my Phone or my PC.
Does anyone have a Solution to this?

change localhost to something like + or *
e.g.
string url = $"http://+:8000/";
Your OS might complain about permissions though.

If so you can try something like: "netsh http add urlacl url=http://+:8000/ user=your_username_here"

@nmnmrm
Copy link

nmnmrm commented Jul 24, 2024

how do i encode an image to bytes?

@Elias-Traunbauer
Copy link

@Tomloyo thats not the right place to ask xD
but since im already here, let me help you.

First you need to load the image into a bitmap.
When you did that, there is a function called LockBits
this returns a pointer to the start of the array.

Then you can just copy all the image bytes into a seperate byte[] you created.

@veso266
Copy link

veso266 commented Oct 5, 2024

How could I make this work with SSL Encryption, essentialy have this function as https server

I tried to take TLS part from Kestrel (its to big, I don't need the whole framework), because I need to perform secure connection with OpenSSL (my os doesn't have the newest ciphers uvailable): https://github.com/tmds/KestrelHttpServer/tree/ab78fb0b8ca4f03eb53795f9a97c8152b045ef6b/src/Kestrel.Tls

Here is what I tried to take out: https://github.com/user-attachments/files/17267580/Kestrel.Tls.zip

and this is how I tried using it

using System;
using System.IO;
using System.Text;
using System.Net;
using System.Threading.Tasks;
using Kestrel.Tls;

namespace HttpListenerExample
{
    class HttpServer
    {
        public static HttpListener listener;
        public static string url = "https://192.168.88.12:443/"; // Change to HTTPS and appropriate port
        public static int pageViews = 0;
        public static int requestCount = 0;

        public static string certificatePath = "my-cert.pem";
        public static string privateKeyPath = "my-key.pem";

        public static string pageData =
            "<!DOCTYPE>" +
            "<html>" +
            "  <head>" +
            "    <title>HttpListener Example</title>" +
            "  </head>" +
            "  <body>" +
            "    <p>Page Views: {0}</p>" +
            "    <form method=\"post\" action=\"shutdown\">" +
            "      <input type=\"submit\" value=\"Shutdown\" {1}>" +
            "    </form>" +
            "  </body>" +
            "</html>";

        public static async Task HandleIncomingConnections()
        {
            bool runServer = true;

            while (runServer)
            {
                // Wait for an incoming connection
                HttpListenerContext ctx = await listener.GetContextAsync();
                Stream innerStream = ctx.Response.OutputStream;

                // Wrap the inner stream with TlsStream
                using (var tlsStream = new TlsStream(innerStream, certificatePath, privateKeyPath, new[] { "http/1.1" }))
                {
                    // Perform the TLS handshake
                    await tlsStream.DoHandshakeAsync();

                    // Handle request
                    HttpListenerRequest req = ctx.Request;
                    HttpListenerResponse resp = ctx.Response;

                    // Print out request info
                    Console.WriteLine("Request #: {0}", ++requestCount);
                    Console.WriteLine(req.Url.ToString());
                    Console.WriteLine(req.HttpMethod);
                    Console.WriteLine(req.UserHostName);
                    Console.WriteLine(req.UserAgent);
                    Console.WriteLine();

                    // Handle shutdown request
                    if ((req.HttpMethod == "POST") && (req.Url.AbsolutePath == "/shutdown"))
                    {
                        Console.WriteLine("Shutdown requested");
                        runServer = false;
                    }

                    // Increment page views, ignoring favicon requests
                    if (req.Url.AbsolutePath != "/favicon.ico")
                        pageViews += 1;

                    // Write the response info
                    string disableSubmit = !runServer ? "disabled" : "";
                    byte[] data = Encoding.UTF8.GetBytes(String.Format(pageData, pageViews, disableSubmit));
                    resp.ContentType = "text/html";
                    resp.ContentEncoding = Encoding.UTF8;
                    resp.ContentLength64 = data.LongLength;

                    // Write out to the response stream
                    await tlsStream.WriteAsync(data, 0, data.Length, default);
                }
            }
        }

        public static void Main(string[] args)
        {
            listener = new HttpListener();
            listener.Prefixes.Add(url);
            listener.Start();
            Console.WriteLine("Listening for connections on {0}", url);

            // Handle requests
            Task listenTask = HandleIncomingConnections();
            listenTask.GetAwaiter().GetResult();

            // Close the listener
            listener.Close();
        }
    }
}

But using firefox, I get PR_CONNECT_RESET_ERROR
and when I tried to connect using OpenSSL like this

openssl s_client -connect domain.local:443

I get this as error

Connecting to 192.168.88.12
CONNECTED(00000128)
write:errno=10054
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 327 bytes
Verification: OK
---
New, (NONE), Cipher is (NONE)
This TLS version forbids renegotiation.
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---

So not sure how to fix my problem
I know my certificates are valid, also I do have libssl-1_1.dll and libcrypto-1_1.dll in my bin directory

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