-
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.
update: 从 CSharp-OpenBMCLAPI 迁移了 SimpleWebServer
- Loading branch information
Showing
9 changed files
with
678 additions
and
0 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Net.Sockets; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace CSharpOpenBMCLAPI.Modules.WebServer | ||
{ | ||
public class Client : IDisposable | ||
{ | ||
public TcpClient TcpClient; | ||
Stream stream; | ||
|
||
public Stream Stream { get => this.stream; } | ||
|
||
public Client(TcpClient client, Stream stream) | ||
{ | ||
this.TcpClient = client; | ||
this.stream = stream; | ||
} | ||
|
||
public void Close() | ||
{ | ||
this.stream.Close(); | ||
this.TcpClient.Close(); | ||
} | ||
|
||
public void Dispose() => this.Close(); | ||
|
||
public async Task<byte[]> Read(int n = 1) | ||
{ | ||
byte[] buffer = new byte[n]; | ||
long length = await this.stream.ReadAsync(buffer); | ||
byte[] data = new byte[length]; | ||
Array.Copy(buffer, data, length); | ||
return data; | ||
} | ||
|
||
public async Task Write(byte[] data) | ||
{ | ||
await this.stream.WriteAsync(data); | ||
await this.stream.FlushAsync(); | ||
} | ||
|
||
public async Task CopyTo(Stream stream) | ||
{ | ||
await this.stream.CopyToAsync(stream); | ||
await this.stream.FlushAsync(); | ||
} | ||
|
||
public async Task CopyFrom(Stream stream) | ||
{ | ||
await stream.CopyToAsync(this.stream); | ||
await stream.FlushAsync(); | ||
} | ||
} | ||
} |
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,128 @@ | ||
using System; | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace CSharpOpenBMCLAPI.Modules.WebServer | ||
{ | ||
public class Header : IDictionary<string, string> | ||
{ | ||
Dictionary<string, string> tables; | ||
|
||
public Header(Dictionary<string, string> tables) | ||
{ | ||
this.tables = tables; | ||
} | ||
|
||
public Header() | ||
{ | ||
this.tables = new Dictionary<string, string>(); | ||
} | ||
|
||
public static Header FromBytes(byte[][] headers) | ||
{ | ||
Dictionary<string, string> tables = new Dictionary<string, string>(); | ||
foreach (var header in headers) | ||
{ | ||
var h = WebUtils.SplitBytes(header, WebUtils.Encode(": "), 1).ToArray(); | ||
if (h.Length < 1) | ||
{ | ||
continue; | ||
} | ||
tables.TryAdd(WebUtils.Decode(h[0]).ToLower(), WebUtils.Decode(h[1])); | ||
} | ||
return new Header(tables); | ||
} | ||
|
||
public object Get(string key, object defaultValue) | ||
{ | ||
return tables.ContainsKey(key) ? tables[key] : defaultValue; | ||
} | ||
|
||
public void Set(string key, object value) | ||
{ | ||
if (value == null) | ||
{ | ||
if (tables.ContainsKey(key)) tables.Remove(key); | ||
return; | ||
} | ||
tables.TryAdd(key, value + ""); | ||
} | ||
|
||
public bool ContainsKey(string key) => tables.ContainsKey(key); | ||
|
||
public override string ToString() => string.Join("\r\n", from kvp in this select $"{kvp.Key}: {kvp.Value}"); | ||
|
||
// Auto generated. | ||
|
||
public string this[string key] { get => tables[key]; set => tables[key] = value; } | ||
|
||
public ICollection<string> Keys => tables.Keys; | ||
|
||
public ICollection<string> Values => tables.Values; | ||
|
||
public int Count => tables.Count; | ||
|
||
public bool IsReadOnly => ((ICollection<KeyValuePair<string, string>>)tables).IsReadOnly; | ||
|
||
public void Add(string key, string value) | ||
{ | ||
tables.Add(key, value); | ||
} | ||
|
||
public void Add(KeyValuePair<string, string> item) | ||
{ | ||
((ICollection<KeyValuePair<string, string>>)tables).Add(item); | ||
} | ||
|
||
public void Clear() | ||
{ | ||
tables.Clear(); | ||
} | ||
|
||
public bool Contains(KeyValuePair<string, string> item) | ||
{ | ||
return tables.Contains(item); | ||
} | ||
|
||
public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex) | ||
{ | ||
((ICollection<KeyValuePair<string, string>>)tables).CopyTo(array, arrayIndex); | ||
} | ||
|
||
public IEnumerator<KeyValuePair<string, string>> GetEnumerator() | ||
{ | ||
return ((IEnumerable<KeyValuePair<string, string>>)tables).GetEnumerator(); | ||
} | ||
|
||
public bool Remove(string key) | ||
{ | ||
return tables.Remove(key); | ||
} | ||
|
||
public bool Remove(KeyValuePair<string, string> item) | ||
{ | ||
return ((ICollection<KeyValuePair<string, string>>)tables).Remove(item); | ||
} | ||
|
||
public bool TryGetValue(string key, [MaybeNullWhen(false)] out string value) | ||
{ | ||
return tables.TryGetValue(key, out value); | ||
} | ||
|
||
public string? TryGetValue(string key) | ||
{ | ||
string? value; | ||
tables.TryGetValue(key, out value); | ||
return value; | ||
} | ||
|
||
IEnumerator IEnumerable.GetEnumerator() | ||
{ | ||
return ((IEnumerable)tables).GetEnumerator(); | ||
} | ||
} | ||
} |
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,16 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Net; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace CSharpOpenBMCLAPI.Modules.WebServer | ||
{ | ||
public class HttpContext | ||
{ | ||
public required Request Request { get; set; } | ||
public required Response Response { get; set; } | ||
public required EndPoint RemoteIPAddress { get; set; } | ||
} | ||
} |
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,53 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace CSharpOpenBMCLAPI.Modules.WebServer | ||
{ | ||
public class Request | ||
{ | ||
internal string? QueryString = ""; | ||
|
||
public string Method { get; set; } | ||
public string Path { get; set; } | ||
public string Protocol { get; set; } | ||
public Header Header { get; set; } | ||
public int BodyLength { get; set; } | ||
public Client Client { get; set; } | ||
public byte[] BodyData { get; set; } | ||
|
||
public Request(Client client, byte[] data) | ||
{ | ||
this.Client = client; | ||
byte[][] temp = WebUtils.SplitBytes(data, WebUtils.Encode("\r\n\r\n"), 2).ToArray(); | ||
this.BodyData = temp[1]; | ||
byte[][] requestHeader = WebUtils.SplitBytes(temp[0], WebUtils.Encode("\r\n")).ToArray(); | ||
temp = WebUtils.SplitBytes(requestHeader[0], WebUtils.Encode(" "), 3).ToArray(); | ||
(Method, Path, Protocol) = (WebUtils.Decode(temp[0]), WebUtils.Decode(temp[1]), WebUtils.Decode(temp[2])); | ||
Header = Header.FromBytes(requestHeader[1..]); | ||
BodyLength = int.Parse(Header.Get("content-length", 0) + ""); | ||
} | ||
|
||
public async Task SkipContent() | ||
{ | ||
await this.Client.Read(BodyLength - BodyData.Length); | ||
} | ||
|
||
public override string ToString() | ||
{ | ||
string result = $""" | ||
{Method} {Path} {Protocol} | ||
Headers: | ||
{string.Join("\n ", this.Header.Select(f => $"{f.Key}: {f.Value}"))} | ||
Data: | ||
{Convert.ToHexString(this.BodyData)} | ||
"""; | ||
return result; | ||
} | ||
} | ||
} |
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,122 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using TeraIO.Extension; | ||
|
||
namespace CSharpOpenBMCLAPI.Modules.WebServer | ||
{ | ||
public class Response | ||
{ | ||
public int StatusCode { get; set; } = 200; | ||
public Header Header { get; set; } = new Header(); | ||
public Stream Stream { get; set; } = new MemoryStream(); | ||
|
||
public async Task Call(Client client, Request request) | ||
{ | ||
if (!Header.ContainsKey("Content-Length")) Header.Set("Content-Length", Stream.Length); | ||
Header.Set("X-Powered-By", "CSharp-SaltWood"); | ||
string responseHeader = $"HTTP/1.1 {StatusCode} {GetStatusMsg(StatusCode)}\r\n{Header}\r\n\r\n"; | ||
await client.Write(responseHeader.Encode()); | ||
Stream.CopyTo(client.Stream); | ||
client.Stream.Flush(); | ||
Stream.Close(); | ||
} | ||
|
||
/// <summary> | ||
/// HTTP 状态码 | ||
/// </summary> | ||
public static readonly Dictionary<int, string> STATUS_CODES = new Dictionary<int, string> | ||
{ | ||
{ 100, "Continue" }, // 继续 | ||
{ 101, "Switching Protocols" }, // 切换协议 | ||
{ 102, "Processing" }, // 处理中 | ||
{ 103, "Early Hints" }, // 预加载资源 | ||
{ 200, "OK" }, // 请求成功 | ||
{ 201, "Created" }, // 成功创建资源 | ||
{ 202, "Accepted" }, // 收到请求 | ||
{ 203, "Non-Authoritative Information" }, // 服务器成功处理请求,但返回信息可能不是原始服务器上的有效集合,可能是本地或第三方拷贝 | ||
{ 204, "No Content" }, // 没有 body | ||
{ 205, "Reset Content" }, // 重置文档 | ||
{ 206, "Partial Content" }, // 部分资源 | ||
{ 207, "Multi-Status" }, // 不知道选哪个状态码,所以干脆全都丢给你 | ||
{ 208, "Already Reported" }, // 在 DAV 里面使用 <dav:propstat> 响应元素以避免重复枚举多个绑定的内部成员到同一个集合 | ||
{ 226, "IM Used" }, // 服务器已经完成了对资源的GET请求,并且响应是对当前实例应用的一个或多个实例操作结果的表示 | ||
{ 300, "Multiple Choices" }, // 不知道你要哪个,干脆全部丢给你,让你自己选 | ||
{ 301, "Moved Pemanently" }, // 资源永久迁移啦 | ||
{ 302, "Found" }, // 暂时搬走了,去这里找;以后有啥更改,还是在老地方通知 | ||
{ 303, "See Other" }, // 用 GET 方法去这个地方找资源,别来我这儿 | ||
{ 304, "Not Modified" }, // 改都没改 | ||
{ 305, "Use Proxy" }, // 你还是开个代理吧 | ||
{ 306, "Unused" }, // 鬼知道有什么用 | ||
{ 307, "Temporary Redirect" }, // 跟 302 一样,但你用的什么方法原来是什么,现在也必须也用什么 | ||
{ 308, "308 Permanent Redirect" }, // 跟 301 一样,但你用的是方法原来是什么,现在也必须也用什么 | ||
{ 400, "Bad Request" }, // 你的锅,给我整不会了 | ||
{ 401, "Unauthorized" }, // 你谁啊? | ||
{ 402, "Payment Required" }, // V 我 50 | ||
{ 403, "Forbidden" }, // 禁 止 访 问 | ||
{ 404, "Not Found" }, // 没 有 | ||
{ 405, "Method Not Allowed" }, // 方 法 不 对 | ||
{ 406, "Not Acceptable" }, // 当 web 服务器在执行服务端驱动型内容协商机制后,没有发现任何符合用户代理给定标准的内容时,就会发送此响应 | ||
{ 407, "Proxy Authentication Required" }, // 你代理谁啊? | ||
{ 408, "Request Time-out" }, // 超 时 了 | ||
{ 409, "Conflict" }, // 公 交 车(划)冲 突 了 | ||
{ 410, "Gone" }, // 资 源 死 了 , 删 了 吧 | ||
{ 411, "Length Required" }, // 你要多长,你不告诉我,我咋知道 | ||
{ 412, "Precondition Failed" }, // 不是不报,条件未满足 | ||
{ 413, "Payload Too Large" }, // 请 求 实 体 太 大 | ||
{ 414, "URI Too Long" }, // 链 接 太 长 | ||
{ 415, "Unsupported Media Type" }, // 请求数据的媒体格式不支持 | ||
{ 416, "Range Not Satisfiable" }, // 你范围太大了 | ||
{ 417, "Expectation Failed" }, // 对不起,我做不到(指 Expect) | ||
{ 418, "I'm a teapot" }, // 我 是 茶 壶 | ||
{ 421, "Misdirected Request" }, // 请求被定向到无法生成响应的服务器 | ||
{ 422, "Unprocessable Entity" }, // 格式正确但语义错误 | ||
{ 423, "Locked" }, // 锁上了 | ||
{ 424, "Failed Dependency" }, // 多米诺骨牌倒了,上一个请求炸了,这个也不行 | ||
{ 425, "Too Early" }, // 你来的真早!当前服务器不愿意冒风险处理请求,请稍等几分钟 | ||
{ 426, "Upgrade Required" }, // 发 现 新 版 本 | ||
{ 428, "Precondition Required" }, // 我是有条件嘀~ | ||
{ 429, "Too Many Requests" }, // 太快了,停下来! | ||
{ 431, "Request Header Fields Too Large" }, // 请求头太大了 | ||
{ 451, "Unavailable For Legal Reasons" }, // 法 律 封 锁 | ||
{ 500, "Internal Server Eror" }, // 给服务器整不会了 | ||
{ 501, "Not Implemented" }, // 我 不 会 | ||
{ 502, "Bad Gateway" }, // 网关炸了 | ||
{ 503, "Service Unavailable" }, // 伺 服 器 維 護 中 , 將 結 束 應 用 程 式 。 | ||
{ 504, "Gateway Time-out" }, // 网关超时 | ||
{ 505, "HTTP Version not supported" }, // HTTP 版本不支持 | ||
{ 506, "Variant Also Negotiates" }, // 服务器配置文件炸了 | ||
{ 507, "Insufficient Storage" }, // 无法在资源上执行该方法 | ||
{ 508, "Loop Detected" }, // 死 循 环 辣 | ||
{ 510, "Not Extended" }, // 你得扩展扩展 | ||
{ 511, "Network Authentication Required" } // 登 录 校 园 网 | ||
}; | ||
|
||
public static string GetStatusMsg(int status) | ||
{ | ||
return STATUS_CODES.ContainsKey(status) ? STATUS_CODES[status] : STATUS_CODES[status / 100 * 100]; | ||
} | ||
|
||
public ValueTask SendFile(string filePath) | ||
{ | ||
this.Stream = File.OpenRead(filePath); | ||
return ValueTask.CompletedTask; | ||
} | ||
|
||
public Task WriteAsync(byte[] buffer, int offset, int count) | ||
{ | ||
return this.Stream.WriteAsync(buffer, offset, count); | ||
} | ||
|
||
public Task WriteAsync(byte[] buffer) | ||
{ | ||
return this.Stream.WriteAsync(buffer, 0, buffer.Length); | ||
} | ||
|
||
public Task WriteAsync(string data) => this.WriteAsync(Encoding.UTF8.GetBytes(data)); | ||
|
||
public void ResetStreamPosition() => this.Stream.Position = 0; | ||
} | ||
} |
Oops, something went wrong.