提交 3760d05a 编写于 作者: JasonWcx's avatar JasonWcx

新建项目

上级 977ee867
namespace Mozi.HttpEmbedded
{
/// <summary>
/// ASCII码表
/// </summary>
public class ASCIICode
{
public const byte NUT =0; //NUL (null) 空字符
public const byte SOH =1; //SOH (start of handing) 标题开始
public const byte STX =2; //STX (start of text) 正文开始
public const byte ETX =3; //ETX (end of text) 正文结束
public const byte EOT =4; //EOT (end of transmission) 传输结束
public const byte ENQ =5; //ENQ (enquiry) 请求
public const byte ACK =6; //ACK (acknowledge) 收到通知
public const byte BEL =7; //BEL (bell) 响铃
public const byte BS =8; //BS (backspace) 退格
public const byte HT =9; //HT (horizontal tab) 水平制表符
public const byte LF =10; //LF (NL line feed; new line) 换行键
public const byte VT =11; //VT (vertical tab) 垂直制表符
public const byte FF =12; //FF (NP form feed; new page) 换页键
public const byte CR =13; //CR (carriage return) 回车键
public const byte SO =14; //SO (shift out) 不用切换
public const byte SI =15; //SI (shift in) 启用切换
public const byte DLE =16; //DLE (data link escape) 数据链路转义
public const byte DCI =17; //DC1 (device control 1) 设备控制1
public const byte DC2 =18; //DC2 (device control 2) 设备控制2
public const byte DC3 =19; //DC3 (device control 3) 设备控制3
public const byte DC4 =20; //DC4 (device control 4) 设备控制4
public const byte NAK =21; //NAK (negative acknowledge) 拒绝接收
public const byte SYN =22; //SYN (synchronous idle) 同步空闲
public const byte TB =23; //ETB (end of trans. block) 传输块结束
public const byte CAN =24; //CAN (cancel) 取消
public const byte EM =25; //EM (end of medium) 介质中断
public const byte SUB =26; //SUB (substitute) 替补
public const byte ESC =27; //ESC (escape) 溢出
public const byte FS =28; //FS (file separator) 文件分割符
public const byte GS =29; //GS (group separator) 分组符
public const byte RS =30; //RS (record separator) 记录分离符
public const byte US =31; //US (unit separator) 单元分隔符
public const byte SPACE =32; //空格
public const byte EXCLAMATION =33; //!
public const byte QUOTE =34; //"
public const byte POUND =35; //#
public const byte DOLLAR =36; //$
public const byte PERCENT =37; //%
public const byte AND =38; //&
public const byte QUOTE_SINGLE =39; //'
public const byte OPEN_PARENTHESIS =40; //(
public const byte CLOSE_PARENTHESIS =41; //)
public const byte MULTIPLY =42; //*
public const byte PLUS =43; //+
public const byte COMMA =44; //,
public const byte MINUS =45; //-
public const byte DOT =46; //.
public const byte DIVIDE =47; // /
public const byte NUM_0 =48; //0
public const byte NUM_1 =49; //1
public const byte NUM_2 =50; //2
public const byte NUM_3 =51; //3
public const byte NUM_4 =52; //4
public const byte NUM_5 =53; //5
public const byte NUM_6 =54; //6
public const byte NUM_7 =55; //7
public const byte NUM_8 =56; //8
public const byte NUM_9 =57; //9
public const byte COLON =58; //:
public const byte SEMICOLON =59; //;
public const byte LESS =60; //<
public const byte EQUAL =61; //=
public const byte MORE =62; //>
public const byte QUESTION =63; //?
public const byte AT =64; //@
public const byte CHAR_A =65; //A
public const byte CHAR_B =66; //B
public const byte CHAR_C =67; //C
public const byte CHAR_D =68; //D
public const byte CHAR_E =69; //E
public const byte CHAR_F =70; //F
public const byte CHAR_G =71; //G
public const byte CHAR_H =72; //H
public const byte CHAR_I =73; //I
public const byte CHAR_J =74; //J
public const byte CHAR_K =75; //K
public const byte CHAR_L =76; //L
public const byte CHAR_M =77; //M
public const byte CHAR_N =78; //N
public const byte CHAR_O =79; //O
public const byte CHAR_P =80; //P
public const byte CHAR_Q =81; //Q
public const byte CHAR_R =82; //R
public const byte CHAR_S =83; //S
public const byte CHAR_T =84; //T
public const byte CHAR_U =85; //U
public const byte CHAR_V =86; //V
public const byte CHAR_W =87; //W
public const byte CHAR_X =88; //X
public const byte CHAR_Y =89; //Y
public const byte CHAR_Z =90; //Z
public const byte OPEN_BRACKET =91; //[
public const byte SLASH =92; //\
public const byte CLOSE_BRACKET =93; //]
public const byte CARET =94; //^
public const byte UNDERSCORE =95; //_
public const byte APOSTR0PHE =96; //`
public const byte CHAR_a =97; //a
public const byte CHAR_b =98; //b
public const byte CHAR_c =99; //c
public const byte CHAR_d =100; //d
public const byte CHAR_e =101; //e
public const byte CHAR_f =102; //f
public const byte CHAR_g =103; //g
public const byte CHAR_h =104; //h
public const byte CHAR_i =105; //i
public const byte CHAR_j =106; //j
public const byte CHAR_k =107; //k
public const byte CHAR_l =108; //l
public const byte CHAR_m =109; //m
public const byte CHAR_n =110; //n
public const byte CHAR_o =111; //o
public const byte CHAR_p =112; //p
public const byte CHAR_q =113; //q
public const byte CHAR_r =114; //r
public const byte CHAR_s =115; //s
public const byte CHAR_t =116; //t
public const byte CHAR_u =117; //u
public const byte CHAR_v =118; //v
public const byte CHAR_w =119; //w
public const byte CHAR_x =120; //x
public const byte CHAR_y =121; //y
public const byte CHAR_z =122; //z
public const byte OPEN_BRACE =123; //{
public const byte OR =124; //|
public const byte CLOSE_BRACE =125; //}
public const byte TILDE =126; //~
public const byte DEL =127; //DEL (delete) 删除
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using Mozi.HttpEmbedded.Encode;
using Mozi.HttpEmbedded.Generic;
namespace Mozi.HttpEmbedded.Auth
{
/// <summary>
/// 认证器
/// </summary>
public class Authenticator
{
private List<User> _users=new List<User>();
public AuthorizationType AuthType { get; private set; }
public virtual bool Check(string data)
{
string authHead = data.Substring(0,data.IndexOf((char)ASCIICode.SPACE));
string authBody = data.Substring(data.IndexOf((char) ASCIICode.SPACE) + 1);
AuthorizationType authType = AbsClassEnum.Get<AuthorizationType>(authHead);
if (authType != null)
{
if (authType.Equals(AuthorizationType.Basic))
{
String userinfo = Base64.From(authBody);
var indBnd = userinfo.IndexOf((char) ASCIICode.COLON);
String username = userinfo.Substring(0, indBnd);
String password = userinfo.Substring(indBnd + 1);
return IsValidUser(username, password);
}
else
{
}
}
return false;
}
/// <summary>
/// 是否有效用户
/// </summary>
/// <param name="userName"></param>
/// <param name="userPassword"></param>
/// <returns></returns>
private bool IsValidUser(string userName, string userPassword)
{
return _users.Any(x => x.UserName.Equals(userName) && x.Password.Equals(userPassword));
}
/// <summary>
/// 设置认证类型
/// </summary>
/// <param name="tp"></param>
/// <returns></returns>
public Authenticator SetAuthType(AuthorizationType tp)
{
AuthType = tp;
return this;
}
/// <summary>
/// 配置用户
/// </summary>
/// <param name="userName"></param>
/// <param name="userPassword"></param>
/// <returns></returns>
public Authenticator SetUser(string userName, string userPassword)
{
var user = _users.Find(x => x.UserName.Equals(userName));
if (user == null)
{
_users.Add(new User() {UserName = userName, Password = userPassword});
}
else
{
user.Password = userPassword;
}
return this;
}
}
public abstract class AuthDatagraph
{
/// <summary>
/// 质询要素
/// </summary>
public string[] ElementsChallenge;
/// <summary>
/// 响应要素
/// </summary>
public string[] ElementsResponse;
public readonly Dictionary<string,object> Challenge=new Dictionary<string, object>();
public Dictionary<string,object> Response = new Dictionary<string, object>();
public abstract AuthDatagraph Parse(string data);
/// <summary>
/// 设置质询要素
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public AuthDatagraph SetClgElement(string key,object value)
{
if (Challenge.ContainsKey(key))
{
Challenge[key] = value;
}
else
{
Challenge.Add(key,value);
}
return this;
}
public string GetChallenge()
{
List<String> clgs=new List<string>();
foreach (var o in Challenge)
{
clgs.Add(String.Format("{0}=\"{1}\"",o.Key,o.Value));
}
var clg = String.Format("{0} {1}", this.GetType().Name, String.Join(",", clgs));
return clg;
}
public abstract string GetResponse();
}
///// <summary>
///// Basic只对用户和密码进行简单的认证
///// <para>
///// 客户端的回应密码是Base64串
///// </para>
///// <code>
///// 报文范例
///// 质询
///// WWW-Authenticate: Digest realm="testrealm@host.com"
///// 响应
///// Authorization: Basic YWRtaW46YWRtaW4=
///// </code>
///// </summary>
//public class Basic : AuthDatagraph
//{
// /// <summary>
// /// 取得返回字符串
// /// </summary>
// /// <returns></returns>
// public override string ToString()
// {
// string sReturn = "";
// return sReturn;
// }
//}
/// <summary>
/// Digest认证
/// <code>
/// 报文范例
/// 质询
/// WWW-Authenticate: Digest realm="testrealm@host.com",
/// qop="auth,auth-int",
/// nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
/// opaque="5ccc069c403ebaf9f0171e9517f40e41"
/// 响应
/// Authorization: Digest realm="testrealm@host.com",
/// username="Mufasa",  //客户端已知信息
/// nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",   //服务器端质询响应信息
/// uri="/dir/index.html", //客户端已知信息
/// qop=auth,   //服务器端质询响应信息
/// nc=00000001, //客户端计算出的信息
/// cnonce="0a4f113b", //客户端计算出的客户端nonce
/// response="6629fae49393a05397450978507c4ef1", //最终的摘要信息 ha3
/// opaque="5ccc069c403ebaf9f0171e9517f40e41"  //服务器端质询响应信息
/// </code>
/// </summary>
public class Digest : AuthDatagraph
{
/// <summary>
/// 取得返回字符串
/// </summary>
/// <returns></returns>
public override string ToString()
{
string sReturn = "";
return sReturn;
}
public override AuthDatagraph Parse(string data)
{
throw new NotImplementedException();
}
public override string GetResponse()
{
throw new NotImplementedException();
}
}
/// <summary>
/// WSSE认证
/// <code>
/// 质询
/// WWW-Authenticate: WSSE realm="testrealm@host.com",
/// profile="UsernameToken" //服务器期望你用UsernameToken规则生成回应 UsernameToken规则:客户端生成一个nonce,然后根据该nonce,密码和当前日时来算出哈希值。
/// 响应
/// Authorization: WSSE profile="UsernameToken"
/// X-WSSE:UsernameToken
/// username="Mufasa",
/// PasswordDigest="Z2Y......",
/// Nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",//客户端将生成一个nonce值,并以该nonce值,密码,当前日时为基础,算出哈希值返回给服务器。
/// Created="2010-01-01T09:00:00Z"
/// </code>
/// </summary>
public class WSSE : AuthDatagraph
{
public override AuthDatagraph Parse(string data)
{
throw new NotImplementedException();
}
public override string GetResponse()
{
throw new NotImplementedException();
}
}
}
using System;
using Mozi.HttpEmbedded.Generic;
namespace Mozi.HttpEmbedded.Auth
{
/// <summary>
/// HTTP认证类型
/// </summary>
public class AuthorizationType:AbsClassEnum
{
public const string REALM = "HttpEmbedded";
/// <summary>
/// 基本认证 明文传输 不安全
/// </summary>
public static readonly AuthorizationType Basic = new AuthorizationType("Basic");
//public static AuthType Digest = new AuthType("Digest");
//public static AuthType WSSE = new AuthType("WSSE");
private string _name;
public String Name
{
get { return _name; }
set { _name = value; }
}
protected override string Tag
{
get { return Name; }
}
private AuthorizationType(String name)
{
_name = name;
}
}
}
\ No newline at end of file
namespace Mozi.HttpEmbedded.Auth
{
/// <summary>
/// 服务器认证用户
/// </summary>
class User
{
public string UserName { get; set; }
public string Password { get; set; }
}
}
namespace Mozi.HttpEmbedded
{
/// <summary>
/// 缓存
/// </summary>
class CacheControl
{
}
}
\ No newline at end of file

\ No newline at end of file
using Mozi.HttpEmbedded.Encode;
namespace Mozi.HttpEmbedded.Compress
{
/// <summary>
/// 压缩选项
/// </summary>
public class CompressOption
{
public TransformEncoding CompressType { get; set; }
public int MinContentLenght { get; set; }
public int CompressLevel { get; set; }
}
}
using System.IO;
using System.IO.Compression;
namespace Mozi.HttpEmbedded.Compress
{
/// <summary>
/// GZip压缩
/// </summary>
public static class GZip
{
public static byte[] Zip(byte[] value)
{
//Transform string into byte[]
byte[] byteArray;
MemoryStream ms = new MemoryStream();
GZipStream sw = new GZipStream(ms,CompressionMode.Compress);
sw.Write(value, 0, value.Length);
byteArray = ms.ToArray();
sw.Dispose();
ms.Dispose();
return byteArray;
}
public static byte[] UnZip(byte[] value)
{
//Transform string into byte[]
byte[] byteArray;
//Prepare for decompress
MemoryStream ms = new MemoryStream(value);
MemoryStream msOut=new MemoryStream();
GZipStream sr = new GZipStream(ms,
CompressionMode.Decompress);
sr.CopyTo(msOut);
byteArray = msOut.ToArray();
sr.Dispose();
ms.Dispose();
msOut.Dispose();
return byteArray;
}
}
}
using System;
using System.Collections.Generic;
using Mozi.HttpEmbedded.Encode;
namespace Mozi.HttpEmbedded.Cookie
{
/// <summary>
/// Cookie
/// </summary>
/// <remarks>
/// name/domain/path 3要素为Cookie的约束条件
/// </remarks>
public class HttpCookie
{
public string Name { get; set; }
public string Value { get; set; }
[Obsolete("HTTP/1.1不再使用此值")]
public string Expires { get; set; }
public int MaxAge { get; set; }
public string Path { get; set; }
public string Domain { get; set; }
public SameSiteMode SameSite { get; set; }
public bool Secure { get; set; }
public bool HttpOnly { get; set; }
//Secure和HttpOnly
public HttpCookie()
{
}
public HttpCookie(string key,string value)
{
}
public override string ToString()
{
List<string> data = new List<string> {string.Format("{0}={1}", Name, Value)};
if (MaxAge != 0)
{
data.Add(string.Format("max-age={0}",MaxAge));
}
data.Add(string.Format("path={0}",Path??"/"));
data.Add(string.Format("domain={0}",Domain??""));
if (SameSite != SameSiteMode.None)
{
data.Add(string.Format("SameSite={0}",Enum.GetName(typeof(SameSiteMode),SameSite)));
}
if (Secure)
{
data.Add("Secure");
}
if (HttpOnly)
{
data.Add("HttpOnly");
}
return string.Join(new string(new []{(char)ASCIICode.SPACE,(char)ASCIICode.SEMICOLON}), data);
}
}
/// <summary>
/// HttpCookie扩展类
/// </summary>
public static class HttpCookieExtension
{
/// <summary>
/// 判断Cookie是否为同一属性 name/domain/path
/// </summary>
/// <param name="hc"></param>
/// <param name="target"></param>
/// <returns></returns>
public static bool Equals(this HttpCookie hc, HttpCookie target)
{
if (hc != null && target != null)
{
hc.Path = hc.Path ?? "/";
target.Path = hc.Path ?? "/";
hc.Domain = hc.Domain ?? "";
target.Domain = target.Domain ?? "";
return hc.Name.Equals(target.Name) && hc.Path.Equals(target.Name) && hc.Domain.Equals(target.Domain);
}
return false;
}
}
public enum SameSiteMode
{
//
// 摘要:
// No SameSite field will be set, the client should follow its default cookie policy.
Unspecified = -1,
//
// 摘要:
// Indicates the client should disable same-site restrictions.
None = 0,
//
// 摘要:
// Indicates the client should send the cookie with "same-site" requests, and with
// "cross-site" top-level navigations.
Lax = 1,
//
// 摘要:
// Indicates the client should only send the cookie with "same-site" requests.
Strict = 2
}
/// <summary>
/// 请求Cookie
/// </summary>
public class RequestCookie
{
private Dictionary<string, string> _data = new Dictionary<string, string>();
private void Append(string name, string value)
{
if (_data.ContainsKey(name))
{
_data[name] = value;
}
else
{
_data.Add(name,value);
}
}
public static RequestCookie Parse(string data)
{
RequestCookie hc = new RequestCookie();
String[] kps = data.Split(new string[] {"; "}, StringSplitOptions.RemoveEmptyEntries);
foreach (var kp in kps)
{
var startIndex = kp.IndexOf((char)ASCIICode.EQUAL);
hc.Append(kp.Substring(0,startIndex),kp.Substring(startIndex+1));
}
return hc;
}
public string Get(string name)
{
return _data[name];
}
public string this[string ind]
{
get { return _data[ind]; }
}
}
/// <summary>
/// 响应Cookie
/// </summary>
public class ResponseCookie
{
private readonly List<HttpCookie> _cookies = new List<HttpCookie>();
/// <summary>
/// 设置Cookie
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public HttpCookie Set(string key, string value)
{
HttpCookie cookie = new HttpCookie() { Name = key, Value = UrlEncoder.EncodeUrl(value) };
if (_cookies.Exists(x => x.Equals(cookie)))
{
cookie = _cookies.Find(x => x.Equals(cookie));
cookie.Value = value;
}
else
{
_cookies.Add(cookie);
}
return cookie;
}
/// <summary>
/// 删除Cookie
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public HttpCookie Delete(string key)
{
HttpCookie cookie = Set(key, "");
cookie.MaxAge = -1000;
return cookie;
}
/// <summary>
/// 取出缓冲区数据
/// </summary>
/// <returns></returns>
public List<byte> GetBuffer()
{
List<byte> data=new List<byte>();
foreach (var cookie in _cookies)
{
data.AddRange(StringEncoder.Encode(string.Format("{0}: {1}",HeaderProperty.SetCookie.PropertyTag, cookie.ToString())));
data.AddRange(TransformHeader.Carriage);
}
return data;
}
}
}
\ No newline at end of file
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<title>错误信息</title>
<meta http-equiv="content-type" charset="UTF-8">
<meta http-equiv="Authorization" content="Jason"/>
<meta name="viewport" content="width=device-width,height=device-height,initial-scale=1,maximum-scale=1,user-scalable=no">
</head>
<body>
<h1 style="color: brown">{Error.Title}-{Error.Code}</h1>
<hr/>
<p><strong>时间:</strong>{Error.Time}</p>
<p><strong>内容:</strong>{Error.Description}</p>
<p><strong>源:</strong>{Error.Source}</p>
</body>
</html>
\ No newline at end of file
using System;
namespace Mozi.HttpEmbedded.Encode
{
/// <summary>
/// B64转码
/// </summary>
public class Base64
{
public static String To(String data)
{
byte[] infos = StringEncoder.Encode(data);
return Convert.ToBase64String(infos, Base64FormattingOptions.None);
}
public static String From(String data)
{
return StringEncoder.Decode(Convert.FromBase64String(data));
}
}
}
using System;
namespace Mozi.HttpEmbedded.Encode
{
/// <summary>
/// ַ
/// </summary>
public static class StringEncoder
{
public static byte[] Encode(String data)
{
return System.Text.Encoding.UTF8.GetBytes(data);
}
public static string Decode(byte[] data)
{
return System.Text.Encoding.UTF8.GetString(data);
}
}
}
\ No newline at end of file
namespace Mozi.HttpEmbedded.Encode
{
/// <summary>
/// 传输压缩类型
/// </summary>
public enum TransformEncoding
{
None,
Gzip,
Deflate,
Bzip2,
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
namespace Mozi.HttpEmbedded.Encode
{
/// <summary>
/// URLַת
/// </summary>
public static class UrlEncoder
{
private static readonly char[] From = {
' ', '"','#','%','&','(', ')', '+',',','/',':', ';','<','=', '>','?', '@','\\','|'
};
private static readonly String[] To = {
"%20", "%22", "%23", "%25", "%26", "%28", "%29", "%2B", "%2C", "%2F", "%3A", "%3B", "%3C", "%3D", "%3E","%3F", "%40", "%5C", "%7C"
};
/// <summary>
/// URLַ
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static String DecodeUrl(String data)
{
for (int i = 0; i < To.Length; i++)
{
String s = To[i];
while (data.Contains(s))
{
data = data.Replace(s, To[i]);
}
}
return data;
}
/// <summary>
/// URLַ
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static String EncodeUrl(String data)
{
for (int i = 0; i < From.Length; i++)
{
char s = From[i];
while (data.Contains(s.ToString()))
{
data = data.Replace(s.ToString(), To[i]);
}
}
return data;
}
/// <summary>
/// ѯַ
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static Dictionary<String,String> ParseQuery(String data)
{
data = DecodeUrl(data);
Dictionary<String,String> res=new Dictionary<string, string>();
String[] querys = data.Split(new[] { (char)ASCIICode.AND }, StringSplitOptions.RemoveEmptyEntries);
foreach (var item in querys)
{
String[] kp = item.Split(new[] { (char)ASCIICode.EQUAL }, StringSplitOptions.RemoveEmptyEntries);
res.Add(kp[0], kp[1] ?? "");
}
return res;
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
namespace Mozi.HttpEmbedded
{
public class File
{
public String FileName { get; set; }
public int FileIndex { get; set; }
public byte[] FileData { get; set; }
}
public class FileCollection
{
private List<File> _files=new List<File>();
public File this[string name] { get { return GetFile(name); } }
public File this[int ind]
{
get { return _files[0]; }
}
public int Length { get { return _files.Count; }}
public File GetFile(string name)
{
return _files.Find(x => x.FileName.Equals(name));
}
internal void Append(File f)
{
_files.Add(f);
}
}
}
\ No newline at end of file
using System;
using System.Reflection;
namespace Mozi.HttpEmbedded.Generic
{
/// <summary>
/// 仿枚举
/// </summary>
public abstract class AbsClassEnum
{
protected abstract String Tag { get; }
/// <summary>
/// 获取方法
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static T Get<T>(string name) where T:AbsClassEnum
{
//T t = Activator.CreateInstance<T>();
FieldInfo[] pis = typeof(T).GetFields(BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Static);
foreach (var info in pis)
{
Object oc=info.GetValue(null);
if (oc != null)
{
if (((T)oc).Tag.Equals(name,StringComparison.OrdinalIgnoreCase))
{
return (T)oc;
};
}
}
return null;
}
/// <summary>
/// 此处判断标识符是否相等
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
return (obj is AbsClassEnum) && ((AbsClassEnum)obj).Tag.Equals(Tag);
}
public static bool operator ==(AbsClassEnum a, AbsClassEnum b)
{
return (object)b != null && ((object)a != null && a.Tag.Equals(b.Tag));
}
/// <summary>
/// 重载!=
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static bool operator !=(AbsClassEnum a, AbsClassEnum b)
{
return (object)a == null || (object)b == null || !a.Tag.Equals(b.Tag);
}
public override int GetHashCode()
{
return Tag.GetHashCode();
}
}
}
\ No newline at end of file
using System.Collections.Generic;
namespace Mozi.HttpEmbedded.Generic
{
public class StringCompareIgnoreCase : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
if (x != null && y != null)
{
return x.ToLowerInvariant() == y.ToLowerInvariant();
}
return false;
}
public int GetHashCode(string obj)
{
return obj.GetHashCode();
}
}
}
\ No newline at end of file
using System;
namespace Mozi.HttpEmbedded
{
/// <summary>
/// ÇëÇó´íÎó
/// </summary>
class HandleError
{
public String Title { get; set; }
public DateTime Time { get; set; }
public String Description { get; set; }
public String Remark { get; set; }
public String Source { get; set; }
}
}
\ No newline at end of file
using System;
using Mozi.HttpEmbedded.Encode;
using Mozi.HttpEmbedded.Generic;
namespace Mozi.HttpEmbedded
{
/// <summary>
/// 头属性
/// </summary>
public class HeaderProperty : AbsClassEnum
{
public static HeaderProperty Accept = new HeaderProperty("Accept");// 用户代理期望的MIME 类型列表 HTTP Content Negotiation HTTP/1.1
public static HeaderProperty AcceptCH = new HeaderProperty("Accept-CH");//
// 列出配置数据,服务器可据此来选择适当的响应。 HTTP Client Hints
public static HeaderProperty AcceptCharset = new HeaderProperty("Accept-Charset");// 列出用户代理支持的字符集。 HTTP Content Negotiation HTTP/1.1
public static HeaderProperty AcceptFeatures = new HeaderProperty("Accept-Features");// HTTP Content Negotiation RFC 2295, §8.2
public static HeaderProperty AcceptEncoding = new HeaderProperty("Accept-Encoding");// 列出用户代理支持的压缩方法。 HTTP Content Negotiation HTTP/1.1
public static HeaderProperty AcceptLanguage = new HeaderProperty("Accept-Language");// 列出用户代理期望的页面语言。 HTTP Content Negotiation HTTP/1.1
//Accept-Ranges
public static HeaderProperty AccessControlAllowCredentials=new HeaderProperty("Access-Control-Allow-Credentials");// HTTP Access Control and Server Side Access Control W3C Cross-Origin Resource Sharing
public static HeaderProperty AccessControlAllowOrigin=new HeaderProperty("Access-Control-Allow-Origin");// HTTP Access Control and Server Side Access Control W3C Cross-Origin Resource Sharing
public static HeaderProperty AccessControlAllowMethods=new HeaderProperty("Access-Control-Allow-Methods");// HTTP Access Control and Server Side Access Control W3C Cross-Origin Resource Sharing
public static HeaderProperty AccessControlAllowHeaders=new HeaderProperty("Access-Control-Allow-Headers");// HTTP Access Control and Server Side Access Control W3C Cross-Origin Resource Sharing
public static HeaderProperty AccessControlMaxAge=new HeaderProperty("Access-Control-Max-Age");// HTTP Access Control and Server Side Access Control W3C Cross-Origin Resource Sharing
public static HeaderProperty AccessControlExposeHeaders=new HeaderProperty("Access-Control-Expose-Headers");// HTTP Access Control and Server Side Access Control W3C Cross-Origin Resource Sharing
public static HeaderProperty AccessControlRequestMethod=new HeaderProperty("Access-Control-Request-Method");// HTTP Access Control and Server Side Access Control W3C Cross-Origin Resource Sharing
public static HeaderProperty AccessControlRequestHeaders=new HeaderProperty("Access-Control-Request-Headers");// HTTP Access Control and Server Side Access Control W3C Cross-Origin Resource Sharing
public static HeaderProperty Age=new HeaderProperty("Age");//
public static HeaderProperty Allow=new HeaderProperty("Allow");//
public static HeaderProperty Alternates=new HeaderProperty("Alternates");// HTTP Content Negotiation RFC 2295, §8.3
public static HeaderProperty Authorization=new HeaderProperty("Authorization");// 包含用服务器验证用户代理的凭证
public static HeaderProperty CacheControl=new HeaderProperty("Cache-Control");// HTTP Caching FAQ
public static HeaderProperty Connection=new HeaderProperty("Connection");//
public static HeaderProperty ContentEncoding=new HeaderProperty("Content-Encoding");//
public static HeaderProperty ContentLanguage=new HeaderProperty("Content-Language");//
public static HeaderProperty ContentLength=new HeaderProperty("Content-Length");//
public static HeaderProperty ContentLocation=new HeaderProperty("Content-Location");//
public static HeaderProperty ContentMD5=new HeaderProperty("Content-MD5");// 未实现 (查看 bug 232030)
public static HeaderProperty ContentRange=new HeaderProperty("Content-Range");//
public static HeaderProperty ContentSecurityPolicy=new HeaderProperty("Content-Security-Policy");// 控制用户代理在一个页面上可以加载使用的资源。 CSP (Content Security Policy) W3C Content Security Policy
public static HeaderProperty ContentType=new HeaderProperty("Content-Type");// 指示服务器文档的MIME 类型。帮助用户代理(浏览器)去处理接收到的数据。
public static HeaderProperty Cookie=new HeaderProperty("Cookie");// RFC 2109
public static HeaderProperty DNT=new HeaderProperty("DNT");// 设置该值为1, 表明用户明确退出任何形式的网上跟踪。 Supported by Firefox 4, Firefox 5 for mobile, IE9, and a few major companies. Tracking Preference Expression (DNT)
public static HeaderProperty Date=new HeaderProperty("Date");//
public static HeaderProperty ETag=new HeaderProperty("ETag");// HTTP Caching FAQ
public static HeaderProperty Expect=new HeaderProperty("Expect");//
public static HeaderProperty Expires=new HeaderProperty("Expires");// HTTP Caching FAQ
public static HeaderProperty From=new HeaderProperty("From");//
public static HeaderProperty Host=new HeaderProperty("Host");//
public static HeaderProperty IfMatch=new HeaderProperty("If-Match");//
public static HeaderProperty IfModifiedSince=new HeaderProperty("If-Modified-Since");// HTTP Caching FAQ
public static HeaderProperty IfNoneMatch=new HeaderProperty("If-None-Match");// HTTP Caching FAQ
public static HeaderProperty IfRange=new HeaderProperty("If-Range");//
public static HeaderProperty IfUnmodifiedSince=new HeaderProperty("If-Unmodified-Since");//
public static HeaderProperty LastEventID=new HeaderProperty("Last-Event-ID");// 给出服务器在先前HTTP连接上接收的最后事件的ID。用于同步文本/事件流。 Server-Sent Events Server-Sent Events spec
public static HeaderProperty LastModified=new HeaderProperty("Last-Modified");// HTTP Caching FAQ
public static HeaderProperty Link=new HeaderProperty("Link");//
//等同于HTML标签中的"link",但它是在HTTP层上,给出一个与获取的资源相关的URL以及关系的种类。
//For the rel=prefetch case, see Link Prefetching FAQ
//Introduced in HTTP 1.1's RFC 2068, section 19.6.2.4, it was removed in the final HTTP 1.1 spec, then reintroduced, with some extensions, in RFC 5988
public static HeaderProperty Location=new HeaderProperty("Location");//
public static HeaderProperty MaxForwards=new HeaderProperty("Max-Forwards");//
public static HeaderProperty Negotiate=new HeaderProperty("Negotiate");// HTTP Content Negotiation RFC 2295, §8.4
public static HeaderProperty Origin=new HeaderProperty("Origin");// HTTP Access Control and Server Side Access Control More recently defined in the Fetch spec (see Fetch API.) Originally defined in W3C Cross-Origin Resource Sharing
public static HeaderProperty Pragma=new HeaderProperty("Pragma");// for the pragma: nocache value see HTTP Caching FAQ
public static HeaderProperty ProxyAuthenticate=new HeaderProperty("Proxy-Authenticate");//
public static HeaderProperty ProxyAuthorization=new HeaderProperty("Proxy-Authorization");//
public static HeaderProperty Range=new HeaderProperty("Range");//
public static HeaderProperty Referer=new HeaderProperty("Referer");//
//(请注意,在HTTP / 0.9规范中引入的正交错误必须在协议的后续版本中保留)
public static HeaderProperty RetryAfter=new HeaderProperty("Retry-After");//
public static HeaderProperty SecWebsocketExtensions=new HeaderProperty("Sec-Websocket-Extensions");// Websockets
public static HeaderProperty SecWebsocketKey=new HeaderProperty("Sec-Websocket-Key");// Websockets
public static HeaderProperty SecWebsocketOrigin=new HeaderProperty("Sec-Websocket-Origin");// Websockets
public static HeaderProperty SecWebsocketProtocol=new HeaderProperty("Sec-Websocket-Protocol");// Websockets
public static HeaderProperty SecWebsocketVersion=new HeaderProperty("Sec-Websocket-Version");// Websockets
public static HeaderProperty Server=new HeaderProperty("Server");//
public static HeaderProperty SetCookie=new HeaderProperty("Set-Cookie");// RFC 2109
public static HeaderProperty SetCookie2=new HeaderProperty("Set-Cookie2");// RFC 2965
public static HeaderProperty StrictTransportSecurity=new HeaderProperty("Strict-Transport-Security");// HTTP Strict Transport Security IETF reference
public static HeaderProperty TCN=new HeaderProperty("TCN");// HTTP Content Negotiation RFC 2295, §8.5
public static HeaderProperty TE=new HeaderProperty("TE");//
public static HeaderProperty Trailer=new HeaderProperty("Trailer");//
//列出将在消息正文之后在尾部块中传输的头。这允许服务器计算一些值,如Content-MD5:在传输数据时。请注意,Trailer:标头不得列出Content-Length :, Trailer:或Transfer-Encoding:headers。
// RFC 2616, §14.40
public static HeaderProperty TransferEncoding=new HeaderProperty("Transfer-Encoding");//
public static HeaderProperty Upgrade=new HeaderProperty("Upgrade");//
public static HeaderProperty UserAgent=new HeaderProperty("User-Agent");// for Gecko's user agents see the User Agents Reference
public static HeaderProperty VariantVary=new HeaderProperty("Variant-Vary");// HTTP Content Negotiation RFC 2295, §8.6
public static HeaderProperty Vary=new HeaderProperty("Vary");//
//列出了用作Web服务器选择特定内容的条件的标头。此服务器对于高效和正确缓存发送的资源很重要。
// HTTP Content Negotiation & HTTP Caching FAQ
public static HeaderProperty Via=new HeaderProperty("Via");//
public static HeaderProperty Warning=new HeaderProperty("Warning");//
public static HeaderProperty WWWAuthenticate=new HeaderProperty("WWW-Authenticate");//
public static HeaderProperty XContentDuration=new HeaderProperty("X-Content-Duration");// Configuring servers for Ogg media
public static HeaderProperty XContentSecurityPolicy=new HeaderProperty("X-Content-Security-Policy");// Using Content Security Policy
public static HeaderProperty XDNSPrefetchControl=new HeaderProperty("X-DNSPrefetch-Control");// Controlling DNS prefetching
public static HeaderProperty XFrameOptions=new HeaderProperty("X-Frame-Options");// The XFrame-Option Response Header
public static HeaderProperty XRequestedWith=new HeaderProperty("X-Requested-With");//
//通常在值为“XMLHttpRequest”时使用
// Not standard
public String PropertyTag { get; set; }
public String PropertyValue { get; set; }
protected override String Tag { get { return PropertyTag; } }
private HeaderProperty()
{
}
private HeaderProperty(String tag)
{
PropertyTag = tag;
}
/// <summary>
/// 转为字符串
/// </summary>
/// <returns></returns>
public override string ToString()
{
return String.Format("{0} {1}", PropertyTag, PropertyValue);
}
/// <summary>
/// 解析
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static HeaderProperty Parse(String data)
{
var tag = data.Substring(0, data.IndexOf((char)ASCIICode.COLON));
var value = data.Substring(data.IndexOf((char) ASCIICode.SPACE) + 1);
return new HeaderProperty(){PropertyTag = tag,PropertyValue = value};
}
/// <summary>
/// 解析
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static HeaderProperty Parse(byte[] data)
{
int itag = -1;
itag = Array.IndexOf(data, ASCIICode.COLON);
byte[] btag = new byte[itag], bvalue = new byte[data.Length - itag-2];
Array.Copy(data,btag,btag.Length);
Array.Copy(data,itag+2,bvalue,0,bvalue.Length);
return new HeaderProperty() { PropertyTag = StringEncoder.Decode(btag), PropertyValue = StringEncoder.Decode(bvalue) };
}
}
}
\ No newline at end of file
namespace Mozi.HttpEmbedded
{
/// <summary>
/// 请求上下文对象
/// </summary>
public class HttpContext
{
public HttpRequest Request { get; set; }
public HttpResponse Response { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Mozi.HttpEmbedded.Cookie;
using Mozi.HttpEmbedded.Encode;
using Mozi.HttpEmbedded.Generic;
namespace Mozi.HttpEmbedded
{
/// <summary>
/// HTTP请求
/// </summary>
public class HttpRequest
{
public ProtocolType Protocol { get; private set; }
/// <summary>
/// 协议版本
/// </summary>
public HttpVersion ProtocolVersion { get; private set; }
/// <summary>
/// 请求路径
/// </summary>
public String Path { get; private set; }
/// <summary>
/// 查询字符串
/// </summary>
public String QueryString { get; private set; }
/// <summary>
/// 查询 索引可忽略大小写
/// </summary>
public Dictionary<String, String> Query = new Dictionary<string, string>(new StringCompareIgnoreCase());
/// <summary>
/// POST 索引可忽略大小写
/// </summary>
public Dictionary<String, String> Post = new Dictionary<string, string>(new StringCompareIgnoreCase());
/// <summary>
/// 可接受压缩算法
/// </summary>
public String AcceptEncoding { get; private set; }
/// <summary>
/// 源地址
/// </summary>
public String Host { get; private set; }
/// <summary>
/// 客户端信息
/// </summary>
public String UserAgent { get; private set; }
/// <summary>
/// 请求方法
/// </summary>
public RequestMethod Method { get; private set; }
/// <summary>
/// 内容类型
/// </summary>
public String ContentType { get; private set; }
/// <summary>
/// 内容大小
/// </summary>
public String ContentLength { get; private set; }
/// <summary>
/// 请求头
/// </summary>
public TransformHeader Headers { get; private set; }
/// <summary>
/// 通讯数据
/// </summary>
public byte[] PackedData { get; private set; }
/// <summary>
/// 首行数据
/// </summary>
public byte[] FirstLineData { get; private set; }
/// <summary>
/// 首行字符串
/// </summary>
public String FirstLineString { get; private set; }
/// <summary>
/// 请求头数据
/// </summary>
public byte[] HeaderData { get; private set; }
/// <summary>
/// 请求数据体
/// </summary>
public byte[] Body { get; private set; }
/// <summary>
/// 文件
/// </summary>
public FileCollection Files { get; private set; }
/// <summary>
/// Cookie
/// </summary>
public RequestCookie Cookies { get; private set; }
public HttpRequest()
{
Headers=new TransformHeader();
Files=new FileCollection();
Cookies = new RequestCookie();
}
/// <summary>
/// 解析请求
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static HttpRequest Parse(byte[] data)
{
HttpRequest req = new HttpRequest() {PackedData = data};
//解析头 判断头部压缩
int posCR = 0;
int posCaret = 0;
int count = 0;
int index=0;
while (Array.IndexOf(data,ASCIICode.CR,posCR+1)>0)
{
posCR = Array.IndexOf(data, ASCIICode.CR, posCR + 1);
//GET / HTTP/1.1\r\nHost: 127.0.0.1:9000\r\n
//User-Agent: Mozilla/5.0 (Windows NT 5.2; rv:22.0) Gecko/20100101 Firefox/22.0\r\n
//Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
//Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3\r\n
//Accept-Encoding: gzip, deflate\r\n
//Authorization: Basic c2Rmc2RmOnNmc2Rm\r\n
//Connection: keep-alive\r\n
//Cache-Control: max-age=0
//GET /amazeui/login.html HTTP/1.1\r\n
//Host: 100.100.0.105:9000\r\n
//User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0\r\n
//Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n
//Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2\r\n
//Accept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\n
//Upgrade-Insecure-Requests: 1\r\n
//Authorization: Basic YWRtaW46YWRtaW4=\r\n\r\n
//连续两个CR
byte[] fragement=new byte[posCR-posCaret];
Array.Copy(data,posCaret,fragement,0,posCR-posCaret);
if (index == 0)
{
SplitFirstLine(ref req, fragement);
}
else
{
SplitHeaders(ref req, fragement);
}
if (Array.IndexOf(data, ASCIICode.CR, posCR + 1) == posCR + 2)
{
break;
}
//跳过分割字节段
posCaret = posCR+2;
index++;
}
//解析Cookie
SplitCookie(ref req);
//解析数据
if (data.Length > posCR + 4)
{
req.Body=new byte[data.Length-(posCR+4)];
Array.Copy(data, posCR + 4,req.Body,0,req.Body.Length);
SplitPayload(ref req,req.Body);
}
return req;
}
~HttpRequest()
{
PackedData = null;
FirstLineData = null;
Body = null;
Headers = null;
HeaderData = null;
Files = null;
}
/// <summary>
/// 解析请求体
/// 区分Content-Type
/// //TODO 先判断包体是否经过压缩
/// </summary>
/// <param name="req"></param>
/// <param name="data"></param>
private static void SplitPayload(ref HttpRequest req,byte[] data)
{
String formType = req.Headers.GetValue(HeaderProperty.ContentType.PropertyTag);
if (formType != null)
{
if (formType.Contains("application/x-www-form-urlencoded"))
{
SplitPayloadFormUrl(ref req, data);
}
else if (formType.Contains("multipart/form-data"))
{
SplitPayloadFormData(ref req, data);
}
else
{
SplitPayloadText(ref req,data);
}
}
}
/// <summary>
/// 分析请求体 application/x-www-form-urlencoded
/// 请求体数据类似于查询字符串
/// </summary>
/// <param name="req"></param>
/// <param name="data"></param>
private static void SplitPayloadFormUrl(ref HttpRequest req, byte[] data)
{
req.Post=UrlEncoder.ParseQuery(StringEncoder.Decode(data));
}
/// <summary>
/// 分析请求体 multipart/form-data
/// </summary>
/// <param name="req"></param>
/// <param name="data"></param>
private static void SplitPayloadFormData(ref HttpRequest req, byte[] data)
{
String contentType=req.Headers.GetValue(HeaderProperty.ContentType.PropertyTag);
String boundary = "";
String[] values=contentType.Split(new[]{(char)ASCIICode.SEMICOLON}, StringSplitOptions.RemoveEmptyEntries);
//取得分割符号boundary
foreach (var s in values)
{
if (s.Trim().StartsWith("boundary"))
{
boundary = s.Trim().Replace("boundary=", "");
break;
}
}
byte[] bBound = StringEncoder.Encode(boundary);
//分割 form-data
int indBoundary=-1, indEnd=-1,indCR=-1,indFragFirst=0,indFragNext=0;
while (Array.IndexOf(data, ASCIICode.MINUS, indBoundary+1) >= 0)
{
try
{
indFragFirst = indFragNext;
indBoundary = Array.IndexOf(data, ASCIICode.MINUS, indBoundary + 1);
//循环检测分割段
bool isFragStart = true;
bool isFragEnd = false;
for (int i = 0; i < bBound.Length; i++)
{
if ((data.Length>=indBoundary+bBound.Length)&&data[indBoundary + i + 2] != bBound[i])
{
isFragStart = false;
}
}
if (isFragStart)
{
indFragNext = indBoundary;
//跳过分割
indBoundary += bBound.Length+2+2;
}
//分割信息段
if (isFragStart && indFragNext > 0)
{
//Console.WriteLine("发现片段{0}-{1},长度{2}Byte", indFragFirst, indFragNext, indFragNext - indFragFirst);
int posCR = 0;
int posCaret = 0;
int index = 0;
byte[] fragbody=new byte[indFragNext-indFragFirst];
Array.Copy(data, indFragFirst,fragbody,0,indFragNext-indFragFirst);
File file=new File();
bool isFile = false;
String fieldName = String.Empty;
String fileName = String.Empty;
while (Array.IndexOf(fragbody, ASCIICode.CR, posCR + 1) > 0)
{
posCR = Array.IndexOf(fragbody, ASCIICode.CR, posCR + 1);
//连续两个CR
if (posCR > 0 && Array.IndexOf(fragbody, ASCIICode.CR, posCR + 1) != posCR + 2)
{
byte[] fragement = new byte[posCR - posCaret];
Array.Copy(fragbody, posCaret, fragement, 0, posCR - posCaret);
//1段为名称信息
if (index == 1)
{
//内容描述信息
//Content-Disposition: form-data; name="fieldNameHere"; filename="fieldName.ext"
String disposition = StringEncoder.Decode(fragement);
String[] headers = disposition.Split(new[] {(char) ASCIICode.SEMICOLON},StringSplitOptions.RemoveEmptyEntries);
fieldName = headers[1].Trim().Replace("name=","").Trim((char)ASCIICode.QUOTE);
if (headers.Length > 2)
{
fileName = headers[2];
if (fileName.Contains("filename="))
{
isFile = true;
fileName = fileName.Trim().Replace("filename=", "").Trim((char)ASCIICode.QUOTE);
}
}
}
}
else
{
break;
}
//跳过分割字节段
posCaret = posCR + 2;
index++;
}
//解析数据
if (data.Length > posCR + 4)
{
var postField = new byte[fragbody.Length - (posCR + 4)];
Array.Copy(fragbody, posCR + 4, postField, 0, postField.Length);
if (isFile)
{
file.FileName = fileName;
file.FileData = postField;
file.FileIndex = req.Files.Length;
req.Files.Append(file);
FileStream fs =new FileStream(AppDomain.CurrentDomain.BaseDirectory + "\\" + file.FileName.ToString(),FileMode.OpenOrCreate);
fs.Write(file.FileData, 0, file.FileData.Length);
fs.Flush();
fs.Dispose();
}
else
{
req.Query.Add(fieldName, StringEncoder.Decode(postField));
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("{0}",indBoundary);
}
}
}
//TODO 后续扩展支撑,暂时不实现
/// <summary>
/// 分析请求体 文本类型
/// application/json
/// text/plain
/// text/xml
/// </summary>
/// <param name="req"></param>
/// <param name="data"></param>
private static void SplitPayloadText(ref HttpRequest req, byte[] data)
{
}
/// <summary>
/// 解析头属性
/// </summary>
/// <param name="req"></param>
/// <param name="data"></param>
private static void SplitHeaders(ref HttpRequest req, byte[] data)
{
HeaderProperty hp = HeaderProperty.Parse(data);
req.Headers.Add(hp.PropertyTag, hp.PropertyValue);
#if DEBUG
Console.WriteLine("{0}:{1}",hp.PropertyTag,hp.PropertyValue);
#endif
}
/// <summary>
/// 解析Cookie
/// </summary>
/// <param name="req"></param>
private static void SplitCookie(ref HttpRequest req)
{
if (req.Headers.Contains(HeaderProperty.Cookie.PropertyTag))
{
req.Cookies = RequestCookie.Parse(req.Headers.GetValue(HeaderProperty.Cookie.PropertyTag));
}
}
/// <summary>
/// 解析首行数据
/// 方法 查询 协议
/// </summary>
/// <param name="req"></param>
/// <param name="data"></param>
private static void SplitFirstLine(ref HttpRequest req, byte[] data)
{
//解析起始行
req.FirstLineData = data;
req.FirstLineString = Encoding.UTF8.GetString(data);
String[] sFirst = req.FirstLineString.Split(new[] { (char)ASCIICode.SPACE }, StringSplitOptions.None);
//方法 查询 协议
String sMethod = sFirst[0];
String sUrl = sFirst[1];
String sProtocol = sFirst[2];
RequestMethod rm = AbsClassEnum.Get<RequestMethod>(sMethod);
req.Method = rm;
String[] urls = sUrl.Split(new[] { (char)ASCIICode.QUESTION }, StringSplitOptions.RemoveEmptyEntries);
req.Path = urls[0];
if (urls.Length > 1)
{
req.QueryString = urls[1];
req.Query = UrlEncoder.ParseQuery(urls[1]);
}
String sProtoType = sProtocol.Substring(0, sProtocol.IndexOf((char)ASCIICode.DIVIDE));
String sProtoVersion = sProtocol.Substring(sProtocol.IndexOf((char)ASCIICode.DIVIDE) + 1);
req.Protocol = AbsClassEnum.Get<ProtocolType>(sProtoType);
req.ProtocolVersion = AbsClassEnum.Get<HttpVersion>(sProtoVersion);
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using Mozi.HttpEmbedded.Cookie;
using Mozi.HttpEmbedded.Encode;
namespace Mozi.HttpEmbedded
{
/// <summary>
/// HTTP响应
/// </summary>
public class HttpResponse
{
private byte[] _body=new byte[0];
/// <summary>
/// 协议版本
/// </summary>
public HttpVersion ProxyVersion { get; set; }
/// <summary>
/// 状态码
/// </summary>
public StatusCode Status { get; set; }
public int ContengLength = 0;
public String Server = "";
/// <summary>
/// 请求头
/// </summary>
public TransformHeader Headers { get; private set; }
/// <summary>
/// 压缩类型
/// </summary>
public String ContentEncoding { get; set; }
/// <summary>
/// 请求数据体
/// </summary>
public byte[] Body { get { return _body; } }
/// <summary>
/// Cookie
/// </summary>
public ResponseCookie Cookies { get; private set; }
public HttpResponse()
{
Headers=new TransformHeader();
ProxyVersion = HttpVersion.Version11;
Cookies=new ResponseCookie();
}
/// <summary>
/// 设置协议版本
/// </summary>
/// <param name="version"></param>
public void SetVersion(HttpVersion version)
{
ProxyVersion = version;
}
/// <summary>
/// 设置状态
/// </summary>
/// <param name="status"></param>
public void SetStatus(StatusCode status)
{
Status = status;
}
/// <summary>
/// 增加头部信息
/// </summary>
/// <param name="head"></param>
/// <returns></returns>
public HttpResponse AddHeader(HeaderProperty head,String value)
{
Headers.Add(head,value);
return this;
}
/// <summary>
/// 增加头部信息
/// </summary>
/// <param name="item"></param>
/// <param name="value"></param>
/// <returns></returns>
public HttpResponse AddHeader(String item,String value)
{
Headers.Add(item,value);
return this;
}
/// <summary>
/// 写入字节数据
/// </summary>
/// <returns></returns>
public HttpResponse Write(byte[] data)
{
if (_body == null)
{
_body = data;
}
else
{
Array.Resize(ref _body, _body.Length + data.Length);
Array.Copy(data,_body,data.Length);
}
return this;
}
/// <summary>
/// 写入文本
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public HttpResponse Write(String data)
{
Write(StringEncoder.Encode(data));
return this;
}
/// <summary>
/// 从缓冲区中取出数据
/// </summary>
/// <returns></returns>
public byte[] GetBuffer()
{
List<byte> data=new List<byte>();
//注入状态信息
data.AddRange(GetStatusLine());
data.AddRange(TransformHeader.Carriage);
//注入包体大小 字节长度
if (_body != null)
{
AddHeader(HeaderProperty.ContentLength, _body.Length.ToString());
}
//注入响应时间
AddHeader(HeaderProperty.Date, DateTime.Now.ToUniversalTime().ToString("r"));
//注入默认头部
data.AddRange(Headers.GetBuffer());
//注入Cookie
data.AddRange(Cookies.GetBuffer());
//注入分割符
data.AddRange(TransformHeader.Carriage);
//注入响应包体
data.AddRange(_body);
return data.ToArray();
}
/// <summary>
/// 响应状态
/// </summary>
/// <returns></returns>
public byte[] GetStatusLine()
{
return StringEncoder.Encode(String.Format("HTTP/{0} {1} {2}", ProxyVersion.Version, Status.Code, Status.Text));
}
/// <summary>
/// 重定向302
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public StatusCode Redirect(string path)
{
Headers.Add(HeaderProperty.Location.PropertyTag, path);
return StatusCode.Found;
}
~HttpResponse()
{
_body = null;
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using Mozi.HttpEmbedded.Auth;
using Mozi.HttpEmbedded.Compress;
using Mozi.HttpEmbedded.Page;
using Mozi.HttpEmbedded.Source;
namespace Mozi.HttpEmbedded
{
/// <summary>
/// Http服务器
/// </summary>
public class HttpServer
{
private readonly SocketServer _sc=new SocketServer();
private int _port=80;
private string _serverName = "HttpEmbedded";
/// <summary>
/// 支持的HTTP服务协议版本
/// </summary>
public HttpVersion ProxyVersion { get; set; }
/// <summary>
/// 是否使用基本认证
/// </summary>
public bool EnableAuth { get; private set; }
private Authenticator Auth { get; set; }
/// <summary>
/// 是否开启压缩
/// </summary>
public bool EnableCompress { get; private set; }
/// <summary>
/// 压缩选项
/// </summary>
public CompressOption ZipOption { get; private set; }
/// <summary>
/// 服务端口
/// </summary>
public int Port
{
get { return _port; }
private set { _port = value; }
}
/// <summary>
/// 时区
/// </summary>
public String Timezone { get; set; }
/// <summary>
/// 编码格式
/// </summary>
public String Encoding { get; set; }
/// <summary>
/// 服务器名称
/// </summary>
public String ServerName
{
get { return _serverName; }
private set { _serverName = value; }
}
/// <summary>
/// 服务器用户
/// </summary>
private List<User> _users=new List<User>();
public HttpServer()
{
Auth=new Authenticator();
_sc.OnServerStart += _sc_OnServerStart;
_sc.OnClientConnect += _sc_OnClientConnect;
_sc.OnReceiveStart += _sc_OnReceiveStart;
_sc.AfterReceiveEnd += _sc_AfterReceiveEnd;
_sc.AfterServerStop += _sc_AfterServerStop;
}
void _sc_AfterServerStop(object sender, ServerArgs args)
{
throw new NotImplementedException();
}
void _sc_OnReceiveStart(object sender, DataTransferArgs args)
{
}
//TODO 响应码处理有问题
/// <summary>
/// 响应请求
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
void _sc_AfterReceiveEnd(object sender, DataTransferArgs args)
{
HttpContext context=new HttpContext();
context.Response=new HttpResponse();
StatusCode sc = StatusCode.Success;
try
{
context.Request = HttpRequest.Parse(args.Data);
if (!EnableAuth)
{
sc = HandleRequest(ref context);
}
else
{
sc = HandleAuth(ref context);
}
}
catch (Exception ex)
{
sc = StatusCode.InternalServerError;
}
finally
{
context.Response.AddHeader(HeaderProperty.Server, ServerName);
context.Response.SetStatus(sc);
args.Socket.Send(context.Response.GetBuffer());
//TODO HTTP/1.1 通过Coonection控制连接 服务器同时对连接进行监测 保证服务器效率
args.Socket.Close(100);
}
GC.Collect();
}
/// <summary>
/// 处理认证
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
private StatusCode HandleAuth(ref HttpContext context)
{
var authorization = context.Request.Headers.GetValue(HeaderProperty.Authorization.PropertyTag);
if (!String.IsNullOrEmpty(authorization) && Auth.Check(authorization))
{
return HandleRequest(ref context);
}
else
{
context.Response.AddHeader(HeaderProperty.WWWAuthenticate, String.Format("{0} realm=\"{1}\"", Auth.AuthType.Name,AuthorizationType.REALM));
return StatusCode.Unauthorized;
}
}
/// <summary>
/// 处理响应
/// </summary>
/// <param name="context"></param>
private StatusCode HandleRequest(ref HttpContext context)
{
RequestMethod method = context.Request.Method;
if (method == RequestMethod.GET || method == RequestMethod.POST || method == RequestMethod.HEAD||method==RequestMethod.PUT ||method==RequestMethod.OPTIONS||method == RequestMethod.DELETE||method==RequestMethod.TRACE||method==RequestMethod.CONNECT)
{
StaticFiles st = StaticFiles.Default;
var path = context.Request.Path;
string fileext = GetFileExt(path);
string contenttype = Mime.GetContentType(fileext);
//判断资源类型
bool isStatic = st.IsStatic(fileext);
context.Response.Headers.Add(HeaderProperty.ContentType, contenttype);
//静态文件处理
if (st.Enabled && isStatic)
{
//响应静态文件
if (st.Exists(path, ""))
{
DateTime dtModified = st.GetLastModified(path);
string ifmodifiedsince =context.Request.Headers.GetValue(HeaderProperty.IfModifiedSince.PropertyTag);
if (st.CheckIfModified(path, ifmodifiedsince))
{
context.Response.Headers.Add(HeaderProperty.LastModified, dtModified.ToString("r"));
context.Response.Write(st.Load(path, ""));
}
else
{
return StatusCode.NotModified;
}
}
else
{
return StatusCode.NotFound;
}
}
else
{
//响应动态页面
return HandleRequestRoutePages(ref context);
}
}
//WEBDAV部分
else
{
}
return StatusCode.Success;
}
/// <summary>
/// 取URL资源扩展名
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
private string GetFileExt(string path)
{
string[] file = path.Split(new[] { (char)ASCIICode.QUESTION }, StringSplitOptions.RemoveEmptyEntries);
string ext = "";
string purepath = file[0];
if (purepath.LastIndexOf((char)ASCIICode.DOT) >= 0)
{
ext = purepath.Substring(purepath.LastIndexOf((char)ASCIICode.DOT) + 1);
}
return ext;
}
/// <summary>
/// 路由页面
/// </summary>
/// <param name="context"></param>
private StatusCode HandleRequestRoutePages(ref HttpContext context)
{
Router router=Router.Default;
if (router.Match(context.Request.Path) != null)
{
router.Invoke(context);
context.Response.Write("<html>"
+ "<head></head>"
+ "<body>"
+ " <strong>Welcome to a Web Server Developed base on c#!</strong>"
+ "</body>"
+ "</html>");
return StatusCode.Success;
}
return StatusCode.NotFound;
}
void _sc_OnServerStart(object sender, ServerArgs args)
{
//throw new NotImplementedException();
}
void _sc_OnClientConnect(object sender, ClientConnectArgs args)
{
//throw new NotImplementedException();
}
/// <summary>
/// 配置服务端口
/// </summary>
/// <param name="port"></param>
/// <returns></returns>
public HttpServer SetPort(int port)
{
_port = port;
return this;
}
/// <summary>
/// 启用认证
/// <para>此方法可连续配置用户</para>
/// </summary>
/// <param name="at"><see cref="E:Auth.AuthType"/></param>
/// <returns></returns>
public HttpServer UseAuth(AuthorizationType at)
{
EnableAuth = true;
Auth.SetAuthType(at);
return this;
}
/// <summary>
/// 设置服务器认证用户
/// <para>如果<see cref="F:EnableAuth"/>=<see cref="Boolean.False"/>,此设置就没有意义</para>
/// </summary>
/// <param name="userName"></param>
/// <param name="userPassword"></param>
/// <returns></returns>
public HttpServer SetUser(String userName, String userPassword)
{
Auth.SetUser(userName, userName);
return this;
}
/// <summary>
/// 启用Gzip
/// </summary>
/// <returns></returns>
public HttpServer UseGzip(CompressOption option)
{
EnableCompress = true;
ZipOption = option;
return this;
}
/// <summary>
/// 允许静态文件访问
/// </summary>
/// <param name="root">静态文件目录</param>
/// <returns></returns>
public HttpServer UseStaticFiles(string root)
{
StaticFiles.Default.Enabled = true;
return this;
}
/// <summary>
///
/// </summary>
/// <param name="page"></param>
/// <returns></returns>
public HttpServer UseErrorPage(String page)
{
throw new NotImplementedException();
}
/// <summary>
/// 设置服务器名称
/// </summary>
/// <param name="serverName"></param>
/// <returns></returns>
public HttpServer SetServerName(string serverName)
{
_serverName = serverName;
return this;
}
/// <summary>
/// 启动服务器
/// </summary>
public void Start()
{
_sc.StartServer(_port);
}
/// <summary>
/// 检查访问黑名单
/// </summary>
private void CheckIfBlocked()
{
}
/// <summary>
/// 关闭服务器
/// </summary>
public void Shutdown()
{
_sc.StopServer();
}
}
}
using System;
using Mozi.HttpEmbedded.Generic;
namespace Mozi.HttpEmbedded
{
/// <summary>
/// HTTP协议版本
/// </summary>
public class HttpVersion : AbsClassEnum
{
[Obsolete("实现HTTP/0.9没有意义")]
public static readonly HttpVersion Version09 = new HttpVersion("0.9");
public static readonly HttpVersion Version11 = new HttpVersion("1.1");
public static readonly HttpVersion Version12 = new HttpVersion("1.2");
public static readonly HttpVersion Version20 = new HttpVersion("2.0");
public String Version { get { return _vervalue; } }
protected override String Tag { get { return Version; } }
private string _vervalue = "";
private HttpVersion(string vervalue)
{
_vervalue = vervalue;
}
/// <summary>
/// 重写ToString方法
/// </summary>
/// <returns></returns>
public override String ToString()
{
return String.Format("HTTP/{0}", _vervalue);
}
}
}
\ No newline at end of file
# MIT License
Copyright (c) 2020 JasonWcx
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{051C3F42-0A53-4A78-A961-28FFA8C17B46}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Mozi.HttpEmbedded</RootNamespace>
<AssemblyName>Mozi.HttpEmbedded</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<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|x86' ">
<PlatformTarget>x86</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.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ASCIICode.cs" />
<Compile Include="Auth\User.cs" />
<Compile Include="Encode\UrlEncoder.cs" />
<Compile Include="Generic\AbsClassEnum.cs" />
<Compile Include="Auth\AuthorizationType.cs" />
<Compile Include="Auth\Authenticator.cs" />
<Compile Include="CacheControl.cs" />
<Compile Include="Encode\Base64.cs" />
<Compile Include="Compress\CompressOption.cs" />
<Compile Include="Compress\GZip.cs" />
<Compile Include="Encode\TransformEncoding.cs" />
<Compile Include="Cookie\HttpCookie.cs" />
<Compile Include="File.cs" />
<Compile Include="Generic\StringCompareIgnoreCase.cs" />
<Compile Include="HandleError.cs" />
<Compile Include="HeaderProperty.cs" />
<Compile Include="HttpContext.cs" />
<Compile Include="HttpServer.cs" />
<Compile Include="HttpVersion.cs" />
<Compile Include="Log\Log.cs" />
<Compile Include="Page\BaseApi.cs" />
<Compile Include="Page\AbsPage.cs" />
<Compile Include="Page\Global.cs" />
<Compile Include="Page\PageCreator.cs" />
<Compile Include="Page\Router.cs" />
<Compile Include="Page\TestApi.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ProtocolType.cs" />
<Compile Include="HttpRequest.cs" />
<Compile Include="RequestMethod.cs" />
<Compile Include="HttpResponse.cs" />
<Compile Include="SocketServer.cs" />
<Compile Include="ServerEvent.cs" />
<Compile Include="Source\Mime.cs" />
<Compile Include="Source\StaticFiles.cs" />
<Compile Include="StateObject.cs" />
<Compile Include="StatusCode.cs" />
<Compile Include="Encode\StringEncoder.cs" />
<Compile Include="TransformHeader.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Docment\Error.html" />
<Content Include="Source\mime.data" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="Changes.md" />
<None Include="License.md" />
<None Include="Readme.md" />
</ItemGroup>
<ItemGroup>
<Folder Include="JSON\" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.0">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4 %28x86 和 x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
<Visible>False</Visible>
<ProductName>Windows Installer 3.1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
\ No newline at end of file
namespace Mozi.HttpEmbedded.Page
{
/// <summary>
/// 页面抽象类
/// </summary>
public abstract class AbsPage
{
protected HttpRequest Request { get; set; }
protected HttpResponse Response { get; set; }
protected HttpContext Context { get; set; }
public abstract void Get();
public abstract void Post();
}
}
namespace Mozi.HttpEmbedded.Page
{
/// <summary>
/// Api抽象类
/// </summary>
public abstract class BaseApi
{
public HttpContext Context { get; internal set; }
}
}
using System.Collections.Generic;
namespace Mozi.HttpEmbedded.Page
{
/// <summary>
/// 全局数据
/// 此处定义的数据会被全局使用 仅只读 功能类似于宏
/// </summary>
public class Global
{
public Dictionary<string,object> _data=new Dictionary<string, object>();
public Global Set()
{
return this;
}
}
}
using System;
namespace Mozi.HttpEmbedded.Page
{
/// <summary>
/// 页面生成器
/// </summary>
public class PageCreator
{
/// <summary>
/// 注入全局数据
/// </summary>
/// <returns></returns>
public PageCreator InjectGlobal()
{
throw new NotImplementedException();
}
/// <summary>
/// 注入临时数据
/// </summary>
/// <returns></returns>
public PageCreator InjectValues()
{
return null;
}
}
}
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Text.RegularExpressions;
namespace Mozi.HttpEmbedded.Page
{
/// <summary>
/// 全局路由
/// </summary>
public sealed class Router
{
private static Router _r;
public static Router Default
{
get { return _r ?? (_r = new Router()); }
}
private List<Assembly> _assemblies=new List<Assembly>();
private List<Type> apis = new List<Type>();
private readonly List<RouteMapper> _mappers = new List<RouteMapper>() { new RouteMapper() { Pattern = "/{controller}/{id}" }, new RouteMapper() { Pattern = "/{controller}.{id}" } };
private Router()
{
}
/// <summary>
/// 调起
/// </summary>
/// <param name="ctx"></param>
internal object Invoke(HttpContext ctx)
{
Assembly ass = Assembly.GetExecutingAssembly();
Type[] types = ass.GetExportedTypes();
foreach (var type in types)
{
if (type.IsSubclassOf(typeof(BaseApi)))
{
apis.Add(type);
}
}
ass.GetModules();
string path = ctx.Request.Path;
//确定路径映射关系
AccessPoint ap=Match(path);
Type cls = apis.Find(x => x.Name.Equals(ap.Domain, StringComparison.OrdinalIgnoreCase));
MethodInfo method = cls.GetMethod(ap.Method,BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public);
ParameterInfo[] pms = method.GetParameters();
object[] args = new object[pms.Length];
for (int i = 0; i < pms.Length; i++)
{
var argname = pms[i].Name;
if (ctx.Request.Query.ContainsKey(argname))
{
args[i] = ctx.Request.Query[argname];
}
if (ctx.Request.Post.ContainsKey(argname))
{
args[i] = ctx.Request.Post[argname];
}
}
object instance = Activator.CreateInstance(cls);
//注入变量
((BaseApi) instance).Context = ctx;
//调用方法
return method.Invoke(instance, BindingFlags.IgnoreCase, null, args, CultureInfo.CurrentCulture);
//调起相关方法
}
/// <summary>
/// 载入模块
/// </summary>
/// <returns></returns>
public Router Register(string filePath)
{
throw new NotImplementedException();
}
/// <summary>
/// 路由注入
/// </summary>
/// <param name="pattern">
/// 匹配范式
/// <para>
/// 范式:<code>api/{controller}/{id}</code> {controller}和{id}为固定参数
/// <list type="table">
/// <item><term><c>{controller}</c></term><description>表示继承自<see cref="C:BaseApi"/>的类</description></item>
/// <item><term><c>{id}</c></term><description>类中的非静态方法名</description></item>
/// </list>
/// <example>
/// 范式1
/// api/{controller}/{id}
/// 范式2
/// api/on{controller}/get{id}
/// 范式3
/// api.{controller}.{id}
/// 范式4
/// api.on{controller}.get{id}
/// </example>
///
/// </para>
/// </param>
/// <returns></returns>
public Router Map(string pattern)
{
_mappers.Add(new RouteMapper(){Pattern = pattern});
return this;
}
public AccessPoint Match(string path)
{
foreach (var mapper in _mappers)
{
if (mapper.Match(path))
{
return mapper.Parse(path);
}
}
return null;
}
/// <summary>
/// 路由映射
/// </summary>
private class RouteMapper
{
private string _pattern = "";
public string Pattern
{
get
{
return _pattern;
}
set
{
_pattern = value;
ApplyPattern(value);
}
}
private string Prefix { get; set; }
private string Suffix { get; set; }
private String Link { get; set; }
private string IdName { get; set; }
private Regex Matcher { get; set; }
public RouteMapper()
{
}
private void ApplyPattern(string pattern)
{
//修正
if (pattern.IndexOf("/", StringComparison.CurrentCulture) != 0)
{
pattern = "/" + pattern;
}
//替换特殊字符
int indCrl = pattern.IndexOf("{controller}", StringComparison.CurrentCulture);
int indID = pattern.IndexOf("{id}", StringComparison.CurrentCulture);
Prefix = pattern.Substring(0, indCrl);
Link = pattern.Substring(indCrl + 12, indID - indCrl - 12);
Suffix = "";
Matcher = new Regex(String.Format("{0}[a-zA-Z]\\w+{1}[a-zA-Z]\\w+{2}", Regex.Escape(Prefix), Regex.Escape(Link), Regex.Escape(Suffix)), RegexOptions.IgnoreCase);
}
/// <summary>
/// 判断是否匹配路由
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public bool Match(string path)
{
if (Matcher.IsMatch(path))
{
return true;
}
return false;
}
/// <summary>
/// 解析入口点
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public AccessPoint Parse(string path)
{
AccessPoint ap = null;
if (Match(path))
{
ap=new AccessPoint();
int indCrl=Prefix.Length;
int indLink=path.IndexOf(Link,indCrl+1,StringComparison.CurrentCulture);
ap.Domain = path.Substring(indCrl, indLink - indCrl);
ap.Method = path.Substring(indLink + Link.Length);
}
return ap;
}
}
public class AccessPoint
{
public String Domain { get; set; }
public String Method { get; set; }
}
}
}
using System;
namespace Mozi.HttpEmbedded.Page
{
/// <summary>
/// API接口使用示范
/// </summary>
public class Test:BaseApi
{
/// <summary>
/// 无参方法
/// </summary>
/// <returns></returns>
public string Hello()
{
return "Welcome to Mozi.HttpEnbedded";
}
//public string Hello(string greet)
//{
// return String.Format("you say {0}", greet);
//}
}
public class Test2 : Test
{
}
}
using System;
using Mozi.HttpEmbedded.Auth;
using Mozi.HttpEmbedded.Page;
namespace Mozi.HttpEmbedded
{
static class Program
{
/// <summary>
/// 入口点
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
HttpServer hs = new HttpServer();
//配置端口并启动服务器
hs.SetPort(9000).Start();
//开启认证
hs.UseAuth(AuthorizationType.Basic).SetUser("admin", "admin");
//开启静态文件支持
hs.UseStaticFiles("");
//路由映射
Router router = Router.Default;
router.Map("services/{controller}/{id}");
Console.ReadLine();
}
}
}
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的常规信息通过以下
// 特性集控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("Mozi.HttpEmbedded")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Jason")]
[assembly: AssemblyProduct("Mozi.HttpEmbedded")]
[assembly: AssemblyCopyright("Copyright © Jason 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 使此程序集中的类型
// 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型,
// 则将该类型上的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("36949856-90c3-4773-a4fb-a6111bb7a6e4")]
// 程序集的版本信息由下面四个值组成:
//
// 主版本
// 次版本
// 内部版本号
// 修订号
//
// 可以指定所有这些值,也可以使用“内部版本号”和“修订号”的默认值,
// 方法是按如下所示使用“*”:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
using System;
using Mozi.HttpEmbedded.Generic;
namespace Mozi.HttpEmbedded
{
/// <summary>
/// 协议类型
/// </summary>
public class ProtocolType : AbsClassEnum
{
public static ProtocolType HTTP = new ProtocolType("HTTP");
public static ProtocolType HTTPS = new ProtocolType("HTTPS");
private string _proxy;
public String Proxy
{
get { return _proxy; }
set { _proxy = value; }
}
protected override String Tag { get { return Proxy; } }
private ProtocolType(String typeName)
{
_proxy = typeName;
}
}
}
\ No newline at end of file
# HttpEmbedded 嵌入式Web服务器
## 项目介绍
HttpEmbedded是一个基于.Net构建的嵌入式Web服务器,为.Net App提供web服务功能。
>嵌入式的目标不是单片机,而是.Net应用程序。
>此项目并不会实现非常丰富的大型Web服务器功能
>项目基于.Net Framework 4.0开发,也可转换为.Net Core/.Net Standard项目
## 特点
HttpEmbedded在Socket之上使用异步单线程模型,构建了一个HTTP服务器。
1. 嵌入式
本项目可作为.Net应用的内嵌Web服务器,亦可作为单独Web服务器
2. 轻量化
项目编译结果小,部署程序占用系统资源少
3. 可用性
开箱即用,配置少,可控性高。同时遵从.Net平台Web项目开发的典型规范。
4. 低耦合
不改变现有业务逻辑,无需对现有代码进行改造,即可实现现有功能面向HTTP提供服务器
5. 可控性
宿主程序对Web服务高度可控
## 典型应用场景
业务体系或功能已开发完成,期望在项目中加入一个Web服务器功能,同时不能对现有代码进行大规模改动。
>在经典的Asp.Net开发中,Web服务的部署高度依赖于IIS,.Net Core项目则可基于Kestrel/IIS部署。基于KESTREL或IIS部署的WEB项目,都基于Asp.Net体系。
## 原理及功能
1. HTTP协议
实现HTTP/1.1 HTTP/1.2
2. 通讯认证
实现基本认证(Basic)
3. Cookie管理
支持标准Cookie
4, HTTP请求方法
GET POST
4. 路由
实现了URL管理,参见Router模块
5. 引用与依赖关系
依赖于.Net Framework
6. Payload压缩
使用GZip压缩
7. 字符编码
字符编码部分使用UTF-8
## 功能与版本迭代
不定期对HttpEmbedded的功能进行完善,解决各种BUG。HTTP标准功能繁多,需要一步步实现。
## 版权说明
本项目采用MIT开源协议,欢迎复制,引用和修改
###使用说明
~~~
HttpServer hs = new HttpServer();
//配置端口并启动服务器
hs.SetPort(9000).Start();
//开启认证
hs.UseAuth(AuthType.Basic).SetUser("admin", "admin");
//加载程序
//路由注入
~~~
###By [Jason][1] on Feb. 5,2020
[1]:mailto:brotherqian@163.com
\ No newline at end of file
using System;
using Mozi.HttpEmbedded.Generic;
namespace Mozi.HttpEmbedded
{
/// <summary>
/// 请求方法
/// </summary>
public class RequestMethod:AbsClassEnum
{
//HTTP/0.9
/// <summary>
/// GET方法请求一个指定资源的表示形式. 使用GET的请求应该只被用于获取数据.
/// </summary>
public static RequestMethod GET=new RequestMethod("GET");
//HTTP/1.0
/// <summary>
/// HEAD方法请求一个与GET请求的响应相同的响应,但没有响应体.
/// </summary>
public static RequestMethod HEAD=new RequestMethod("HEAD");
/// <summary>
/// POST方法用于将实体提交到指定的资源,通常导致在服务器上的状态变化或副作用.
/// </summary>
public static RequestMethod POST=new RequestMethod("POST");
//HTTP/1.1
/// <summary>
/// PUT方法用请求有效载荷替换目标资源的所有当前表示。
/// </summary>
public static RequestMethod PUT=new RequestMethod("PUT");
/// <summary>
/// DELETE方法删除指定的资源。
/// </summary>
public static RequestMethod DELETE=new RequestMethod("DELETE");
/// <summary>
/// CONNECT方法建立一个到由目标资源标识的服务器的隧道。
/// </summary>
public static RequestMethod CONNECT=new RequestMethod("CONNECT");
/// <summary>
/// OPTIONS方法用于描述目标资源的通信选项。
/// </summary>
public static RequestMethod OPTIONS=new RequestMethod("OPTIONS");
/// <summary>
/// TRACE方法沿着到目标资源的路径执行一个消息环回测试。
/// </summary>
public static RequestMethod TRACE=new RequestMethod("TRACE");
/// <summary>
/// PATCH方法用于对资源应用部分修改。
/// </summary>
public static RequestMethod PATCH = new RequestMethod("PATCH");
//WEBDAV
/// <summary>
/// 从Web资源中检索以XML格式存储的属性。它也被重载,以允许一个检索远程系统的集合结构(也叫目录层次结构)
/// </summary>
public static RequestMethod PROPFIND = new RequestMethod("PROPFIND");
/// <summary>
/// 在单个原子性动作中更改和删除资源的多个属性
/// </summary>
public static RequestMethod PROPPATCH = new RequestMethod("PROPPATCH");
/// <summary>
/// 创建集合或者目录
/// </summary>
public static RequestMethod MKCOL = new RequestMethod("MKCOL");
/// <summary>
/// 将资源从一个URI复制到另外一个URI
/// </summary>
public static RequestMethod COPY = new RequestMethod("COPY");
/// <summary>
/// 将资源从一个URI移动到另外一个URI
/// </summary>
public static RequestMethod MOVE = new RequestMethod("MOVE");
/// <summary>
/// 锁定一个资源。WebDAV支持共享锁和互斥锁
/// </summary>
public static RequestMethod LOCK = new RequestMethod("LOCK");
/// <summary>
/// 解除资源的锁定
/// </summary>
public static RequestMethod UNLOCK = new RequestMethod("UNLOCK");
/// <summary>
/// 方法名
/// </summary>
public String Name
{
get { return _name; }
set { _name = value; }
}
protected override String Tag { get { return Name; } }
private string _name;
private RequestMethod(String name)
{
_name = name;
}
}
}
using System;
using System.Net.Sockets;
namespace Mozi.HttpEmbedded
{
public delegate void ServerStart(object sender, ServerArgs args);
public delegate void AfterServerStop(object sender, ServerArgs args);
public delegate void ClientDisConnect(object sender, ClientConnectArgs ars);
public delegate void ClientConnect(object sender, ClientConnectArgs args);
public delegate void ReceiveStart(object sender, DataTransferArgs args);
public delegate void ReceiveEnd(object sender, DataTransferArgs args);
public class ServerArgs:EventArgs{
public DateTime StartTime { get; set; }
public DateTime StopTime { get; set; }
}
public class ClientConnectArgs:EventArgs
{
private Socket _socket;
public Socket Client
{
get { return _socket; }
set { _socket = value; }
}
}
public class DataTransferArgs:EventArgs
{
public byte[] Data { get; set; }
public String IP { get; set; }
public int Port { get; set; }
public Socket Socket { get; set; }
~DataTransferArgs()
{
Data = null;
}
}
}
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Threading;
namespace Mozi.HttpEmbedded
{
/// <summary>
/// 异步单线程
/// </summary>
public class SocketServer
{
//private static SocketServer _mSocketServer;
private int _iPort = 9000;
private List<Socket> _socketDocker;
private Socket sc;
/// <summary>
/// 服务器启动事件
/// </summary>
public event ServerStart OnServerStart;
/// <summary>
/// 客户端连接事件
/// </summary>
public event ClientConnect OnClientConnect;
public event ClientDisConnect AfterClientDisConnect;
/// <summary>
/// 数据接收开始事件
/// </summary>
public event ReceiveStart OnReceiveStart;
/// <summary>
/// 数据接收完成事件
/// </summary>
public event ReceiveEnd AfterReceiveEnd;
/// <summary>
/// 服务器停用事件
/// </summary>
public event AfterServerStop AfterServerStop;
/// <summary>
/// 端口
/// </summary>
public int Port
{
get { return _iPort; }
}
public Socket SocketMain
{
get { return sc; }
}
public SocketServer()
{
_socketDocker = new List<Socket>();
}
//TODO 测试此处是否有BUG
/// <summary>
/// 启动服务器
/// </summary>
/// <param name="port"></param>
public void StartServer(int port)
{
_iPort = port;
if (sc == null)
{
sc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
}
else
{
sc.Close();
}
System.Net.IPEndPoint endpoint = new System.Net.IPEndPoint(System.Net.IPAddress.Any, _iPort);
sc.Bind(endpoint);
sc.Listen(_iPort);
//回调服务器启动事件
if (OnServerStart != null)
{
OnServerStart(this, new ServerArgs() { StartTime=DateTime.Now,StopTime=DateTime.MinValue });
}
sc.BeginAccept(new AsyncCallback(CallbackAccept), sc);
}
/// <summary>
/// 关闭服务器
/// </summary>
public void StopServer()
{
_socketDocker.Clear();
try
{
sc.Shutdown(SocketShutdown.Both);
if (AfterServerStop != null)
{
AfterServerStop(sc, null);
}
}
catch
{
}
}
/// <summary>
/// 开始连接回调
/// </summary>
/// <param name="iar"></param>
private void CallbackAccept(IAsyncResult iar)
{
Socket server = (Socket)iar.AsyncState;
Socket client = server.EndAccept(iar);
_socketDocker.Add(client);
server.BeginAccept(CallbackAccept, server);
if (OnClientConnect != null)
{
OnClientConnect(this, new ClientConnectArgs() {
Client=client
});
}
StateObject so = new StateObject()
{
WorkSocket = client,
Id = Guid.NewGuid().ToString(),
IP = ((System.Net.IPEndPoint)client.RemoteEndPoint).Address.ToString(),
RemotePort = ((System.Net.IPEndPoint)client.RemoteEndPoint).Port,
};
try
{
client.BeginReceive(so.Buffer, 0, StateObject.BufferSize, 0, CallbackReceive, so);
if (OnReceiveStart != null)
{
OnReceiveStart(this, new DataTransferArgs());
}
}
catch(Exception ex)
{
var ex2 = ex;
}
}
/// <summary>
/// 接收数据回调
/// </summary>
/// <param name="iar"></param>
private void CallbackReceive(IAsyncResult iar)
{
StateObject so = (StateObject)iar.AsyncState;
Socket client = so.WorkSocket;
if (client.Connected)
{
int iByteRead = client.EndReceive(iar);
if (iByteRead >0)
{
//置空数据连接
so.ResetBuffer(iByteRead);
if (client.Available > 0)
{
//Thread.Sleep(10);
client.BeginReceive(so.Buffer, 0, StateObject.BufferSize, 0, CallbackReceive, so);
}
else
{
_socketDocker.Remove(client);
if (AfterReceiveEnd != null)
{
AfterReceiveEnd(this,
new DataTransferArgs()
{
Data = so.Data.ToArray(),
IP = so.IP,
Port = so.RemotePort,
Socket = so.WorkSocket
});
}
}
}
else
{
_socketDocker.Remove(client);
if (AfterReceiveEnd != null)
{
AfterReceiveEnd(this,
new DataTransferArgs()
{
Data = so.Data.ToArray(),
IP = so.IP,
Port = so.RemotePort,
Socket = so.WorkSocket
});
}
}
}
else
{
_socketDocker.Remove(client);
if (AfterReceiveEnd != null)
{
AfterReceiveEnd(this,
new DataTransferArgs()
{
Data = so.Data.ToArray(),
IP = so.IP,
Port = so.RemotePort,
Socket = so.WorkSocket
});
}
client.Dispose();
}
}
}
}
using System;
using System.Collections.Generic;
using Mozi.HttpEmbedded.Generic;
namespace Mozi.HttpEmbedded.Source
{
/// <summary>
/// MIME容器
/// </summary>
public class Mime
{
/// <summary>
/// 索引忽略大小写
/// </summary>
public static readonly Dictionary<string, string> Types = new Dictionary<string, string>(new StringCompareIgnoreCase())
{
{"3gp","video/3gpp"},
{"aab","application/x-authoware-bin"},
{"aam","application/x-authoware-map"},
{"aas","application/x-authoware-seg"},
{"ai","application/postscript"},
{"aif","audio/x-aiff"},
{"aifc","audio/x-aiff"},
{"aiff","audio/x-aiff"},
{"als","audio/X-Alpha5"},
{"amc","application/x-mpeg"},
{"ani","application/octet-stream"},
{"asc","text/plain"},
{"asd","application/astound"},
{"asf","video/x-ms-asf"},
{"asn","application/astound"},
{"asp","application/x-asap"},
{"asx","video/x-ms-asf"},
{"au","audio/basic"},
{"avb","application/octet-stream"},
{"avi","video/x-msvideo"},
{"awb","audio/amr-wb"},
{"bcpio","application/x-bcpio"},
{"bin","application/octet-stream"},
{"bld","application/bld"},
{"bld2","application/bld2"},
{"bmp","application/x-MS-bmp"},
{"bpk","application/octet-stream"},
{"bz2","application/x-bzip2"},
{"cal","image/x-cals"},
{"ccn","application/x-cnc"},
{"cco","application/x-cocoa"},
{"cdf","application/x-netcdf"},
{"cgi","magnus-internal/cgi"},
{"chat","application/x-chat"},
{"class","application/octet-stream"},
{"clp","application/x-msclip"},
{"cmx","application/x-cmx"},
{"co","application/x-cult3d-object"},
{"cod","image/cis-cod"},
{"cpio","application/x-cpio"},
{"cpt","application/mac-compactpro"},
{"crd","application/x-mscardfile"},
{"csh","application/x-csh"},
{"csm","chemical/x-csml"},
{"csml","chemical/x-csml"},
{"css","text/css"},
{"cur","application/octet-stream"},
{"dcm","x-lml/x-evm"},
{"dcr","application/x-director"},
{"dcx","image/x-dcx"},
{"dhtml","text/html"},
{"dir","application/x-director"},
{"dll","application/octet-stream"},
{"dmg","application/octet-stream"},
{"dms","application/octet-stream"},
{"doc","application/msword"},
{"dot","application/x-dot"},
{"dvi","application/x-dvi"},
{"dwf","drawing/x-dwf"},
{"dwg","application/x-autocad"},
{"dxf","application/x-autocad"},
{"dxr","application/x-director"},
{"ebk","application/x-expandedbook"},
{"emb","chemical/x-embl-dl-nucleotide"},
{"embl","chemical/x-embl-dl-nucleotide"},
{"eps","application/postscript"},
{"eri","image/x-eri"},
{"es","audio/echospeech"},
{"esl","audio/echospeech"},
{"etc","application/x-earthtime"},
{"etx","text/x-setext"},
{"evm","x-lml/x-evm"},
{"evy","application/x-envoy"},
{"exe","application/octet-stream"},
{"fh4","image/x-freehand"},
{"fh5","image/x-freehand"},
{"fhc","image/x-freehand"},
{"fif","image/fif"},
{"fm","application/x-maker"},
{"fpx","image/x-fpx"},
{"fvi","video/isivideo"},
{"gau","chemical/x-gaussian-input"},
{"gca","application/x-gca-compressed"},
{"gdb","x-lml/x-gdb"},
{"gif","image/gif"},
{"gps","application/x-gps"},
{"gtar","application/x-gtar"},
{"gz","application/x-gzip"},
{"hdf","application/x-hdf"},
{"hdm","text/x-hdml"},
{"hdml","text/x-hdml"},
{"hlp","application/winhlp"},
{"hqx","application/mac-binhex40"},
{"htm","text/html"},
{"html","text/html"},
{"hts","text/html"},
{"ice","x-conference/x-cooltalk"},
{"ico","application/octet-stream"},
{"ief","image/ief"},
{"ifm","image/gif"},
{"ifs","image/ifs"},
{"imy","audio/melody"},
{"ins","application/x-NET-Install"},
{"ips","application/x-ipscript"},
{"ipx","application/x-ipix"},
{"it","audio/x-mod"},
{"itz","audio/x-mod"},
{"ivr","i-world/i-vrml"},
{"j2k","image/j2k"},
{"jad","text/vnd.sun.j2me.app-descriptor"},
{"jam","application/x-jam"},
{"jar","application/java-archive"},
{"jnlp","application/x-java-jnlp-file"},
{"jpe","image/jpeg"},
{"jpeg","image/jpeg"},
{"jpg","image/jpeg"},
{"jpz","image/jpeg"},
{"js","application/x-javascript"},
{"jwc","application/jwc"},
{"kjx","application/x-kjx"},
{"lak","x-lml/x-lak"},
{"latex","application/x-latex"},
{"lcc","application/fastman"},
{"lcl","application/x-digitalloca"},
{"lcr","application/x-digitalloca"},
{"lgh","application/lgh"},
{"lha","application/octet-stream"},
{"lml","x-lml/x-lml"},
{"lmlpack","x-lml/x-lmlpack"},
{"lsf","video/x-ms-asf"},
{"lsx","video/x-ms-asf"},
{"lzh","application/x-lzh"},
{"m13","application/x-msmediaview"},
{"m14","application/x-msmediaview"},
{"m15","audio/x-mod"},
{"m3u","audio/x-mpegurl"},
{"m3url","audio/x-mpegurl"},
{"ma1","audio/ma1"},
{"ma2","audio/ma2"},
{"ma3","audio/ma3"},
{"ma5","audio/ma5"},
{"man","application/x-troff-man"},
{"map","magnus-internal/imagemap"},
{"mbd","application/mbedlet"},
{"mct","application/x-mascot"},
{"mdb","application/x-msaccess"},
{"mdz","audio/x-mod"},
{"me","application/x-troff-me"},
{"mel","text/x-vmel"},
{"mi","application/x-mif"},
{"mid","audio/midi"},
{"midi","audio/midi"},
{"mif","application/x-mif"},
{"mil","image/x-cals"},
{"mio","audio/x-mio"},
{"mmf","application/x-skt-lbs"},
{"mng","video/x-mng"},
{"mny","application/x-msmoney"},
{"moc","application/x-mocha"},
{"mocha","application/x-mocha"},
{"mod","audio/x-mod"},
{"mof","application/x-yumekara"},
{"mol","chemical/x-mdl-molfile"},
{"mop","chemical/x-mopac-input"},
{"mov","video/quicktime"},
{"movie","video/x-sgi-movie"},
{"mp2","audio/x-mpeg"},
{"mp3","audio/x-mpeg"},
{"mp4","video/mp4"},
{"mpc","application/vnd.mpohun.certificate"},
{"mpe","video/mpeg"},
{"mpeg","video/mpeg"},
{"mpg","video/mpeg"},
{"mpg4","video/mp4"},
{"mpga","audio/mpeg"},
{"mpn","application/vnd.mophun.application"},
{"mpp","application/vnd.ms-project"},
{"mps","application/x-mapserver"},
{"mrl","text/x-mrml"},
{"mrm","application/x-mrm"},
{"ms","application/x-troff-ms"},
{"mts","application/metastream"},
{"mtx","application/metastream"},
{"mtz","application/metastream"},
{"mzv","application/metastream"},
{"nar","application/zip"},
{"nbmp","image/nbmp"},
{"nc","application/x-netcdf"},
{"ndb","x-lml/x-ndb"},
{"ndwn","application/ndwn"},
{"nif","application/x-nif"},
{"nmz","application/x-scream"},
{"nokia-op-logo","image/vnd.nok-oplogo-color"},
{"npx","application/x-netfpx"},
{"nsnd","audio/nsnd"},
{"nva","application/x-neva1"},
{"oda","application/oda"},
{"oom","application/x-AtlasMate-Plugin"},
{"pac","audio/x-pac"},
{"pae","audio/x-epac"},
{"pan","application/x-pan"},
{"pbm","image/x-portable-bitmap"},
{"pcx","image/x-pcx"},
{"pda","image/x-pda"},
{"pdb","chemical/x-pdb"},
{"pdf","application/pdf"},
{"pfr","application/font-tdpfr"},
{"pgm","image/x-portable-graymap"},
{"pict","image/x-pict"},
{"pm","application/x-perl"},
{"pmd","application/x-pmd"},
{"png","image/png"},
{"pnm","image/x-portable-anymap"},
{"pnz","image/png"},
{"pot","application/vnd.ms-powerpoint"},
{"ppm","image/x-portable-pixmap"},
{"pps","application/vnd.ms-powerpoint"},
{"ppt","application/vnd.ms-powerpoint"},
{"pqf","application/x-cprplayer"},
{"pqi","application/cprplayer"},
{"prc","application/x-prc"},
{"proxy","application/x-ns-proxy-autoconfig"},
{"ps","application/postscript"},
{"ptlk","application/listenup"},
{"pub","application/x-mspublisher"},
{"pvx","video/x-pv-pvx"},
{"qcp","audio/vnd.qcelp"},
{"qt","video/quicktime"},
{"qti","image/x-quicktime"},
{"qtif","image/x-quicktime"},
{"r3t","text/vnd.rn-realtext3d"},
{"ra","audio/x-pn-realaudio"},
{"ram","audio/x-pn-realaudio"},
{"rar","application/x-rar-compressed"},
{"ras","image/x-cmu-raster"},
{"rdf","application/rdf+xml"},
{"rf","image/vnd.rn-realflash"},
{"rgb","image/x-rgb"},
{"rlf","application/x-richlink"},
{"rm","audio/x-pn-realaudio"},
{"rmf","audio/x-rmf"},
{"rmm","audio/x-pn-realaudio"},
{"rmvb","audio/x-pn-realaudio"},
{"rnx","application/vnd.rn-realplayer"},
{"roff","application/x-troff"},
{"rp","image/vnd.rn-realpix"},
{"rpm","audio/x-pn-realaudio-plugin"},
{"rt","text/vnd.rn-realtext"},
{"rte","x-lml/x-gps"},
{"rtf","application/rtf"},
{"rtg","application/metastream"},
{"rtx","text/richtext"},
{"rv","video/vnd.rn-realvideo"},
{"rwc","application/x-rogerwilco"},
{"s3m","audio/x-mod"},
{"s3z","audio/x-mod"},
{"sca","application/x-supercard"},
{"scd","application/x-msschedule"},
{"sdf","application/e-score"},
{"sea","application/x-stuffit"},
{"sgm","text/x-sgml"},
{"sgml","text/x-sgml"},
{"sh","application/x-sh"},
{"shar","application/x-shar"},
{"shtml","magnus-internal/parsed-html"},
{"shw","application/presentations"},
{"si6","image/si6"},
{"si7","image/vnd.stiwap.sis"},
{"si9","image/vnd.lgtwap.sis"},
{"sis","application/vnd.symbian.install"},
{"sit","application/x-stuffit"},
{"skd","application/x-Koan"},
{"skm","application/x-Koan"},
{"skp","application/x-Koan"},
{"skt","application/x-Koan"},
{"slc","application/x-salsa"},
{"smd","audio/x-smd"},
{"smi","application/smil"},
{"smil","application/smil"},
{"smp","application/studiom"},
{"smz","audio/x-smd"},
{"snd","audio/basic"},
{"spc","text/x-speech"},
{"spl","application/futuresplash"},
{"spr","application/x-sprite"},
{"sprite","application/x-sprite"},
{"spt","application/x-spt"},
{"src","application/x-wais-source"},
{"stk","application/hyperstudio"},
{"stm","audio/x-mod"},
{"sv4cpio","application/x-sv4cpio"},
{"sv4crc","application/x-sv4crc"},
{"svf","image/vnd"},
{"svg","image/svg-xml"},
{"svh","image/svh"},
{"svr","x-world/x-svr"},
{"swf","application/x-shockwave-flash"},
{"swfl","application/x-shockwave-flash"},
{"t","application/x-troff"},
{"tad","application/octet-stream"},
{"talk","text/x-speech"},
{"tar","application/x-tar"},
{"taz","application/x-tar"},
{"tbp","application/x-timbuktu"},
{"tbt","application/x-timbuktu"},
{"tcl","application/x-tcl"},
{"tex","application/x-tex"},
{"texi","application/x-texinfo"},
{"texinfo","application/x-texinfo"},
{"tgz","application/x-tar"},
{"thm","application/vnd.eri.thm"},
{"tif","image/tiff"},
{"tiff","image/tiff"},
{"tki","application/x-tkined"},
{"tkined","application/x-tkined"},
{"toc","application/toc"},
{"toy","image/toy"},
{"tr","application/x-troff"},
{"trk","x-lml/x-gps"},
{"trm","application/x-msterminal"},
{"tsi","audio/tsplayer"},
{"tsp","application/dsptype"},
{"tsv","text/tab-separated-values"},
{"ttf","application/octet-stream"},
{"ttz","application/t-time"},
{"txt","text/plain"},
{"ult","audio/x-mod"},
{"ustar","application/x-ustar"},
{"uu","application/x-uuencode"},
{"uue","application/x-uuencode"},
{"vcd","application/x-cdlink"},
{"vcf","text/x-vcard"},
{"vdo","video/vdo"},
{"vib","audio/vib"},
{"viv","video/vivo"},
{"vivo","video/vivo"},
{"vmd","application/vocaltec-media-desc"},
{"vmf","application/vocaltec-media-file"},
{"vmi","application/x-dreamcast-vms-info"},
{"vms","application/x-dreamcast-vms"},
{"vox","audio/voxware"},
{"vqe","audio/x-twinvq-plugin"},
{"vqf","audio/x-twinvq"},
{"vql","audio/x-twinvq"},
{"vre","x-world/x-vream"},
{"vrml","x-world/x-vrml"},
{"vrt","x-world/x-vrt"},
{"vrw","x-world/x-vream"},
{"vts","workbook/formulaone"},
{"wav","audio/x-wav"},
{"wax","audio/x-ms-wax"},
{"wbmp","image/vnd.wap.wbmp"},
{"web","application/vnd.xara"},
{"wi","image/wavelet"},
{"wis","application/x-InstallShield"},
{"wm","video/x-ms-wm"},
{"wma","audio/x-ms-wma"},
{"wmd","application/x-ms-wmd"},
{"wmf","application/x-msmetafile"},
{"wml","text/vnd.wap.wml"},
{"wmlc","application/vnd.wap.wmlc"},
{"wmls","text/vnd.wap.wmlscript"},
{"wmlsc","application/vnd.wap.wmlscriptc"},
{"wmlscript","text/vnd.wap.wmlscript"},
{"woff","application/octet-stream"},
{"woff2","application/octet-stream"},
{"wmv","audio/x-ms-wmv"},
{"wmx","video/x-ms-wmx"},
{"wmz","application/x-ms-wmz"},
{"wpng","image/x-up-wpng"},
{"wpt","x-lml/x-gps"},
{"wri","application/x-mswrite"},
{"wrl","x-world/x-vrml"},
{"wrz","x-world/x-vrml"},
{"ws","text/vnd.wap.wmlscript"},
{"wsc","application/vnd.wap.wmlscriptc"},
{"wv","video/wavelet"},
{"wvx","video/x-ms-wvx"},
{"wxl","application/x-wxl"},
{"x-gzip","application/x-gzip"},
{"xar","application/vnd.xara"},
{"xbm","image/x-xbitmap"},
{"xdm","application/x-xdma"},
{"xdma","application/x-xdma"},
{"xdw","application/vnd.fujixerox.docuworks"},
{"xht","application/xhtml+xml"},
{"xhtm","application/xhtml+xml"},
{"xhtml","application/xhtml+xml"},
{"xla","application/vnd.ms-excel"},
{"xlc","application/vnd.ms-excel"},
{"xll","application/x-excel"},
{"xlm","application/vnd.ms-excel"},
{"xls","application/vnd.ms-excel"},
{"xlt","application/vnd.ms-excel"},
{"xlw","application/vnd.ms-excel"},
{"xm","audio/x-mod"},
{"xml","text/xml"},
{"xmz","audio/x-mod"},
{"xpi","application/x-xpinstall"},
{"xpm","image/x-xpixmap"},
{"xsit","text/xml"},
{"xsl","text/xml"},
{"xul","text/xul"},
{"xwd","image/x-xwindowdump"},
{"xyz","chemical/x-pdb"},
{"yz1","application/x-yz1"},
{"z","application/x-compress"},
{"zac","application/x-zaurus-zac"},
{"zip","application/zip"},
};
/// <summary>
/// 增加MIME类型
/// </summary>
/// <param name="ext"></param>
/// <param name="contentType"></param>
public static void Add(string ext, string contentType)
{
if (!Types.ContainsKey(ext))
{
Types.Add(ext,contentType);
}
}
/// <summary>
/// 移除类型
/// </summary>
/// <param name="ext"></param>
public static void Remove(string ext)
{
Types.Remove(ext);
}
/// <summary>
/// 获取资源类型
/// </summary>
/// <param name="ext"></param>
/// <returns></returns>
public static string GetContentType(string ext)
{
if (String.IsNullOrEmpty(ext)||!Types.ContainsKey(ext))
{
return "text/html";
}
else
{
return Types[ext];
}
}
}
}
using System;
using System.Globalization;
using System.IO;
namespace Mozi.HttpEmbedded.Source
{
/// <summary>
/// 静态资源管理
/// </summary>
internal sealed class StaticFiles
{
public bool Enabled { get; set; }
private string _root;
private static StaticFiles _staticfiles;
public static StaticFiles Default
{
get { return _staticfiles ?? (_staticfiles = new StaticFiles()); }
}
private StaticFiles()
{
_root = AppDomain.CurrentDomain.BaseDirectory+"/";
init();
}
public StaticFiles SetRoot(string root)
{
_root = root;
return this;
}
/// <summary>
/// 初始化
/// </summary>
private void init()
{
//载入MIME类型
}
/// <summary>
/// 判断是否静态文件
/// </summary>
/// <param name="ext"></param>
/// <returns></returns>
public bool IsStatic(string ext)
{
if (!string.IsNullOrEmpty(ext) && Mime.Types.ContainsKey(ext))
{
return true;
}
return false;
}
/// <summary>
/// 检查文件是否存在
/// </summary>
/// <param name="path"></param>
/// <param name="ext"></param>
/// <returns></returns>
public bool Exists(string path, string ext)
{
return System.IO.File.Exists(_root + "\\" + path);
}
/// <summary>
/// 检查最后修改日期
/// </summary>
/// <param name="path"></param>
/// <param name="ifModifiedSince"></param>
/// <returns></returns>
public bool CheckIfModified(string path, string ifModifiedSince)
{
DateTime dtModified=System.IO.File.GetLastWriteTime(_root + "\\" + path);
try
{
if (!String.IsNullOrEmpty(ifModifiedSince))
{
DateTime dtSince = DateTime.ParseExact(ifModifiedSince, "ddd, dd MMM yyyy HH:mm:ss GMT",
CultureInfo.InvariantCulture,
DateTimeStyles.AdjustToUniversal).ToLocalTime();
if (dtModified <= dtSince)
{
return false;
}
}
}
catch
{
}
return true;
}
/// <summary>
/// 取文件最后修改日期
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public DateTime GetLastModified(string path)
{
return System.IO.File.GetLastWriteTime(_root + "\\" + path);
}
/// <summary>
/// 提取文件流
/// </summary>
/// <param name="path"></param>
/// <param name="ext"></param>
/// <returns></returns>
public byte[] Load(string path,string ext)
{
using (FileStream fs = new FileStream(_root + "\\" + path, FileMode.Open))
{
byte[] data = new byte[fs.Length];
fs.Read(data, 0, data.Length);
return data;
}
}
}
}
 text/html html htm shtml;
text/css css;
text/xml xml;
image/gif gif;
image/jpeg jpeg jpg;
application/javascript js;
application/atom+xml atom;
application/rss+xml rss;
text/mathml mml;
text/plain txt;
text/vnd.sun.j2me.app-descriptor jad;
text/vnd.wap.wml wml;
text/x-component htc;
image/png png;
image/svg+xml svg svgz;
image/tiff tif tiff;
image/vnd.wap.wbmp wbmp;
image/webp webp;
image/x-icon ico;
image/x-jng jng;
image/x-ms-bmp bmp;
font/woff woff;
font/woff2 woff2;
application/java-archive jar war ear;
application/json json;
application/mac-binhex40 hqx;
application/msword doc;
application/pdf pdf;
application/postscript ps eps ai;
application/rtf rtf;
application/vnd.apple.mpegurl m3u8;
application/vnd.google-earth.kml+xml kml;
application/vnd.google-earth.kmz kmz;
application/vnd.ms-excel xls;
application/vnd.ms-fontobject eot;
application/vnd.ms-powerpoint ppt;
application/vnd.oasis.opendocument.graphics odg;
application/vnd.oasis.opendocument.presentation odp;
application/vnd.oasis.opendocument.spreadsheet ods;
application/vnd.oasis.opendocument.text odt;
application/vnd.openxmlformats-officedocument.presentationml.presentation pptx;
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx;
application/vnd.openxmlformats-officedocument.wordprocessingml.document docx;
application/vnd.wap.wmlc wmlc;
application/x-7z-compressed 7z;
application/x-cocoa cco;
application/x-java-archive-diff jardiff;
application/x-java-jnlp-file jnlp;
application/x-makeself run;
application/x-perl pl pm;
application/x-pilot prc pdb;
application/x-rar-compressed rar;
application/x-redhat-package-manager rpm;
application/x-sea sea;
application/x-shockwave-flash swf;
application/x-stuffit sit;
application/x-tcl tcl tk;
application/x-x509-ca-cert der pem crt;
application/x-xpinstall xpi;
application/xhtml+xml xhtml;
application/xspf+xml xspf;
application/zip zip;
application/octet-stream bin exe dll;
application/octet-stream deb;
application/octet-stream dmg;
application/octet-stream iso img;
application/octet-stream msi msp msm;
audio/midi mid midi kar;
audio/mpeg mp3;
audio/ogg ogg;
audio/x-m4a m4a;
audio/x-realaudio ra;
video/3gpp 3gpp 3gp;
video/mp2t ts;
video/mp4 mp4;
video/mpeg mpeg mpg;
video/quicktime mov;
video/webm webm;
video/x-flv flv;
video/x-m4v m4v;
video/x-mng mng;
video/x-ms-asf asx asf;
video/x-ms-wmv wmv;
video/x-msvideo avi;
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
namespace Mozi.HttpEmbedded
{
/// <summary>
/// TCP缓冲对象
/// </summary>
class StateObject
{
public String Id {get;set;} //连接标识符
public Socket WorkSocket = null;
public int RemotePort = 0;
public static int BufferSize = 1024;
public List<byte> Data=new List<byte>();
public byte[] Buffer = new byte[BufferSize];
public StringBuilder sb = new StringBuilder();
public String IP { get; set; }
//TODO 此处没有完整处理包体,会有多读取的冗余数据
public void ResetBuffer(int count)
{
byte[] data=new byte[count>0?count:Buffer.Length];
Array.Copy(Buffer,data,data.Length);
Data.AddRange(data);
Buffer = new byte[BufferSize];
}
~StateObject()
{
Buffer = null;
}
}
}
此差异已折叠。
using System;
using System.Collections.Generic;
using System.Text;
using Mozi.HttpEmbedded.Generic;
namespace Mozi.HttpEmbedded
{
/// <summary>
/// 通讯头报文
/// </summary>
public class TransformHeader
{
public static byte[] Carriage = { (byte)ASCIICode.CR, (byte)ASCIICode.LF };
/// <summary>
/// 通讯头集合
/// </summary>
private Dictionary<string, string> HeaderData = new Dictionary<string, string>(new StringCompareIgnoreCase())
{
};
/// <summary>
/// 是否包含
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public bool Contains(string key)
{
return HeaderData.ContainsKey(key);
}
/// <summary>
/// 获取值
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public string GetValue(string key)
{
return HeaderData.ContainsKey(key)?HeaderData[key]:null;
}
/// <summary>
/// 增加头部信息
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public TransformHeader Add(String key, String value)
{
if (HeaderData.ContainsKey(key))
{
HeaderData[key] = value;
}
else
{
HeaderData.Add(key, value);
}
return this;
}
/// <summary>
/// 增加头部信息
/// </summary>
/// <param name="header"></param>
/// <returns></returns>
public TransformHeader Add(HeaderProperty header,String value)
{
Add(header.PropertyTag,value);
return this;
}
/// <summary>
/// 获取缓存数据 带分割符号
/// </summary>
/// <returns></returns>
public byte[] GetBuffer()
{
List<byte> buffer=new List<byte>();
foreach (var item in HeaderData)
{
buffer.AddRange(Encoding.UTF8.GetBytes(String.Format("{0}: {1}",item.Key,item.Value)));
buffer.AddRange(Carriage);
}
return buffer.ToArray();
}
public string this[string key]
{
get { return HeaderData[key]; }
set { HeaderData[key] = value; }
}
}
}
\ No newline at end of file
<?xml version="1.0"?>
<configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30330.147
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mozi.HttpEmbedded", "Mozi.HttpEmbedded\Mozi.HttpEmbedded.csproj", "{051C3F42-0A53-4A78-A961-28FFA8C17B46}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{051C3F42-0A53-4A78-A961-28FFA8C17B46}.Debug|x86.ActiveCfg = Debug|x86
{051C3F42-0A53-4A78-A961-28FFA8C17B46}.Debug|x86.Build.0 = Debug|x86
{051C3F42-0A53-4A78-A961-28FFA8C17B46}.Release|x86.ActiveCfg = Release|x86
{051C3F42-0A53-4A78-A961-28FFA8C17B46}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F418C6F4-7675-411F-B07A-188AE793196B}
EndGlobalSection
EndGlobal
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册