Merge branch 'master' of gitea.azcona-becker.net:alcetech/web-framework-dotnet
This commit is contained in:
commit
732daae00e
@ -8,6 +8,8 @@ namespace MBS.Web;
|
|||||||
public abstract class WebApplication : Application
|
public abstract class WebApplication : Application
|
||||||
{
|
{
|
||||||
protected abstract int DefaultPort { get; }
|
protected abstract int DefaultPort { get; }
|
||||||
|
public int Port { get { return WebServer.EndPoint.Port; } }
|
||||||
|
public WebServer WebServer { get; private set; }
|
||||||
|
|
||||||
public string VirtualBasePath { get; set; } = "/";
|
public string VirtualBasePath { get; set; } = "/";
|
||||||
|
|
||||||
@ -40,20 +42,39 @@ public abstract class WebApplication : Application
|
|||||||
return new WebServer(new IPEndPoint(IPAddress.Any, DefaultPort));
|
return new WebServer(new IPEndPoint(IPAddress.Any, DefaultPort));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool shuttingDown = false;
|
||||||
|
protected override void StopInternal(int exitCode = 0)
|
||||||
|
{
|
||||||
|
shuttingDown = true;
|
||||||
|
if (!SupportEmbeddedHosting)
|
||||||
|
{
|
||||||
|
// we do not want to System.Environment.Exit() here
|
||||||
|
// if we are hosted in another process (e.g. for tests)
|
||||||
|
base.StopInternal(exitCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SupportEmbeddedHosting { get; set; } = false;
|
||||||
|
|
||||||
protected override void OnStartup(EventArgs e)
|
protected override void OnStartup(EventArgs e)
|
||||||
{
|
{
|
||||||
base.OnStartup(e);
|
base.OnStartup(e);
|
||||||
|
|
||||||
WebServer server = CreateWebServer();
|
WebServer = CreateWebServer();
|
||||||
Console.WriteLine("attempting to start server listening on port {0}", DefaultPort);
|
Console.WriteLine("attempting to start server listening on port {0}", DefaultPort);
|
||||||
|
|
||||||
OnServerCreated(new WebServerCreatedEventArgs(server));
|
OnServerCreated(new WebServerCreatedEventArgs(WebServer));
|
||||||
server.ProcessRequest += server_OnProcessRequest;
|
WebServer.ProcessRequest += server_OnProcessRequest;
|
||||||
server.Start();
|
WebServer.Start();
|
||||||
|
|
||||||
while (true)
|
if (!SupportEmbeddedHosting)
|
||||||
{
|
{
|
||||||
Thread.Sleep(50);
|
while (!shuttingDown)
|
||||||
|
{
|
||||||
|
Thread.Sleep(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
WebServer.Stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Data.SqlTypes;
|
using System.Data.SqlTypes;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
using MBS.Core;
|
using MBS.Core;
|
||||||
|
|
||||||
namespace MBS.Web;
|
namespace MBS.Web;
|
||||||
@ -21,8 +22,9 @@ public class WebRequest
|
|||||||
public WebHeaderCollection Headers { get; }
|
public WebHeaderCollection Headers { get; }
|
||||||
|
|
||||||
public ReadOnlyDictionary<string, string> Form { get; }
|
public ReadOnlyDictionary<string, string> Form { get; }
|
||||||
|
public string Content { get; }
|
||||||
|
|
||||||
public WebRequest(string version, string method, string path, WebHeaderCollection headers, Dictionary<string, string> pathVariables, Dictionary<string, string> form)
|
public WebRequest(string version, string method, string path, WebHeaderCollection headers, Dictionary<string, string> pathVariables)
|
||||||
{
|
{
|
||||||
Version = version;
|
Version = version;
|
||||||
Method = method;
|
Method = method;
|
||||||
@ -67,9 +69,40 @@ public class WebRequest
|
|||||||
}
|
}
|
||||||
Headers = headers;
|
Headers = headers;
|
||||||
PathVariables = pathVariables;
|
PathVariables = pathVariables;
|
||||||
|
}
|
||||||
|
public WebRequest(string version, string method, string path, WebHeaderCollection headers, Dictionary<string, string> pathVariables, string content) : this(version, method, path, headers, pathVariables)
|
||||||
|
{
|
||||||
|
Content = content;
|
||||||
|
Form = ReadOnlyDictionary<string, string>.Empty;
|
||||||
|
}
|
||||||
|
public WebRequest(string version, string method, string path, WebHeaderCollection headers, Dictionary<string, string> pathVariables, Dictionary<string, string> form) : this(version, method, path, headers, pathVariables)
|
||||||
|
{
|
||||||
|
Content = UrlAndFormEncode(form);
|
||||||
Form = new ReadOnlyDictionary<string, string>(form);
|
Form = new ReadOnlyDictionary<string, string>(form);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string UrlAndFormEncode(string v)
|
||||||
|
{
|
||||||
|
v = v.UrlEncode();
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
private string UrlAndFormEncode(IEnumerable<KeyValuePair<string, string>> v)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
int i = 0;
|
||||||
|
foreach (KeyValuePair<string, string> kvp in v)
|
||||||
|
{
|
||||||
|
sb.Append(UrlAndFormEncode(kvp.Key));
|
||||||
|
sb.Append('=');
|
||||||
|
sb.Append(UrlAndFormEncode(kvp.Value));
|
||||||
|
if (i < v.Count())
|
||||||
|
{
|
||||||
|
sb.Append('&');
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
private string UrlAndFormDecode(string v)
|
private string UrlAndFormDecode(string v)
|
||||||
{
|
{
|
||||||
v = v.Replace('+', ' '); // must be done first to not decode '%..' => ' '
|
v = v.Replace('+', ' '); // must be done first to not decode '%..' => ' '
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System.Net;
|
using System.Diagnostics;
|
||||||
|
using System.Net;
|
||||||
using System.Net.Security;
|
using System.Net.Security;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Security.Authentication;
|
using System.Security.Authentication;
|
||||||
@ -257,6 +258,11 @@ public class WebServer
|
|||||||
Dictionary<string, string> form = new Dictionary<string, string>();
|
Dictionary<string, string> form = new Dictionary<string, string>();
|
||||||
|
|
||||||
string contentLength = headers["Content-Length"];
|
string contentLength = headers["Content-Length"];
|
||||||
|
string contentType = headers["Content-Type"];
|
||||||
|
|
||||||
|
// I never thought I'd ever actually use this!
|
||||||
|
Union<string, Dictionary<string, string>> content;
|
||||||
|
|
||||||
if (contentLength != "")
|
if (contentLength != "")
|
||||||
{
|
{
|
||||||
int contentLengthInt = Int32.Parse(contentLength);
|
int contentLengthInt = Int32.Parse(contentLength);
|
||||||
@ -265,16 +271,24 @@ public class WebServer
|
|||||||
char[] buffer = new char[contentLengthInt];
|
char[] buffer = new char[contentLengthInt];
|
||||||
sr.ReadBlock(buffer, 0, contentLengthInt);
|
sr.ReadBlock(buffer, 0, contentLengthInt);
|
||||||
|
|
||||||
string content = new string(buffer);
|
string contentString = new string(buffer);
|
||||||
Console.Error.WriteLine(content);
|
Console.Error.WriteLine(contentString);
|
||||||
|
|
||||||
if (!String.IsNullOrEmpty(content))
|
if (!String.IsNullOrEmpty(contentString))
|
||||||
{
|
{
|
||||||
string[] kvps = content.Split(new char[] { '&' });
|
if (contentType == "application/x-www-form-urlencoded")
|
||||||
foreach (string kvp in kvps)
|
|
||||||
{
|
{
|
||||||
string[] pv = kvp.Split(new char[] { '=' });
|
string[] kvps = contentString.Split(new char[] { '&' });
|
||||||
form[pv[0].UrlDecode()] = pv[1].UrlDecode().Replace('+', ' ');
|
foreach (string kvp in kvps)
|
||||||
|
{
|
||||||
|
string[] pv = kvp.Split(new char[] { '=' });
|
||||||
|
form[pv[0].UrlDecode()] = pv[1].UrlDecode().Replace('+', ' ');
|
||||||
|
}
|
||||||
|
content = form;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
content = contentString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -379,27 +393,56 @@ public class WebServer
|
|||||||
{
|
{
|
||||||
// Console.WriteLine("Client connected");
|
// Console.WriteLine("Client connected");
|
||||||
|
|
||||||
try
|
if (Debugger.IsAttached)
|
||||||
{
|
{
|
||||||
HandleClient(client);
|
// do not catch generic exceptions when running under a debugger
|
||||||
|
try
|
||||||
|
{
|
||||||
|
HandleClient(client);
|
||||||
|
}
|
||||||
|
catch (System.Net.Sockets.SocketException ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("caught SocketException; ignoring");
|
||||||
|
Console.Error.WriteLine(ex.Message);
|
||||||
|
}
|
||||||
|
catch (System.IO.IOException ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("caught IOException; ignoring");
|
||||||
|
Console.Error.WriteLine(ex.Message);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("caught exception of type {0}", ex.GetType().FullName);
|
||||||
|
Console.Error.WriteLine(ex.Message);
|
||||||
|
Console.Error.WriteLine(ex.StackTrace);
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
catch (System.Net.Sockets.SocketException ex)
|
else
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine("caught SocketException; ignoring");
|
// also catch generic exceptions if we are not debugging
|
||||||
Console.Error.WriteLine(ex.Message);
|
try
|
||||||
|
{
|
||||||
|
HandleClient(client);
|
||||||
|
}
|
||||||
|
catch (System.Net.Sockets.SocketException ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("caught SocketException; ignoring");
|
||||||
|
Console.Error.WriteLine(ex.Message);
|
||||||
|
}
|
||||||
|
catch (System.IO.IOException ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("caught IOException; ignoring");
|
||||||
|
Console.Error.WriteLine(ex.Message);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("caught exception of type {0}", ex.GetType().FullName);
|
||||||
|
Console.Error.WriteLine(ex.Message);
|
||||||
|
Console.Error.WriteLine(ex.StackTrace);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (System.IO.IOException ex)
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine("caught IOException; ignoring");
|
|
||||||
Console.Error.WriteLine(ex.Message);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine("caught exception of type {0}", ex.GetType().FullName);
|
|
||||||
Console.Error.WriteLine(ex.Message);
|
|
||||||
Console.Error.WriteLine(ex.StackTrace);
|
|
||||||
}
|
|
||||||
|
|
||||||
client.Close();
|
client.Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user