diff --git a/HTTPServer.cs b/HTTPServer.cs index 71422b1..be583ee 100644 --- a/HTTPServer.cs +++ b/HTTPServer.cs @@ -8,6 +8,8 @@ using ln.types.net; using System.Globalization; using ln.http.exceptions; using System.Threading; +using ln.types; +using ln.http.router; namespace ln.http @@ -166,6 +168,9 @@ namespace ln.http { keepAlive = false; } + + response?.ContentStream?.Dispose(); + } while (keepAlive); } catch (Exception e) @@ -183,7 +188,6 @@ namespace ln.http } HttpRequest.ClearCurrent(); - connection.GetStream().Close(); } finally { @@ -197,5 +201,45 @@ namespace ln.http Logger.Log(LogLevel.INFO, "{0} {1} {2} {3}",startTime.ToString("yyyyMMdd-HH:mm:ss"),duration.ToString(CultureInfo.InvariantCulture),httpRequest.Hostname,httpRequest.RequestURL); } + + + public static void StartSimpleServer(string[] arguments) + { + ArgumentContainer argumentContainer = new ArgumentContainer(new Argument[] + { + new Argument('p',"port",8080), + new Argument('l',"listen","127.0.0.1"), + new Argument('c', "catch",null) + }); + + argumentContainer.Parse(arguments); + + SimpleRouter router = new SimpleRouter(); + router.AddSimpleRoute("/*", new RouterTarget((request) => + { + HttpResponse response = new HttpResponse(request); + response.StatusCode = 404; + response.SetHeader("content-type", "text/plain"); + response.ContentWriter.WriteLine("404 Not Found"); + response.ContentWriter.Flush(); + return response; + }), -100); + + foreach (String path in argumentContainer.AdditionalArguments) + { + StaticRouter staticRouter = new StaticRouter(path); + staticRouter.AddIndex("index.html"); + staticRouter.AddIndex("index.htm"); + router.AddSimpleRoute("/*", staticRouter); + } + + if (argumentContainer['c'].IsSet) + router.AddSimpleRoute("/*", new RouterTarget((request) => router.Route(argumentContainer['c'].Value,request)),0); + + HTTPServer server = new HTTPServer(new Endpoint(IPv6.Parse(argumentContainer['l'].Value),argumentContainer['p'].IntegerValue), + new LoggingRouter(router)); + server.Start(); + } + } } diff --git a/HttpReader.cs b/HttpReader.cs index a79acc4..050697a 100644 --- a/HttpReader.cs +++ b/HttpReader.cs @@ -4,6 +4,9 @@ using System.Text; using System.Collections.Generic; using System.Net; using ln.types.net; +using ln.http.message; +using ln.http.io; +using ln.http.message.parser; namespace ln.http { @@ -20,11 +23,13 @@ namespace ln.http public Endpoint RemoteEndpoint { get; private set; } + public HeaderContainer Headers { get; private set; } + public String Method { get; private set; } public String URL { get; private set; } public String Protocol { get; private set; } - public Dictionary Headers { get; } = new Dictionary(); + //public Dictionary Headers { get; } = new Dictionary(); public bool Valid { get; private set; } = false; @@ -40,19 +45,18 @@ namespace ln.http public void Read() { - ReadRequestHead(); + UnbufferedStreamReader reader = new UnbufferedStreamReader(Stream); + string requestLine = reader.ReadLine(); + string[] requestTokens = requestLine.Split(new char[0], StringSplitOptions.RemoveEmptyEntries); - if (blen == 0) - return; + if (requestTokens.Length != 3) + throw new FormatException("request line malformed"); - Method = ReadToken(); - SkipWhiteSpace(); - URL = ReadToken(); - SkipWhiteSpace(); - Protocol = ReadToken(); - ReadLine(); + Method = requestTokens[0]; + URL = requestTokens[1]; + Protocol = requestTokens[2]; - ReadHeaders(); + Headers = HTTP.ReadHeader(reader); Valid = true; } @@ -170,16 +174,16 @@ namespace ln.http return value.Trim(); } - public void ReadHeaders() - { - while (bptr < hlen) - { - String name = ReadHeaderName(); - String value = ReadHeaderValue(); + //public void ReadHeaders() + //{ + // while (bptr < hlen) + // { + // String name = ReadHeaderName(); + // String value = ReadHeaderValue(); - Headers.Add(name, value); - } - } + // Headers.Add(name, value); + // } + //} public int ReadRequestBody(byte[] dst,int offset,int length) { diff --git a/HttpRequest.cs b/HttpRequest.cs index 8519eb7..e4a5d2a 100644 --- a/HttpRequest.cs +++ b/HttpRequest.cs @@ -6,6 +6,7 @@ using ln.http.exceptions; using ln.types.net; using System.Threading; using ln.http.session; +using ln.http.message; namespace ln.http { @@ -14,7 +15,8 @@ namespace ln.http static ThreadLocal current = new ThreadLocal(); static public HttpRequest Current => current.Value; - Dictionary requestHeaders; + //Dictionary requestHeaders; + HeaderContainer requestHeaders; Dictionary requestCookies; Dictionary requestParameters; @@ -38,6 +40,7 @@ namespace ln.http public Session Session { get; set; } public HttpUser CurrentUser => Session.CurrentUser; + public HeaderContainer RequestHeaders => requestHeaders; public MemoryStream ContentStream { get; } public TextReader ContentReader @@ -70,7 +73,8 @@ namespace ln.http Protocol = httpReader.Protocol; RequestURL = httpReader.URL; - requestHeaders = new Dictionary(httpReader.Headers); + //requestHeaders = new Dictionary(httpReader.Headers); + requestHeaders = httpReader.Headers; requestCookies = new Dictionary(); requestParameters = new Dictionary(); @@ -156,7 +160,7 @@ namespace ln.http name = name.ToUpper(); if (requestHeaders.ContainsKey(name)) - return requestHeaders[name]; + return requestHeaders[name].Value; return def; } diff --git a/exceptions/BadRequestException.cs b/exceptions/BadRequestException.cs new file mode 100644 index 0000000..2a62857 --- /dev/null +++ b/exceptions/BadRequestException.cs @@ -0,0 +1,20 @@ +// /** +// * File: BadRequest.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +namespace ln.http.exceptions +{ + public class BadRequestException: HttpException + { + public BadRequestException() + :base(400,"Bad Request") + { + } + } +} diff --git a/exceptions/DisposeConnectionException.cs b/exceptions/DisposeConnectionException.cs new file mode 100644 index 0000000..720b421 --- /dev/null +++ b/exceptions/DisposeConnectionException.cs @@ -0,0 +1,10 @@ +using System; +namespace ln.http.exceptions +{ + public class DisposeConnectionException : Exception + { + public DisposeConnectionException() + { + } + } +} diff --git a/io/UnbufferedStreamreader.cs b/io/UnbufferedStreamreader.cs new file mode 100644 index 0000000..6d8b16d --- /dev/null +++ b/io/UnbufferedStreamreader.cs @@ -0,0 +1,68 @@ +// /** +// * File: UnbufferedStreamreader.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +using System.IO; +using System.Text; +namespace ln.http.io +{ + public class UnbufferedStreamReader : TextReader + { + public Stream Stream { get; } + + public UnbufferedStreamReader(Stream stream) + { + Stream = stream; + } + + public override int Read() => Stream.ReadByte(); + + public override string ReadLine() + { + StringBuilder stringBuilder = new StringBuilder(); + + char ch; + + while ((ch = (char)Stream.ReadByte()) != -1) + { + if (ch == '\r') + { + ch = (char)Stream.ReadByte(); + if (ch == '\n') + return stringBuilder.ToString(); + + stringBuilder.Append('\r'); + } + stringBuilder.Append(ch); + } + + if ((ch == -1) && (stringBuilder.Length == 0)) + return null; + + return stringBuilder.ToString(); + } + + public string ReadToken() + { + StringBuilder stringBuilder = new StringBuilder(); + char ch = (char)Stream.ReadByte(); + + while (char.IsWhiteSpace(ch)) + ch = (char)Stream.ReadByte(); + + while (!char.IsWhiteSpace(ch)) + { + stringBuilder.Append(ch); + ch = (char)Stream.ReadByte(); + } + + return stringBuilder.ToString(); + } + } +} diff --git a/ln.http.csproj b/ln.http.csproj index 688f188..2299c76 100644 --- a/ln.http.csproj +++ b/ln.http.csproj @@ -68,13 +68,23 @@ - + + + + + + + + + + + @@ -85,7 +95,9 @@ - + + + diff --git a/ln.http.simple/Program.cs b/ln.http.simple/Program.cs new file mode 100644 index 0000000..a8e57a0 --- /dev/null +++ b/ln.http.simple/Program.cs @@ -0,0 +1,9 @@ +using System; + +namespace ln.http.simple +{ + class MainClass + { + public static void Main(string[] args) => HTTPServer.StartSimpleServer(args); + } +} diff --git a/ln.http.simple/Properties/AssemblyInfo.cs b/ln.http.simple/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..9f59996 --- /dev/null +++ b/ln.http.simple/Properties/AssemblyInfo.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("ln.http.simple")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] diff --git a/ln.http.simple/ln.http.simple.csproj b/ln.http.simple/ln.http.simple.csproj new file mode 100644 index 0000000..48cf0c3 --- /dev/null +++ b/ln.http.simple/ln.http.simple.csproj @@ -0,0 +1,45 @@ + + + + Debug + x86 + {516D661A-2DFE-4AC7-A32F-28CCF5F6A5F1} + Exe + ln.http.simple + ln.http.simple + v4.7 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + x86 + + + true + bin\Release + prompt + 4 + true + x86 + + + + + + + + + + + {CEEEEB41-3059-46A2-A871-2ADE22C013D9} + ln.http + + + + \ No newline at end of file diff --git a/message/Header.cs b/message/Header.cs new file mode 100644 index 0000000..cf7a026 --- /dev/null +++ b/message/Header.cs @@ -0,0 +1,159 @@ +// /** +// * File: Header.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Linq; +using System.Net.Sockets; +namespace ln.http.message +{ + public class Header + { + public string Name { get; } + + string rawvalue; + public string RawValue { + get => rawvalue; + set => SetValue(value); + } + + string comments; + public string Comments => comments; + + string value; + public string Value + { + get => value; + set => SetValue(value); + } + + Dictionary parameters; + + public Header(string headerLine) + { + int colon = headerLine.IndexOf(':'); + if (colon == -1) + throw new FormatException("expected to find :"); + + Name = headerLine.Substring(0, colon).ToUpper(); + SetValue(headerLine.Substring(colon + 1)); + } + public Header(string name, string value) + { + Name = name.ToUpper(); + SetValue(value); + } + + public void SetValue(string newValue) + { + rawvalue = newValue; + value = ParseValue(new StringReader(newValue.Trim()),out comments); + + // at least MIME Content-* header follow the parameter syntax... + if (Name.StartsWith("CONTENT-", StringComparison.InvariantCulture)) + { + ParseParameters(); + } + } + + public bool ContainsParameter(string parameterName) => parameters.ContainsKey(parameterName.ToUpper()); + public string GetParameter(string parameterName) => parameters[parameterName.ToUpper()]; + public string GetParameter(string parameterName,string defaultValue) => parameters[parameterName.ToUpper()]; + + string ParseComment(TextReader reader) + { + StringBuilder commentBuilder = new StringBuilder(); + ParseComment(reader, commentBuilder); + return commentBuilder.ToString(); + } + void ParseComment(TextReader reader,StringBuilder commentBuilder) + { + int ch; + while (((ch = reader.Read()) != -1) && (ch != ')')) + commentBuilder.Append((char)ch); + } + public virtual string ParseValue(TextReader reader,out string parsedComments) + { + StringBuilder stringBuilder = new StringBuilder(); + StringBuilder commentBuilder = new StringBuilder(); + + int ch; + while (((ch = reader.Read())!=-1)) + { + if (ch == '(') + { + commentBuilder.Append(ParseComment(reader)); + } else + { + stringBuilder.Append((char)ch); + } + } + parsedComments = commentBuilder.ToString().Trim(); + return stringBuilder.ToString().Trim(); + } + + public void ParseParameters() + { + if (parameters != null) + return; + + parameters = new Dictionary(); + + int semicolon = value.IndexOf(';'); + if (semicolon > 0) + { + TokenReader tokenReader = new TokenReader(new StringReader(value.Substring(semicolon))); + while (tokenReader.Peek() != -1) + { + if (tokenReader.Read() != ';') + throw new FormatException(); + + string pName = tokenReader.ReadToken().ToUpper(); + if (tokenReader.Read() != '=') + throw new FormatException("expected ="); + + string pValue = (tokenReader.Peek() == '"') ? tokenReader.ReadQuotedString() : tokenReader.ReadToken(); + parameters.Add(pName, pValue); + } + + value = value.Substring(0, semicolon).Trim(); + } + } + + //void parseValue(string v) + //{ + // rawValue = v; + // TokenReader tokenReader = new TokenReader(parseComments(new StringReader(v))); + // StringBuilder stringBuilder = new StringBuilder(); + + // int ch; + // while (((ch = tokenReader.Read()) != -1) && (ch != ';')) + // stringBuilder.Append((char)ch); + + // Value = stringBuilder.ToString(); + + // while (tokenReader.Peek() != -1) + // { + // string pName = tokenReader.ReadToken(); + // if (tokenReader.Read() != '=') + // throw new FormatException("expected ="); + + // string pValue = (tokenReader.Peek() == '"') ? tokenReader.ReadQuotedString() : tokenReader.ReadToken(); + // parameters.Add(pName, pValue); + // } + + + //} + + public override int GetHashCode() => Name.GetHashCode(); + public override bool Equals(object obj) => (obj is Header you) && Name.Equals(you.Name); + } +} diff --git a/message/HeaderContainer.cs b/message/HeaderContainer.cs new file mode 100644 index 0000000..b4d0638 --- /dev/null +++ b/message/HeaderContainer.cs @@ -0,0 +1,76 @@ +// /** +// * File: HeaderContainer.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using ln.http.io; +namespace ln.http.message +{ + public class HeaderContainer + { + Dictionary headers = new Dictionary(); + + public HeaderContainer() + { + } + public HeaderContainer(Stream stream):this(new UnbufferedStreamReader(stream)) + { + } + public HeaderContainer(TextReader reader) + { + List headerLines = new List(); + string currentline = reader.ReadLine(); + while (!currentline.Equals(string.Empty)) + { + if (char.IsWhiteSpace(currentline[0])) + { + headerLines[headerLines.Count - 1] = headerLines[headerLines.Count - 1] + currentline; + } + else + { + headerLines.Add(currentline); + } + currentline = reader.ReadLine(); + } + + foreach (string headerLine in headerLines) + { + Header header = new Header(headerLine); + headers.Add(header.Name, header); + } + } + + public Header this[string name] + { + get => headers[name.ToUpper()]; + } + + public void Add(Header header)=> headers.Add(header.Name, header); + + public bool ContainsKey(string name) => headers.ContainsKey(name.ToUpper()); + public bool Contains(string name) => headers.ContainsKey(name.ToUpper()); + public string Get(string name) => this[name].Value; + public void Set(string name,string value) + { + name = name.ToUpper(); + if (!headers.TryGetValue(name,out Header header)) + { + header = new Header(name); + headers.Add(name, header); + } + header.Value = value; + } + public void Remove(string name) => headers.Remove(name.ToUpper()); + + public IEnumerable Keys => headers.Keys; + + } +} diff --git a/message/Message.cs b/message/Message.cs new file mode 100644 index 0000000..de85c27 --- /dev/null +++ b/message/Message.cs @@ -0,0 +1,153 @@ +// /** +// * File: Message.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Globalization; +using ln.http.io; +using ln.types; +using ln.http.message.parser; +namespace ln.http.message +{ + public class Message + { + public HeaderContainer Headers { get; private set; } + + byte[] bodyData; + int bodyOffset; + int bodyLength; + + List parts; + + bool isMultipart; + public bool IsMultipart => isMultipart; + + public Message() + { + Setup(new HeaderContainer(), new byte[0], 0, 0); + } + + public Message(HeaderContainer headers, byte[] body) + : this(headers, body, 0, body.Length) { } + + public Message(HeaderContainer headers, byte[] body, int offset, int length) + { + Setup(headers, body, offset, length); + } + + public Message(byte[] body, int offset, int length) + { + MemoryStream memoryStream = new MemoryStream(body, offset, length); + HeaderContainer headers = MIME.ReadHeader(new UnbufferedStreamReader(memoryStream)); + + if (memoryStream.Position >= length) + throw new FormatException("MIME header section too long"); + + Setup(headers, body, offset + (int)memoryStream.Position, length - (int)memoryStream.Position); + } + + public Message(Stream stream) + { + HeaderContainer headers = MIME.ReadHeader(new UnbufferedStreamReader(stream)); + + byte[] data = stream.ReadToEnd(); + + Setup(headers, data, 0, data.Length); + } + + private void Setup(HeaderContainer headers, byte[] body, int offset, int length) + { + Headers = headers; + + bodyData = body; + bodyOffset = offset; + bodyLength = length; + + string ct = Headers["Content-Type"].Value; + isMultipart = ct.StartsWith("multipart/", StringComparison.InvariantCulture) || ct.StartsWith("message/", StringComparison.InvariantCulture); + } + + public void ReadParts() + { + parts = new List(); + + if (isMultipart) + { + string boundary = Headers["Content-Type"].GetParameter("boundary"); + string delimiter = "--" + boundary; + int[] indeces = FindIndeces(bodyData, bodyOffset, bodyLength, Encoding.ASCII.GetBytes(delimiter)); + + for (int n = 1; n < indeces.Length; n++) + { + Message part = new Message(bodyData, indeces[n - 1], indeces[n] - indeces[n - 1]); + parts.Add(part); + } + + } + } + + int[] FindIndeces(byte[] data,int offset,int length,byte[] pattern) + { + List offsets = new List(); + List validated = new List(); + + for (int n = offset; n < (length - pattern.Length); n++) + { + int p = 0; + while ((p < pattern.Length) && (data[n + p] == pattern[p])) + p++; + + if (p == pattern.Length) + { + if ((n == offset) || ((n >= (offset + 2)) && (data[offset + n - 2] == '\r') && (data[offset + n - 1] == '\n'))) + { + n += pattern.Length; + + while ((n < (offset + length)) && (data[n - 2] != '\r') && (data[n - 1] != '\n')) + n++; + + validated.Add(n); + + + if (((offset + length) > (n + 1)) && (data[n] == '-') && (data[n + 1] == '-')) + break; + } + } + } + + return validated.ToArray(); + } + + + public Stream OpenBodyStream() => new MemoryStream(bodyData, bodyOffset, bodyLength); + + public IEnumerable Parts + { + get + { + if (parts == null) + ReadParts(); + return parts; + } + } + + public bool HasHeader(string name) => Headers.Contains(name); + public Header GetHeader(string name) => Headers[name]; + public void SetHeader(string name, string value) => Headers.Set(name, value); + public void RemoveHeader(String name) => Headers.Remove(name); + + public override string ToString() + { + return base.ToString(); + } + + } +} diff --git a/mime/MimeTypeMap.cs b/message/MimeTypeMap.cs similarity index 97% rename from mime/MimeTypeMap.cs rename to message/MimeTypeMap.cs index 7bddbfc..47766a4 100644 --- a/mime/MimeTypeMap.cs +++ b/message/MimeTypeMap.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; -namespace ln.http.mime +namespace ln.http.message { public static class MimeTypeMap { diff --git a/message/TokenReader.cs b/message/TokenReader.cs new file mode 100644 index 0000000..ad5cefe --- /dev/null +++ b/message/TokenReader.cs @@ -0,0 +1,68 @@ +// /** +// * File: TokenReader.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +using System.IO; +using System.Text; +using System.Linq; +namespace ln.http.message +{ + public class TokenReader : TextReader + { + public static char[] specialChars = new char[] { '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=' }; + + public TextReader BaseReader { get; } + + public TokenReader(String text) + :this(new StringReader(text)) + {} + public TokenReader(TextReader baseReader) + { + BaseReader = baseReader; + } + + public override int Read() => BaseReader.Read(); + public override int Peek() => BaseReader.Peek(); + + public string ReadToken() + { + while ((BaseReader.Peek() != -1) && char.IsWhiteSpace((char)BaseReader.Peek())) + BaseReader.Read(); + + StringBuilder stringBuilder = new StringBuilder(); + int ch; + do + { + ch = BaseReader.Peek(); + + if ((ch <= ' ') || specialChars.Contains((char)ch)) + return stringBuilder.ToString(); + + stringBuilder.Append((char)BaseReader.Read()); + } while (ch != -1); + + return stringBuilder.ToString(); + } + + public string ReadQuotedString() + { + StringBuilder stringBuilder = new StringBuilder(); + + if (BaseReader.Read() != '"') + throw new FormatException("quoted string must start with \""); + + int ch; + while (((ch = BaseReader.Read()) != -1) && (ch != '"')) + stringBuilder.Append((char)ch); + + return stringBuilder.ToString(); + } + + } +} diff --git a/message/parser/HTTP.cs b/message/parser/HTTP.cs new file mode 100644 index 0000000..167ca49 --- /dev/null +++ b/message/parser/HTTP.cs @@ -0,0 +1,40 @@ +// /** +// * File: HTTP.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +using System.IO; +using System.Collections.Generic; +using ln.http.exceptions; +namespace ln.http.message.parser +{ + public static class HTTP + { + public static HeaderContainer ReadHeader(TextReader reader) + { + List headerLines = new List(); + string currentline = reader.ReadLine(); + while (!currentline.Equals(string.Empty)) + { + if (char.IsWhiteSpace(currentline[0])) + throw new BadRequestException(); + + headerLines.Add(currentline.Trim()); + + currentline = reader.ReadLine(); + } + + HeaderContainer headerContainer = new HeaderContainer(); + + foreach (string headerLine in headerLines) + headerContainer.Add(new Header(headerLine)); + + return headerContainer; + } + } +} diff --git a/message/parser/MIME.cs b/message/parser/MIME.cs new file mode 100644 index 0000000..19d314c --- /dev/null +++ b/message/parser/MIME.cs @@ -0,0 +1,43 @@ +// /** +// * File: MIME.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +using System.IO; +using System.Collections.Generic; +namespace ln.http.message.parser +{ + public static class MIME + { + public static HeaderContainer ReadHeader(TextReader reader) + { + List headerLines = new List(); + string currentline = reader.ReadLine(); + while (!currentline.Equals(string.Empty)) + { + if (char.IsWhiteSpace(currentline[0])) + { + headerLines[headerLines.Count - 1] = headerLines[headerLines.Count - 1] + currentline; + } + else + { + headerLines.Add(currentline); + } + currentline = reader.ReadLine(); + } + + HeaderContainer headerContainer = new HeaderContainer(); + + foreach (string headerLine in headerLines) + headerContainer.Add(new Header(headerLine)); + + return headerContainer; + } + + } +} diff --git a/router/FileRouter.cs b/router/FileRouter.cs index 1561a7e..cdef10d 100644 --- a/router/FileRouter.cs +++ b/router/FileRouter.cs @@ -8,7 +8,7 @@ // * // **/ using System.IO; -using ln.http.mime; +using ln.http.message; namespace ln.http.router { public class FileRouter : IHttpRouter diff --git a/router/StaticRouter.cs b/router/StaticRouter.cs index b1419dc..69bb885 100644 --- a/router/StaticRouter.cs +++ b/router/StaticRouter.cs @@ -9,7 +9,7 @@ // **/ using System; using System.IO; -using ln.http.mime; +using ln.http.message; using System.Collections.Generic; namespace ln.http.router { @@ -53,9 +53,12 @@ namespace ln.http.router if (File.Exists(finalPath)) { - HttpResponse httpResponse = new HttpResponse(httpRequest, new FileStream(finalPath, FileMode.Open)); - httpResponse.SetHeader("content-type", MimeTypeMap.GetMimeType(Path.GetExtension(finalPath))); - return httpResponse; + lock (this) + { + HttpResponse httpResponse = new HttpResponse(httpRequest, new FileStream(finalPath, FileMode.Open)); + httpResponse.SetHeader("content-type", MimeTypeMap.GetMimeType(Path.GetExtension(finalPath))); + return httpResponse; + } } return null; } diff --git a/router/WebsocketRouter.cs b/router/WebsocketRouter.cs new file mode 100644 index 0000000..f3470f6 --- /dev/null +++ b/router/WebsocketRouter.cs @@ -0,0 +1,32 @@ +using System; +using ln.http.websocket; +using ln.http.exceptions; +using ln.logging; +namespace ln.http.router +{ + public class WebsocketRouter : IHttpRouter + { + Func createWebsocket; + + public WebsocketRouter(Func createWebsocketDelegate) + { + createWebsocket = createWebsocketDelegate; + } + + public WebSocket CreateWebSocket(HttpRequest request) => createWebsocket(request); + + public HttpResponse Route(string path, HttpRequest httpRequest) + { + WebSocket websocket = CreateWebSocket(httpRequest); + try + { + websocket.Run(); + } + catch (Exception e) + { + Logging.Log(e); + } + throw new DisposeConnectionException(); + } + } +} diff --git a/websocket/WebSocket.cs b/websocket/WebSocket.cs index f22182a..8ef780b 100644 --- a/websocket/WebSocket.cs +++ b/websocket/WebSocket.cs @@ -33,7 +33,6 @@ namespace ln.http.websocket public delegate void WebSocketEventDelegate(WebSocket sender,WebSocketEventArgs e); - public abstract class WebSocket { public HTTPServer HTTPServer => HttpRequest.HTTPServer;