HttpServer, HttpProcessor and IHttpHandler.
This commit is contained in:
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
*.exe
|
||||||
|
*.pdb
|
||||||
|
*/bin/*
|
||||||
|
*/obj/*
|
||||||
|
*.csproj.user
|
||||||
|
*.suo
|
||||||
|
.vs
|
||||||
39
VAR.HttpServer.MiniServerTest/Program.cs
Normal file
39
VAR.HttpServer.MiniServerTest/Program.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace VAR.HttpServer.MiniServerTest
|
||||||
|
{
|
||||||
|
internal class Program
|
||||||
|
{
|
||||||
|
private static void Main(string[] args)
|
||||||
|
{
|
||||||
|
HttpServer httpServer = new HttpServer
|
||||||
|
{
|
||||||
|
Port = 3000,
|
||||||
|
Handler = new HelloWorldHttpHandler(),
|
||||||
|
LogDegugMessage = (msg) => Console.WriteLine("DEBUG: {0}", msg),
|
||||||
|
LogException = (ex) =>
|
||||||
|
{
|
||||||
|
Console.WriteLine("!!!!! Exception !!!!");
|
||||||
|
Console.WriteLine("Message: {0}", ex.Message);
|
||||||
|
Console.WriteLine("StackTrace: {0}", ex.StackTrace);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Console.Title = string.Format("MiniHTTPServer@{0}", httpServer.Port);
|
||||||
|
Console.WriteLine("HTTP Server started on {0} port", httpServer.Port);
|
||||||
|
httpServer.Start();
|
||||||
|
|
||||||
|
Process proc = Process.Start(string.Format("http://localhost:{0}", httpServer.Port));
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HelloWorldHttpHandler : IHttpHandler
|
||||||
|
{
|
||||||
|
public void HandleRequest(HttpProcessor p)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Responding to {0}", p.Socket.Client.RemoteEndPoint);
|
||||||
|
p.ResponseSuccess();
|
||||||
|
p.OutputStream.WriteLine("Hello World!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
VAR.HttpServer.MiniServerTest/Properties/AssemblyInfo.cs
Normal file
15
VAR.HttpServer.MiniServerTest/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
[assembly: AssemblyTitle("VAR.HttpServer.MiniServerTest")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("VAR")]
|
||||||
|
[assembly: AssemblyProduct("VAR.HttpServer.MiniServerTest")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © VAR 2019")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
[assembly: Guid("6bd47b75-3dc2-4559-9311-e3d8117d4a89")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{6BD47B75-3DC2-4559-9311-E3D8117D4A89}</ProjectGuid>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<RootNamespace>VAR.HttpServer.MiniServerTest</RootNamespace>
|
||||||
|
<AssemblyName>VAR.HttpServer.MiniServerTest</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
<Deterministic>true</Deterministic>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\VAR.HttpServer\VAR.HttpServer.csproj">
|
||||||
|
<Project>{e865ba90-11f7-46d4-b718-a536b0b76c35}</Project>
|
||||||
|
<Name>VAR.HttpServer</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
||||||
31
VAR.HttpServer.sln
Normal file
31
VAR.HttpServer.sln
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 15
|
||||||
|
VisualStudioVersion = 15.0.28307.572
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VAR.HttpServer", "VAR.HttpServer\VAR.HttpServer.csproj", "{E865BA90-11F7-46D4-B718-A536B0B76C35}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VAR.HttpServer.MiniServerTest", "VAR.HttpServer.MiniServerTest\VAR.HttpServer.MiniServerTest.csproj", "{6BD47B75-3DC2-4559-9311-E3D8117D4A89}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{E865BA90-11F7-46D4-B718-A536B0B76C35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{E865BA90-11F7-46D4-B718-A536B0B76C35}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{E865BA90-11F7-46D4-B718-A536B0B76C35}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{E865BA90-11F7-46D4-B718-A536B0B76C35}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{6BD47B75-3DC2-4559-9311-E3D8117D4A89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{6BD47B75-3DC2-4559-9311-E3D8117D4A89}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{6BD47B75-3DC2-4559-9311-E3D8117D4A89}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{6BD47B75-3DC2-4559-9311-E3D8117D4A89}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {0CFDBA5E-334F-41CB-8AA0-EEF9191EDD64}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
263
VAR.HttpServer/HttpProcessor.cs
Normal file
263
VAR.HttpServer/HttpProcessor.cs
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace VAR.HttpServer
|
||||||
|
{
|
||||||
|
public class HttpProcessor
|
||||||
|
{
|
||||||
|
private static readonly int MaxPostSize = 10 * 1024 * 1024; // 10MB
|
||||||
|
|
||||||
|
private TcpClient _socket;
|
||||||
|
public TcpClient Socket { get { return _socket; } }
|
||||||
|
|
||||||
|
private IHttpHandler _handler;
|
||||||
|
public IHttpHandler Handler { get { return _handler; } }
|
||||||
|
|
||||||
|
private readonly Action<string> _logDebugMessage = null;
|
||||||
|
|
||||||
|
private readonly Action<Exception> _logException = null;
|
||||||
|
|
||||||
|
private Stream _inputStream;
|
||||||
|
|
||||||
|
private StreamWriter _outputStream;
|
||||||
|
public StreamWriter OutputStream { get { return _outputStream; } }
|
||||||
|
|
||||||
|
private string _httpRequest;
|
||||||
|
|
||||||
|
private string _httpMethod;
|
||||||
|
public string HttpMethod { get { return _httpMethod; } }
|
||||||
|
|
||||||
|
private string _httpResource;
|
||||||
|
public string HttpResource { get { return _httpResource; } }
|
||||||
|
|
||||||
|
private string _httpResourceLowercase;
|
||||||
|
public string HttpResourceLowercase { get { return _httpResourceLowercase; } }
|
||||||
|
|
||||||
|
private string _httpQuery;
|
||||||
|
public string HttpQuery { get { return _httpQuery; } }
|
||||||
|
|
||||||
|
private string _httpProtocolVersionString;
|
||||||
|
|
||||||
|
private Dictionary<string, string> _httpHeaders = new Dictionary<string, string>();
|
||||||
|
public Dictionary<string, string> HttpHeader { get { return _httpHeaders; } }
|
||||||
|
|
||||||
|
private string _postString;
|
||||||
|
public string PostString { get { return _postString; } }
|
||||||
|
|
||||||
|
private Dictionary<string, string> _httpParams = new Dictionary<string, string>();
|
||||||
|
public Dictionary<string, string> HttpParams { get { return _httpParams; } }
|
||||||
|
|
||||||
|
public bool IsPostback { get { return _httpMethod.ToUpper().Equals("POST"); } }
|
||||||
|
|
||||||
|
public HttpProcessor(TcpClient s, IHttpHandler handler, Action<string> logDebugMessage, Action<Exception> logException)
|
||||||
|
{
|
||||||
|
_socket = s;
|
||||||
|
_handler = handler;
|
||||||
|
_logDebugMessage = logDebugMessage;
|
||||||
|
_logException = logException;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Process()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_inputStream = new BufferedStream(_socket.GetStream());
|
||||||
|
_outputStream = new StreamWriter(new BufferedStream(_socket.GetStream()));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_httpParams.Clear();
|
||||||
|
_httpHeaders.Clear();
|
||||||
|
ParseRequest();
|
||||||
|
ReadHeaders();
|
||||||
|
ReadPostData();
|
||||||
|
if (_handler != null)
|
||||||
|
{
|
||||||
|
_handler.HandleRequest(this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ResponseServerError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
ResponseServerError();
|
||||||
|
}
|
||||||
|
_outputStream.Flush();
|
||||||
|
_inputStream = null;
|
||||||
|
_outputStream = null;
|
||||||
|
_socket.Close();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logException?.Invoke(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string StreamReadLine(Stream inputStream)
|
||||||
|
{
|
||||||
|
int next_char;
|
||||||
|
string data = string.Empty;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
next_char = inputStream.ReadByte();
|
||||||
|
if (next_char == '\n') { break; }
|
||||||
|
if (next_char == '\r') { continue; }
|
||||||
|
if (next_char == -1) { Thread.Sleep(1); continue; };
|
||||||
|
data += Convert.ToChar(next_char);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseRequest()
|
||||||
|
{
|
||||||
|
string request = StreamReadLine(_inputStream);
|
||||||
|
string[] tokens = request.Split(' ');
|
||||||
|
if (tokens.Length != 3)
|
||||||
|
{
|
||||||
|
throw new Exception("invalid HTTP request line");
|
||||||
|
}
|
||||||
|
_httpRequest = request;
|
||||||
|
_httpMethod = tokens[0].ToUpper();
|
||||||
|
_httpResource = tokens[1];
|
||||||
|
_httpQuery = string.Empty;
|
||||||
|
_httpProtocolVersionString = tokens[2];
|
||||||
|
|
||||||
|
if (_httpResource.Contains("?"))
|
||||||
|
{
|
||||||
|
int idx = _httpResource.IndexOf('?');
|
||||||
|
_httpQuery = _httpResource.Substring(idx + 1);
|
||||||
|
_httpResource = _httpResource.Substring(0, idx);
|
||||||
|
ReadParms(_httpQuery);
|
||||||
|
}
|
||||||
|
_httpResourceLowercase = _httpResource.ToLower();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadHeaders()
|
||||||
|
{
|
||||||
|
string line;
|
||||||
|
while ((line = StreamReadLine(_inputStream)) != null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(line))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int separator = line.IndexOf(':');
|
||||||
|
if (separator == -1)
|
||||||
|
{
|
||||||
|
throw new Exception("invalid http header line: " + line);
|
||||||
|
}
|
||||||
|
string name = line.Substring(0, separator);
|
||||||
|
int pos = separator + 1;
|
||||||
|
while ((pos < line.Length) && (line[pos] == ' '))
|
||||||
|
{
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
string value = line.Substring(pos, line.Length - pos);
|
||||||
|
_httpHeaders[name] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const int BufferSize = 4096;
|
||||||
|
|
||||||
|
private void ReadPostData()
|
||||||
|
{
|
||||||
|
int content_len = 0;
|
||||||
|
MemoryStream ms = new MemoryStream();
|
||||||
|
if (this._httpHeaders.ContainsKey("Content-Length"))
|
||||||
|
{
|
||||||
|
content_len = Convert.ToInt32(this._httpHeaders["Content-Length"]);
|
||||||
|
if (content_len > MaxPostSize)
|
||||||
|
{
|
||||||
|
throw new Exception(
|
||||||
|
string.Format("POST Content-Length({0}) too big for this simple server",
|
||||||
|
content_len));
|
||||||
|
}
|
||||||
|
byte[] buf = new byte[BufferSize];
|
||||||
|
int to_read = content_len;
|
||||||
|
while (to_read > 0)
|
||||||
|
{
|
||||||
|
int numread = this._inputStream.Read(buf, 0, Math.Min(BufferSize, to_read));
|
||||||
|
if (numread == 0)
|
||||||
|
{
|
||||||
|
if (to_read == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("client disconnected during post");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
to_read -= numread;
|
||||||
|
ms.Write(buf, 0, numread);
|
||||||
|
}
|
||||||
|
ms.Seek(0, SeekOrigin.Begin);
|
||||||
|
}
|
||||||
|
_postString = new StreamReader(ms).ReadToEnd();
|
||||||
|
if (string.IsNullOrEmpty(_postString) == false)
|
||||||
|
{
|
||||||
|
ReadParms(_postString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadParms(string parms)
|
||||||
|
{
|
||||||
|
string[] parmPairs = parms.Split('&');
|
||||||
|
foreach (string parmPair in parmPairs)
|
||||||
|
{
|
||||||
|
string[] tokens = parmPair.Split('=');
|
||||||
|
string key = HttpUtility.UrlDecode(tokens[0]);
|
||||||
|
string value = string.Empty;
|
||||||
|
if (tokens.Length > 1)
|
||||||
|
{
|
||||||
|
value = HttpUtility.UrlDecode(tokens[1]);
|
||||||
|
}
|
||||||
|
if (_httpParams.ContainsKey(key))
|
||||||
|
{
|
||||||
|
_httpParams[key] = value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_httpParams.Add(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResponseSuccess(string contentType = "text/html")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_outputStream.WriteLine("HTTP/1.0 200 OK");
|
||||||
|
_outputStream.WriteLine(string.Format("Content-Type: {0}", contentType));
|
||||||
|
_outputStream.WriteLine("Connection: close");
|
||||||
|
_outputStream.WriteLine("");
|
||||||
|
_outputStream.Flush();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logException?.Invoke(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResponseServerError()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_outputStream.WriteLine("HTTP/1.0 500 Internal server error");
|
||||||
|
_outputStream.WriteLine("Connection: close");
|
||||||
|
_outputStream.WriteLine("");
|
||||||
|
_outputStream.Flush();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logException?.Invoke(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
110
VAR.HttpServer/HttpServer.cs
Normal file
110
VAR.HttpServer/HttpServer.cs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace VAR.HttpServer
|
||||||
|
{
|
||||||
|
public class HttpServer
|
||||||
|
{
|
||||||
|
protected int _port = 8000;
|
||||||
|
public int Port
|
||||||
|
{
|
||||||
|
get { return _port; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_isActive) { throw new Exception("HttpServer: Can't set port while active"); }
|
||||||
|
_port = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TcpListener _listener = null;
|
||||||
|
|
||||||
|
private Thread _thread = null;
|
||||||
|
|
||||||
|
private bool _isActive = false;
|
||||||
|
public bool IsActive { get { return _isActive; } }
|
||||||
|
|
||||||
|
private IHttpHandler _handler = null;
|
||||||
|
public IHttpHandler Handler
|
||||||
|
{
|
||||||
|
get { return _handler; }
|
||||||
|
set { _handler = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Action<string> _logDebugMessage = null;
|
||||||
|
public Action<string> LogDegugMessage { set { _logDebugMessage = value; } }
|
||||||
|
|
||||||
|
private Action<Exception> _logException = null;
|
||||||
|
public Action<Exception> LogException { set { _logException = value; } }
|
||||||
|
|
||||||
|
private void ListenConnections()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (_isActive)
|
||||||
|
{
|
||||||
|
TcpClient client = _listener.AcceptTcpClient();
|
||||||
|
HttpProcessor responseProcessor = new HttpProcessor(client, _handler, _logDebugMessage, _logException);
|
||||||
|
Thread responseThread = new Thread(new ThreadStart(responseProcessor.Process));
|
||||||
|
responseThread.Start();
|
||||||
|
}
|
||||||
|
_listener = null;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
_isActive = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Start()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_isActive || _thread != null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_listener = new TcpListener(IPAddress.Any, _port)
|
||||||
|
{
|
||||||
|
ExclusiveAddressUse = false
|
||||||
|
};
|
||||||
|
_listener.Start();
|
||||||
|
_isActive = true;
|
||||||
|
_thread = new Thread(new ThreadStart(ListenConnections));
|
||||||
|
_thread.Start();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logException?.Invoke(ex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Stop()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_isActive == false || _thread == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isActive = false;
|
||||||
|
if (_listener != null)
|
||||||
|
{
|
||||||
|
_listener.Stop();
|
||||||
|
}
|
||||||
|
_thread = null;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logException?.Invoke(ex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
127
VAR.HttpServer/HttpUtility.cs
Normal file
127
VAR.HttpServer/HttpUtility.cs
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace VAR.HttpServer
|
||||||
|
{
|
||||||
|
public sealed class HttpUtility
|
||||||
|
{
|
||||||
|
public static string UrlDecode(string str, Encoding e = null)
|
||||||
|
{
|
||||||
|
if (null == str) { return null; }
|
||||||
|
|
||||||
|
if (str.IndexOf('%') == -1 && str.IndexOf('+') == -1)
|
||||||
|
{
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e == null)
|
||||||
|
{
|
||||||
|
e = Encoding.UTF8;
|
||||||
|
}
|
||||||
|
|
||||||
|
long len = str.Length;
|
||||||
|
var bytes = new List<byte>();
|
||||||
|
int xchar;
|
||||||
|
char ch;
|
||||||
|
|
||||||
|
for (int i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
ch = str[i];
|
||||||
|
if (ch == '%' && i + 2 < len && str[i + 1] != '%')
|
||||||
|
{
|
||||||
|
if (str[i + 1] == 'u' && i + 5 < len)
|
||||||
|
{
|
||||||
|
xchar = HexToInt(str, i + 2, 4);
|
||||||
|
if (xchar != -1)
|
||||||
|
{
|
||||||
|
WriteCharBytes(bytes, (char)xchar, e);
|
||||||
|
i += 5;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteCharBytes(bytes, '%', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((xchar = HexToInt(str, i + 1, 2)) != -1)
|
||||||
|
{
|
||||||
|
WriteCharBytes(bytes, (char)xchar, e);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteCharBytes(bytes, '%', e);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == '+')
|
||||||
|
{
|
||||||
|
WriteCharBytes(bytes, ' ', e);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteCharBytes(bytes, ch, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] buf = bytes.ToArray();
|
||||||
|
bytes = null;
|
||||||
|
return e.GetString(buf);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteCharBytes(IList buf, char ch, Encoding e)
|
||||||
|
{
|
||||||
|
if (ch > 255)
|
||||||
|
{
|
||||||
|
foreach (byte b in e.GetBytes(char.ToString(ch)))
|
||||||
|
{
|
||||||
|
buf.Add(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buf.Add((byte)ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int HexDigitToInt(byte b)
|
||||||
|
{
|
||||||
|
char c = (char)b;
|
||||||
|
if (c >= '0' && c <= '9')
|
||||||
|
{
|
||||||
|
return c - '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c >= 'a' && c <= 'f')
|
||||||
|
{
|
||||||
|
return c - 'a' + 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c >= 'A' && c <= 'F')
|
||||||
|
{
|
||||||
|
return c - 'A' + 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int HexToInt(string str, int offset, int length)
|
||||||
|
{
|
||||||
|
int val = 0;
|
||||||
|
int end = length + offset;
|
||||||
|
for (int i = offset; i < end; i++)
|
||||||
|
{
|
||||||
|
char c = str[i];
|
||||||
|
if (c > 127) { return -1; }
|
||||||
|
|
||||||
|
int current = HexDigitToInt((byte)c);
|
||||||
|
if (current == -1) { return -1; }
|
||||||
|
val = (val << 4) + current;
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
VAR.HttpServer/IHttpHandler.cs
Normal file
7
VAR.HttpServer/IHttpHandler.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace VAR.HttpServer
|
||||||
|
{
|
||||||
|
public interface IHttpHandler
|
||||||
|
{
|
||||||
|
void HandleRequest(HttpProcessor processor);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
VAR.HttpServer/Properties/AssemblyInfo.cs
Normal file
15
VAR.HttpServer/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
[assembly: AssemblyTitle("VAR.HttpServer")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("VAR")]
|
||||||
|
[assembly: AssemblyProduct("VAR.HttpServer")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © VAR 2019")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
[assembly: Guid("e865ba90-11f7-46d4-b718-a536b0b76c35")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||||
51
VAR.HttpServer/VAR.HttpServer.csproj
Normal file
51
VAR.HttpServer/VAR.HttpServer.csproj
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{E865BA90-11F7-46D4-B718-A536B0B76C35}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>VAR.HttpServer</RootNamespace>
|
||||||
|
<AssemblyName>VAR.HttpServer</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<Deterministic>true</Deterministic>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="HttpProcessor.cs" />
|
||||||
|
<Compile Include="HttpServer.cs" />
|
||||||
|
<Compile Include="HttpUtility.cs" />
|
||||||
|
<Compile Include="IHttpHandler.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
||||||
Reference in New Issue
Block a user