-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
308 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
using Newtonsoft.Json; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Net; | ||
using System.Text; | ||
using System.Text.Json.Serialization; | ||
using System.Threading.Tasks; | ||
using TeraIO.Data; | ||
|
||
namespace TeraIO.Network.Http | ||
{ | ||
public struct HttpClientRequest | ||
{ | ||
public string? Url { get; set; } | ||
public string? ContentType { get; set; } | ||
public string Method { get; set; } | ||
public byte[] Content { get; set; } | ||
public HttpListenerRequest Request { get; set; } | ||
public HttpListenerResponse Response { get; set; } | ||
public Stream InputStream { get => this.Request.InputStream; } | ||
public Stream OutputStream { get => this.Response.OutputStream; } | ||
public int ResponseStatusCode { get => this.Response.StatusCode; set => this.Response.StatusCode = value; } | ||
|
||
public T? TryParseTo<T>() where T : class | ||
{ | ||
T? result = null; | ||
try | ||
{ | ||
result = JsonConvert.DeserializeObject<T>(this.Text); | ||
} | ||
catch (Exception ex) | ||
{ | ||
Console.WriteLine(ex); | ||
} | ||
return result; | ||
} | ||
|
||
public T ParseTo<T>() where T : class | ||
{ | ||
T? result = JsonConvert.DeserializeObject<T>(this.Text); | ||
if (result == null) | ||
{ | ||
throw new ArgumentNullException(nameof(result)); | ||
} | ||
return result; | ||
} | ||
|
||
public int Send(string content) | ||
{ | ||
byte[] bytes = Encoding.UTF8.GetBytes(content); | ||
this.OutputStream.Write(bytes); | ||
return bytes.Length; | ||
} | ||
|
||
public int Send(byte[] bytes) | ||
{ | ||
this.OutputStream.Write(bytes); | ||
return bytes.Length; | ||
} | ||
|
||
public string Text | ||
{ | ||
get | ||
{ | ||
return Encoding.UTF8.GetString(Content); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace TeraIO.Network.Http | ||
{ | ||
public class HttpHandlerFunction : Attribute | ||
{ | ||
public string Route { get; set; } | ||
public List<string> Methods { get; set; } | ||
|
||
public HttpHandlerFunction(string route, string methods = "GET") | ||
{ | ||
this.Route = route; | ||
this.Methods = methods.Split(' ').ToList(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace TeraIO.Network.Http | ||
{ | ||
public static class HttpServer | ||
{ | ||
/// <summary> | ||
/// 通过一个继承自 <see cref="HttpServerAppBase"/> 的子类来简单创建一个 <see cref="HttpServer"/> | ||
/// </summary> | ||
/// <param name="app"></param> | ||
/// <returns> | ||
/// 生成的 <see cref="HttpServerAppBase"/> 对象,调用 <see cref="HttpServerAppBase.Run"/> 来启动 <see cref="HttpServer"/> | ||
/// </returns> | ||
static HttpServerAppBase LoadNew(HttpServerAppBase app) | ||
{ | ||
Type type = app.GetType(); | ||
|
||
// 获取所有被标记为HttpHandlerFunction的方法 | ||
MethodInfo[] methodInfos = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); | ||
Dictionary<HttpHandlerFunction, MethodInfo> methods = new(); | ||
|
||
// 遍历方法数组,检查每个方法是否被标记为HttpHandlerFunction | ||
foreach (MethodInfo methodInfo in methodInfos) | ||
{ | ||
HttpHandlerFunction? attr = Attribute.GetCustomAttribute(methodInfo, typeof(HttpHandlerFunction)) as HttpHandlerFunction; | ||
ParameterInfo[] parameters = methodInfo.GetParameters(); | ||
if (attr != null && parameters.Length > 0 && parameters[0].ParameterType == typeof(HttpClientRequest)) | ||
{ | ||
methods.Add(attr, methodInfo); | ||
} | ||
} | ||
app.methods = methods; | ||
return app; | ||
} | ||
|
||
/// <summary> | ||
/// 通过一个 <see cref="MethodInfo"/>[] 来简单创建一个 <see cref="HttpServer"/> | ||
/// </summary> | ||
/// <param name="app"></param> | ||
/// <returns> | ||
/// 生成的 <see cref="HttpServerAppBase"/> 对象,调用 <see cref="HttpServerAppBase.Run"/> 来启动 <see cref="HttpServer"/> | ||
/// </returns> | ||
static HttpServerAppBase LoadNew(IList<MethodInfo> methodInfos) | ||
{ | ||
HttpServerAppBase app = new HttpServerAppBase(); | ||
Dictionary<HttpHandlerFunction, MethodInfo> methods = new(); | ||
// 遍历方法数组,检查每个方法是否被标记为HttpHandlerFunction | ||
foreach (MethodInfo methodInfo in methodInfos) | ||
{ | ||
HttpHandlerFunction? attr = Attribute.GetCustomAttribute(methodInfo, typeof(HttpHandlerFunction)) as HttpHandlerFunction; | ||
ParameterInfo[] parameters = methodInfo.GetParameters(); | ||
if (attr != null && parameters.Length > 0 && parameters[0].ParameterType == typeof(HttpClientRequest)) | ||
{ | ||
methods.Add(attr, methodInfo); | ||
} | ||
} | ||
app.methods = methods; | ||
return app; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Net; | ||
using System.Reflection; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using TeraIO.Extension; | ||
|
||
namespace TeraIO.Network.Http | ||
{ | ||
/// <summary> | ||
/// <see cref="HttpServer"/> 返回的 HttpServer 实例。调用 <see cref="HttpServerAppBase.Run"/> 来启动一个简单的 Http 服务器 | ||
/// 正常情况下,你不应该创建这个类的实例,而是由 <see cref="HttpServer"/> 创建或者通过它的子类来获得它的实例! | ||
/// </summary> | ||
public class HttpServerAppBase | ||
{ | ||
|
||
public string HOST => "127.0.0.1"; | ||
public int PORT => 1280; | ||
|
||
public List<string> UriPrefixes { get; set; } | ||
CancellationToken cancellationToken; | ||
Check warning on line 23 in Network/Http/HttpServerAppBase.cs GitHub Actions / build (Debug)
Check warning on line 23 in Network/Http/HttpServerAppBase.cs GitHub Actions / build (Debug)
Check warning on line 23 in Network/Http/HttpServerAppBase.cs GitHub Actions / build (Release)
|
||
public Dictionary<HttpHandlerFunction, MethodInfo> methods; | ||
|
||
public HttpServerAppBase(Dictionary<HttpHandlerFunction, MethodInfo>? methods = null) | ||
{ | ||
this.cancellationToken = new CancellationToken(); | ||
if (methods == null) | ||
{ | ||
this.methods = new Dictionary<HttpHandlerFunction, MethodInfo>(); | ||
} | ||
else | ||
{ | ||
this.methods = methods; | ||
} | ||
this.UriPrefixes = new List<string>(); | ||
} | ||
|
||
public void Run() | ||
{ | ||
HttpListener listener = new HttpListener(); | ||
listener.Prefixes.Merge(this.UriPrefixes); | ||
listener.Start(); | ||
HandleHttpConnection(listener); | ||
} | ||
|
||
public HttpClientRequest ParseHttpRequest(HttpListenerContext context) | ||
{ | ||
HttpClientRequest result = new HttpClientRequest | ||
{ | ||
Method = context.Request.HttpMethod, | ||
Content = GetBytesFromStream(context.Request.InputStream), | ||
ContentType = context.Request.ContentType, | ||
Url = context.Request.RawUrl, | ||
Request = context.Request, | ||
Response = context.Response | ||
}; | ||
return result; | ||
} | ||
|
||
public void HandleHttpConnection(HttpListener listener) | ||
{ | ||
while (true) | ||
{ | ||
HttpListenerContext context = listener.GetContext(); | ||
Thread task = new Thread(() => HandleHttpConnectionSingle(context)); | ||
task.Start(); | ||
} | ||
} | ||
|
||
public void HandleHttpConnectionSingle(HttpListenerContext context) | ||
{ | ||
HttpListenerRequest request = context.Request; | ||
HttpListenerResponse response = context.Response; | ||
//Console.WriteLine(IsCorrectRoute(request.RawUrl, "/index")); | ||
MethodInfo[] methods = this.methods.Where(kvp => IsCorrectRoute(request.RawUrl, kvp.Key.Route)).Select(kvp => kvp.Value).OrderBy(f => f.Name.Length).Reverse().ToArray(); | ||
if (methods != null) | ||
{ | ||
foreach (MethodInfo method in methods) | ||
{ | ||
if (method != null) | ||
{ | ||
//var a = Convert.ChangeType(this, this.GetType()); | ||
object[] parameters = new object[] { ParseHttpRequest(context) }; | ||
method.Invoke(this, parameters); | ||
break; | ||
} | ||
} | ||
} | ||
response.Close(); | ||
} | ||
|
||
public string GetTemplateString(string name) | ||
{ | ||
StreamReader sr = new StreamReader($"./templates/{name}"); | ||
string t = sr.ReadToEnd(); | ||
return t; | ||
} | ||
|
||
public byte[] GetTemplateByteArray(string name) | ||
{ | ||
StreamReader sr = new StreamReader($"./templates/{name}"); | ||
Stream t = sr.BaseStream; | ||
return GetBytesFromStream(t); | ||
} | ||
|
||
private bool IsCorrectRoute(string? raw, string? route) | ||
{ | ||
if (string.IsNullOrWhiteSpace(raw)) | ||
{ | ||
return false; | ||
} | ||
if (string.IsNullOrWhiteSpace(route)) | ||
{ | ||
return false; | ||
} | ||
if (raw.EndsWith('/') && raw.StartsWith(route.EndsWith('/') ? route : $"{route}/")) | ||
{ | ||
return true; | ||
} | ||
if ((!raw.EndsWith('/')) && raw.StartsWith(route)) | ||
{ | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
public byte[] GetBytesFromStream(Stream s) | ||
{ | ||
byte[] buffer = new byte[4096]; | ||
List<byte> result = new List<byte>(); | ||
while (s.Read(buffer, 0, 4096) > 0) | ||
{ | ||
result = result.Concat(buffer).ToList(); | ||
} | ||
return result.ToArray(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters