Dodaj pliki projektów.
This commit is contained in:
parent
2871ac9ef5
commit
ec256f2f53
343
Program.cs
Normal file
343
Program.cs
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
using System.IO.Compression;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
|
||||||
|
namespace myownhttp
|
||||||
|
{
|
||||||
|
internal class Program
|
||||||
|
{
|
||||||
|
public enum HTTPMeth
|
||||||
|
{
|
||||||
|
undefined = -1,
|
||||||
|
GET = 0,
|
||||||
|
POST = 1 // add more in future
|
||||||
|
}
|
||||||
|
public class HTTPReq
|
||||||
|
{
|
||||||
|
public bool valid = false;
|
||||||
|
|
||||||
|
public string Path = "";
|
||||||
|
public string HTTPVersion = "";
|
||||||
|
public string Content = "";
|
||||||
|
public HTTPMeth Method = HTTPMeth.undefined;
|
||||||
|
|
||||||
|
|
||||||
|
public Dictionary<string,string> Headers = new Dictionary<string,string>();
|
||||||
|
|
||||||
|
public HTTPReq(string request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
var split = request.ReplaceLineEndings().Split(Environment.NewLine);
|
||||||
|
var splitfirst = split[0].Split(" ");
|
||||||
|
if (splitfirst[0] == "GET") Method = HTTPMeth.GET;
|
||||||
|
else if (splitfirst[0] == "POST") Method = HTTPMeth.POST;
|
||||||
|
Path = splitfirst[1]; // check if the path
|
||||||
|
HTTPVersion = splitfirst[2];
|
||||||
|
|
||||||
|
// headers
|
||||||
|
for (int i = 1; i < split.Length; i++)
|
||||||
|
{
|
||||||
|
var e = split[i];
|
||||||
|
if(e == "")
|
||||||
|
{
|
||||||
|
if(i+1 < split.Length)
|
||||||
|
{
|
||||||
|
var arraycontent = split.Skip(i);
|
||||||
|
foreach(var item in arraycontent)
|
||||||
|
{
|
||||||
|
Content += item + "\r\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var esplit = e.Split(": ");
|
||||||
|
Headers.Add(esplit[0], esplit[1]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
valid = true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"HTTP REQUEST -> {(valid ? "Valid" : "Invalid")}, {(Method switch { HTTPMeth.undefined => "Unset/Undefined", HTTPMeth.GET => "GET", HTTPMeth.POST => "POST", _ => "MError"})} {Path} {HTTPVersion}";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public class HTTPRes
|
||||||
|
{
|
||||||
|
byte[] contentBytes = [];
|
||||||
|
public byte[] GetBytes() { return contentBytes; }
|
||||||
|
public HTTPRes SetBytes(byte[] bytes) { contentBytes = bytes; return this; }
|
||||||
|
public HTTPRes SetBytes(string str) { contentBytes = Encoding.UTF8.GetBytes(str); return this; }
|
||||||
|
|
||||||
|
public string ContentType = GetMimeType("txt");
|
||||||
|
string Status = GetStatusFromNum(200);
|
||||||
|
|
||||||
|
public string GetStatus() { return Status; }
|
||||||
|
public HTTPRes SetStatus(int num) { SetStatus(GetStatusFromNum(num)); return this; }
|
||||||
|
public HTTPRes SetStatus(string status) { Status = status; return this; }
|
||||||
|
|
||||||
|
|
||||||
|
public byte[] Build()
|
||||||
|
{
|
||||||
|
return Encoding.UTF8.GetBytes(
|
||||||
|
$"HTTP/1.1 {Status}\r\n" +
|
||||||
|
$"Content-Type: {ContentType}{(ContentType.Contains("text") ? "; charset=utf-8" : "")}\n" +
|
||||||
|
$"Content-Length: {contentBytes.Length}" +
|
||||||
|
$"\n" +
|
||||||
|
$"\n"
|
||||||
|
).Concat(contentBytes).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public abstract class HTTPHandler
|
||||||
|
{
|
||||||
|
public HTTPHandler() { }
|
||||||
|
|
||||||
|
public virtual void Handle(HTTPReq request, HTTPRes response, Action next)
|
||||||
|
{
|
||||||
|
// this is just a base, do nothing
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class HTTPGetHandler : HTTPHandler
|
||||||
|
{
|
||||||
|
string Path = "";
|
||||||
|
Action<HTTPReq, HTTPRes, Action> Action = (HTTPReq req, HTTPRes res, Action next) => { };
|
||||||
|
public HTTPGetHandler(string path, Action<HTTPReq, HTTPRes, Action> action) { Path = path; Action = action; }
|
||||||
|
|
||||||
|
public override void Handle(HTTPReq request, HTTPRes response, Action next)
|
||||||
|
{
|
||||||
|
if (request.Path == Path) Action(request, response, next);
|
||||||
|
else next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class HTTPServeFileHandler : HTTPHandler
|
||||||
|
{
|
||||||
|
HTTPGetHandler replacement;
|
||||||
|
public HTTPServeFileHandler(string path, string filepath, string MimeType)
|
||||||
|
{
|
||||||
|
replacement = new(path, (req, res, next) =>
|
||||||
|
{
|
||||||
|
if (File.Exists(filepath))
|
||||||
|
{
|
||||||
|
res.SetBytes(File.ReadAllBytes(filepath)).SetStatus(200).ContentType = MimeType;
|
||||||
|
}
|
||||||
|
else next();
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Handle(HTTPReq request, HTTPRes response, Action next)
|
||||||
|
{
|
||||||
|
replacement.Handle(request, response, next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HTTPStaticFilesHandler : HTTPHandler
|
||||||
|
{
|
||||||
|
string Files = "";
|
||||||
|
public HTTPStaticFilesHandler(string pathWithStatic) { Files = pathWithStatic; }
|
||||||
|
|
||||||
|
public override void Handle(HTTPReq request, HTTPRes response, Action next)
|
||||||
|
{
|
||||||
|
var pat = request.Path.Replace("..", "");
|
||||||
|
if (pat.EndsWith("/")) pat += "index.html";
|
||||||
|
if (File.Exists(Files + Path.DirectorySeparatorChar + pat))
|
||||||
|
{
|
||||||
|
var ex = Path.GetExtension(Files + Path.DirectorySeparatorChar + pat);
|
||||||
|
if (ex == string.Empty) ex = ".txt";
|
||||||
|
ex = ex.Substring(1);
|
||||||
|
response.SetBytes(File.ReadAllBytes(Files + Path.DirectorySeparatorChar + pat)).SetStatus(200).ContentType = GetMimeType(ex);
|
||||||
|
}
|
||||||
|
else next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class HTTPConditionHandler : HTTPHandler
|
||||||
|
{
|
||||||
|
HTTPHandler Handler = null;
|
||||||
|
Func<HTTPReq, HTTPRes, bool> Condition = (HTTPReq request, HTTPRes response) => { return false; };
|
||||||
|
public HTTPConditionHandler(Func<bool> cond, HTTPHandler handler) { Handler = handler; }
|
||||||
|
|
||||||
|
public override void Handle(HTTPReq request, HTTPRes response, Action next)
|
||||||
|
{
|
||||||
|
if(Handler == null || !Condition(request,response))
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Handler.Handle(request, response, next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class HTTPServer
|
||||||
|
{
|
||||||
|
TcpListener listener;
|
||||||
|
public HTTPServer(int port)
|
||||||
|
{
|
||||||
|
listener = new(IPAddress.Any, 8003);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<HTTPHandler> Handlers = new();
|
||||||
|
|
||||||
|
public void Listen()
|
||||||
|
{
|
||||||
|
listener.Start();
|
||||||
|
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var a = listener.AcceptTcpClient();
|
||||||
|
|
||||||
|
var s = a.GetStream();
|
||||||
|
|
||||||
|
byte[] bytes = new byte[2048];
|
||||||
|
string data = null;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
// Loop to receive all the data sent by the client.
|
||||||
|
while ((i = s.Read(bytes, 0, bytes.Length)) != 0)
|
||||||
|
{
|
||||||
|
// Translate data bytes to a ASCII string.
|
||||||
|
data = Encoding.ASCII.GetString(bytes, 0, i);
|
||||||
|
|
||||||
|
var req = new HTTPReq(data);
|
||||||
|
|
||||||
|
Console.WriteLine(req.ToString());
|
||||||
|
|
||||||
|
if (req.valid)
|
||||||
|
{
|
||||||
|
HTTPRes res = new HTTPRes();
|
||||||
|
int nextAct = 0;
|
||||||
|
Action next = () => { };
|
||||||
|
next = () => {
|
||||||
|
if(nextAct >= Handlers.Count)
|
||||||
|
{
|
||||||
|
|
||||||
|
res.SetBytes("404 Not found.").SetStatus(404).ContentType = GetMimeType("txt");
|
||||||
|
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
Handlers[nextAct].Handle(req, res, () => {
|
||||||
|
|
||||||
|
nextAct++;
|
||||||
|
next();
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
next();
|
||||||
|
|
||||||
|
s.Write(res.Build());
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
// Send back a response.
|
||||||
|
var nf = "uuh";
|
||||||
|
var nfb = Encoding.UTF8.GetBytes(nf);
|
||||||
|
s.Write(
|
||||||
|
Encoding.UTF8.GetBytes(
|
||||||
|
$"HTTP/1.1 404 Not Found\n" +
|
||||||
|
$"Content-Type: text/plain; charset=utf-8\n" +
|
||||||
|
$"Content-Length: {nfb.Length}" +
|
||||||
|
$"\n" +
|
||||||
|
$"\n" +
|
||||||
|
$"{nf}"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Console.WriteLine("hello");
|
||||||
|
|
||||||
|
HTTPServer http = new(8003);
|
||||||
|
|
||||||
|
http.Handlers.Add(new HTTPStaticFilesHandler("htdocs"));
|
||||||
|
|
||||||
|
/*http.Handlers.Add(new HTTPGetHandler("/", (req, res, next) =>
|
||||||
|
{
|
||||||
|
res.SetStatus(200).SetBytes("Hello! This is a test");
|
||||||
|
}));
|
||||||
|
http.Handlers.Add(new HTTPGetHandler("/2", (req, res, next) =>
|
||||||
|
{
|
||||||
|
res.SetStatus(200).SetBytes("Hello! This is a second test!");
|
||||||
|
}));
|
||||||
|
|
||||||
|
http.Handlers.Add(new HTTPServeFileHandler("/favicon.ico", "favicon.ico",GetMimeType("ico")));*/
|
||||||
|
|
||||||
|
http.Listen();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetStatusFromNum(int v)
|
||||||
|
{
|
||||||
|
return v switch
|
||||||
|
{
|
||||||
|
200 => "200 OK",
|
||||||
|
400 => "400 Bad Request",
|
||||||
|
401 => "401 Unauthorized",
|
||||||
|
403 => "403 Forbidden",
|
||||||
|
404 => "404 Not Found",
|
||||||
|
_ => "501 Not Implemented"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private static string GetMimeType(string v)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
image/apng: Animated Portable Network Graphics (APNG)
|
||||||
|
image/avif : AV1 Image File Format (AVIF)
|
||||||
|
image/gif: Graphics Interchange Format (GIF)
|
||||||
|
image/jpeg: Joint Photographic Expert Group image (JPEG)
|
||||||
|
image/png: Portable Network Graphics (PNG)
|
||||||
|
image/svg+xml: Scalable Vector Graphics (SVG)
|
||||||
|
image/webp: Web Picture format (WEBP)
|
||||||
|
*/
|
||||||
|
return v switch
|
||||||
|
{
|
||||||
|
// text
|
||||||
|
"txt" => "text/plain",
|
||||||
|
"css" => "text/css",
|
||||||
|
"js" => "text/javascript",
|
||||||
|
"html" => "text/html",
|
||||||
|
"json" => "application/json",
|
||||||
|
// image
|
||||||
|
"webp" => "image/webp",
|
||||||
|
"png" => "image/png",
|
||||||
|
"jpg" or "jpeg" => "image/jpeg",
|
||||||
|
"svg" => "image/svg+xml",
|
||||||
|
"gif" => "image/gif",
|
||||||
|
"ico" => "image/x-icon",
|
||||||
|
// just download it man
|
||||||
|
_ => "application/octet-stream"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
10
myownhttp.csproj
Normal file
10
myownhttp.csproj
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
25
myownhttp.sln
Normal file
25
myownhttp.sln
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.14.36203.30 d17.14
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "myownhttp", "myownhttp.csproj", "{4E977172-6187-481D-B819-982A278D925E}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{4E977172-6187-481D-B819-982A278D925E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{4E977172-6187-481D-B819-982A278D925E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{4E977172-6187-481D-B819-982A278D925E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{4E977172-6187-481D-B819-982A278D925E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {FB36ECD5-0874-4AB9-A8BB-2AF5DE85A8F9}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
Loading…
Reference in New Issue
Block a user