-
-
Save anonymous/bfe618a877fc658acbab to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using HttpMachine; | |
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Net; | |
using System.Net.Sockets; | |
using System.Text; | |
using System.Threading.Tasks; | |
namespace Foo { | |
public sealed class Client { | |
internal Stream Stream { get; set; } | |
} | |
public enum HttpMethod { | |
Get, | |
Post, | |
Put, | |
Delete, | |
Head, | |
} | |
public delegate Task<HttpResponse> RequestHandler(HttpRequest request); | |
public sealed class HttpRequest { | |
public HttpMethod Method { get; set; } | |
public string Path { get; set; } | |
public string QueryString { get; set; } | |
public IDictionary<string, string> Headers { get; set; } | |
public List<byte> Body { get; set; } | |
public HttpRequest() { | |
Headers = new Dictionary<string, string>(); | |
Body = new List<byte>(); | |
} | |
} | |
internal static class StreamExtensions { | |
public static async Task WriteAsync(this Stream stream, string @string) { | |
var buffer = UTF8Encoding.UTF8.GetBytes(@string); | |
await stream.WriteAsync(buffer, 0, buffer.Length); | |
} | |
} | |
public sealed class HttpResponse { | |
public uint StatusCode { get; set; } | |
public IDictionary<string, string> Headers { get; set; } | |
public Stream BodyStream { get; set; } | |
public HttpResponse() { | |
StatusCode = 200; | |
Headers = new Dictionary<string, string>{ | |
{"transfer-encoding", "chunked"} | |
}; | |
} | |
public async Task WriteToStreamAsync(Stream stream) { | |
await stream.WriteAsync(String.Format("HTTP/1.1 {0}\r\n", StatusCode)); | |
foreach (var header in Headers) { | |
await stream.WriteAsync(String.Format("{0}: {1}\r\n", header.Key, header.Value)); | |
} | |
await stream.WriteAsync("\r\n"); | |
if (BodyStream != null) { | |
var buffer = new byte[1024]; | |
int nread; | |
do { | |
nread = await BodyStream.ReadAsync(buffer, 0, buffer.Length); | |
await stream.WriteAsync(String.Format("{0:X}\r\n", nread)); | |
await stream.WriteAsync(buffer, 0, nread); | |
await stream.WriteAsync("\r\n"); | |
} while (nread != -1); | |
await stream.WriteAsync("0\r\n\r\n"); | |
} | |
} | |
} | |
public class HttpServer { | |
private sealed class ParserHandler : IHttpParserHandler { | |
private Stream Stream { get; set; } | |
private HttpRequest Request { get; set; } | |
private string HeaderName { get; set; } | |
public event EventHandler<HttpRequest> OnRequest; | |
public ParserHandler(Stream stream) { | |
Stream = stream; | |
} | |
public void OnMessageBegin(HttpParser parser) { | |
Request = new HttpRequest(); | |
} | |
public void OnMethod(HttpParser parser, string method) { | |
Request.Method = new Dictionary<string, HttpMethod> { | |
{"GET", HttpMethod.Get}, | |
{"POST", HttpMethod.Post}, | |
{"PUT", HttpMethod.Put}, | |
{"DELETE", HttpMethod.Delete}, | |
{"HEAD", HttpMethod.Head}, | |
}[method]; | |
} | |
public void OnRequestUri(HttpParser parser, string requestUri) { | |
Request.Path = requestUri; | |
} | |
public void OnFragment(HttpParser parser, string fragment) { | |
// wtf is this | |
} | |
public void OnQueryString(HttpParser parser, string queryString) { | |
Request.QueryString = queryString; | |
} | |
public void OnHeaderName(HttpParser parser, string name) { | |
HeaderName = name; | |
} | |
public void OnHeaderValue(HttpParser parser, string value) { | |
if (Request.Headers.ContainsKey(HeaderName)) { | |
Request.Headers[HeaderName] += "," + value; | |
} else { | |
Request.Headers[HeaderName] = value; | |
} | |
} | |
public void OnHeadersEnd(HttpParser parser) { /* no-op */ } | |
public void OnBody(HttpParser parser, ArraySegment<byte> data) { | |
Request.Body.AddRange(data); | |
} | |
public void OnMessageEnd(HttpParser parser) { | |
OnRequest.Invoke(this, Request); | |
} | |
} | |
public RequestHandler RequestHandler { get; set; } | |
public void Run(string host, int port) { | |
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); | |
var endPoint = new IPEndPoint(IPAddress.Parse(host), port); | |
socket.Bind(endPoint); | |
socket.Listen(128); | |
for (; ; ) { | |
var clientSocket = socket.Accept(); | |
var stream = new NetworkStream(clientSocket); | |
HandleClient(new Client { Stream = stream }); | |
} | |
} | |
private async Task HandleClient(Client client) { | |
var parserHandler = new ParserHandler(client.Stream); | |
var parser = new HttpParser(parserHandler); | |
parserHandler.OnRequest += async (sender, request) => { | |
var response = await RequestHandler(request); | |
response.WriteToStreamAsync(client.Stream); | |
}; | |
for (; ; ) { | |
var buffer = new byte[1024]; | |
int bytesRead; | |
do { | |
bytesRead = await client.Stream.ReadAsync(buffer, 0, buffer.Length); | |
var bytesParsed = parser.Execute(new ArraySegment<byte>(buffer)); | |
if (bytesParsed != bytesRead) { | |
// TODO: Decent error. | |
throw new Exception(); | |
} | |
} while (bytesRead != -1); | |
parser.Execute(new ArraySegment<byte>()); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment