• 基于byte[]的HTTP协议头分析代码


    最近需要为组件实现一个HTTP的扩展包,所以简单地实现了HTTP协议分析。对于HTTP协议就不详细解说了网上的资料太丰富了,这里主要描述如何通过byte[]流分析出HTTP协议头信息。HTTP协议头有两个协议字符是比较重要的分别就是' '和':',前者要描述每个头信息的结束,而后则是属性名和属性值的分隔符号。

    实现

    由于并没有使用Stream来处理所以在分析的时候就不能使用ReadLine来的方便,只能通过分析byte来解决。估计有朋友会问直接把byte[]打包成Stream就方便了,其实主要是使用场问题,有可能一次过来的byte[]包括多个http请求。所以包装成stream用readline的方法成本高不划算。以下看下主体分析代码:

     1         public bool Import(byte[] data, ref int offset, ref int count)
     2         {
     3             byte[] buffer = mBuffer;
     4             while (count > 0)
     5             {
     6                 buffer[mHeaderLength] = data[offset];
     7                 mHeaderLength++;
     8                 offset++;
     9                 count--;
    10                 if (mHeaderLength >= HEADER_BUFFER_LENGT)
    11                     throw new NetTcpException("header data too long!");
    12                 if (mBuffer[mHeaderLength - 1] == mWrap[1] && mBuffer[mHeaderLength - 2] == mWrap[0])
    13                 {
    14                     if (Action == null)
    15                     {
    16                         Action = Encoding.UTF8.GetString(buffer, mStartIndex, mHeaderLength - mStartIndex - 2);
    17                         mStartIndex = mHeaderLength;
    18                     }
    19                     else
    20                     {
    21                         if (mBuffer[mHeaderLength - 3] == mWrap[1] && mBuffer[mHeaderLength - 4] == mWrap[0])
    22                         {
    23                             if (mLastPropertyName != null)
    24                             {
    25                                 this[mLastPropertyName] = Encoding.UTF8.GetString(buffer, mStartIndex, mHeaderLength - mStartIndex - 2);
    26                             }
    27                             return true;
    28                         }
    29                         else
    30                         {
    31                             if (mLastPropertyName != null)
    32                             {
    33                                 this[mLastPropertyName] = Encoding.UTF8.GetString(buffer, mStartIndex, mHeaderLength - mStartIndex - 2);
    34                                 mStartIndex = mHeaderLength;
    35                                 mLastPropertyName = null;
    36                             }
    37                         }
    38                     }
    39                 }
    40                 else if (mBuffer[mHeaderLength - 1] == mNameEof[0] && mLastPropertyName == null)
    41                 {
    42                     mLastPropertyName = Encoding.UTF8.GetString(buffer, mStartIndex, mHeaderLength - mStartIndex - 1);
    43                     mStartIndex = mHeaderLength;
    44                 }
    45 
    46             }
    47             return false;
    48 
    49         }

     代码比较简单,通过一次遍历buffer就把Http请求行为和相应用的头属性都分析出来的。由于一个byte[]有可能包括多个HTTP请求(特殊场景),所以参数上使用ref主要是通过外层这个byte[]是否有多个多header要处理。

    单元测试

    开发人员应该习惯写单元测试,好处是很明显就是实现的代码的可测性,如果可测性差那代码就要从设计上进行调整了。下面是针对以上代码的两种单元测试,主要测试分析代码在不同情况下是否可以良好地工作。
     1 [TestMethod]
     2         public void HeaderImport()
     3         {
     4             string header = "Post
    name:henry
    email:
    
    ";
     5             byte[] data = Encoding.UTF8.GetBytes(header);
     6             int offset = 0;
     7             int count = data.Length;
     8             byte[] buffer = new byte[1024 * 4];
     9             HttpHeader hh = new HttpHeader(buffer);
    10             if (hh.Import(data, ref offset, ref count))
    11             {
    12                 Assert.AreEqual(hh.RequestType, "Post");
    13                 Assert.AreEqual(hh["name"], "henry");
    14                 Assert.AreEqual(hh["email"], "");
    15             }
    16 
    17         }
    18 
    19         [TestMethod]
    20         public void HeaderImport1()
    21         {
    22             string header = "Post
    name:henry
    email:henryfan@msn.com
    
    ";
    23             byte[] data = Encoding.UTF8.GetBytes(header);
    24             int offset = 0;
    25             int count = data.Length;
    26             byte[] buffer = new byte[1024 * 4];
    27             HttpHeader hh = new HttpHeader(buffer);
    28 
    29             if (hh.Import(data, ref offset, ref count))
    30             {
    31                 Assert.AreEqual(hh.RequestType, "Post");
    32                 Assert.AreEqual(hh["name"], "henry");
    33                 Assert.AreEqual(hh["email"], "henryfan@msn.com");
    34                 hh = new HttpHeader(buffer);
    35             }
    36 
    37 
    38             header = "Get
    name:henry
    ";
    39             data = Encoding.UTF8.GetBytes(header);
    40             offset = 0;
    41             count = data.Length;
    42             hh.Import(data, ref offset, ref count);
    43 
    44 
    45             header = "email:henryfan@msn.com";
    46             data = Encoding.UTF8.GetBytes(header);
    47             offset = 0;
    48             count = data.Length;
    49             hh.Import(data, ref offset, ref count);
    50 
    51             header = "
    ";
    52             data = Encoding.UTF8.GetBytes(header);
    53             offset = 0;
    54             count = data.Length;
    55             hh.Import(data, ref offset, ref count);
    56 
    57             header = "
    
    ";
    58             data = Encoding.UTF8.GetBytes(header);
    59             offset = 0;
    60             count = data.Length;
    61 
    62             if (hh.Import(data, ref offset, ref count))
    63             {
    64                 Assert.AreEqual(hh.RequestType, "Get");
    65                 Assert.AreEqual(hh["name"], "henry");
    66                 Assert.AreEqual(hh["email"], "henryfan@msn.com");
    67             }
    68 
    69         }
    70     }

     HttpHeader完整代码

      1   public class HttpHeader 
      2     {
      3 
      4         public const int HEADER_BUFFER_LENGT = 1024 * 4;
      5 
      6         public const string HEADER_CONTENT_LENGTH = "Content-Length";
      7 
      8         public const string HEADER_ACCEPT = "Accept";
      9 
     10         public const string HEADER_ACCEPT_ENCODING = "Accept-Encoding";
     11 
     12         public const string HEADER_ACCEPT_LANGUAGE = "Accept-Language";
     13 
     14         public const string HEADER_CONNNECTION = "Connection";
     15 
     16         public const string HEADER_COOKIE = "Cookie";
     17 
     18         public const string HEADER_HOST = "Host";
     19 
     20         public const string HEADER_USER_AGENT = "User-Agent";
     21 
     22         public const string HEADER_CONTENT_ENCODING = "Content-Encoding";
     23 
     24         public const string HEADER_CONTENT_TYPE="Content-Type";
     25 
     26         public const string HEADER_SERVER = "Server";
     27 
     28         private static byte[] mNameEof = Encoding.UTF8.GetBytes(":");
     29 
     30         private static byte[] mWrap = Encoding.UTF8.GetBytes("
    ");
     31 
     32         public HttpHeader()
     33         {
     34 
     35         }
     36 
     37         public HttpHeader(byte[] buffer)
     38         {
     39             mBuffer = buffer;
     40         }
     41 
     42         private byte[] mBuffer;
     43 
     44         private int mHeaderLength = 0;
     45 
     46         private int mStartIndex = 0;
     47 
     48         private string mRequestType;
     49 
     50         private string mUrl;
     51 
     52         private string mAction;
     53 
     54         private long? mLength;
     55 
     56         private string mHttpVersion;
     57 
     58         private string mLastPropertyName = null;
     59 
     60         private Dictionary<string, string> mProperties = new Dictionary<string, string>();
     61 
     62         public string Accept
     63         {
     64             get
     65             {
     66                 return this[HEADER_ACCEPT];
     67             }
     68             set
     69             {
     70                 this[HEADER_ACCEPT] = value;
     71             }
     72         }
     73 
     74         public string AcceptEncoding
     75         {
     76             get
     77             {
     78                 return this[HEADER_ACCEPT_ENCODING];
     79             }
     80             set
     81             {
     82                 this[HEADER_ACCEPT_ENCODING] = value;
     83             }
     84         }
     85 
     86         public string AcceptLanguage
     87         {
     88             get
     89             {
     90                 return this[HEADER_ACCEPT_LANGUAGE];
     91             }
     92             set
     93             {
     94                 this[HEADER_ACCEPT_LANGUAGE] = value;
     95             }
     96         }
     97 
     98         public string Connection
     99         {
    100             get
    101             {
    102                 return this[HEADER_CONNNECTION];
    103             }
    104             set
    105             {
    106                 this[HEADER_CONNNECTION] = value;
    107             }
    108         }
    109 
    110         public string Cookie
    111         {
    112             get
    113             {
    114                 return this[HEADER_COOKIE];
    115             }
    116             set
    117             {
    118                 this[HEADER_COOKIE] = value;
    119             }
    120         }
    121 
    122         public string Host
    123         {
    124             get
    125             {
    126                 return this[HEADER_HOST];
    127             }
    128             set
    129             {
    130                 this[HEADER_HOST] = value;
    131             }
    132         }
    133 
    134         public string UserAgent
    135         {
    136             get
    137             {
    138                 return this[HEADER_USER_AGENT];
    139             }
    140             set
    141             {
    142                 this[HEADER_USER_AGENT] = value;
    143             }
    144         }
    145 
    146         public string ContentEncoding
    147         {
    148             get
    149             {
    150                 return this[HEADER_CONTENT_ENCODING];
    151             }
    152             set
    153             {
    154                 this[HEADER_CONTENT_ENCODING] = value;
    155             }
    156         }
    157 
    158         public string ContentType
    159         {
    160             get
    161             {
    162                 return this[HEADER_CONTENT_TYPE];
    163             }
    164             set
    165             {
    166                 this[HEADER_CONTENT_TYPE] = value;
    167             }
    168         }
    169 
    170         public string Server
    171         {
    172             get
    173             {
    174                 return this[HEADER_SERVER];
    175             }
    176             set
    177             {
    178                 this[HEADER_SERVER] = value;
    179             }
    180         }
    181 
    182         public string Action
    183         {
    184             get
    185             {
    186                 return mAction;
    187             }
    188             set
    189             {
    190                 mAction = value;
    191                 string[] values = mAction.Split(' ');
    192                 if (values.Length > 0)
    193                     mRequestType = values[0];
    194                 if (values.Length > 1)
    195                     mUrl = values[1];
    196                 if (values.Length > 2)
    197                     mHttpVersion = values[2];
    198 
    199             }
    200         }
    201 
    202         public string HttpVersion
    203         {
    204             get
    205             {
    206                 return mHttpVersion;
    207             }
    208         }
    209 
    210         public string RequestType
    211         {
    212             get
    213             {
    214                 return mRequestType;
    215             }
    216         }
    217 
    218         public string Url
    219         {
    220             get
    221             {
    222                 return mUrl;
    223             }
    224         }
    225 
    226         public IDictionary<string, string> Properties
    227         {
    228             get
    229             {
    230                 return mProperties;
    231             }
    232         }
    233 
    234         public string this[string header]
    235         {
    236             get
    237             {
    238                 string value = null;
    239                 mProperties.TryGetValue(header, out value);
    240                 return value;
    241             }
    242             set
    243             {
    244                 mProperties[header] = value;
    245             }
    246 
    247         }
    248 
    249         public long Length
    250         {
    251             get
    252             {
    253                 if (mLength == null)
    254                 {
    255                     string value = this["CONTENT_LENGTH"];
    256                     if (value == null)
    257                         mLength = 0;
    258                     else
    259                         mLength = long.Parse(value);
    260                 }
    261                 return mLength.Value;
    262             }
    263             set
    264             {
    265                 mProperties["CONTENT_LENGTH"] = value.ToString();
    266             }
    267         }
    268 
    269         public bool Import(byte[] data, ref int offset, ref int count)
    270         {
    271             byte[] buffer = mBuffer;
    272             while (count > 0)
    273             {
    274                 buffer[mHeaderLength] = data[offset];
    275                 mHeaderLength++;
    276                 offset++;
    277                 count--;
    278                 if (mHeaderLength >= HEADER_BUFFER_LENGT)
    279                     throw new NetTcpException("header data too long!");
    280                 if (mBuffer[mHeaderLength - 1] == mWrap[1] && mBuffer[mHeaderLength - 2] == mWrap[0])
    281                 {
    282                     if (Action == null)
    283                     {
    284                         Action = Encoding.UTF8.GetString(buffer, mStartIndex, mHeaderLength - mStartIndex - 2);
    285                         mStartIndex = mHeaderLength;
    286                     }
    287                     else
    288                     {
    289                         if (mBuffer[mHeaderLength - 3] == mWrap[1] && mBuffer[mHeaderLength - 4] == mWrap[0])
    290                         {
    291                             if (mLastPropertyName != null)
    292                             {
    293                                 this[mLastPropertyName] = Encoding.UTF8.GetString(buffer, mStartIndex, mHeaderLength - mStartIndex - 2);
    294                             }
    295                             return true;
    296                         }
    297                         else
    298                         {
    299                             if (mLastPropertyName != null)
    300                             {
    301                                 this[mLastPropertyName] = Encoding.UTF8.GetString(buffer, mStartIndex, mHeaderLength - mStartIndex - 2);
    302                                 mStartIndex = mHeaderLength;
    303                                 mLastPropertyName = null;
    304                             }
    305                         }
    306                     }
    307                 }
    308                 else if (mBuffer[mHeaderLength - 1] == mNameEof[0] && mLastPropertyName == null)
    309                 {
    310                     mLastPropertyName = Encoding.UTF8.GetString(buffer, mStartIndex, mHeaderLength - mStartIndex - 1);
    311                     mStartIndex = mHeaderLength;
    312                 }
    313 
    314             }
    315             return false;
    316 
    317         }
    318 
    319     }
    View Code
  • 相关阅读:
    HTML常用标签(自用,可能不严谨,勿怪)
    Nginx负载均衡和反向代理设置
    Django的列表反序
    Python装饰器通用样式
    WCF、Web API、WCF REST、Web Service的区别
    C++11 标准新特性: 右值引用与转移语义
    在windows下vs使用pthread
    部分浏览器记住密码后可能会带来的问题
    SQL Server、 My SQL、PG Sql、Oracle、 Access 不同数据库sql差异
    sql中select语句的逻辑执行顺序
  • 原文地址:https://www.cnblogs.com/smark/p/3480147.html
Copyright © 2020-2023  润新知