WIP
parent
f06af9d2c9
commit
92024abb13
|
@ -8,6 +8,8 @@ using ln.types.net;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using ln.http.exceptions;
|
using ln.http.exceptions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using ln.types;
|
||||||
|
using ln.http.router;
|
||||||
|
|
||||||
|
|
||||||
namespace ln.http
|
namespace ln.http
|
||||||
|
@ -166,6 +168,9 @@ namespace ln.http
|
||||||
{
|
{
|
||||||
keepAlive = false;
|
keepAlive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
response?.ContentStream?.Dispose();
|
||||||
|
|
||||||
} while (keepAlive);
|
} while (keepAlive);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -183,7 +188,6 @@ namespace ln.http
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpRequest.ClearCurrent();
|
HttpRequest.ClearCurrent();
|
||||||
|
|
||||||
connection.GetStream().Close();
|
connection.GetStream().Close();
|
||||||
} finally
|
} 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);
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,9 @@ using System.Text;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using ln.types.net;
|
using ln.types.net;
|
||||||
|
using ln.http.message;
|
||||||
|
using ln.http.io;
|
||||||
|
using ln.http.message.parser;
|
||||||
|
|
||||||
namespace ln.http
|
namespace ln.http
|
||||||
{
|
{
|
||||||
|
@ -20,11 +23,13 @@ namespace ln.http
|
||||||
|
|
||||||
public Endpoint RemoteEndpoint { get; private set; }
|
public Endpoint RemoteEndpoint { get; private set; }
|
||||||
|
|
||||||
|
public HeaderContainer Headers { get; private set; }
|
||||||
|
|
||||||
public String Method { get; private set; }
|
public String Method { get; private set; }
|
||||||
public String URL { get; private set; }
|
public String URL { get; private set; }
|
||||||
public String Protocol { get; private set; }
|
public String Protocol { get; private set; }
|
||||||
|
|
||||||
public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>();
|
//public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>();
|
||||||
|
|
||||||
public bool Valid { get; private set; } = false;
|
public bool Valid { get; private set; } = false;
|
||||||
|
|
||||||
|
@ -40,19 +45,18 @@ namespace ln.http
|
||||||
|
|
||||||
public void Read()
|
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)
|
if (requestTokens.Length != 3)
|
||||||
return;
|
throw new FormatException("request line malformed");
|
||||||
|
|
||||||
Method = ReadToken();
|
Method = requestTokens[0];
|
||||||
SkipWhiteSpace();
|
URL = requestTokens[1];
|
||||||
URL = ReadToken();
|
Protocol = requestTokens[2];
|
||||||
SkipWhiteSpace();
|
|
||||||
Protocol = ReadToken();
|
|
||||||
ReadLine();
|
|
||||||
|
|
||||||
ReadHeaders();
|
Headers = HTTP.ReadHeader(reader);
|
||||||
|
|
||||||
Valid = true;
|
Valid = true;
|
||||||
}
|
}
|
||||||
|
@ -170,16 +174,16 @@ namespace ln.http
|
||||||
return value.Trim();
|
return value.Trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReadHeaders()
|
//public void ReadHeaders()
|
||||||
{
|
//{
|
||||||
while (bptr < hlen)
|
// while (bptr < hlen)
|
||||||
{
|
// {
|
||||||
String name = ReadHeaderName();
|
// String name = ReadHeaderName();
|
||||||
String value = ReadHeaderValue();
|
// String value = ReadHeaderValue();
|
||||||
|
|
||||||
Headers.Add(name, value);
|
// Headers.Add(name, value);
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
public int ReadRequestBody(byte[] dst,int offset,int length)
|
public int ReadRequestBody(byte[] dst,int offset,int length)
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,6 +6,7 @@ using ln.http.exceptions;
|
||||||
using ln.types.net;
|
using ln.types.net;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using ln.http.session;
|
using ln.http.session;
|
||||||
|
using ln.http.message;
|
||||||
|
|
||||||
namespace ln.http
|
namespace ln.http
|
||||||
{
|
{
|
||||||
|
@ -14,7 +15,8 @@ namespace ln.http
|
||||||
static ThreadLocal<HttpRequest> current = new ThreadLocal<HttpRequest>();
|
static ThreadLocal<HttpRequest> current = new ThreadLocal<HttpRequest>();
|
||||||
static public HttpRequest Current => current.Value;
|
static public HttpRequest Current => current.Value;
|
||||||
|
|
||||||
Dictionary<String, String> requestHeaders;
|
//Dictionary<String, String> requestHeaders;
|
||||||
|
HeaderContainer requestHeaders;
|
||||||
Dictionary<String, String> requestCookies;
|
Dictionary<String, String> requestCookies;
|
||||||
Dictionary<string, String> requestParameters;
|
Dictionary<string, String> requestParameters;
|
||||||
|
|
||||||
|
@ -38,6 +40,7 @@ namespace ln.http
|
||||||
public Session Session { get; set; }
|
public Session Session { get; set; }
|
||||||
public HttpUser CurrentUser => Session.CurrentUser;
|
public HttpUser CurrentUser => Session.CurrentUser;
|
||||||
|
|
||||||
|
public HeaderContainer RequestHeaders => requestHeaders;
|
||||||
|
|
||||||
public MemoryStream ContentStream { get; }
|
public MemoryStream ContentStream { get; }
|
||||||
public TextReader ContentReader
|
public TextReader ContentReader
|
||||||
|
@ -70,7 +73,8 @@ namespace ln.http
|
||||||
Protocol = httpReader.Protocol;
|
Protocol = httpReader.Protocol;
|
||||||
RequestURL = httpReader.URL;
|
RequestURL = httpReader.URL;
|
||||||
|
|
||||||
requestHeaders = new Dictionary<string, string>(httpReader.Headers);
|
//requestHeaders = new Dictionary<string, string>(httpReader.Headers);
|
||||||
|
requestHeaders = httpReader.Headers;
|
||||||
requestCookies = new Dictionary<string, string>();
|
requestCookies = new Dictionary<string, string>();
|
||||||
requestParameters = new Dictionary<string, string>();
|
requestParameters = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
@ -156,7 +160,7 @@ namespace ln.http
|
||||||
name = name.ToUpper();
|
name = name.ToUpper();
|
||||||
|
|
||||||
if (requestHeaders.ContainsKey(name))
|
if (requestHeaders.ContainsKey(name))
|
||||||
return requestHeaders[name];
|
return requestHeaders[name].Value;
|
||||||
|
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
using System;
|
||||||
|
namespace ln.http.exceptions
|
||||||
|
{
|
||||||
|
public class DisposeConnectionException : Exception
|
||||||
|
{
|
||||||
|
public DisposeConnectionException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -68,13 +68,23 @@
|
||||||
<Compile Include="IHTTPResource.cs" />
|
<Compile Include="IHTTPResource.cs" />
|
||||||
<Compile Include="router\VirtualHostRouter.cs" />
|
<Compile Include="router\VirtualHostRouter.cs" />
|
||||||
<Compile Include="IHttpRouter.cs" />
|
<Compile Include="IHttpRouter.cs" />
|
||||||
<Compile Include="mime\MimeTypeMap.cs" />
|
<Compile Include="message\MimeTypeMap.cs" />
|
||||||
<Compile Include="router\RouterTarget.cs" />
|
<Compile Include="router\RouterTarget.cs" />
|
||||||
<Compile Include="router\SimpleRouter.cs" />
|
<Compile Include="router\SimpleRouter.cs" />
|
||||||
<Compile Include="router\StaticRouter.cs" />
|
<Compile Include="router\StaticRouter.cs" />
|
||||||
<Compile Include="router\FileRouter.cs" />
|
<Compile Include="router\FileRouter.cs" />
|
||||||
<Compile Include="router\LoggingRouter.cs" />
|
<Compile Include="router\LoggingRouter.cs" />
|
||||||
<Compile Include="exceptions\MethodNotAllowedException.cs" />
|
<Compile Include="exceptions\MethodNotAllowedException.cs" />
|
||||||
|
<Compile Include="router\WebsocketRouter.cs" />
|
||||||
|
<Compile Include="exceptions\DisposeConnectionException.cs" />
|
||||||
|
<Compile Include="message\Message.cs" />
|
||||||
|
<Compile Include="message\Header.cs" />
|
||||||
|
<Compile Include="message\HeaderContainer.cs" />
|
||||||
|
<Compile Include="io\UnbufferedStreamreader.cs" />
|
||||||
|
<Compile Include="message\TokenReader.cs" />
|
||||||
|
<Compile Include="message\parser\MIME.cs" />
|
||||||
|
<Compile Include="message\parser\HTTP.cs" />
|
||||||
|
<Compile Include="exceptions\BadRequestException.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="exceptions\" />
|
<Folder Include="exceptions\" />
|
||||||
|
@ -85,7 +95,9 @@
|
||||||
<Folder Include="cert\" />
|
<Folder Include="cert\" />
|
||||||
<Folder Include="listener\" />
|
<Folder Include="listener\" />
|
||||||
<Folder Include="router\" />
|
<Folder Include="router\" />
|
||||||
<Folder Include="mime\" />
|
<Folder Include="message\" />
|
||||||
|
<Folder Include="io\" />
|
||||||
|
<Folder Include="message\parser\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\ln.logging\ln.logging.csproj">
|
<ProjectReference Include="..\ln.logging\ln.logging.csproj">
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ln.http.simple
|
||||||
|
{
|
||||||
|
class MainClass
|
||||||
|
{
|
||||||
|
public static void Main(string[] args) => HTTPServer.StartSimpleServer(args);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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("")]
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||||
|
<ProjectGuid>{516D661A-2DFE-4AC7-A32F-28CCF5F6A5F1}</ProjectGuid>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<RootNamespace>ln.http.simple</RootNamespace>
|
||||||
|
<AssemblyName>ln.http.simple</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<ExternalConsole>true</ExternalConsole>
|
||||||
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release</OutputPath>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<ExternalConsole>true</ExternalConsole>
|
||||||
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\ln.http.csproj">
|
||||||
|
<Project>{CEEEEB41-3059-46A2-A871-2ADE22C013D9}</Project>
|
||||||
|
<Name>ln.http</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
|
@ -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<string, string> 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<string, string>();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<string, Header> headers = new Dictionary<string, Header>();
|
||||||
|
|
||||||
|
public HeaderContainer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public HeaderContainer(Stream stream):this(new UnbufferedStreamReader(stream))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public HeaderContainer(TextReader reader)
|
||||||
|
{
|
||||||
|
List<String> headerLines = new List<string>();
|
||||||
|
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<string> Keys => headers.Keys;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Message> 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<Message>();
|
||||||
|
|
||||||
|
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<int> offsets = new List<int>();
|
||||||
|
List<int> validated = new List<int>();
|
||||||
|
|
||||||
|
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<Message> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace ln.http.mime
|
namespace ln.http.message
|
||||||
{
|
{
|
||||||
public static class MimeTypeMap
|
public static class MimeTypeMap
|
||||||
{
|
{
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<String> headerLines = new List<string>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<String> headerLines = new List<string>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,7 @@
|
||||||
// *
|
// *
|
||||||
// **/
|
// **/
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using ln.http.mime;
|
using ln.http.message;
|
||||||
namespace ln.http.router
|
namespace ln.http.router
|
||||||
{
|
{
|
||||||
public class FileRouter : IHttpRouter
|
public class FileRouter : IHttpRouter
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
// **/
|
// **/
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using ln.http.mime;
|
using ln.http.message;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
namespace ln.http.router
|
namespace ln.http.router
|
||||||
{
|
{
|
||||||
|
@ -53,9 +53,12 @@ namespace ln.http.router
|
||||||
|
|
||||||
if (File.Exists(finalPath))
|
if (File.Exists(finalPath))
|
||||||
{
|
{
|
||||||
HttpResponse httpResponse = new HttpResponse(httpRequest, new FileStream(finalPath, FileMode.Open));
|
lock (this)
|
||||||
httpResponse.SetHeader("content-type", MimeTypeMap.GetMimeType(Path.GetExtension(finalPath)));
|
{
|
||||||
return httpResponse;
|
HttpResponse httpResponse = new HttpResponse(httpRequest, new FileStream(finalPath, FileMode.Open));
|
||||||
|
httpResponse.SetHeader("content-type", MimeTypeMap.GetMimeType(Path.GetExtension(finalPath)));
|
||||||
|
return httpResponse;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<HttpRequest, WebSocket> createWebsocket;
|
||||||
|
|
||||||
|
public WebsocketRouter(Func<HttpRequest, WebSocket> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,7 +33,6 @@ namespace ln.http.websocket
|
||||||
|
|
||||||
public delegate void WebSocketEventDelegate(WebSocket sender,WebSocketEventArgs e);
|
public delegate void WebSocketEventDelegate(WebSocket sender,WebSocketEventArgs e);
|
||||||
|
|
||||||
|
|
||||||
public abstract class WebSocket
|
public abstract class WebSocket
|
||||||
{
|
{
|
||||||
public HTTPServer HTTPServer => HttpRequest.HTTPServer;
|
public HTTPServer HTTPServer => HttpRequest.HTTPServer;
|
||||||
|
|
Loading…
Reference in New Issue