Reimplementation through ln.protocols.helper and code cleanup
parent
4c0caac073
commit
8012f01dde
|
@ -0,0 +1,13 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Rider ignored files
|
||||||
|
/.idea.ln.http.iml
|
||||||
|
/contentModel.xml
|
||||||
|
/modules.xml
|
||||||
|
/projectSettingsUpdater.xml
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
2
build.ln
2
build.ln
|
@ -3,7 +3,7 @@
|
||||||
"dotnet"
|
"dotnet"
|
||||||
],
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"NUGET_SOURCE": "https://nexus.niclas-thobaben.de/repository/l--n.de/",
|
"NUGET_SOURCE": "https://nexus.l--n.de/repository/ln.net/",
|
||||||
"CONFIGURATION": "Release"
|
"CONFIGURATION": "Release"
|
||||||
},
|
},
|
||||||
"stages": [
|
"stages": [
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using ln.http;
|
||||||
|
using ln.http.listener;
|
||||||
|
using ln.http.router;
|
||||||
|
|
||||||
|
namespace ln.http.service
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
StaticRouter staticRouter = new StaticRouter(".");
|
||||||
|
HTTPServer httpServer = new HTTPServer(new HttpListener(8888), new LoggingRouter(staticRouter));
|
||||||
|
|
||||||
|
httpServer.Start();
|
||||||
|
|
||||||
|
lock (httpServer)
|
||||||
|
Monitor.Wait(httpServer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
<LangVersion>9</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="../ln.http/ln.http.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
28
ln.http.sln
28
ln.http.sln
|
@ -7,6 +7,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.http", "ln.http\ln.http.
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.http.tests", "ln.http.tests\ln.http.tests.csproj", "{476CD242-9329-449C-95E4-A5317635B223}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.http.tests", "ln.http.tests\ln.http.tests.csproj", "{476CD242-9329-449C-95E4-A5317635B223}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.http.service", "ln.http.service\ln.http.service.csproj", "{FE139A5A-A388-4656-AD15-149012EDB9D0}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.protocols.helper", "..\ln.protocols.helper\ln.protocols.helper.csproj", "{1C1D3A17-A615-4686-90BD-F0E221EAC89C}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -44,5 +48,29 @@ Global
|
||||||
{476CD242-9329-449C-95E4-A5317635B223}.Release|x64.Build.0 = Release|Any CPU
|
{476CD242-9329-449C-95E4-A5317635B223}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{476CD242-9329-449C-95E4-A5317635B223}.Release|x86.ActiveCfg = Release|Any CPU
|
{476CD242-9329-449C-95E4-A5317635B223}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{476CD242-9329-449C-95E4-A5317635B223}.Release|x86.Build.0 = Release|Any CPU
|
{476CD242-9329-449C-95E4-A5317635B223}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{FE139A5A-A388-4656-AD15-149012EDB9D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{FE139A5A-A388-4656-AD15-149012EDB9D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{FE139A5A-A388-4656-AD15-149012EDB9D0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{FE139A5A-A388-4656-AD15-149012EDB9D0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{FE139A5A-A388-4656-AD15-149012EDB9D0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{FE139A5A-A388-4656-AD15-149012EDB9D0}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{FE139A5A-A388-4656-AD15-149012EDB9D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{FE139A5A-A388-4656-AD15-149012EDB9D0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{FE139A5A-A388-4656-AD15-149012EDB9D0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{FE139A5A-A388-4656-AD15-149012EDB9D0}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{FE139A5A-A388-4656-AD15-149012EDB9D0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{FE139A5A-A388-4656-AD15-149012EDB9D0}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{1C1D3A17-A615-4686-90BD-F0E221EAC89C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1C1D3A17-A615-4686-90BD-F0E221EAC89C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1C1D3A17-A615-4686-90BD-F0E221EAC89C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{1C1D3A17-A615-4686-90BD-F0E221EAC89C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{1C1D3A17-A615-4686-90BD-F0E221EAC89C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{1C1D3A17-A615-4686-90BD-F0E221EAC89C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{1C1D3A17-A615-4686-90BD-F0E221EAC89C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1C1D3A17-A615-4686-90BD-F0E221EAC89C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{1C1D3A17-A615-4686-90BD-F0E221EAC89C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{1C1D3A17-A615-4686-90BD-F0E221EAC89C}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{1C1D3A17-A615-4686-90BD-F0E221EAC89C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{1C1D3A17-A615-4686-90BD-F0E221EAC89C}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
@ -1,17 +1,53 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using ln.http.router;
|
||||||
|
using ln.type;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
namespace ln.http.tests
|
namespace ln.http.tests
|
||||||
{
|
{
|
||||||
public class Tests
|
public class Tests
|
||||||
{
|
{
|
||||||
|
HTTPServer server;
|
||||||
|
int testPort;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
|
if (server != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SimpleRouter testRouter = new SimpleRouter();
|
||||||
|
|
||||||
|
StaticRouter staticRouter = new StaticRouter(AppContext.BaseDirectory);
|
||||||
|
testRouter.AddSimpleRoute("/static/*", staticRouter);
|
||||||
|
|
||||||
|
server = new HTTPServer(testRouter);
|
||||||
|
server.AddEndpoint(new Endpoint(IPv6.ANY,0));
|
||||||
|
|
||||||
|
server.Start();
|
||||||
|
|
||||||
|
testPort = server.Listeners[0].LocalEndpoint.Port;
|
||||||
|
TestContext.Error.WriteLine("Using Port {0}", testPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test1()
|
public void Test1()
|
||||||
{
|
{
|
||||||
|
HttpClient client = new HttpClient();
|
||||||
|
HttpResponseMessage response = client.GetAsync(String.Format("http://localhost:{0}/static/test.txt", testPort)).Result;
|
||||||
|
|
||||||
|
Assert.AreEqual(System.Net.HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
byte[] contentBytes = response.Content.ReadAsByteArrayAsync().Result;
|
||||||
|
byte[] fileBytes = File.ReadAllBytes("test.txt");
|
||||||
|
|
||||||
|
CollectionAssert.AreEqual(fileBytes, contentBytes);
|
||||||
|
|
||||||
|
//server.Stop();
|
||||||
|
|
||||||
Assert.Pass();
|
Assert.Pass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,24 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
|
|
||||||
|
<LangVersion>9</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0"/>
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
||||||
|
|
||||||
|
<ProjectReference Include="../ln.http/ln.http.csproj" />
|
||||||
|
<PackageReference Include="ln.type" Version="0.1.7" />
|
||||||
|
|
||||||
|
<Content Include="test.txt">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
// /**
|
|
||||||
// * File: AuthenticationProvider.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;
|
|
||||||
namespace ln.http
|
|
||||||
{
|
|
||||||
public abstract class AuthenticationProvider
|
|
||||||
{
|
|
||||||
public AuthenticationProvider()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract IEnumerable<HttpUser> EnumerateUsers();
|
|
||||||
public abstract HttpUser Authenticate(HttpRequest httpRequest);
|
|
||||||
|
|
||||||
public virtual HttpUser GetHttpUser(String authenticationName)
|
|
||||||
{
|
|
||||||
foreach (HttpUser httpUser in EnumerateUsers())
|
|
||||||
{
|
|
||||||
if (httpUser.AuthenticationName.Equals(authenticationName))
|
|
||||||
{
|
|
||||||
return httpUser;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new KeyNotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
// /**
|
|
||||||
// * File: AuthorizationMask.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
|
|
||||||
{
|
|
||||||
public static class AuthorizationMask
|
|
||||||
{
|
|
||||||
public static readonly long A_ACCESS = (1l << 0);
|
|
||||||
public static readonly long A_READ = (1l << 0);
|
|
||||||
public static readonly long A_WRITE = (1l << 0);
|
|
||||||
public static readonly long A_EXEC = (1l << 0);
|
|
||||||
|
|
||||||
public static readonly long A_SUPER = (-1);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,7 +5,6 @@ using ln.threading;
|
||||||
using ln.application;
|
using ln.application;
|
||||||
using ln.http.listener;
|
using ln.http.listener;
|
||||||
using ln.http.connections;
|
using ln.http.connections;
|
||||||
using System.Globalization;
|
|
||||||
using ln.http.exceptions;
|
using ln.http.exceptions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using ln.type;
|
using ln.type;
|
||||||
|
@ -22,7 +21,7 @@ namespace ln.http
|
||||||
|
|
||||||
public IHttpRouter Router { get; set; }
|
public IHttpRouter Router { get; set; }
|
||||||
|
|
||||||
public bool IsRunning => !shutdown && (threadPool.CurrentPoolSize > 0);
|
public bool IsRunning => !shutdown;
|
||||||
public Logger Logger { get; set; }
|
public Logger Logger { get; set; }
|
||||||
|
|
||||||
bool shutdown = false;
|
bool shutdown = false;
|
||||||
|
@ -30,16 +29,12 @@ namespace ln.http
|
||||||
List<Listener> listeners = new List<Listener>();
|
List<Listener> listeners = new List<Listener>();
|
||||||
public Listener[] Listeners => listeners.ToArray();
|
public Listener[] Listeners => listeners.ToArray();
|
||||||
|
|
||||||
DynamicPool threadPool;
|
|
||||||
public DynamicPool ThreadPool => threadPool;
|
|
||||||
|
|
||||||
HashSet<Connection> currentConnections = new HashSet<Connection>();
|
HashSet<Connection> currentConnections = new HashSet<Connection>();
|
||||||
public IEnumerable<Connection> CurrentConnections => currentConnections;
|
public IEnumerable<Connection> CurrentConnections => currentConnections;
|
||||||
|
|
||||||
public HTTPServer()
|
public HTTPServer()
|
||||||
{
|
{
|
||||||
Logger = Logger.Default;
|
Logger = Logger.Default;
|
||||||
threadPool = new DynamicPool(1024);
|
|
||||||
}
|
}
|
||||||
public HTTPServer(IHttpRouter router)
|
public HTTPServer(IHttpRouter router)
|
||||||
: this()
|
: this()
|
||||||
|
@ -63,14 +58,14 @@ namespace ln.http
|
||||||
|
|
||||||
public void StartListener(Listener listener)
|
public void StartListener(Listener listener)
|
||||||
{
|
{
|
||||||
|
if (listener.IsOpen)
|
||||||
|
return;
|
||||||
|
|
||||||
listener.Open();
|
listener.Open();
|
||||||
|
|
||||||
threadPool.Enqueue(
|
DynamicThreadPool.DefaultPool.Enqueue(
|
||||||
() => listener.AcceptMany(
|
() => listener.AcceptMany(
|
||||||
(connection) => threadPool.Enqueue(
|
(connection) => this.HandleConnection(connection))
|
||||||
() => this.HandleConnection(connection)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,10 +82,8 @@ namespace ln.http
|
||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
threadPool.Start();
|
|
||||||
|
|
||||||
foreach (Listener listener in listeners)
|
foreach (Listener listener in listeners)
|
||||||
StartListener(listener);
|
if (!listener.IsOpen) StartListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
|
@ -123,101 +116,97 @@ namespace ln.http
|
||||||
connection.Close();
|
connection.Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
threadPool.Stop(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleConnection(Connection connection)
|
private void HandleConnection(Connection connection, bool dontClose = false)
|
||||||
{
|
{
|
||||||
lock (this.currentConnections)
|
lock (this.currentConnections)
|
||||||
currentConnections.Add(connection);
|
currentConnections.Add(connection);
|
||||||
|
|
||||||
try
|
bool keepalive = false;
|
||||||
|
|
||||||
|
do
|
||||||
{
|
{
|
||||||
HttpRequest httpRequest = null;
|
using (HttpRequest httpRequest = connection.ReadRequest(this))
|
||||||
bool keepAlive = true;
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
do
|
HttpResponse httpResponse = null;
|
||||||
|
|
||||||
|
if (httpRequest == null)
|
||||||
|
break;
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
httpRequest = connection.ReadRequest(this);
|
httpResponse = Router.Route(new HttpRoutingContext(httpRequest), httpRequest);
|
||||||
|
if (httpResponse == null)
|
||||||
|
httpResponse = HttpResponse.NotFound();
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Logging.Log(exception);
|
||||||
|
if ((exception is HttpException httpException) && (httpException.HttpResponse != null))
|
||||||
|
httpResponse = httpException.HttpResponse;
|
||||||
|
else
|
||||||
|
httpResponse = HttpResponse.InternalServerError()
|
||||||
|
.Content(String.Format("An internal error occured ({0})", exception.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
if (httpRequest == null)
|
httpResponse.WriteTo(connection.GetStream());
|
||||||
break;
|
httpResponse?.ContentStream?.Dispose();
|
||||||
|
|
||||||
HttpResponse response;
|
keepalive = httpResponse.GetHeader("connection", "keep-alive").Equals("keep-alive") && httpRequest
|
||||||
try
|
.GetRequestHeader("connection",
|
||||||
{
|
httpRequest.Protocol.Equals("HTTP/1.1") ? "keep-alive" : "close").Contains("keep-alive",
|
||||||
response = Router.Route(new HttpRoutingContext(httpRequest),httpRequest);
|
StringComparison.InvariantCultureIgnoreCase);
|
||||||
}
|
|
||||||
catch (HttpException httpExc)
|
|
||||||
{
|
|
||||||
response = new HttpResponse((HttpStatusCode)httpExc.StatusCode).Content(httpExc.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response == null)
|
|
||||||
response = HttpResponse.NotFound().Content(String.Format("The URI {0} could not be found on this server.", httpRequest.URI));
|
|
||||||
|
|
||||||
keepAlive = httpRequest.GetRequestHeader("connection", "keep-alive").Equals("keep-alive") && response.GetHeader("connection", "keep-alive").Equals("keep-alive");
|
|
||||||
response.SetHeader("connection", keepAlive ? "keep-alive" : "close");
|
|
||||||
|
|
||||||
SendResponse(connection.GetStream(), httpRequest, response);
|
|
||||||
|
|
||||||
response?.ContentStream?.Dispose();
|
|
||||||
|
|
||||||
} while (keepAlive);
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
} while (keepalive);
|
||||||
{
|
|
||||||
Logging.Log(e);
|
lock (this.currentConnections)
|
||||||
HttpResponse
|
currentConnections.Remove(connection);
|
||||||
.InternalServerError()
|
connection.Close();
|
||||||
.Content(e)
|
|
||||||
.SendResponse(connection.GetStream(), httpRequest);
|
// new threading.Promise<HttpRequest>((resolve, reject)=>{
|
||||||
}
|
// resolve(connection.ReadRequest(this));
|
||||||
|
// })
|
||||||
HttpRequest.ClearCurrent();
|
// .Then((httpRequest)=>
|
||||||
connection.Close();
|
// {
|
||||||
} finally
|
// if (httpRequest == null)
|
||||||
{
|
// return null;
|
||||||
lock (currentConnections)
|
// return Router.Route(new HttpRoutingContext(httpRequest),httpRequest);
|
||||||
currentConnections.Remove(connection);
|
// })
|
||||||
}
|
// // .Then((httpResponse)=>{
|
||||||
|
// // if (httpResponse == null)
|
||||||
|
// // throw new Exception("no response returned");
|
||||||
|
// // return httpResponse;
|
||||||
|
// // })
|
||||||
|
// .Catch((exception)=>{
|
||||||
|
// Logging.Log(exception);
|
||||||
|
// if ((exception is HttpException httpException) && (httpException.HttpResponse != null))
|
||||||
|
// return httpException.HttpResponse;
|
||||||
|
// return HttpResponse
|
||||||
|
// .InternalServerError()
|
||||||
|
// .Content(String.Format("An internal error occured ({0})", exception.ToString()));
|
||||||
|
// })
|
||||||
|
// .Then((httpResponse)=>
|
||||||
|
// {
|
||||||
|
// if (httpResponse == null)
|
||||||
|
// return false;
|
||||||
|
//
|
||||||
|
// httpResponse.SendResponse(connection.GetStream());
|
||||||
|
// httpResponse?.ContentStream?.Dispose();
|
||||||
|
// return httpResponse.GetHeader("connection", "keep-alive").Equals("keep-alive");
|
||||||
|
// })
|
||||||
|
// .Then((keepalive)=>{
|
||||||
|
// if (keepalive)
|
||||||
|
// HandleConnection(connection, true);
|
||||||
|
// })
|
||||||
|
// .Finally(()=>{
|
||||||
|
// lock (this.currentConnections)
|
||||||
|
// currentConnections.Remove(connection);
|
||||||
|
// connection.Close();
|
||||||
|
// })
|
||||||
|
// ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SendResponse(Stream stream, HttpRequest request, HttpResponse response)
|
|
||||||
{
|
|
||||||
response.SendResponse(stream, request);
|
|
||||||
/*
|
|
||||||
request.FinishRequest();
|
|
||||||
response.SetHeader("Content-Length", response.ContentStream.Length.ToString());
|
|
||||||
|
|
||||||
StreamWriter streamWriter = new StreamWriter(stream);
|
|
||||||
streamWriter.NewLine = "\r\n";
|
|
||||||
|
|
||||||
streamWriter.WriteLine("{0} {1} {2}", request.Protocol, (int)response.HttpStatusCode, response.HttpStatusCode.ToString());
|
|
||||||
foreach (String headerName in response.GetHeaderNames())
|
|
||||||
{
|
|
||||||
streamWriter.WriteLine("{0}: {1}", headerName, response.GetHeader(headerName));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (HttpCookie httpCookie in response.Cookies)
|
|
||||||
{
|
|
||||||
streamWriter.WriteLine("Set-Cookie: {0}", httpCookie.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
streamWriter.WriteLine();
|
|
||||||
streamWriter.Flush();
|
|
||||||
|
|
||||||
response.ContentStream.Position = 0;
|
|
||||||
response.ContentStream.CopyTo(stream);
|
|
||||||
response.ContentStream.Close();
|
|
||||||
response.ContentStream.Dispose();
|
|
||||||
|
|
||||||
stream.Flush();
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void StartSimpleServer(string[] arguments)
|
public static void StartSimpleServer(string[] arguments)
|
||||||
{
|
{
|
||||||
ArgumentContainer argumentContainer = new ArgumentContainer(new Argument[]
|
ArgumentContainer argumentContainer = new ArgumentContainer(new Argument[]
|
||||||
|
|
|
@ -1,157 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using ln.threading;
|
|
||||||
using System.Net;
|
|
||||||
using ln.type;
|
|
||||||
using ln.logging;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
namespace ln.http
|
|
||||||
{
|
|
||||||
//public delegate void HTTPServerConnectionEvent(HTTPServerConnection connection);
|
|
||||||
|
|
||||||
//public class HTTPServerConnection : PoolJob
|
|
||||||
//{
|
|
||||||
// public static ThreadLocal<HTTPServerConnection> Current { get; } = new ThreadLocal<HTTPServerConnection>();
|
|
||||||
// static HashSet<HTTPServerConnection> currentConnections = new HashSet<HTTPServerConnection>();
|
|
||||||
// public static HTTPServerConnection[] CurrentConnections => currentConnections.ToArray();
|
|
||||||
|
|
||||||
// public HTTPServer HTTPServer { get; }
|
|
||||||
// public TcpClient TcpClient { get; }
|
|
||||||
|
|
||||||
// public HttpRequest CurrentRequest { get; protected set; }
|
|
||||||
|
|
||||||
// public event HTTPServerConnectionEvent AbortRequested;
|
|
||||||
|
|
||||||
// public DateTime Created { get; }
|
|
||||||
// public DateTime Interpreted { get; set; }
|
|
||||||
// public DateTime Finished { get; set; }
|
|
||||||
|
|
||||||
// public HTTPServerConnection(HTTPServer httpServer,TcpClient tcpClient)
|
|
||||||
// {
|
|
||||||
// HTTPServer = httpServer;
|
|
||||||
// TcpClient = tcpClient;
|
|
||||||
// Created = DateTime.Now;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public virtual HttpResponse GetResponse(HttpRequest httpRequest,HttpApplication httpApplication) => httpApplication.GetResponse(httpRequest);
|
|
||||||
|
|
||||||
// public virtual void Abort()
|
|
||||||
// {
|
|
||||||
// if (AbortRequested != null)
|
|
||||||
// AbortRequested(this);
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// public override void RunJob()
|
|
||||||
// {
|
|
||||||
// HTTPServerConnection saveCurrent = Current.Value;
|
|
||||||
// Current.Value = this;
|
|
||||||
// lock (currentConnections)
|
|
||||||
// currentConnections.Add(this);
|
|
||||||
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// setState("reading http request");
|
|
||||||
|
|
||||||
// HttpReader httpReader = new HttpReader(TcpClient.GetStream());
|
|
||||||
// httpReader.Read();
|
|
||||||
|
|
||||||
// if (!httpReader.Valid)
|
|
||||||
// return;
|
|
||||||
|
|
||||||
// HttpResponse response = null;
|
|
||||||
|
|
||||||
// using (CurrentRequest = new HttpRequest(this.HTTPServer,httpReader, (IPEndPoint)TcpClient.Client.LocalEndPoint))
|
|
||||||
// {
|
|
||||||
// Interpreted = DateTime.Now;
|
|
||||||
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// HttpApplication application = HTTPServer.GetHttpApplication(new URI(CurrentRequest.BaseURI.ToString()));
|
|
||||||
|
|
||||||
// application.Authenticate(CurrentRequest);
|
|
||||||
// application.Authorize(CurrentRequest);
|
|
||||||
|
|
||||||
// setState("handling http request");
|
|
||||||
|
|
||||||
// response = GetResponse(CurrentRequest, application);
|
|
||||||
// }
|
|
||||||
// catch (Exception e)
|
|
||||||
// {
|
|
||||||
// setState("handling exception");
|
|
||||||
|
|
||||||
// response = new HttpResponse(CurrentRequest, "text/plain");
|
|
||||||
// response.StatusCode = 500;
|
|
||||||
// response.ContentWriter.WriteLine("Exception caught: {0}", e);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// setState("sending response");
|
|
||||||
|
|
||||||
// if (response == null)
|
|
||||||
// {
|
|
||||||
// Logging.Log(LogLevel.DEBUG, "Request {0} returned no Response", CurrentRequest);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// if (!response.HasCustomContentStream)
|
|
||||||
// {
|
|
||||||
// response.ContentWriter.Flush();
|
|
||||||
// MemoryStream cstream = (MemoryStream)response.ContentStream;
|
|
||||||
// cstream.Position = 0;
|
|
||||||
|
|
||||||
// response.SetHeader("content-length", cstream.Length.ToString());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (CurrentRequest.Session != null)
|
|
||||||
// HTTPServer?.SessionCache?.ApplySessionID(response, CurrentRequest.Session);
|
|
||||||
|
|
||||||
// response.AddCookie("LN_SEEN", DateTime.Now.ToString());
|
|
||||||
|
|
||||||
// SendResponse(TcpClient.GetStream(), response);
|
|
||||||
// TcpClient.Close();
|
|
||||||
|
|
||||||
// Finished = DateTime.Now;
|
|
||||||
|
|
||||||
// HTTPServer.Log(Created, (Finished - Created).TotalMilliseconds, CurrentRequest, response);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// finally
|
|
||||||
// {
|
|
||||||
// Current.Value = saveCurrent;
|
|
||||||
// lock (currentConnections)
|
|
||||||
// currentConnections.Remove(this);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public static void SendResponse(Stream stream, HttpResponse response)
|
|
||||||
// {
|
|
||||||
// StreamWriter streamWriter = new StreamWriter(stream);
|
|
||||||
// streamWriter.NewLine = "\r\n";
|
|
||||||
|
|
||||||
// streamWriter.WriteLine("{0} {1} {2}", response.HttpRequest.Protocol, response.StatusCode, response.StatusMessage);
|
|
||||||
// foreach (String headerName in response.GetHeaderNames())
|
|
||||||
// {
|
|
||||||
// streamWriter.WriteLine("{0}: {1}", headerName, response.GetHeader(headerName));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// foreach (HttpCookie httpCookie in response.Cookies)
|
|
||||||
// {
|
|
||||||
// streamWriter.WriteLine("Set-Cookie: {0}", httpCookie.ToString());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// streamWriter.WriteLine();
|
|
||||||
// streamWriter.Flush();
|
|
||||||
|
|
||||||
// response.ContentStream.CopyTo(stream);
|
|
||||||
// response.ContentStream.Close();
|
|
||||||
// response.ContentStream.Dispose();
|
|
||||||
|
|
||||||
// streamWriter.Flush();
|
|
||||||
// }
|
|
||||||
|
|
||||||
//}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
using System;
|
|
||||||
namespace ln.http
|
|
||||||
{
|
|
||||||
public class HttpHeader
|
|
||||||
{
|
|
||||||
public String Name { get; }
|
|
||||||
public String Value { get; }
|
|
||||||
|
|
||||||
public HttpHeader(String rawHeader)
|
|
||||||
{
|
|
||||||
int colon = rawHeader.IndexOf(':');
|
|
||||||
if (colon < 0)
|
|
||||||
throw new FormatException("rawHeader must contain at least one colon");
|
|
||||||
|
|
||||||
Name = rawHeader.Substring(0, colon).Trim().ToUpper();
|
|
||||||
Value = rawHeader.Substring(colon + 1).Trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpHeader(String headerName,String headerValue)
|
|
||||||
{
|
|
||||||
if (String.Empty.Equals(headerName))
|
|
||||||
throw new ArgumentException("headerName needs to contain at least one character", nameof(headerName));
|
|
||||||
|
|
||||||
Name = headerName.ToUpper();
|
|
||||||
Value = headerValue.ToUpper();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
using System;
|
|
||||||
using ln.collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections;
|
|
||||||
|
|
||||||
namespace ln.http
|
|
||||||
{
|
|
||||||
public class HttpHeaders : IEnumerable<HttpHeader>
|
|
||||||
{
|
|
||||||
MappingBTree<string, HttpHeader> headers = new MappingBTree<string, HttpHeader>((value) => value.Name);
|
|
||||||
|
|
||||||
public HttpHeaders()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpHeader this[string headerName] => headers[headerName.ToUpper()];
|
|
||||||
public bool Contains(string headerName) => headers.ContainsKey(headerName.ToUpper());
|
|
||||||
|
|
||||||
public void Add(String headerName, String headerValue) => Add(new HttpHeader(headerName, headerValue));
|
|
||||||
public void Add(HttpHeader httpHeader) => headers.Add(httpHeader);
|
|
||||||
|
|
||||||
public void Remove(HttpHeader httpHeader) => headers.Remove(httpHeader);
|
|
||||||
public void Remove(string headerName) => headers.RemoveKey(headerName.ToUpper());
|
|
||||||
|
|
||||||
public void Set(String headerName, String headerValue)
|
|
||||||
{
|
|
||||||
if (headers.ContainsKey(headerName))
|
|
||||||
Remove(headerName);
|
|
||||||
|
|
||||||
Add(new HttpHeader(headerName, headerValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerator<HttpHeader> GetEnumerator() => headers.Values.GetEnumerator();
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => headers.Values.GetEnumerator();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,6 +3,6 @@
|
||||||
namespace ln.http
|
namespace ln.http
|
||||||
{
|
{
|
||||||
public enum HttpMethod {
|
public enum HttpMethod {
|
||||||
HEAD, GET, POST, PUT, PATCH, DELETE, CONNECT, OPTIONS, TRACE
|
UNKOWN, HEAD, GET, POST, PUT, PATCH, DELETE, CONNECT, OPTIONS, TRACE, PROPFIND, MKCOL, LOCK, UNLOCK
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,320 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net;
|
|
||||||
using ln.type;
|
|
||||||
using ln.http.message;
|
|
||||||
using ln.http.io;
|
|
||||||
using ln.http.message.parser;
|
|
||||||
|
|
||||||
namespace ln.http
|
|
||||||
{
|
|
||||||
//public class HttpReader
|
|
||||||
//{
|
|
||||||
// delegate bool ReadCondition(int b);
|
|
||||||
|
|
||||||
// public Stream Stream { get; }
|
|
||||||
|
|
||||||
// private byte[] buffer = new byte[8192];
|
|
||||||
// private int hlen;
|
|
||||||
// private int blen;
|
|
||||||
// private int bptr;
|
|
||||||
|
|
||||||
// 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<string, string> Headers { get; } = new Dictionary<string, string>();
|
|
||||||
|
|
||||||
// public bool Valid { get; private set; } = false;
|
|
||||||
|
|
||||||
// public HttpReader(Stream stream)
|
|
||||||
// {
|
|
||||||
// Stream = stream;
|
|
||||||
// }
|
|
||||||
// public HttpReader(Stream stream,Endpoint remoteEndpoint)
|
|
||||||
// {
|
|
||||||
// Stream = stream;
|
|
||||||
// RemoteEndpoint = remoteEndpoint;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public void Read()
|
|
||||||
// {
|
|
||||||
// UnbufferedStreamReader reader = new UnbufferedStreamReader(Stream);
|
|
||||||
// string requestLine = reader.ReadLine();
|
|
||||||
// string[] requestTokens = requestLine.Split(new char[0], StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
|
|
||||||
// if (requestTokens.Length != 3)
|
|
||||||
// throw new FormatException("request line malformed");
|
|
||||||
|
|
||||||
// Method = requestTokens[0];
|
|
||||||
// URL = requestTokens[1];
|
|
||||||
// Protocol = requestTokens[2];
|
|
||||||
|
|
||||||
// Headers = HTTP.ReadHeader(reader);
|
|
||||||
|
|
||||||
// Valid = true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public int ReadByte()
|
|
||||||
// {
|
|
||||||
// if (bptr >= blen)
|
|
||||||
// return -1;
|
|
||||||
// return buffer[bptr++];
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public int Current
|
|
||||||
// {
|
|
||||||
// get
|
|
||||||
// {
|
|
||||||
// if (bptr >= blen)
|
|
||||||
// return -1;
|
|
||||||
// return buffer[bptr];
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public void Reverse()
|
|
||||||
// {
|
|
||||||
// if ((bptr > 0) && (bptr < blen))
|
|
||||||
// bptr--;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public void ReadRequestHead()
|
|
||||||
// {
|
|
||||||
// bptr = 0;
|
|
||||||
// do
|
|
||||||
// {
|
|
||||||
// int rlen = Stream.Read(buffer, blen, buffer.Length - blen);
|
|
||||||
// if (rlen == 0)
|
|
||||||
// throw new IOException();
|
|
||||||
|
|
||||||
// blen += rlen;
|
|
||||||
// while (bptr <= (blen - 4))
|
|
||||||
// {
|
|
||||||
// if (
|
|
||||||
// (buffer[bptr + 0] == '\r') &&
|
|
||||||
// (buffer[bptr + 1] == '\n') &&
|
|
||||||
// (buffer[bptr + 2] == '\r') &&
|
|
||||||
// (buffer[bptr + 3] == '\n')
|
|
||||||
// )
|
|
||||||
// {
|
|
||||||
// hlen = bptr;
|
|
||||||
// bptr = 0;
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// bptr++;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// byte[] nbuffer = new byte[buffer.Length << 1];
|
|
||||||
// Array.Copy(buffer, nbuffer, buffer.Length);
|
|
||||||
// buffer = nbuffer;
|
|
||||||
// } while (blen >= buffer.Length);
|
|
||||||
// bptr = 0;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private String ReadConditional(ReadCondition readCondition,bool reverse = true)
|
|
||||||
// {
|
|
||||||
// StringBuilder stringBuilder = new StringBuilder();
|
|
||||||
// int b = ReadByte();
|
|
||||||
// while ((b != -1) && (readCondition(b)) )
|
|
||||||
// {
|
|
||||||
// stringBuilder.Append((char)b);
|
|
||||||
// b = ReadByte();
|
|
||||||
// }
|
|
||||||
// if (reverse)
|
|
||||||
// Reverse();
|
|
||||||
|
|
||||||
// return stringBuilder.ToString();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public void SkipWhiteSpace()
|
|
||||||
// {
|
|
||||||
// while (Char.IsWhiteSpace((char)ReadByte())) { };
|
|
||||||
// Reverse();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public String ReadToken()
|
|
||||||
// {
|
|
||||||
// return ReadConditional((b) => !Char.IsWhiteSpace((char)b));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public String ReadLine()
|
|
||||||
// {
|
|
||||||
// int p = bptr;
|
|
||||||
// while (p < blen - 1)
|
|
||||||
// {
|
|
||||||
// if ((buffer[p] == '\r') && (buffer[p + 1] == '\n'))
|
|
||||||
// {
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// p++;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// string result = Encoding.ASCII.GetString(buffer, bptr, p - bptr);
|
|
||||||
// bptr = p + 2;
|
|
||||||
// return result;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public String ReadHeaderName()
|
|
||||||
// {
|
|
||||||
// return ReadConditional((b) => b != ':', false).ToUpper();
|
|
||||||
// }
|
|
||||||
// public String ReadHeaderValue()
|
|
||||||
// {
|
|
||||||
// String value = ReadLine();
|
|
||||||
// while (Char.IsWhiteSpace((char)Current))
|
|
||||||
// {
|
|
||||||
// value = value + ReadLine();
|
|
||||||
// }
|
|
||||||
// return value.Trim();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //public void ReadHeaders()
|
|
||||||
// //{
|
|
||||||
// // while (bptr < hlen)
|
|
||||||
// // {
|
|
||||||
// // String name = ReadHeaderName();
|
|
||||||
// // String value = ReadHeaderValue();
|
|
||||||
|
|
||||||
// // Headers.Add(name, value);
|
|
||||||
// // }
|
|
||||||
// //}
|
|
||||||
|
|
||||||
// public int ReadRequestBody(byte[] dst,int offset,int length)
|
|
||||||
// {
|
|
||||||
// //int nRead = 0;
|
|
||||||
// //if (bptr < blen)
|
|
||||||
// //{
|
|
||||||
// // int len = Math.Min(length, blen - bptr);
|
|
||||||
// // Array.Copy(buffer, bptr, dst, offset, len);
|
|
||||||
// // bptr += len;
|
|
||||||
// // length -= len;
|
|
||||||
// // offset += len;
|
|
||||||
// // nRead += len;
|
|
||||||
// //}
|
|
||||||
// int nRead = 0;
|
|
||||||
// while (length > 0)
|
|
||||||
// {
|
|
||||||
// int nr = Stream.Read(dst, offset, length);
|
|
||||||
// if (nr > 0)
|
|
||||||
// {
|
|
||||||
// nRead += nr;
|
|
||||||
// length -= nr;
|
|
||||||
// offset += nr;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return nRead;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// /*
|
|
||||||
// byte[] buffer;
|
|
||||||
// int blen = 0;
|
|
||||||
// int bptr = 0;
|
|
||||||
|
|
||||||
// public HttpReader(Stream stream)
|
|
||||||
// {
|
|
||||||
// Stream = stream;
|
|
||||||
// buffer = new byte[1024];
|
|
||||||
// }
|
|
||||||
// public HttpReader(Stream stream, int buffersize)
|
|
||||||
// {
|
|
||||||
// Stream = stream;
|
|
||||||
// buffer = new byte[buffersize];
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private int read()
|
|
||||||
// {
|
|
||||||
// if (bptr >= blen)
|
|
||||||
// {
|
|
||||||
// bptr = 0;
|
|
||||||
// blen = Stream.Read(buffer, 0, buffer.Length);
|
|
||||||
// if (blen <= 0)
|
|
||||||
// throw new EndOfStreamException();
|
|
||||||
// }
|
|
||||||
// return buffer[bptr++];
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private int ReadTo(byte[] b,int offset,byte[] mark)
|
|
||||||
// {
|
|
||||||
// int pm = 0;
|
|
||||||
// int p = offset;
|
|
||||||
// while (p < b.Length)
|
|
||||||
// {
|
|
||||||
// b[p] = (byte)read();
|
|
||||||
// if (b[p] == mark[pm])
|
|
||||||
// {
|
|
||||||
// pm++;
|
|
||||||
// if (pm >= mark.Length)
|
|
||||||
// {
|
|
||||||
// p++;
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// pm = 0;
|
|
||||||
// }
|
|
||||||
// p++;
|
|
||||||
// }
|
|
||||||
// return p;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// public String ReadRequestLine(int maxSize = 1024)
|
|
||||||
// {
|
|
||||||
// byte[] b = new byte[maxSize];
|
|
||||||
// int l = ReadTo(b, 0, new byte[] { 0x0d, 0x0a });
|
|
||||||
// return Encoding.ASCII.GetString(b, 0, l);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public Dictionary<string,string> ReadHTTPHeaders()
|
|
||||||
// {
|
|
||||||
// byte[] b = new byte[8192];
|
|
||||||
// int hlen = ReadTo(b, 0, new byte[] { 0x0d, 0x0a, 0x0d, 0x0a });
|
|
||||||
|
|
||||||
// Dictionary<string, string> headers = new Dictionary<string, string>();
|
|
||||||
|
|
||||||
// string rawHeaders = Encoding.ASCII.GetString(b, 0, hlen);
|
|
||||||
// String[] rawLines = rawHeaders.Split(new String[] { "\r\n" }, StringSplitOptions.None);
|
|
||||||
|
|
||||||
// for (int n = rawLines.Length-1; n >= 0 ; n--)
|
|
||||||
// {
|
|
||||||
// if ((rawLines[n].Length > 0) && (Char.IsWhiteSpace(rawLines[n][0])))
|
|
||||||
// {
|
|
||||||
// rawLines[n - 1] = rawLines[n - 1] + rawLines[n];
|
|
||||||
// rawLines[n] = null;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// foreach (String rawLine in rawLines)
|
|
||||||
// {
|
|
||||||
// if (rawLine != null)
|
|
||||||
// {
|
|
||||||
// int colon = rawLine.IndexOf(':');
|
|
||||||
// if (colon > 0)
|
|
||||||
// {
|
|
||||||
// String name = rawLine.Substring(0, colon).Trim().ToUpper();
|
|
||||||
// String value = rawLine.Substring(colon + 1).Trim();
|
|
||||||
// headers.Add(name, value);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return headers;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public byte[] ReadRequestBody(byte[] buffer)
|
|
||||||
// {
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
// */
|
|
||||||
//}
|
|
||||||
|
|
||||||
}
|
|
|
@ -2,195 +2,83 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using ln.type;
|
using System.Net;
|
||||||
using ln.http.exceptions;
|
using ln.protocols.helper;
|
||||||
using System.Threading;
|
using ln.rtp;
|
||||||
using ln.http.session;
|
|
||||||
using ln.http.message;
|
|
||||||
using ln.http.io;
|
|
||||||
using ln.http.message.parser;
|
|
||||||
|
|
||||||
namespace ln.http
|
namespace ln.http
|
||||||
{
|
{ public class HttpRequest : IDisposable
|
||||||
public class HttpRequest : IDisposable
|
{
|
||||||
{
|
public Request BaseRequest { get; }
|
||||||
static ThreadLocal<HttpRequest> current = new ThreadLocal<HttpRequest>();
|
public HTTPServer Server { get; }
|
||||||
static public HttpRequest Current => current.Value;
|
|
||||||
|
public String Protocol => BaseRequest.Protocol;
|
||||||
|
public HttpMethod Method { get; }
|
||||||
|
|
||||||
|
public IPAddress ClientAddress { get; }
|
||||||
|
public String Host { get; }
|
||||||
|
public int Port { get; }
|
||||||
|
public bool TLS { get; }
|
||||||
|
|
||||||
|
public HeaderContainer Headers => BaseRequest.Headers;
|
||||||
|
public QueryStringParameters Query { get; private set; }
|
||||||
|
|
||||||
|
public Stream ContentStream => BaseRequest.ContentStream;
|
||||||
|
public Stream ConnectionStream => BaseRequest.ContentStream.BaseStream;
|
||||||
|
|
||||||
|
public Uri BaseUri { get; }
|
||||||
|
public Uri RequestUri { get; }
|
||||||
|
|
||||||
|
|
||||||
//Dictionary<String, String> requestHeaders;
|
|
||||||
HeaderContainer requestHeaders;
|
|
||||||
Dictionary<String, String> requestCookies;
|
Dictionary<String, String> requestCookies;
|
||||||
Dictionary<string, String> requestParameters;
|
Dictionary<string, String> requestParameters;
|
||||||
|
|
||||||
public HTTPServer HTTPServer { get; }
|
|
||||||
|
|
||||||
public Endpoint RemoteEndpoint { get; private set; }
|
public HttpRequest(HTTPServer httpServer, Request baseRequest)
|
||||||
public Endpoint LocalEndpoint { get; private set; }
|
|
||||||
|
|
||||||
public Uri BaseURI { get; set; }
|
|
||||||
public Uri URI { get; private set; }
|
|
||||||
|
|
||||||
[Obsolete]
|
|
||||||
public String Method { get => HttpMethod.ToString(); private set => throw new NotImplementedException(); }
|
|
||||||
public HttpMethod HttpMethod { get; private set; }
|
|
||||||
public String RequestURL { get; private set; }
|
|
||||||
public String Protocol { get; private set; }
|
|
||||||
|
|
||||||
public String Hostname { get; private set; }
|
|
||||||
public int Port { get; private set; }
|
|
||||||
|
|
||||||
public QueryStringParameters Query { get; private set; }
|
|
||||||
|
|
||||||
public Session Session { get; set; }
|
|
||||||
public HttpUser CurrentUser => Session.CurrentUser;
|
|
||||||
|
|
||||||
public HeaderContainer RequestHeaders => requestHeaders;
|
|
||||||
|
|
||||||
MemoryStream contentStream;
|
|
||||||
public MemoryStream ContentStream
|
|
||||||
{
|
{
|
||||||
get
|
Server = httpServer;
|
||||||
|
BaseRequest = baseRequest;
|
||||||
|
|
||||||
|
if (Enum.TryParse(BaseRequest.Method, out HttpMethod httpMethod))
|
||||||
|
Method = httpMethod;
|
||||||
|
else
|
||||||
|
Method = HttpMethod.UNKOWN;
|
||||||
|
|
||||||
|
Headers.TryGetValue("Host", out string host);
|
||||||
|
|
||||||
|
if (Headers.TryGetValue("X-Forwarded-Host", out string forwardedHost))
|
||||||
|
host = forwardedHost;
|
||||||
|
if (Headers.TryGetValue("X-Forwarded-For", out string forwardedFor))
|
||||||
|
ClientAddress = IPAddress.Parse(forwardedFor.ReadToken(','));
|
||||||
|
if (Headers.TryGetValue("X-Forwarded-Proto", out string forwardedProto))
|
||||||
|
TLS = forwardedProto.Equals("https");
|
||||||
|
|
||||||
|
if (Headers.TryGetValue("Forwarded", out string forwarded))
|
||||||
{
|
{
|
||||||
if (contentStream == null)
|
// ToDo: Implement parser
|
||||||
ReadRequestBody();
|
|
||||||
return contentStream;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public TextReader ContentReader
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (contentReader == null)
|
|
||||||
contentReader = new StreamReader(ContentStream);
|
|
||||||
return contentReader;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int requestBodyLength;
|
|
||||||
byte[] requestBody;
|
|
||||||
StreamReader contentReader;
|
|
||||||
|
|
||||||
Stream connectionStream;
|
|
||||||
UnbufferedStreamReader connectionReader;
|
|
||||||
|
|
||||||
public Stream GetConnectionStream() => connectionStream;
|
|
||||||
|
|
||||||
public HttpRequest(HTTPServer httpServer, Stream clientStream, Endpoint localEndpoint, Endpoint remoteEndpoint)
|
|
||||||
{
|
|
||||||
HTTPServer = httpServer;
|
|
||||||
connectionStream = clientStream;
|
|
||||||
connectionReader = new UnbufferedStreamReader(connectionStream);
|
|
||||||
|
|
||||||
LocalEndpoint = localEndpoint;
|
|
||||||
RemoteEndpoint = remoteEndpoint;
|
|
||||||
|
|
||||||
ReadRequestLine();
|
|
||||||
|
|
||||||
requestHeaders = HTTP.ReadHeader(connectionReader);
|
|
||||||
requestCookies = new Dictionary<string, string>();
|
|
||||||
requestParameters = new Dictionary<string, string>();
|
|
||||||
|
|
||||||
Setup();
|
|
||||||
|
|
||||||
requestBodyLength = int.Parse(GetRequestHeader("content-length", "0"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReadRequestLine()
|
|
||||||
{
|
|
||||||
string requestLine = connectionReader.ReadLine();
|
|
||||||
|
|
||||||
if (requestLine == null)
|
|
||||||
throw new ConnectionClosedException("UnbufferedStreamReader.ReadLine() returned null");
|
|
||||||
|
|
||||||
string[] requestTokens = requestLine.Split(new char[0], StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
|
|
||||||
if (requestTokens.Length != 3)
|
|
||||||
throw new BadRequestException();
|
|
||||||
|
|
||||||
HttpMethod = (HttpMethod)Enum.Parse(typeof(HttpMethod), requestTokens[0]);
|
|
||||||
RequestURL = requestTokens[1];
|
|
||||||
Protocol = requestTokens[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ReadRequestBody()
|
|
||||||
{
|
|
||||||
requestBody = new byte[requestBodyLength];
|
|
||||||
|
|
||||||
if (requestBodyLength > 0)
|
|
||||||
{
|
|
||||||
int nRead = 0;
|
|
||||||
int length = requestBodyLength;
|
|
||||||
|
|
||||||
while (length > 0)
|
|
||||||
{
|
|
||||||
int nr = connectionStream.Read(requestBody, nRead, length);
|
|
||||||
if (nr > 0)
|
|
||||||
{
|
|
||||||
nRead += nr;
|
|
||||||
length -= nr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
contentStream = new MemoryStream(requestBody);
|
int colon = host.IndexOf(':');
|
||||||
}
|
if (colon != -1)
|
||||||
|
|
||||||
public void FinishRequest()
|
|
||||||
{
|
|
||||||
if ((requestBodyLength > 0) && (requestBody == null))
|
|
||||||
{
|
{
|
||||||
int nRead = 0;
|
Host = host.Substring(0, colon).Trim();
|
||||||
int length = requestBodyLength;
|
Port = int.Parse(host.Substring(colon + 1).Trim());
|
||||||
byte[] discard = new byte[8192];
|
|
||||||
|
|
||||||
while (length > 0)
|
|
||||||
{
|
|
||||||
int nr = connectionStream.Read(discard,0,length > discard.Length ? discard.Length : length);
|
|
||||||
if (nr > 0)
|
|
||||||
{
|
|
||||||
nRead += nr;
|
|
||||||
length -= nr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Host = host.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseUri = new UriBuilder(TLS ? "https:" : "http:", Host, Port).Uri;
|
||||||
|
RequestUri = new Uri(BaseUri, BaseRequest.RequestUri);
|
||||||
|
Query = new QueryStringParameters(RequestUri.Query);
|
||||||
|
|
||||||
|
if (Headers.TryGetValue("Cookie", out string cookies))
|
||||||
|
SetupCookies(cookies);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MakeCurrent() => current.Value = this;
|
private void SetupCookies(string cookies)
|
||||||
public static void ClearCurrent() => current.Value = null;
|
|
||||||
|
|
||||||
private void Setup()
|
|
||||||
{
|
{
|
||||||
SetupResourceURI();
|
|
||||||
SetupCookies();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* SetupResourceURI()
|
|
||||||
*
|
|
||||||
* Setup the following fields:
|
|
||||||
*
|
|
||||||
* - Hostname
|
|
||||||
* - Port
|
|
||||||
* - BaseURI
|
|
||||||
* - URI
|
|
||||||
* - Query
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
private void SetupResourceURI()
|
|
||||||
{
|
|
||||||
String host = GetRequestHeader("HOST");
|
|
||||||
String[] hostTokens = host.Split(':');
|
|
||||||
|
|
||||||
Hostname = hostTokens[0];
|
|
||||||
Port = (hostTokens.Length > 1) ? int.Parse(hostTokens[1]) : LocalEndpoint.Port;
|
|
||||||
BaseURI = new UriBuilder("http", Hostname, Port).Uri;
|
|
||||||
URI = new Uri(BaseURI, RequestURL);
|
|
||||||
Query = new QueryStringParameters(URI.Query);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetupCookies()
|
|
||||||
{
|
|
||||||
string cookies = GetRequestHeader("COOKIE");
|
|
||||||
foreach (String cookie in cookies.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
|
foreach (String cookie in cookies.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
|
||||||
{
|
{
|
||||||
string[] c = cookie.Split(new char[] { '=' }, 2);
|
string[] c = cookie.Split(new char[] { '=' }, 2);
|
||||||
|
@ -207,56 +95,47 @@ namespace ln.http
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
//return string.Format("[HttpRequest: RemoteEndpoint={0}, Hostname={1} Port={2} URI={4}, Method={4}, RequestURL={5}, Protocol={6} Query={7}]", RemoteEndpoint, URI, Method, RequestURL, Protocol, Hostname, Port,Query);
|
//return string.Format("[HttpRequest: RemoteEndpoint={0}, Hostname={1} Port={2} URI={4}, Method={4}, RequestURL={5}, Protocol={6} Query={7}]", RemoteEndpoint, URI, Method, RequestURL, Protocol, Hostname, Port,Query);
|
||||||
return base.ToString();
|
return base.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String GetRequestHeader(String name)
|
public String GetRequestHeader(String name)
|
||||||
{
|
{
|
||||||
return GetRequestHeader(name, "");
|
return GetRequestHeader(name, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String GetRequestHeader(String name, String def)
|
public String GetRequestHeader(String name, String def)
|
||||||
{
|
{
|
||||||
name = name.ToUpper();
|
if (Headers.TryGetValue(name, out string value))
|
||||||
|
return value;
|
||||||
if (requestHeaders.ContainsKey(name))
|
return def;
|
||||||
return requestHeaders[name].Value;
|
}
|
||||||
|
|
||||||
return def;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String[] RequestHeaderNames => requestHeaders.Keys.ToArray();
|
|
||||||
|
|
||||||
public String[] CookieNames => requestCookies.Keys.ToArray();
|
public String[] CookieNames => requestCookies.Keys.ToArray();
|
||||||
public bool ContainsCookie(String name)
|
public bool ContainsCookie(String name) => this.requestCookies.ContainsKey(name);
|
||||||
{
|
public String GetCookie(String name) => requestCookies[name];
|
||||||
return this.requestCookies.ContainsKey(name);
|
|
||||||
}
|
public bool TryGetCookie(string cookieName, out string cookieValue) =>
|
||||||
public String GetCookie(String name)
|
requestCookies.TryGetValue(cookieName, out cookieValue);
|
||||||
{
|
|
||||||
return requestCookies[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ContainsParameter(string parameterName) => requestParameters.ContainsKey(parameterName);
|
public bool ContainsParameter(string parameterName) => requestParameters.ContainsKey(parameterName);
|
||||||
public String GetParameter(String parameterName) => GetParameter(parameterName, null);
|
public String GetParameter(String parameterName) => GetParameter(parameterName, null);
|
||||||
public String GetParameter(String parameterName,String defaultValue)
|
|
||||||
|
public String GetParameter(String parameterName, String defaultValue)
|
||||||
{
|
{
|
||||||
if (!requestParameters.TryGetValue(parameterName, out string value))
|
if (!requestParameters.TryGetValue(parameterName, out string value))
|
||||||
value = defaultValue;
|
value = defaultValue;
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
public void SetParameter(String parameterName,String parameterValue) => requestParameters[parameterName] = parameterValue;
|
|
||||||
|
public void SetParameter(String parameterName, String parameterValue) =>
|
||||||
|
requestParameters[parameterName] = parameterValue;
|
||||||
|
|
||||||
public IEnumerable<string> ParameterNames => requestParameters.Keys;
|
public IEnumerable<string> ParameterNames => requestParameters.Keys;
|
||||||
|
|
||||||
|
|
||||||
public string self()
|
|
||||||
{
|
|
||||||
return BaseURI.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public HttpResponse Redirect(string location, params object[] p) => Redirect(303, location, p);
|
public HttpResponse Redirect(string location, params object[] p) => Redirect(303, location, p);
|
||||||
|
|
||||||
public HttpResponse Redirect(int status, string location, params object[] p)
|
public HttpResponse Redirect(int status, string location, params object[] p)
|
||||||
{
|
{
|
||||||
location = string.Format(location, p);
|
location = string.Format(location, p);
|
||||||
|
@ -268,11 +147,19 @@ namespace ln.http
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public HttpResponse Redirect(HttpStatusCode statusCode, string location, params object[] p)
|
||||||
{
|
{
|
||||||
contentReader?.Dispose();
|
if (p?.Length > 0)
|
||||||
ContentStream?.Dispose();
|
location = string.Format(location, p);
|
||||||
|
|
||||||
|
HttpResponse response = new HttpResponse(statusCode);
|
||||||
|
response.SetHeader("Location", location);
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
ContentStream?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,8 +7,6 @@ namespace ln.http
|
||||||
{
|
{
|
||||||
public class HttpResponse
|
public class HttpResponse
|
||||||
{
|
{
|
||||||
//public HttpRequest HttpRequest { get; }
|
|
||||||
|
|
||||||
public Stream ContentStream { get; private set; }
|
public Stream ContentStream { get; private set; }
|
||||||
public TextWriter ContentWriter { get; private set; }
|
public TextWriter ContentWriter { get; private set; }
|
||||||
public bool HasCustomContentStream { get; private set; }
|
public bool HasCustomContentStream { get; private set; }
|
||||||
|
@ -16,7 +14,6 @@ namespace ln.http
|
||||||
Dictionary<string, List<String>> headers = new Dictionary<string, List<string>>();
|
Dictionary<string, List<String>> headers = new Dictionary<string, List<string>>();
|
||||||
List<HttpCookie> cookies = new List<HttpCookie>();
|
List<HttpCookie> cookies = new List<HttpCookie>();
|
||||||
|
|
||||||
|
|
||||||
public HttpResponse() : this(HttpStatusCode.OK)
|
public HttpResponse() : this(HttpStatusCode.OK)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -132,6 +129,9 @@ namespace ln.http
|
||||||
public static HttpResponse Accepted() => new HttpResponse(HttpStatusCode.Accepted);
|
public static HttpResponse Accepted() => new HttpResponse(HttpStatusCode.Accepted);
|
||||||
public static HttpResponse NoContent() => new HttpResponse(HttpStatusCode.NoContent);
|
public static HttpResponse NoContent() => new HttpResponse(HttpStatusCode.NoContent);
|
||||||
|
|
||||||
|
public static HttpResponse MultiStatus() =>
|
||||||
|
new HttpResponse(HttpStatusCode.MultiStatus).Header("content-type", "application/xml");
|
||||||
|
|
||||||
public static HttpResponse MovedPermanently() => new HttpResponse(HttpStatusCode.MovedPermanently);
|
public static HttpResponse MovedPermanently() => new HttpResponse(HttpStatusCode.MovedPermanently);
|
||||||
public static HttpResponse TemporaryRedirect() => new HttpResponse(HttpStatusCode.TemporaryRedirect);
|
public static HttpResponse TemporaryRedirect() => new HttpResponse(HttpStatusCode.TemporaryRedirect);
|
||||||
public static HttpResponse PermanentRedirect() => new HttpResponse(HttpStatusCode.PermanentRedirect);
|
public static HttpResponse PermanentRedirect() => new HttpResponse(HttpStatusCode.PermanentRedirect);
|
||||||
|
@ -142,6 +142,8 @@ namespace ln.http
|
||||||
public static HttpResponse Forbidden() => new HttpResponse(HttpStatusCode.Forbidden);
|
public static HttpResponse Forbidden() => new HttpResponse(HttpStatusCode.Forbidden);
|
||||||
public static HttpResponse MethodNotAllowed() => new HttpResponse(HttpStatusCode.MethodNotAllowed);
|
public static HttpResponse MethodNotAllowed() => new HttpResponse(HttpStatusCode.MethodNotAllowed);
|
||||||
public static HttpResponse RequestTimeout() => new HttpResponse(HttpStatusCode.RequestTimeout);
|
public static HttpResponse RequestTimeout() => new HttpResponse(HttpStatusCode.RequestTimeout);
|
||||||
|
public static HttpResponse Conflict() => new HttpResponse(HttpStatusCode.Conflict);
|
||||||
|
public static HttpResponse UnsupportedMediaType() => new HttpResponse(HttpStatusCode.UnsupportedMediaType);
|
||||||
public static HttpResponse ImATeapot() => new HttpResponse(HttpStatusCode.ImATeapot);
|
public static HttpResponse ImATeapot() => new HttpResponse(HttpStatusCode.ImATeapot);
|
||||||
public static HttpResponse UpgradeRequired() => new HttpResponse(HttpStatusCode.UpgradeRequired);
|
public static HttpResponse UpgradeRequired() => new HttpResponse(HttpStatusCode.UpgradeRequired);
|
||||||
|
|
||||||
|
@ -169,19 +171,12 @@ namespace ln.http
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void WriteTo(Stream stream)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public virtual void SendResponse(Stream stream, HttpRequest httpRequest)
|
|
||||||
{
|
{
|
||||||
httpRequest.FinishRequest();
|
|
||||||
SetHeader("Content-Length", ContentStream.Length.ToString());
|
SetHeader("Content-Length", ContentStream.Length.ToString());
|
||||||
|
|
||||||
StreamWriter streamWriter = new StreamWriter(stream);
|
StreamWriter streamWriter = new StreamWriter(stream) {NewLine = "\r\n"};
|
||||||
streamWriter.NewLine = "\r\n";
|
streamWriter.WriteLine("{0} {1} {2}", "HTTP/1.1", (int)HttpStatusCode, HttpStatusCode.ToString());
|
||||||
|
|
||||||
streamWriter.WriteLine("{0} {1} {2}", httpRequest.Protocol, (int)HttpStatusCode, HttpStatusCode.ToString());
|
|
||||||
foreach (String headerName in GetHeaderNames())
|
foreach (String headerName in GetHeaderNames())
|
||||||
{
|
{
|
||||||
streamWriter.WriteLine("{0}: {1}", headerName, GetHeader(headerName));
|
streamWriter.WriteLine("{0}: {1}", headerName, GetHeader(headerName));
|
||||||
|
@ -202,9 +197,5 @@ namespace ln.http
|
||||||
|
|
||||||
stream.Flush();
|
stream.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
using System;
|
|
||||||
using ln.logging;
|
|
||||||
using ln.http.router;
|
|
||||||
namespace ln.http
|
|
||||||
{
|
|
||||||
//public abstract class HttpRouter : IHttpRouter
|
|
||||||
//{
|
|
||||||
// public HttpRouter()
|
|
||||||
// {
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public abstract IHTTPResource FindResource(HttpRequest httpRequest);
|
|
||||||
|
|
||||||
// public virtual HttpResponse Route(HttpRoutingContext routingContext, HttpRequest httpRequest)
|
|
||||||
// {
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// IHTTPResource resource = FindResource(httpRequest);
|
|
||||||
// return resource.GetResponse(httpRequest);
|
|
||||||
// } catch (Exception e)
|
|
||||||
// {
|
|
||||||
// Logging.Log(e);
|
|
||||||
// if (httpRequest != null)
|
|
||||||
// {
|
|
||||||
// HttpResponse httpResponse = new HttpResponse(httpRequest);
|
|
||||||
// httpResponse.StatusCode = 500;
|
|
||||||
// httpResponse.ContentWriter.WriteLine("500 Internal Server Error");
|
|
||||||
|
|
||||||
// return httpResponse;
|
|
||||||
// }
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
//}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
// /**
|
|
||||||
// * File: HttpUser.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
|
|
||||||
{
|
|
||||||
public class HttpUser
|
|
||||||
{
|
|
||||||
public String AuthenticationName { get; private set; }
|
|
||||||
public virtual String DisplayName { get; private set; }
|
|
||||||
|
|
||||||
public long AccessRightsMask { get; private set; }
|
|
||||||
|
|
||||||
public HttpUser()
|
|
||||||
{
|
|
||||||
AuthenticationName = "";
|
|
||||||
DisplayName = "Anonymous";
|
|
||||||
AccessRightsMask = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
using System;
|
|
||||||
namespace ln.http
|
|
||||||
{
|
|
||||||
public interface IHTTPResource
|
|
||||||
{
|
|
||||||
HttpResponse GetResponse(HttpRequest httpRequest);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,6 +3,8 @@ using ln.type;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Net.Security;
|
using System.Net.Security;
|
||||||
|
using ln.protocols.helper;
|
||||||
|
|
||||||
namespace ln.http.client
|
namespace ln.http.client
|
||||||
{
|
{
|
||||||
public class HttpClientRequest
|
public class HttpClientRequest
|
||||||
|
@ -19,7 +21,7 @@ namespace ln.http.client
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpHeaders Headers { get; } = new HttpHeaders();
|
public HeaderContainer Headers { get; } = new HeaderContainer();
|
||||||
public String Method { get; set; }
|
public String Method { get; set; }
|
||||||
|
|
||||||
URI uri;
|
URI uri;
|
||||||
|
|
|
@ -10,9 +10,11 @@
|
||||||
using System;
|
using System;
|
||||||
using ln.type;
|
using ln.type;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
using ln.logging;
|
using ln.logging;
|
||||||
using ln.http.listener;
|
using ln.http.listener;
|
||||||
using ln.http.exceptions;
|
using ln.http.exceptions;
|
||||||
|
using ln.protocols.helper;
|
||||||
|
|
||||||
namespace ln.http.connections
|
namespace ln.http.connections
|
||||||
{
|
{
|
||||||
|
@ -34,7 +36,9 @@ namespace ln.http.connections
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return new HttpRequest(httpServer, GetStream(), Listener.LocalEndpoint, new Endpoint(RemoteHost, RemotePort));
|
if (HttpLikeProtocolReader.ReadRequest(GetStream(), Encoding.UTF8, out Request request))
|
||||||
|
return new HttpRequest(httpServer, request);
|
||||||
|
return null;
|
||||||
} catch (IOException)
|
} catch (IOException)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
// **/
|
// **/
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Security.AccessControl;
|
|
||||||
using ln.type;
|
using ln.type;
|
||||||
using System.Net.Security;
|
using System.Net.Security;
|
||||||
using ln.http.listener;
|
using ln.http.listener;
|
||||||
|
@ -29,6 +28,7 @@ namespace ln.http.connections
|
||||||
{
|
{
|
||||||
Connection = connection;
|
Connection = connection;
|
||||||
sslStream = new SslStream(connection.GetStream(),false, null, localCertificateSelectionCallback);
|
sslStream = new SslStream(connection.GetStream(),false, null, localCertificateSelectionCallback);
|
||||||
|
sslStream.AuthenticateAsServer(new System.Security.Cryptography.X509Certificates.X509Certificate());
|
||||||
}
|
}
|
||||||
|
|
||||||
public override HttpRequest ReadRequest(HTTPServer server)
|
public override HttpRequest ReadRequest(HTTPServer server)
|
||||||
|
|
|
@ -6,6 +6,8 @@ namespace ln.http.exceptions
|
||||||
{
|
{
|
||||||
public int StatusCode { get; } = 500;
|
public int StatusCode { get; } = 500;
|
||||||
|
|
||||||
|
public virtual HttpResponse HttpResponse { get; protected set; }
|
||||||
|
|
||||||
public HttpException(String message)
|
public HttpException(String message)
|
||||||
: base(message)
|
: base(message)
|
||||||
{
|
{
|
||||||
|
|
|
@ -25,6 +25,8 @@ namespace ln.http.listener
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override Endpoint LocalEndpoint => new Endpoint(tcpListener.LocalEndpoint);
|
||||||
|
|
||||||
public override Connection Accept() => new HttpConnection(this,tcpListener.AcceptTcpClient());
|
public override Connection Accept() => new HttpConnection(this,tcpListener.AcceptTcpClient());
|
||||||
|
|
||||||
public override bool IsOpen => (tcpListener != null);
|
public override bool IsOpen => (tcpListener != null);
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace ln.http.listener
|
||||||
public IPv6 Listen { get; }
|
public IPv6 Listen { get; }
|
||||||
public int Port { get; }
|
public int Port { get; }
|
||||||
|
|
||||||
public Endpoint LocalEndpoint => new Endpoint(Listen, Port);
|
public virtual Endpoint LocalEndpoint => new Endpoint(Listen, Port);
|
||||||
|
|
||||||
public abstract bool IsOpen { get; }
|
public abstract bool IsOpen { get; }
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,27 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
<Version>0.4.2</Version>
|
<Version>0.4.3-ci</Version>
|
||||||
<Authors>Harald Wolff-Thobaben</Authors>
|
<Authors>Harald Wolff-Thobaben</Authors>
|
||||||
<Company>l--n.de</Company>
|
<Company>l--n.de</Company>
|
||||||
<Description />
|
<Description />
|
||||||
<Copyright>(c) 2020 Harald Wolff-Thobaben</Copyright>
|
<Copyright>(c) 2020 Harald Wolff-Thobaben</Copyright>
|
||||||
<PackageTags>http server</PackageTags>
|
<PackageTags>http server</PackageTags>
|
||||||
|
<LangVersion>9</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ln.application" Version="0.1" />
|
<PackageReference Include="ln.application" Version="0.1" />
|
||||||
<PackageReference Include="ln.collections" Version="0.1" />
|
<PackageReference Include="ln.collections" Version="0.1" />
|
||||||
<PackageReference Include="ln.logging" Version="1.0" />
|
<PackageReference Include="ln.logging" Version="1.0" />
|
||||||
<PackageReference Include="ln.threading" Version="0.1" />
|
<PackageReference Include="ln.threading" Version="0.2.1" />
|
||||||
<PackageReference Include="ln.type" Version="0.1-*" />
|
<PackageReference Include="ln.type" Version="0.1-*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\ln.protocols.helper\ln.protocols.helper.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -1,159 +0,0 @@
|
||||||
// /**
|
|
||||||
// * 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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
// /**
|
|
||||||
// * 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;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,153 +0,0 @@
|
||||||
// /**
|
|
||||||
// * 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.type;
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,767 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace ln.http.message
|
|
||||||
{
|
|
||||||
public static class MimeTypeMap
|
|
||||||
{
|
|
||||||
private static readonly Lazy<IDictionary<string, string>> _mappings = new Lazy<IDictionary<string, string>>(BuildMappings);
|
|
||||||
|
|
||||||
private static IDictionary<string, string> BuildMappings()
|
|
||||||
{
|
|
||||||
var mappings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) {
|
|
||||||
|
|
||||||
#region Big freaking list of mime types
|
|
||||||
|
|
||||||
// maps both ways,
|
|
||||||
// extension -> mime type
|
|
||||||
// and
|
|
||||||
// mime type -> extension
|
|
||||||
//
|
|
||||||
// any mime types on left side not pre-loaded on right side, are added automatically
|
|
||||||
// some mime types can map to multiple extensions, so to get a deterministic mapping,
|
|
||||||
// add those to the dictionary specifcially
|
|
||||||
//
|
|
||||||
// combination of values from Windows 7 Registry and
|
|
||||||
// from C:\Windows\System32\inetsrv\config\applicationHost.config
|
|
||||||
// some added, including .7z and .dat
|
|
||||||
//
|
|
||||||
// Some added based on http://www.iana.org/assignments/media-types/media-types.xhtml
|
|
||||||
// which lists mime types, but not extensions
|
|
||||||
//
|
|
||||||
{".323", "text/h323"},
|
|
||||||
{".3g2", "video/3gpp2"},
|
|
||||||
{".3gp", "video/3gpp"},
|
|
||||||
{".3gp2", "video/3gpp2"},
|
|
||||||
{".3gpp", "video/3gpp"},
|
|
||||||
{".7z", "application/x-7z-compressed"},
|
|
||||||
{".aa", "audio/audible"},
|
|
||||||
{".AAC", "audio/aac"},
|
|
||||||
{".aaf", "application/octet-stream"},
|
|
||||||
{".aax", "audio/vnd.audible.aax"},
|
|
||||||
{".ac3", "audio/ac3"},
|
|
||||||
{".aca", "application/octet-stream"},
|
|
||||||
{".accda", "application/msaccess.addin"},
|
|
||||||
{".accdb", "application/msaccess"},
|
|
||||||
{".accdc", "application/msaccess.cab"},
|
|
||||||
{".accde", "application/msaccess"},
|
|
||||||
{".accdr", "application/msaccess.runtime"},
|
|
||||||
{".accdt", "application/msaccess"},
|
|
||||||
{".accdw", "application/msaccess.webapplication"},
|
|
||||||
{".accft", "application/msaccess.ftemplate"},
|
|
||||||
{".acx", "application/internet-property-stream"},
|
|
||||||
{".AddIn", "text/xml"},
|
|
||||||
{".ade", "application/msaccess"},
|
|
||||||
{".adobebridge", "application/x-bridge-url"},
|
|
||||||
{".adp", "application/msaccess"},
|
|
||||||
{".ADT", "audio/vnd.dlna.adts"},
|
|
||||||
{".ADTS", "audio/aac"},
|
|
||||||
{".afm", "application/octet-stream"},
|
|
||||||
{".ai", "application/postscript"},
|
|
||||||
{".aif", "audio/aiff"},
|
|
||||||
{".aifc", "audio/aiff"},
|
|
||||||
{".aiff", "audio/aiff"},
|
|
||||||
{".air", "application/vnd.adobe.air-application-installer-package+zip"},
|
|
||||||
{".amc", "application/mpeg"},
|
|
||||||
{".anx", "application/annodex"},
|
|
||||||
{".apk", "application/vnd.android.package-archive" },
|
|
||||||
{".application", "application/x-ms-application"},
|
|
||||||
{".art", "image/x-jg"},
|
|
||||||
{".asa", "application/xml"},
|
|
||||||
{".asax", "application/xml"},
|
|
||||||
{".ascx", "application/xml"},
|
|
||||||
{".asd", "application/octet-stream"},
|
|
||||||
{".asf", "video/x-ms-asf"},
|
|
||||||
{".ashx", "application/xml"},
|
|
||||||
{".asi", "application/octet-stream"},
|
|
||||||
{".asm", "text/plain"},
|
|
||||||
{".asmx", "application/xml"},
|
|
||||||
{".aspx", "application/xml"},
|
|
||||||
{".asr", "video/x-ms-asf"},
|
|
||||||
{".asx", "video/x-ms-asf"},
|
|
||||||
{".atom", "application/atom+xml"},
|
|
||||||
{".au", "audio/basic"},
|
|
||||||
{".avi", "video/x-msvideo"},
|
|
||||||
{".axa", "audio/annodex"},
|
|
||||||
{".axs", "application/olescript"},
|
|
||||||
{".axv", "video/annodex"},
|
|
||||||
{".bas", "text/plain"},
|
|
||||||
{".bcpio", "application/x-bcpio"},
|
|
||||||
{".bin", "application/octet-stream"},
|
|
||||||
{".bmp", "image/bmp"},
|
|
||||||
{".c", "text/plain"},
|
|
||||||
{".cab", "application/octet-stream"},
|
|
||||||
{".caf", "audio/x-caf"},
|
|
||||||
{".calx", "application/vnd.ms-office.calx"},
|
|
||||||
{".cat", "application/vnd.ms-pki.seccat"},
|
|
||||||
{".cc", "text/plain"},
|
|
||||||
{".cd", "text/plain"},
|
|
||||||
{".cdda", "audio/aiff"},
|
|
||||||
{".cdf", "application/x-cdf"},
|
|
||||||
{".cer", "application/x-x509-ca-cert"},
|
|
||||||
{".cfg", "text/plain"},
|
|
||||||
{".chm", "application/octet-stream"},
|
|
||||||
{".class", "application/x-java-applet"},
|
|
||||||
{".clp", "application/x-msclip"},
|
|
||||||
{".cmd", "text/plain"},
|
|
||||||
{".cmx", "image/x-cmx"},
|
|
||||||
{".cnf", "text/plain"},
|
|
||||||
{".cod", "image/cis-cod"},
|
|
||||||
{".config", "application/xml"},
|
|
||||||
{".contact", "text/x-ms-contact"},
|
|
||||||
{".coverage", "application/xml"},
|
|
||||||
{".cpio", "application/x-cpio"},
|
|
||||||
{".cpp", "text/plain"},
|
|
||||||
{".crd", "application/x-mscardfile"},
|
|
||||||
{".crl", "application/pkix-crl"},
|
|
||||||
{".crt", "application/x-x509-ca-cert"},
|
|
||||||
{".cs", "text/plain"},
|
|
||||||
{".csdproj", "text/plain"},
|
|
||||||
{".csh", "application/x-csh"},
|
|
||||||
{".csproj", "text/plain"},
|
|
||||||
{".css", "text/css"},
|
|
||||||
{".csv", "text/csv"},
|
|
||||||
{".cur", "application/octet-stream"},
|
|
||||||
{".cxx", "text/plain"},
|
|
||||||
{".dat", "application/octet-stream"},
|
|
||||||
{".datasource", "application/xml"},
|
|
||||||
{".dbproj", "text/plain"},
|
|
||||||
{".dcr", "application/x-director"},
|
|
||||||
{".def", "text/plain"},
|
|
||||||
{".deploy", "application/octet-stream"},
|
|
||||||
{".der", "application/x-x509-ca-cert"},
|
|
||||||
{".dgml", "application/xml"},
|
|
||||||
{".dib", "image/bmp"},
|
|
||||||
{".dif", "video/x-dv"},
|
|
||||||
{".dir", "application/x-director"},
|
|
||||||
{".disco", "text/xml"},
|
|
||||||
{".divx", "video/divx"},
|
|
||||||
{".dll", "application/x-msdownload"},
|
|
||||||
{".dll.config", "text/xml"},
|
|
||||||
{".dlm", "text/dlm"},
|
|
||||||
{".doc", "application/msword"},
|
|
||||||
{".docm", "application/vnd.ms-word.document.macroEnabled.12"},
|
|
||||||
{".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
|
|
||||||
{".dot", "application/msword"},
|
|
||||||
{".dotm", "application/vnd.ms-word.template.macroEnabled.12"},
|
|
||||||
{".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"},
|
|
||||||
{".dsp", "application/octet-stream"},
|
|
||||||
{".dsw", "text/plain"},
|
|
||||||
{".dtd", "text/xml"},
|
|
||||||
{".dtsConfig", "text/xml"},
|
|
||||||
{".dv", "video/x-dv"},
|
|
||||||
{".dvi", "application/x-dvi"},
|
|
||||||
{".dwf", "drawing/x-dwf"},
|
|
||||||
{".dwg", "application/acad"},
|
|
||||||
{".dwp", "application/octet-stream"},
|
|
||||||
{".dxf", "application/x-dxf" },
|
|
||||||
{".dxr", "application/x-director"},
|
|
||||||
{".eml", "message/rfc822"},
|
|
||||||
{".emz", "application/octet-stream"},
|
|
||||||
{".eot", "application/vnd.ms-fontobject"},
|
|
||||||
{".eps", "application/postscript"},
|
|
||||||
{".es", "application/ecmascript"},
|
|
||||||
{".etl", "application/etl"},
|
|
||||||
{".etx", "text/x-setext"},
|
|
||||||
{".evy", "application/envoy"},
|
|
||||||
{".exe", "application/vnd.microsoft.portable-executable"},
|
|
||||||
{".exe.config", "text/xml"},
|
|
||||||
{".f4v", "video/mp4"},
|
|
||||||
{".fdf", "application/vnd.fdf"},
|
|
||||||
{".fif", "application/fractals"},
|
|
||||||
{".filters", "application/xml"},
|
|
||||||
{".fla", "application/octet-stream"},
|
|
||||||
{".flac", "audio/flac"},
|
|
||||||
{".flr", "x-world/x-vrml"},
|
|
||||||
{".flv", "video/x-flv"},
|
|
||||||
{".fsscript", "application/fsharp-script"},
|
|
||||||
{".fsx", "application/fsharp-script"},
|
|
||||||
{".generictest", "application/xml"},
|
|
||||||
{".gif", "image/gif"},
|
|
||||||
{".gpx", "application/gpx+xml"},
|
|
||||||
{".group", "text/x-ms-group"},
|
|
||||||
{".gsm", "audio/x-gsm"},
|
|
||||||
{".gtar", "application/x-gtar"},
|
|
||||||
{".gz", "application/x-gzip"},
|
|
||||||
{".h", "text/plain"},
|
|
||||||
{".hdf", "application/x-hdf"},
|
|
||||||
{".hdml", "text/x-hdml"},
|
|
||||||
{".hhc", "application/x-oleobject"},
|
|
||||||
{".hhk", "application/octet-stream"},
|
|
||||||
{".hhp", "application/octet-stream"},
|
|
||||||
{".hlp", "application/winhlp"},
|
|
||||||
{".hpp", "text/plain"},
|
|
||||||
{".hqx", "application/mac-binhex40"},
|
|
||||||
{".hta", "application/hta"},
|
|
||||||
{".htc", "text/x-component"},
|
|
||||||
{".htm", "text/html"},
|
|
||||||
{".html", "text/html"},
|
|
||||||
{".htt", "text/webviewhtml"},
|
|
||||||
{".hxa", "application/xml"},
|
|
||||||
{".hxc", "application/xml"},
|
|
||||||
{".hxd", "application/octet-stream"},
|
|
||||||
{".hxe", "application/xml"},
|
|
||||||
{".hxf", "application/xml"},
|
|
||||||
{".hxh", "application/octet-stream"},
|
|
||||||
{".hxi", "application/octet-stream"},
|
|
||||||
{".hxk", "application/xml"},
|
|
||||||
{".hxq", "application/octet-stream"},
|
|
||||||
{".hxr", "application/octet-stream"},
|
|
||||||
{".hxs", "application/octet-stream"},
|
|
||||||
{".hxt", "text/html"},
|
|
||||||
{".hxv", "application/xml"},
|
|
||||||
{".hxw", "application/octet-stream"},
|
|
||||||
{".hxx", "text/plain"},
|
|
||||||
{".i", "text/plain"},
|
|
||||||
{".ico", "image/x-icon"},
|
|
||||||
{".ics", "application/octet-stream"},
|
|
||||||
{".idl", "text/plain"},
|
|
||||||
{".ief", "image/ief"},
|
|
||||||
{".iii", "application/x-iphone"},
|
|
||||||
{".inc", "text/plain"},
|
|
||||||
{".inf", "application/octet-stream"},
|
|
||||||
{".ini", "text/plain"},
|
|
||||||
{".inl", "text/plain"},
|
|
||||||
{".ins", "application/x-internet-signup"},
|
|
||||||
{".ipa", "application/x-itunes-ipa"},
|
|
||||||
{".ipg", "application/x-itunes-ipg"},
|
|
||||||
{".ipproj", "text/plain"},
|
|
||||||
{".ipsw", "application/x-itunes-ipsw"},
|
|
||||||
{".iqy", "text/x-ms-iqy"},
|
|
||||||
{".isp", "application/x-internet-signup"},
|
|
||||||
{".isma", "application/octet-stream"},
|
|
||||||
{".ismv", "application/octet-stream"},
|
|
||||||
{".ite", "application/x-itunes-ite"},
|
|
||||||
{".itlp", "application/x-itunes-itlp"},
|
|
||||||
{".itms", "application/x-itunes-itms"},
|
|
||||||
{".itpc", "application/x-itunes-itpc"},
|
|
||||||
{".IVF", "video/x-ivf"},
|
|
||||||
{".jar", "application/java-archive"},
|
|
||||||
{".java", "application/octet-stream"},
|
|
||||||
{".jck", "application/liquidmotion"},
|
|
||||||
{".jcz", "application/liquidmotion"},
|
|
||||||
{".jfif", "image/pjpeg"},
|
|
||||||
{".jnlp", "application/x-java-jnlp-file"},
|
|
||||||
{".jpb", "application/octet-stream"},
|
|
||||||
{".jpe", "image/jpeg"},
|
|
||||||
{".jpeg", "image/jpeg"},
|
|
||||||
{".jpg", "image/jpeg"},
|
|
||||||
{".js", "application/javascript"},
|
|
||||||
{".json", "application/json"},
|
|
||||||
{".jsx", "text/jscript"},
|
|
||||||
{".jsxbin", "text/plain"},
|
|
||||||
{".latex", "application/x-latex"},
|
|
||||||
{".library-ms", "application/windows-library+xml"},
|
|
||||||
{".lit", "application/x-ms-reader"},
|
|
||||||
{".loadtest", "application/xml"},
|
|
||||||
{".lpk", "application/octet-stream"},
|
|
||||||
{".lsf", "video/x-la-asf"},
|
|
||||||
{".lst", "text/plain"},
|
|
||||||
{".lsx", "video/x-la-asf"},
|
|
||||||
{".lzh", "application/octet-stream"},
|
|
||||||
{".m13", "application/x-msmediaview"},
|
|
||||||
{".m14", "application/x-msmediaview"},
|
|
||||||
{".m1v", "video/mpeg"},
|
|
||||||
{".m2t", "video/vnd.dlna.mpeg-tts"},
|
|
||||||
{".m2ts", "video/vnd.dlna.mpeg-tts"},
|
|
||||||
{".m2v", "video/mpeg"},
|
|
||||||
{".m3u", "audio/x-mpegurl"},
|
|
||||||
{".m3u8", "audio/x-mpegurl"},
|
|
||||||
{".m4a", "audio/m4a"},
|
|
||||||
{".m4b", "audio/m4b"},
|
|
||||||
{".m4p", "audio/m4p"},
|
|
||||||
{".m4r", "audio/x-m4r"},
|
|
||||||
{".m4v", "video/x-m4v"},
|
|
||||||
{".mac", "image/x-macpaint"},
|
|
||||||
{".mak", "text/plain"},
|
|
||||||
{".man", "application/x-troff-man"},
|
|
||||||
{".manifest", "application/x-ms-manifest"},
|
|
||||||
{".map", "text/plain"},
|
|
||||||
{".master", "application/xml"},
|
|
||||||
{".mbox", "application/mbox"},
|
|
||||||
{".mda", "application/msaccess"},
|
|
||||||
{".mdb", "application/x-msaccess"},
|
|
||||||
{".mde", "application/msaccess"},
|
|
||||||
{".mdp", "application/octet-stream"},
|
|
||||||
{".me", "application/x-troff-me"},
|
|
||||||
{".mfp", "application/x-shockwave-flash"},
|
|
||||||
{".mht", "message/rfc822"},
|
|
||||||
{".mhtml", "message/rfc822"},
|
|
||||||
{".mid", "audio/mid"},
|
|
||||||
{".midi", "audio/mid"},
|
|
||||||
{".mix", "application/octet-stream"},
|
|
||||||
{".mk", "text/plain"},
|
|
||||||
{".mk3d", "video/x-matroska-3d"},
|
|
||||||
{".mka", "audio/x-matroska"},
|
|
||||||
{".mkv", "video/x-matroska"},
|
|
||||||
{".mmf", "application/x-smaf"},
|
|
||||||
{".mno", "text/xml"},
|
|
||||||
{".mny", "application/x-msmoney"},
|
|
||||||
{".mod", "video/mpeg"},
|
|
||||||
{".mov", "video/quicktime"},
|
|
||||||
{".movie", "video/x-sgi-movie"},
|
|
||||||
{".mp2", "video/mpeg"},
|
|
||||||
{".mp2v", "video/mpeg"},
|
|
||||||
{".mp3", "audio/mpeg"},
|
|
||||||
{".mp4", "video/mp4"},
|
|
||||||
{".mp4v", "video/mp4"},
|
|
||||||
{".mpa", "video/mpeg"},
|
|
||||||
{".mpe", "video/mpeg"},
|
|
||||||
{".mpeg", "video/mpeg"},
|
|
||||||
{".mpf", "application/vnd.ms-mediapackage"},
|
|
||||||
{".mpg", "video/mpeg"},
|
|
||||||
{".mpp", "application/vnd.ms-project"},
|
|
||||||
{".mpv2", "video/mpeg"},
|
|
||||||
{".mqv", "video/quicktime"},
|
|
||||||
{".ms", "application/x-troff-ms"},
|
|
||||||
{".msg", "application/vnd.ms-outlook"},
|
|
||||||
{".msi", "application/octet-stream"},
|
|
||||||
{".mso", "application/octet-stream"},
|
|
||||||
{".mts", "video/vnd.dlna.mpeg-tts"},
|
|
||||||
{".mtx", "application/xml"},
|
|
||||||
{".mvb", "application/x-msmediaview"},
|
|
||||||
{".mvc", "application/x-miva-compiled"},
|
|
||||||
{".mxf", "application/mxf"},
|
|
||||||
{".mxp", "application/x-mmxp"},
|
|
||||||
{".nc", "application/x-netcdf"},
|
|
||||||
{".nsc", "video/x-ms-asf"},
|
|
||||||
{".nws", "message/rfc822"},
|
|
||||||
{".ocx", "application/octet-stream"},
|
|
||||||
{".oda", "application/oda"},
|
|
||||||
{".odb", "application/vnd.oasis.opendocument.database"},
|
|
||||||
{".odc", "application/vnd.oasis.opendocument.chart"},
|
|
||||||
{".odf", "application/vnd.oasis.opendocument.formula"},
|
|
||||||
{".odg", "application/vnd.oasis.opendocument.graphics"},
|
|
||||||
{".odh", "text/plain"},
|
|
||||||
{".odi", "application/vnd.oasis.opendocument.image"},
|
|
||||||
{".odl", "text/plain"},
|
|
||||||
{".odm", "application/vnd.oasis.opendocument.text-master"},
|
|
||||||
{".odp", "application/vnd.oasis.opendocument.presentation"},
|
|
||||||
{".ods", "application/vnd.oasis.opendocument.spreadsheet"},
|
|
||||||
{".odt", "application/vnd.oasis.opendocument.text"},
|
|
||||||
{".oga", "audio/ogg"},
|
|
||||||
{".ogg", "audio/ogg"},
|
|
||||||
{".ogv", "video/ogg"},
|
|
||||||
{".ogx", "application/ogg"},
|
|
||||||
{".one", "application/onenote"},
|
|
||||||
{".onea", "application/onenote"},
|
|
||||||
{".onepkg", "application/onenote"},
|
|
||||||
{".onetmp", "application/onenote"},
|
|
||||||
{".onetoc", "application/onenote"},
|
|
||||||
{".onetoc2", "application/onenote"},
|
|
||||||
{".opus", "audio/ogg"},
|
|
||||||
{".orderedtest", "application/xml"},
|
|
||||||
{".osdx", "application/opensearchdescription+xml"},
|
|
||||||
{".otf", "application/font-sfnt"},
|
|
||||||
{".otg", "application/vnd.oasis.opendocument.graphics-template"},
|
|
||||||
{".oth", "application/vnd.oasis.opendocument.text-web"},
|
|
||||||
{".otp", "application/vnd.oasis.opendocument.presentation-template"},
|
|
||||||
{".ots", "application/vnd.oasis.opendocument.spreadsheet-template"},
|
|
||||||
{".ott", "application/vnd.oasis.opendocument.text-template"},
|
|
||||||
{".oxt", "application/vnd.openofficeorg.extension"},
|
|
||||||
{".p10", "application/pkcs10"},
|
|
||||||
{".p12", "application/x-pkcs12"},
|
|
||||||
{".p7b", "application/x-pkcs7-certificates"},
|
|
||||||
{".p7c", "application/pkcs7-mime"},
|
|
||||||
{".p7m", "application/pkcs7-mime"},
|
|
||||||
{".p7r", "application/x-pkcs7-certreqresp"},
|
|
||||||
{".p7s", "application/pkcs7-signature"},
|
|
||||||
{".pbm", "image/x-portable-bitmap"},
|
|
||||||
{".pcast", "application/x-podcast"},
|
|
||||||
{".pct", "image/pict"},
|
|
||||||
{".pcx", "application/octet-stream"},
|
|
||||||
{".pcz", "application/octet-stream"},
|
|
||||||
{".pdf", "application/pdf"},
|
|
||||||
{".pfb", "application/octet-stream"},
|
|
||||||
{".pfm", "application/octet-stream"},
|
|
||||||
{".pfx", "application/x-pkcs12"},
|
|
||||||
{".pgm", "image/x-portable-graymap"},
|
|
||||||
{".pic", "image/pict"},
|
|
||||||
{".pict", "image/pict"},
|
|
||||||
{".pkgdef", "text/plain"},
|
|
||||||
{".pkgundef", "text/plain"},
|
|
||||||
{".pko", "application/vnd.ms-pki.pko"},
|
|
||||||
{".pls", "audio/scpls"},
|
|
||||||
{".pma", "application/x-perfmon"},
|
|
||||||
{".pmc", "application/x-perfmon"},
|
|
||||||
{".pml", "application/x-perfmon"},
|
|
||||||
{".pmr", "application/x-perfmon"},
|
|
||||||
{".pmw", "application/x-perfmon"},
|
|
||||||
{".png", "image/png"},
|
|
||||||
{".pnm", "image/x-portable-anymap"},
|
|
||||||
{".pnt", "image/x-macpaint"},
|
|
||||||
{".pntg", "image/x-macpaint"},
|
|
||||||
{".pnz", "image/png"},
|
|
||||||
{".pot", "application/vnd.ms-powerpoint"},
|
|
||||||
{".potm", "application/vnd.ms-powerpoint.template.macroEnabled.12"},
|
|
||||||
{".potx", "application/vnd.openxmlformats-officedocument.presentationml.template"},
|
|
||||||
{".ppa", "application/vnd.ms-powerpoint"},
|
|
||||||
{".ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12"},
|
|
||||||
{".ppm", "image/x-portable-pixmap"},
|
|
||||||
{".pps", "application/vnd.ms-powerpoint"},
|
|
||||||
{".ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"},
|
|
||||||
{".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"},
|
|
||||||
{".ppt", "application/vnd.ms-powerpoint"},
|
|
||||||
{".pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12"},
|
|
||||||
{".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
|
|
||||||
{".prf", "application/pics-rules"},
|
|
||||||
{".prm", "application/octet-stream"},
|
|
||||||
{".prx", "application/octet-stream"},
|
|
||||||
{".ps", "application/postscript"},
|
|
||||||
{".psc1", "application/PowerShell"},
|
|
||||||
{".psd", "application/octet-stream"},
|
|
||||||
{".psess", "application/xml"},
|
|
||||||
{".psm", "application/octet-stream"},
|
|
||||||
{".psp", "application/octet-stream"},
|
|
||||||
{".pst", "application/vnd.ms-outlook"},
|
|
||||||
{".pub", "application/x-mspublisher"},
|
|
||||||
{".pwz", "application/vnd.ms-powerpoint"},
|
|
||||||
{".qht", "text/x-html-insertion"},
|
|
||||||
{".qhtm", "text/x-html-insertion"},
|
|
||||||
{".qt", "video/quicktime"},
|
|
||||||
{".qti", "image/x-quicktime"},
|
|
||||||
{".qtif", "image/x-quicktime"},
|
|
||||||
{".qtl", "application/x-quicktimeplayer"},
|
|
||||||
{".qxd", "application/octet-stream"},
|
|
||||||
{".ra", "audio/x-pn-realaudio"},
|
|
||||||
{".ram", "audio/x-pn-realaudio"},
|
|
||||||
{".rar", "application/x-rar-compressed"},
|
|
||||||
{".ras", "image/x-cmu-raster"},
|
|
||||||
{".rat", "application/rat-file"},
|
|
||||||
{".rc", "text/plain"},
|
|
||||||
{".rc2", "text/plain"},
|
|
||||||
{".rct", "text/plain"},
|
|
||||||
{".rdlc", "application/xml"},
|
|
||||||
{".reg", "text/plain"},
|
|
||||||
{".resx", "application/xml"},
|
|
||||||
{".rf", "image/vnd.rn-realflash"},
|
|
||||||
{".rgb", "image/x-rgb"},
|
|
||||||
{".rgs", "text/plain"},
|
|
||||||
{".rm", "application/vnd.rn-realmedia"},
|
|
||||||
{".rmi", "audio/mid"},
|
|
||||||
{".rmp", "application/vnd.rn-rn_music_package"},
|
|
||||||
{".roff", "application/x-troff"},
|
|
||||||
{".rpm", "audio/x-pn-realaudio-plugin"},
|
|
||||||
{".rqy", "text/x-ms-rqy"},
|
|
||||||
{".rtf", "application/rtf"},
|
|
||||||
{".rtx", "text/richtext"},
|
|
||||||
{".rvt", "application/octet-stream" },
|
|
||||||
{".ruleset", "application/xml"},
|
|
||||||
{".s", "text/plain"},
|
|
||||||
{".safariextz", "application/x-safari-safariextz"},
|
|
||||||
{".scd", "application/x-msschedule"},
|
|
||||||
{".scr", "text/plain"},
|
|
||||||
{".sct", "text/scriptlet"},
|
|
||||||
{".sd2", "audio/x-sd2"},
|
|
||||||
{".sdp", "application/sdp"},
|
|
||||||
{".sea", "application/octet-stream"},
|
|
||||||
{".searchConnector-ms", "application/windows-search-connector+xml"},
|
|
||||||
{".setpay", "application/set-payment-initiation"},
|
|
||||||
{".setreg", "application/set-registration-initiation"},
|
|
||||||
{".settings", "application/xml"},
|
|
||||||
{".sgimb", "application/x-sgimb"},
|
|
||||||
{".sgml", "text/sgml"},
|
|
||||||
{".sh", "application/x-sh"},
|
|
||||||
{".shar", "application/x-shar"},
|
|
||||||
{".shtml", "text/html"},
|
|
||||||
{".sit", "application/x-stuffit"},
|
|
||||||
{".sitemap", "application/xml"},
|
|
||||||
{".skin", "application/xml"},
|
|
||||||
{".skp", "application/x-koan" },
|
|
||||||
{".sldm", "application/vnd.ms-powerpoint.slide.macroEnabled.12"},
|
|
||||||
{".sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide"},
|
|
||||||
{".slk", "application/vnd.ms-excel"},
|
|
||||||
{".sln", "text/plain"},
|
|
||||||
{".slupkg-ms", "application/x-ms-license"},
|
|
||||||
{".smd", "audio/x-smd"},
|
|
||||||
{".smi", "application/octet-stream"},
|
|
||||||
{".smx", "audio/x-smd"},
|
|
||||||
{".smz", "audio/x-smd"},
|
|
||||||
{".snd", "audio/basic"},
|
|
||||||
{".snippet", "application/xml"},
|
|
||||||
{".snp", "application/octet-stream"},
|
|
||||||
{".sol", "text/plain"},
|
|
||||||
{".sor", "text/plain"},
|
|
||||||
{".spc", "application/x-pkcs7-certificates"},
|
|
||||||
{".spl", "application/futuresplash"},
|
|
||||||
{".spx", "audio/ogg"},
|
|
||||||
{".src", "application/x-wais-source"},
|
|
||||||
{".srf", "text/plain"},
|
|
||||||
{".SSISDeploymentManifest", "text/xml"},
|
|
||||||
{".ssm", "application/streamingmedia"},
|
|
||||||
{".sst", "application/vnd.ms-pki.certstore"},
|
|
||||||
{".stl", "application/vnd.ms-pki.stl"},
|
|
||||||
{".sv4cpio", "application/x-sv4cpio"},
|
|
||||||
{".sv4crc", "application/x-sv4crc"},
|
|
||||||
{".svc", "application/xml"},
|
|
||||||
{".svg", "image/svg+xml"},
|
|
||||||
{".swf", "application/x-shockwave-flash"},
|
|
||||||
{".step", "application/step"},
|
|
||||||
{".stp", "application/step"},
|
|
||||||
{".t", "application/x-troff"},
|
|
||||||
{".tar", "application/x-tar"},
|
|
||||||
{".tcl", "application/x-tcl"},
|
|
||||||
{".testrunconfig", "application/xml"},
|
|
||||||
{".testsettings", "application/xml"},
|
|
||||||
{".tex", "application/x-tex"},
|
|
||||||
{".texi", "application/x-texinfo"},
|
|
||||||
{".texinfo", "application/x-texinfo"},
|
|
||||||
{".tgz", "application/x-compressed"},
|
|
||||||
{".thmx", "application/vnd.ms-officetheme"},
|
|
||||||
{".thn", "application/octet-stream"},
|
|
||||||
{".tif", "image/tiff"},
|
|
||||||
{".tiff", "image/tiff"},
|
|
||||||
{".tlh", "text/plain"},
|
|
||||||
{".tli", "text/plain"},
|
|
||||||
{".toc", "application/octet-stream"},
|
|
||||||
{".tr", "application/x-troff"},
|
|
||||||
{".trm", "application/x-msterminal"},
|
|
||||||
{".trx", "application/xml"},
|
|
||||||
{".ts", "video/vnd.dlna.mpeg-tts"},
|
|
||||||
{".tsv", "text/tab-separated-values"},
|
|
||||||
{".ttf", "application/font-sfnt"},
|
|
||||||
{".tts", "video/vnd.dlna.mpeg-tts"},
|
|
||||||
{".txt", "text/plain"},
|
|
||||||
{".u32", "application/octet-stream"},
|
|
||||||
{".uls", "text/iuls"},
|
|
||||||
{".user", "text/plain"},
|
|
||||||
{".ustar", "application/x-ustar"},
|
|
||||||
{".vb", "text/plain"},
|
|
||||||
{".vbdproj", "text/plain"},
|
|
||||||
{".vbk", "video/mpeg"},
|
|
||||||
{".vbproj", "text/plain"},
|
|
||||||
{".vbs", "text/vbscript"},
|
|
||||||
{".vcf", "text/x-vcard"},
|
|
||||||
{".vcproj", "application/xml"},
|
|
||||||
{".vcs", "text/plain"},
|
|
||||||
{".vcxproj", "application/xml"},
|
|
||||||
{".vddproj", "text/plain"},
|
|
||||||
{".vdp", "text/plain"},
|
|
||||||
{".vdproj", "text/plain"},
|
|
||||||
{".vdx", "application/vnd.ms-visio.viewer"},
|
|
||||||
{".vml", "text/xml"},
|
|
||||||
{".vscontent", "application/xml"},
|
|
||||||
{".vsct", "text/xml"},
|
|
||||||
{".vsd", "application/vnd.visio"},
|
|
||||||
{".vsi", "application/ms-vsi"},
|
|
||||||
{".vsix", "application/vsix"},
|
|
||||||
{".vsixlangpack", "text/xml"},
|
|
||||||
{".vsixmanifest", "text/xml"},
|
|
||||||
{".vsmdi", "application/xml"},
|
|
||||||
{".vspscc", "text/plain"},
|
|
||||||
{".vss", "application/vnd.visio"},
|
|
||||||
{".vsscc", "text/plain"},
|
|
||||||
{".vssettings", "text/xml"},
|
|
||||||
{".vssscc", "text/plain"},
|
|
||||||
{".vst", "application/vnd.visio"},
|
|
||||||
{".vstemplate", "text/xml"},
|
|
||||||
{".vsto", "application/x-ms-vsto"},
|
|
||||||
{".vsw", "application/vnd.visio"},
|
|
||||||
{".vsx", "application/vnd.visio"},
|
|
||||||
{".vtt", "text/vtt"},
|
|
||||||
{".vtx", "application/vnd.visio"},
|
|
||||||
{".wasm", "application/wasm"},
|
|
||||||
{".wav", "audio/wav"},
|
|
||||||
{".wave", "audio/wav"},
|
|
||||||
{".wax", "audio/x-ms-wax"},
|
|
||||||
{".wbk", "application/msword"},
|
|
||||||
{".wbmp", "image/vnd.wap.wbmp"},
|
|
||||||
{".wcm", "application/vnd.ms-works"},
|
|
||||||
{".wdb", "application/vnd.ms-works"},
|
|
||||||
{".wdp", "image/vnd.ms-photo"},
|
|
||||||
{".webarchive", "application/x-safari-webarchive"},
|
|
||||||
{".webm", "video/webm"},
|
|
||||||
{".webp", "image/webp"}, /* https://en.wikipedia.org/wiki/WebP */
|
|
||||||
{".webtest", "application/xml"},
|
|
||||||
{".wiq", "application/xml"},
|
|
||||||
{".wiz", "application/msword"},
|
|
||||||
{".wks", "application/vnd.ms-works"},
|
|
||||||
{".WLMP", "application/wlmoviemaker"},
|
|
||||||
{".wlpginstall", "application/x-wlpg-detect"},
|
|
||||||
{".wlpginstall3", "application/x-wlpg3-detect"},
|
|
||||||
{".wm", "video/x-ms-wm"},
|
|
||||||
{".wma", "audio/x-ms-wma"},
|
|
||||||
{".wmd", "application/x-ms-wmd"},
|
|
||||||
{".wmf", "application/x-msmetafile"},
|
|
||||||
{".wml", "text/vnd.wap.wml"},
|
|
||||||
{".wmlc", "application/vnd.wap.wmlc"},
|
|
||||||
{".wmls", "text/vnd.wap.wmlscript"},
|
|
||||||
{".wmlsc", "application/vnd.wap.wmlscriptc"},
|
|
||||||
{".wmp", "video/x-ms-wmp"},
|
|
||||||
{".wmv", "video/x-ms-wmv"},
|
|
||||||
{".wmx", "video/x-ms-wmx"},
|
|
||||||
{".wmz", "application/x-ms-wmz"},
|
|
||||||
{".woff", "application/font-woff"},
|
|
||||||
{".woff2", "application/font-woff2"},
|
|
||||||
{".wpl", "application/vnd.ms-wpl"},
|
|
||||||
{".wps", "application/vnd.ms-works"},
|
|
||||||
{".wri", "application/x-mswrite"},
|
|
||||||
{".wrl", "x-world/x-vrml"},
|
|
||||||
{".wrz", "x-world/x-vrml"},
|
|
||||||
{".wsc", "text/scriptlet"},
|
|
||||||
{".wsdl", "text/xml"},
|
|
||||||
{".wvx", "video/x-ms-wvx"},
|
|
||||||
{".x", "application/directx"},
|
|
||||||
{".xaf", "x-world/x-vrml"},
|
|
||||||
{".xaml", "application/xaml+xml"},
|
|
||||||
{".xap", "application/x-silverlight-app"},
|
|
||||||
{".xbap", "application/x-ms-xbap"},
|
|
||||||
{".xbm", "image/x-xbitmap"},
|
|
||||||
{".xdr", "text/plain"},
|
|
||||||
{".xht", "application/xhtml+xml"},
|
|
||||||
{".xhtml", "application/xhtml+xml"},
|
|
||||||
{".xla", "application/vnd.ms-excel"},
|
|
||||||
{".xlam", "application/vnd.ms-excel.addin.macroEnabled.12"},
|
|
||||||
{".xlc", "application/vnd.ms-excel"},
|
|
||||||
{".xld", "application/vnd.ms-excel"},
|
|
||||||
{".xlk", "application/vnd.ms-excel"},
|
|
||||||
{".xll", "application/vnd.ms-excel"},
|
|
||||||
{".xlm", "application/vnd.ms-excel"},
|
|
||||||
{".xls", "application/vnd.ms-excel"},
|
|
||||||
{".xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12"},
|
|
||||||
{".xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12"},
|
|
||||||
{".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
|
|
||||||
{".xlt", "application/vnd.ms-excel"},
|
|
||||||
{".xltm", "application/vnd.ms-excel.template.macroEnabled.12"},
|
|
||||||
{".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"},
|
|
||||||
{".xlw", "application/vnd.ms-excel"},
|
|
||||||
{".xml", "text/xml"},
|
|
||||||
{".xmp", "application/octet-stream" },
|
|
||||||
{".xmta", "application/xml"},
|
|
||||||
{".xof", "x-world/x-vrml"},
|
|
||||||
{".XOML", "text/plain"},
|
|
||||||
{".xpm", "image/x-xpixmap"},
|
|
||||||
{".xps", "application/vnd.ms-xpsdocument"},
|
|
||||||
{".xrm-ms", "text/xml"},
|
|
||||||
{".xsc", "application/xml"},
|
|
||||||
{".xsd", "text/xml"},
|
|
||||||
{".xsf", "text/xml"},
|
|
||||||
{".xsl", "text/xml"},
|
|
||||||
{".xslt", "text/xml"},
|
|
||||||
{".xsn", "application/octet-stream"},
|
|
||||||
{".xss", "application/xml"},
|
|
||||||
{".xspf", "application/xspf+xml"},
|
|
||||||
{".xtp", "application/octet-stream"},
|
|
||||||
{".xwd", "image/x-xwindowdump"},
|
|
||||||
{".z", "application/x-compress"},
|
|
||||||
{".zip", "application/zip"},
|
|
||||||
|
|
||||||
{"application/fsharp-script", ".fsx"},
|
|
||||||
{"application/msaccess", ".adp"},
|
|
||||||
{"application/msword", ".doc"},
|
|
||||||
{"application/octet-stream", ".bin"},
|
|
||||||
{"application/onenote", ".one"},
|
|
||||||
{"application/postscript", ".eps"},
|
|
||||||
{"application/step", ".step"},
|
|
||||||
{"application/vnd.ms-excel", ".xls"},
|
|
||||||
{"application/vnd.ms-powerpoint", ".ppt"},
|
|
||||||
{"application/vnd.ms-works", ".wks"},
|
|
||||||
{"application/vnd.visio", ".vsd"},
|
|
||||||
{"application/x-director", ".dir"},
|
|
||||||
{"application/x-shockwave-flash", ".swf"},
|
|
||||||
{"application/x-x509-ca-cert", ".cer"},
|
|
||||||
{"application/x-zip-compressed", ".zip"},
|
|
||||||
{"application/xhtml+xml", ".xhtml"},
|
|
||||||
{"application/xml", ".xml"}, // anomoly, .xml -> text/xml, but application/xml -> many thingss, but all are xml, so safest is .xml
|
|
||||||
{"audio/aac", ".AAC"},
|
|
||||||
{"audio/aiff", ".aiff"},
|
|
||||||
{"audio/basic", ".snd"},
|
|
||||||
{"audio/mid", ".midi"},
|
|
||||||
{"audio/wav", ".wav"},
|
|
||||||
{"audio/x-m4a", ".m4a"},
|
|
||||||
{"audio/x-mpegurl", ".m3u"},
|
|
||||||
{"audio/x-pn-realaudio", ".ra"},
|
|
||||||
{"audio/x-smd", ".smd"},
|
|
||||||
{"image/bmp", ".bmp"},
|
|
||||||
{"image/jpeg", ".jpg"},
|
|
||||||
{"image/pict", ".pic"},
|
|
||||||
{"image/png", ".png"}, //Defined in [RFC-2045], [RFC-2048]
|
|
||||||
{"image/x-png", ".png"}, //See https://www.w3.org/TR/PNG/#A-Media-type :"It is recommended that implementations also recognize the media type "image/x-png"."
|
|
||||||
{"image/tiff", ".tiff"},
|
|
||||||
{"image/x-macpaint", ".mac"},
|
|
||||||
{"image/x-quicktime", ".qti"},
|
|
||||||
{"message/rfc822", ".eml"},
|
|
||||||
{"text/html", ".html"},
|
|
||||||
{"text/plain", ".txt"},
|
|
||||||
{"text/scriptlet", ".wsc"},
|
|
||||||
{"text/xml", ".xml"},
|
|
||||||
{"video/3gpp", ".3gp"},
|
|
||||||
{"video/3gpp2", ".3gp2"},
|
|
||||||
{"video/mp4", ".mp4"},
|
|
||||||
{"video/mpeg", ".mpg"},
|
|
||||||
{"video/quicktime", ".mov"},
|
|
||||||
{"video/vnd.dlna.mpeg-tts", ".m2t"},
|
|
||||||
{"video/x-dv", ".dv"},
|
|
||||||
{"video/x-la-asf", ".lsf"},
|
|
||||||
{"video/x-ms-asf", ".asf"},
|
|
||||||
{"x-world/x-vrml", ".xof"},
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
var cache = mappings.ToList(); // need ToList() to avoid modifying while still enumerating
|
|
||||||
|
|
||||||
foreach (var mapping in cache)
|
|
||||||
{
|
|
||||||
if (!mappings.ContainsKey(mapping.Value))
|
|
||||||
{
|
|
||||||
mappings.Add(mapping.Value, mapping.Key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return mappings;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetMimeType(string extension)
|
|
||||||
{
|
|
||||||
if (extension == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("extension");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!extension.StartsWith("."))
|
|
||||||
{
|
|
||||||
extension = "." + extension;
|
|
||||||
}
|
|
||||||
|
|
||||||
string mime;
|
|
||||||
|
|
||||||
return _mappings.Value.TryGetValue(extension, out mime) ? mime : "application/octet-stream";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetExtension(string mimeType)
|
|
||||||
{
|
|
||||||
return GetExtension(mimeType, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetExtension(string mimeType, bool throwErrorIfNotFound)
|
|
||||||
{
|
|
||||||
if (mimeType == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("mimeType");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mimeType.StartsWith("."))
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Requested mime type is not valid: " + mimeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
string extension;
|
|
||||||
|
|
||||||
if (_mappings.Value.TryGetValue(mimeType, out extension))
|
|
||||||
{
|
|
||||||
return extension;
|
|
||||||
}
|
|
||||||
if (throwErrorIfNotFound)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Requested mime type is not registered: " + mimeType);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
// /**
|
|
||||||
// * 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
// /**
|
|
||||||
// * 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
// /**
|
|
||||||
// * 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,8 @@
|
||||||
// *
|
// *
|
||||||
// **/
|
// **/
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using ln.http.message;
|
using ln.http.mime;
|
||||||
|
|
||||||
namespace ln.http.router
|
namespace ln.http.router
|
||||||
{
|
{
|
||||||
public class FileRouter : IHttpRouter
|
public class FileRouter : IHttpRouter
|
||||||
|
@ -27,6 +28,7 @@ namespace ln.http.router
|
||||||
{
|
{
|
||||||
HttpResponse httpResponse = new HttpResponse(httpRequest, new FileStream(FileName, FileMode.Open));
|
HttpResponse httpResponse = new HttpResponse(httpRequest, new FileStream(FileName, FileMode.Open));
|
||||||
httpResponse.SetHeader("content-type", MimeTypeMap.GetMimeType(Path.GetExtension(FileName)));
|
httpResponse.SetHeader("content-type", MimeTypeMap.GetMimeType(Path.GetExtension(FileName)));
|
||||||
|
|
||||||
|
|
||||||
return httpResponse;
|
return httpResponse;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace ln.http.router
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
public string RoutedPath { get; set; }
|
public string RoutedPath { get; set; }
|
||||||
|
|
||||||
public HttpRoutingContext(HttpRequest httpRequest) : this(httpRequest, httpRequest.URI.AbsolutePath) { }
|
public HttpRoutingContext(HttpRequest httpRequest) : this(httpRequest, httpRequest?.RequestUri?.AbsolutePath ?? "") { }
|
||||||
public HttpRoutingContext(HttpRequest httpRequest, string path)
|
public HttpRoutingContext(HttpRequest httpRequest, string path)
|
||||||
{
|
{
|
||||||
HttpRequest = httpRequest;
|
HttpRequest = httpRequest;
|
||||||
|
@ -48,6 +48,5 @@ namespace ln.http.router
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,8 +48,8 @@ namespace ln.http.router
|
||||||
start,
|
start,
|
||||||
duration,
|
duration,
|
||||||
response?.StatusCode.ToString() ?? "-",
|
response?.StatusCode.ToString() ?? "-",
|
||||||
httpRequest.Method,
|
httpRequest?.Method,
|
||||||
httpRequest.RequestURL
|
httpRequest?.RequestUri
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,19 +25,19 @@ namespace ln.http.router
|
||||||
|
|
||||||
public virtual HttpResponse Dispatch(HttpRequest request)
|
public virtual HttpResponse Dispatch(HttpRequest request)
|
||||||
{
|
{
|
||||||
switch (request.Method.ToUpper())
|
switch (request.Method)
|
||||||
{
|
{
|
||||||
case "HEAD":
|
case HttpMethod.HEAD:
|
||||||
return HEAD(request);
|
return HEAD(request);
|
||||||
case "GET":
|
case HttpMethod.GET:
|
||||||
return GET(request);
|
return GET(request);
|
||||||
case "PROPFIND":
|
case HttpMethod.PROPFIND:
|
||||||
return PROPFIND(request);
|
return PROPFIND(request);
|
||||||
case "POST":
|
case HttpMethod.POST:
|
||||||
return POST(request);
|
return POST(request);
|
||||||
case "PUT":
|
case HttpMethod.PUT:
|
||||||
return PUT(request);
|
return PUT(request);
|
||||||
case "DELETE":
|
case HttpMethod.DELETE:
|
||||||
return DELETE(request);
|
return DELETE(request);
|
||||||
default:
|
default:
|
||||||
throw new MethodNotAllowedException();
|
throw new MethodNotAllowedException();
|
||||||
|
|
|
@ -9,8 +9,9 @@
|
||||||
// **/
|
// **/
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using ln.http.message;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using ln.http.mime;
|
||||||
|
|
||||||
namespace ln.http.router
|
namespace ln.http.router
|
||||||
{
|
{
|
||||||
public class StaticRouter : IHttpRouter
|
public class StaticRouter : IHttpRouter
|
||||||
|
@ -36,7 +37,7 @@ namespace ln.http.router
|
||||||
|
|
||||||
public HttpResponse Route(HttpRoutingContext routingContext, HttpRequest httpRequest)
|
public HttpResponse Route(HttpRoutingContext routingContext, HttpRequest httpRequest)
|
||||||
{
|
{
|
||||||
string finalPath = Path.Combine(RootPath, routingContext.Path.Substring(1));
|
string finalPath = routingContext.Path.Length > 0 ? Path.Combine(RootPath, routingContext.Path.Substring(1)) : ".";
|
||||||
|
|
||||||
if (Directory.Exists(finalPath))
|
if (Directory.Exists(finalPath))
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using ln.http.exceptions;
|
using ln.http.exceptions;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace ln.http.router
|
namespace ln.http.router
|
||||||
|
@ -28,21 +28,21 @@ namespace ln.http.router
|
||||||
}
|
}
|
||||||
public VirtualHostRouter(VirtualHostRouter source)
|
public VirtualHostRouter(VirtualHostRouter source)
|
||||||
: this(source.virtualHosts) { }
|
: this(source.virtualHosts) { }
|
||||||
|
|
||||||
public void Add(string hostname,IHttpRouter router) => virtualHosts.Add(hostname, router);
|
public void Add(string hostname,IHttpRouter router) => virtualHosts.Add(hostname, router);
|
||||||
public void Remove(string hostname) => virtualHosts.Remove(hostname);
|
public void Remove(string hostname) => virtualHosts.Remove(hostname);
|
||||||
public bool Contains(string hostname) => virtualHosts.ContainsKey(hostname);
|
public bool Contains(string hostname) => virtualHosts.ContainsKey(hostname);
|
||||||
|
|
||||||
public bool TryGetValue(string hostname, out IHttpRouter router) => virtualHosts.TryGetValue(hostname, out router);
|
public bool TryGetValue(string hostname, out IHttpRouter router) => virtualHosts.TryGetValue(hostname, out router);
|
||||||
|
|
||||||
public HttpResponse Route(HttpRoutingContext routingContext, HttpRequest httpRequest)
|
public HttpResponse Route(HttpRoutingContext routingContext, HttpRequest httpRequest)
|
||||||
{
|
{
|
||||||
if (virtualHosts.TryGetValue(httpRequest.Hostname, out IHttpRouter virtualHost))
|
if (virtualHosts.TryGetValue(httpRequest.Host, out IHttpRouter virtualHost))
|
||||||
return virtualHost.Route(routingContext, httpRequest);
|
return virtualHost.Route(routingContext, httpRequest);
|
||||||
if (DefaultRoute != null)
|
if (DefaultRoute != null)
|
||||||
return DefaultRoute.Route(routingContext, httpRequest);
|
return DefaultRoute.Route(routingContext, httpRequest);
|
||||||
|
|
||||||
throw new HttpException(410, string.Format("Gone. Hostname {0} not found on this server.", httpRequest.Hostname));
|
throw new HttpException(410, string.Format("Gone. Hostname {0} not found on this server.", httpRequest.Host));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
/*
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
|
@ -67,3 +68,4 @@ namespace ln.http.session
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
/*
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
namespace ln.http.session
|
namespace ln.http.session
|
||||||
{
|
{
|
||||||
|
@ -18,7 +19,7 @@ namespace ln.http.session
|
||||||
/*
|
/*
|
||||||
* Create a new Session instance
|
* Create a new Session instance
|
||||||
*
|
*
|
||||||
*/
|
#1#
|
||||||
public virtual Session CreateSession()
|
public virtual Session CreateSession()
|
||||||
{
|
{
|
||||||
return new Session();
|
return new Session();
|
||||||
|
@ -27,7 +28,7 @@ namespace ln.http.session
|
||||||
/*
|
/*
|
||||||
* Find and return SessionID from given HttpRequest
|
* Find and return SessionID from given HttpRequest
|
||||||
*
|
*
|
||||||
*/
|
#1#
|
||||||
public virtual Guid FindSessionID(HttpRequest httpRequest)
|
public virtual Guid FindSessionID(HttpRequest httpRequest)
|
||||||
{
|
{
|
||||||
if (httpRequest.ContainsCookie("SID_LN"))
|
if (httpRequest.ContainsCookie("SID_LN"))
|
||||||
|
@ -40,7 +41,7 @@ namespace ln.http.session
|
||||||
/*
|
/*
|
||||||
* Apply SessionID of the given Session to the given Response
|
* Apply SessionID of the given Session to the given Response
|
||||||
*
|
*
|
||||||
*/
|
#1#
|
||||||
public virtual void ApplySessionID(HttpResponse httpResponse,Session session)
|
public virtual void ApplySessionID(HttpResponse httpResponse,Session session)
|
||||||
{
|
{
|
||||||
HttpCookie sessionCookie = new HttpCookie("SID_LN",session.SessionID.ToString());
|
HttpCookie sessionCookie = new HttpCookie("SID_LN",session.SessionID.ToString());
|
||||||
|
@ -73,3 +74,4 @@ namespace ln.http.session
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
|
@ -35,7 +35,7 @@ namespace ln.http.websocket
|
||||||
|
|
||||||
public abstract class WebSocket
|
public abstract class WebSocket
|
||||||
{
|
{
|
||||||
public HTTPServer HTTPServer => HttpRequest.HTTPServer;
|
public HTTPServer HTTPServer => HttpRequest.Server;
|
||||||
public HttpRequest HttpRequest { get; }
|
public HttpRequest HttpRequest { get; }
|
||||||
public Stream Stream { get; }
|
public Stream Stream { get; }
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ namespace ln.http.websocket
|
||||||
public WebSocket(HttpRequest httpRequest)
|
public WebSocket(HttpRequest httpRequest)
|
||||||
{
|
{
|
||||||
HttpRequest = httpRequest;
|
HttpRequest = httpRequest;
|
||||||
Stream = httpRequest.GetConnectionStream();
|
Stream = httpRequest.ConnectionStream;
|
||||||
|
|
||||||
if ((!httpRequest.GetRequestHeader("upgrade", "").Contains("websocket")) && (!httpRequest.GetRequestHeader("connection", "").Contains("Upgrade")))
|
if ((!httpRequest.GetRequestHeader("upgrade", "").Contains("websocket")) && (!httpRequest.GetRequestHeader("connection", "").Contains("Upgrade")))
|
||||||
throw new HttpException(400, "This resource is a websocket endpoint only");
|
throw new HttpException(400, "This resource is a websocket endpoint only");
|
||||||
|
@ -66,8 +66,7 @@ namespace ln.http.websocket
|
||||||
Convert.ToBase64String(SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(acceptKey)))
|
Convert.ToBase64String(SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(acceptKey)))
|
||||||
);
|
);
|
||||||
|
|
||||||
HTTPServer.SendResponse(Stream, httpRequest, httpResponse);
|
httpResponse.WriteTo(Stream);
|
||||||
//HTTPServerConnection.Current.Value.AbortRequested += (connection) => Close();
|
|
||||||
State = WebSocketState.OPEN;
|
State = WebSocketState.OPEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -154,7 +154,7 @@ namespace ln.http.websocket
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public override void SendResponse(Stream stream, HttpRequest httpRequest)
|
public void SendResponse(Stream stream, HttpRequest httpRequest)
|
||||||
{
|
{
|
||||||
lock (this)
|
lock (this)
|
||||||
{
|
{
|
||||||
|
@ -180,11 +180,10 @@ namespace ln.http.websocket
|
||||||
Convert.ToBase64String(SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(acceptKey)))
|
Convert.ToBase64String(SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(acceptKey)))
|
||||||
);
|
);
|
||||||
|
|
||||||
HTTPServer.SendResponse(Stream, httpRequest, httpResponse);
|
base.WriteTo(Stream);
|
||||||
//HTTPServerConnection.Current.Value.AbortRequested += (connection) => Close();
|
|
||||||
State = WebSocketState.OPEN;
|
State = WebSocketState.OPEN;
|
||||||
}
|
}
|
||||||
Run();
|
Run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue