using System; using System.Collections.Generic; using System.Text; using Mozi.HttpEmbedded.Cookie; using Mozi.HttpEmbedded.Encode; using Mozi.HttpEmbedded.Generic; namespace Mozi.HttpEmbedded { //TODO 应将 GET/POST 查询字段进行区分 /// /// HTTP请求 /// public class HttpRequest { /// /// 协议类型,参看值 /// public ProtocolType Protocol { get; protected set; } /// /// 协议版本 /// public HttpVersion ProtocolVersion { get; protected set; } /// /// 请求路径 /// public string Path { get; protected set; } /// /// 查询字符串 /// public string QueryString { get; protected set; } /// /// 查询 索引可忽略大小写 /// public Dictionary Query = new Dictionary(new StringCompareIgnoreCase()); /// /// POST 索引可忽略大小写 /// public Dictionary Post = new Dictionary(new StringCompareIgnoreCase()); /// /// 可接受压缩算法 /// public string AcceptEncoding { get; protected set; } /// /// 请求地址 /// public string Host { get; protected set; } /// /// 客户端信息 /// public string UserAgent { get; protected set; } /// /// 请求方法 /// public RequestMethod Method { get; protected set; } /// /// 内容类型 /// public string ContentType { get; protected set; } /// /// 内容编码类型 /// public string ContentCharset { get; protected set; } /// /// 内容大小 /// public string ContentLength { get; protected set; } /// /// 请求头 /// public TransformHeader Headers { get; protected set; } ///// ///// 二进制通讯数据 ///// //public byte[] PackData { get; protected set; } /// /// 原始请求首行数据 /// public byte[] RequestLineData { get; protected set; } /// /// 原始请求首行字符串 /// public string RequestLineString { get; protected set; } /// /// 请求头数据 /// public byte[] HeaderData { get; protected set; } /// /// 请求数据体 /// public byte[] Body { get; protected set; } /// /// 文件 /// public FileCollection Files { get; protected set; } /// /// Cookie /// public RequestCookie Cookies { get; protected set; } /// /// 客户机IP地址 /// public string ClientAddress { get; internal set; } /// /// 客户机是否已通过认证 /// public bool IsAuthorized { get; internal set; } /// /// 客户机接受的语言选项 /// public LanguageOrder[] AcceptLanguage { get; private set; } /// /// 请求的来源地址 /// public string Referer { get; private set; } /// /// 构造函数 /// public HttpRequest() { //默认HTTP/1.1 ProtocolVersion = HttpVersion.Version11; Headers = new TransformHeader(); Files = new FileCollection(); Cookies = new RequestCookie(); Body = new byte[] { }; } /// /// 解析请求数据包 /// /// /// 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 /// /// /// /// /// public static HttpRequest Parse(byte[] data) { HttpRequest req = new HttpRequest(); //解析头 int posCR = 0; int posCaret = 0; int count = 0; int indLine = 0; int dataLength = data.Length; while ((posCR < dataLength) && Array.IndexOf(data, ASCIICode.CR, posCR + 1) > 0) { posCR = Array.IndexOf(data, ASCIICode.CR, posCR + 1); //连续两个CR byte[] fragement = new byte[posCR - posCaret]; Array.Copy(data, posCaret, fragement, 0, posCR - posCaret); if (indLine == 0) { ParseRequestLine(ref req, fragement); } else { ParseHeaders(ref req, fragement); } if ((Array.IndexOf(data, ASCIICode.CR, posCR + 1) == posCR + 2)) { break; } //跳过分割字节段 posCaret = posCR + 2; indLine++; //TODO 置空对象 } //头信息解析 //HOST ParseHeaderHost(ref req); //User-Agent ParseHeaderUserAgent(ref req); //Accept-Language ParseHeaderAcceptLanguage(ref req); //Referer ParseHeaderReferer(ref req); //Content-Type ParseHeaderContentType(ref req); //Range ParseHeaderRange(ref req); //解析Cookie ParseCookie(ref req); //TODO 此处是否需要分辨GET/POST //解析数据 荷载部分 if (data.Length > posCR + 4) { req.Body = new byte[data.Length - (posCR + 4)]; //TODO 此处又重新生成一个数据对象,导致内存占用过大 Array.Copy(data, posCR + 4, req.Body, 0, req.Body.Length); ParsePayload(ref req, req.Body); } return req; } //TODO HTTP/2.0 是基于二进制数据帧,此处需要重新适配 //TODO 先判断包体是否经过压缩 /// /// 解析请求体正文 /// /// /// private static void ParsePayload(ref HttpRequest req, byte[] data) { string formType = req.Headers.GetValue(HeaderProperty.ContentType.PropertyName); if (formType != null) { if (formType.Contains("application/x-www-form-urlencoded")) { ParsePayloadFormUrl(ref req,ref data); } else if (formType.Contains("multipart/form-data")) { ParsePayloadFormData(ref req,ref data); } else { ParsePayloadText(ref req,ref data); } } } /// /// 分析请求体 application/x-www-form-urlencoded /// 请求体数据类似于查询字符串 /// /// /// private static void ParsePayloadFormUrl(ref HttpRequest req,ref byte[] data) { req.Post = UrlEncoder.ParseQuery(StringEncoder.Decode(data)); } //TODO 文件流应写入缓冲区,否则会造成内存占用过大 //DONE 此处仅能解析一个文件,继续修改代码 //TODO multipart/form-data 也能提交文本内容,此处未能很好的处理 /// /// 分析请求体 multipart/form-data /// /// /// /// /// /// --{boundary} /// Content-Disposition: form-data; name="{file.fieldname}"; filename="{file.name}"\r\n /// Content-Type: application/octet-stream\r\n /// \r\n /// {file.binary}\r\n /// \r\n /// --{boundary}\r\n /// Content-Disposition: form-data; name="{file.fieldname}"; filename="{file.name}"\r\n /// Content-Type: application/octet-stream\r\n /// \r\n /// {file.binary}\r\n /// --{boundary}\r\n /// Content-Disposition: form-data; name="{field.name}"\r\n /// \r\n /// --{boundary}--\r\n /// /// private static void ParsePayloadFormData(ref HttpRequest req,ref byte[] data) { string contentType = req.Headers.GetValue(HeaderProperty.ContentType.PropertyName); string boundary = ""; //此处仅用;分割,提高通用性 string[] values = contentType.Split(new[] { ((char)ASCIICode.SEMICOLON).ToString() }, 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, indFragFirst/*分割起点*/ = 0, indFragNext/*下段分割起点*/ = 0; while ((indBoundary + 1) < data.Length && 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.Length >= (indBoundary + i + 2 - 1) && 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, posCaret/*分割起始位*/ = 0, index = 0; byte[] fragment = new byte[indFragNext - indFragFirst]; Array.Copy(data, indFragFirst, fragment, 0, indFragNext - indFragFirst); bool isFile = false; string fieldName = string.Empty; string fileName = string.Empty; /// ///-----------------------------97671069125495\r\n ///Content-Disposition: form-data; name=\"mailaddress\"\r\n ///\r\n ///abcdefg ///\r\n /// //提取字段头属性 while ((posCR = Array.IndexOf(fragment, ASCIICode.CR, posCR + 1)) > 0) { //TODO 对普通字段的处理有问题 byte[] fraghead = new byte[posCR - posCaret]; Array.Copy(fragment, posCaret, fraghead, 0, posCR - posCaret); //Content-Disposition if (index == 1) { //内容描述信息 //Content-Disposition: form-data; name="{field.name}"; filename="{file.name}" string disposition = StringEncoder.Decode(fraghead); string[] headers = disposition.Split(new[] { ((char)ASCIICode.SEMICOLON).ToString() }, 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); } } } //Content-Type if (index == 2) { } //跳过分割字节段 posCaret = posCR + 2; index++; //连续两个CR if (Array.IndexOf(fragment, ASCIICode.CR, posCR + 1) != posCR + 2) { } else { break; } } //解析数据 if (data.Length > posCR + 4) { //内容末端还有两个字符(\r\n),故此处再减两个字节 var postField = new byte[fragment.Length - (posCR + 4 + 2)]; Array.Copy(fragment, posCR + 4, postField, 0, postField.Length); if (isFile) { File file = new File(); file.FileName = HtmlEncoder.EntityCodeToString(fileName); file.FileData = postField; file.FileIndex = req.Files.Length; file.FieldName = fieldName; //file.FileTempSavePath = AppDomain.CurrentDomain.BaseDirectory + @"Temp\" + file.FileName.ToString(); req.Files.Append(file); ////写入临时目录 //FileStream fs = new FileStream(file.FileTempSavePath, FileMode.OpenOrCreate); //int length = fragbody.Length - (posCR + 4); //int blockSize = 1024; //int count = length / blockSize; //byte[] blockData = new byte[blockSize]; //int mode = length % blockSize; //for (int i = 0; i < count; i++) //{ // Array.Copy(fragbody, posCR + 4 + i * blockData.Length, blockData, 0, blockData.Length); // fs.Write(blockData, 0, blockData.Length); //} //if (mode > 0) //{ // Array.Resize(ref blockData, mode); // Array.Copy(fragbody, posCR + 4 + blockSize * count, blockData, 0, blockData.Length); // fs.Write(blockData, 0, blockData.Length); //} //fs.Flush(); //fs.Dispose(); } else { //var postField = new byte[fragbody.Length - (posCR + 4)]; req.Query.Add(fieldName, StringEncoder.Decode(postField)); } //TODO 置空对象 fragment = null; postField = null; } } } catch (Exception ex) { Console.WriteLine("Exception occurs while parsing multipart/form-data:{0}", ex.Message); } } } //TODO 后续扩展实现,暂时不实现 /// /// 分析请求体 文本类型 /// application/json /// text/plain /// text/xml /// /// /// private static void ParsePayloadText(ref HttpRequest req,ref byte[] data) { } /// /// 解析头属性 /// /// /// private static void ParseHeaders(ref HttpRequest req, byte[] data) { HeaderProperty hp = HeaderProperty.Parse(data); req.Headers.Add(hp.PropertyName, hp.PropertyValue); #if DEBUG Console.WriteLine("{0}:{1}",hp.PropertyTag,hp.PropertyValue); #endif } /// /// 解析UserAgent /// /// private static void ParseHeaderUserAgent(ref HttpRequest req) { if (req.Headers.Contains(HeaderProperty.UserAgent.PropertyName)) { req.UserAgent = req.Headers.GetValue(HeaderProperty.UserAgent.PropertyName); } } /// /// 解析请求目标主机地址 /// /// private static void ParseHeaderHost(ref HttpRequest req) { if (req.Headers.Contains(HeaderProperty.Host.PropertyName)) { req.Host = req.Headers.GetValue(HeaderProperty.Host.PropertyName); } } /// /// 解析来源页面地址 /// /// private static void ParseHeaderReferer(ref HttpRequest req) { if (req.Headers.Contains(HeaderProperty.Referer.PropertyName)) { req.Referer = req.Headers.GetValue(HeaderProperty.Referer.PropertyName); } } /// /// 解析接受语言排序 /// 语法:zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3 /// 分割符号为, /// /// private static void ParseHeaderAcceptLanguage(ref HttpRequest req) { if (req.Headers.Contains(HeaderProperty.AcceptLanguage.PropertyName)) { var language = req.Headers.GetValue(HeaderProperty.AcceptLanguage.PropertyName) ?? ""; var languages = language.Split(new char[] { (char)ASCIICode.COMMA }, StringSplitOptions.RemoveEmptyEntries); req.AcceptLanguage = new LanguageOrder[languages.Length]; try { for (int i = 0; i < languages.Length; i++) { var lan = languages[i]; var lans = lan.Split(new char[] { (char)ASCIICode.COMMA }, StringSplitOptions.RemoveEmptyEntries); req.AcceptLanguage[i] = new LanguageOrder() { LanguageName = lans[0], Weight = lans.Length > 1 ? int.Parse(lans[1]) : 1 }; } } catch { } } } /// /// 解析文档类型 /// /// private static void ParseHeaderContentType(ref HttpRequest req) { if (req.Headers.Contains(HeaderProperty.ContentType.PropertyName)) { var contentType = req.Headers.GetValue(HeaderProperty.ContentType.PropertyName); string[] cts = contentType.Split(new[] { ((char)ASCIICode.SEMICOLON).ToString() + ((char)ASCIICode.SPACE).ToString() }, StringSplitOptions.RemoveEmptyEntries); if (cts.Length > 0) { req.ContentType = cts[0]; if (cts.Length > 2) { req.ContentCharset = cts[2]; } } } } /// /// 解析文档请求范围 /// /// 此功能主要应用于断点续传 /// /// /// private static void ParseHeaderRange(ref HttpRequest req) { } /// /// 解析Cookie /// /// private static void ParseCookie(ref HttpRequest req) { if (req.Headers.Contains(HeaderProperty.Cookie.PropertyName)) { req.Cookies = RequestCookie.Parse(req.Headers.GetValue(HeaderProperty.Cookie.PropertyName)); } } /// /// 解析首行数据 /// 方法 查询 协议 /// /// /// private static void ParseRequestLine(ref HttpRequest req, byte[] data) { //解析起始行 req.RequestLineData = data; req.RequestLineString = Encoding.UTF8.GetString(data); string[] sFirst = req.RequestLineString.Split(new[] { (char)ASCIICode.SPACE }, StringSplitOptions.None); //方法 查询 协议 string sMethod = sFirst[0]; string sUrl = sFirst[1]; string sProtocol = sFirst[2]; RequestMethod rm = AbsClassEnum.Get(sMethod); req.Method = rm; //判断方法是否是已知方法 if (Equals(req.Method,null)) { req.Method = new RequestMethod(sMethod); } 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(sProtoType); req.ProtocolVersion = AbsClassEnum.Get(sProtoVersion); } //TODO 此功能需要重试以进行验证 /// /// 数据重播 /// /// 头属性名传大写 /// public byte[] GetBuffer(bool headerKeyNameUpperCase) { List data = new List(); //注入状态信息 data.AddRange(GetRequestLine()); data.AddRange(TransformHeader.Carriage); //注入默认头部 data.AddRange(Headers.GetBuffer(headerKeyNameUpperCase)); //注入Cookie data.AddRange(Cookies.GetBuffer()); //注入分割符 data.AddRange(TransformHeader.Carriage); //注入响应包体 data.AddRange(Body); return data.ToArray(); } /// /// 数据重播 /// /// public byte[] GetBuffer() { return GetBuffer(false); } /// /// 设置头信息 /// /// /// /// public HttpRequest SetHeader(string key,string value) { Headers.Add(key, value); return this; } /// /// 设置头信息 /// /// /// /// public HttpRequest SetHeader(HeaderProperty key,string value) { Headers.Add(key, value); return this; } /// /// 应用头信息集合 /// /// /// public HttpRequest SetHeaders(TransformHeader headers) { Headers = headers; return this; } /// /// 设置请求路径 /// /// /// public HttpRequest SetPath(string path) { Path = path; return this; } /// /// 设置请求方法 /// /// /// public HttpRequest SetMethod(RequestMethod method) { Method = method; return this; } /// /// 设置请求HTTP协议 /// /// /// public HttpRequest SetProtocol(HttpVersion version) { ProtocolVersion = version; return this; } public HttpRequest SetBody(byte[] data) { Body = data; ContentLength = Body.Length.ToString(); return this; } /// /// 生成请求行 /// /// public byte[] GetRequestLine() { return StringEncoder.Encode(string.Format("{0} {1} HTTP/{2}", Method.Name, Path, ProtocolVersion.Version)); } ~HttpRequest() { //PackData = null; RequestLineData = null; Body = null; Headers = null; HeaderData = null; Files = null; AcceptLanguage = null; } } /// /// HttpRequest扩展 /// public static class HttpRequestExtension { } }