HttpMessage,这里表示一个抽象的Http消息,除了包含消息头,消息体等属性外,还负责初始化消息头,解析消息体长度,确认消息是Request,Response等功能。
Code
public class HttpMessage
{
public const string PROTOCOL = "HTTP";
public const string CONTENT_LENGTH_HEADER = "Content-Length";
public MessageType MessageType = MessageType.UnKnow;
public BytesNode BodyStr;
public int ContentLength;
public Dictionary<string, string> Headers = new Dictionary<string, string>();
internal BytesNode HeaderStr;
public string Protocol;
private string startLine;
public object SyncRoot = new object(); //todo:暂时没用
public HttpMessage()
{
}
public HttpMessage(HttpMessage message)
{
startLine = message.startLine;
Headers = message.Headers;
BodyStr = message.BodyStr;
}
internal void InitHeaders(List<BytesLine> lines)
{
if (MessageType == MessageType.UnKnow)
{
#region 解析MessageType,ContentLength及填充消息头
for (int i = 0; i < lines.Count; i++)
{
BytesLine line = lines[i];
if (i == 0)
{
string tempStr = line.FullString;
MessageType = tempStr.StartsWith(PROTOCOL)
?
MessageType.Response
: MessageType.Request;
startLine = tempStr;
continue;
}
if (line.Pos1 == -1) throw new ApplicationException("header line error:"
+ line.FullString);
//todo:暂时不考虑多个同名的头
Headers[line.String1] = line.String2;
if (Headers.ContainsKey(CONTENT_LENGTH_HEADER))
ContentLength = int.Parse(Headers[CONTENT_LENGTH_HEADER].Trim());
}
#endregion
}
}
internal HttpRequest AsRequest()
{
if (MessageType != MessageType.Request)
throw new ApplicationException("this message is not request");
HttpRequest request = new HttpRequest(this);
string[] tempArr = startLine.Split(' ');
if (tempArr.Length != 3) throw new ApplicationException("start line error:" + startLine);
request.Method = tempArr[0].Trim();
request.Uri = tempArr[1];
request.Protocol = tempArr[2];
if (!request.Protocol.StartsWith(PROTOCOL))
throw new ApplicationException("Protocol error:" + request.Protocol);
return request;
}
internal HttpResponse AsResponse()
{
if (MessageType != MessageType.Response)
throw new ApplicationException("this message is not response");
HttpResponse response = new HttpResponse(this);
string[] tempArr = startLine.Split(' ');
if (tempArr.Length != 3) throw new ApplicationException("start line error:" + startLine);
response.Protocol = tempArr[0];
if (!response.Protocol.StartsWith(PROTOCOL))
throw new ApplicationException("Protocol error:" + response.Protocol);
response.StatusCode = int.Parse(tempArr[1].Trim()); //todo:可能有200.1这样的应答
response.Desc = tempArr[2]; //todo:不考虑应答描述包含空格的情况
return response;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append(startLine);
sb.AppendLine();
foreach (KeyValuePair<string,string> pair in Headers)
{
sb.AppendFormat("{0}:{1}", pair.Key, pair.Value);
sb.AppendLine();
}
sb.AppendLine();
sb.AppendLine();
if (BodyStr != null) sb.Append(Encoding.UTF8.GetString(BodyStr.Bytes));
return sb.ToString();
}
}
HttpParser,主要的协议解析类,入口是Parse方法,可以把每次socket收到的字节数组去调用该方法,然后订阅RequestReceived,ResponseReceived,Error等方法。具体的算法看代码吧,说不清楚。
Code
namespace WawaSoft.HttpStack {
class NodeIndex {
public NodeIndex(BytesNode node, int index) {
_node = node;
_index = index;
}
private BytesNode _node;
private int _index;
public BytesNode Node {
get { return _node; }
}
public int Index {
get { return _index; }
}
}
public class HttpParser {
private HttpMessage _currentMessage;
private object _syncRoot = new object();
private BytesNode _headerNode;
private BytesNode _tailNode;
private bool _waitParseBody = false;
public void Parse(byte[] tempBuffer) {
lock (_syncRoot) {
try {
SetNodes(tempBuffer);
if (_waitParseBody)
ReadBody(_currentMessage);
else {
ReadHeaders();
}
fireEvent();
if(!_waitParseBody)ReadHeaders();
}
catch (Exception ex) {
Action<Exception> temp = Error;
if (temp != null)
temp(ex);
}
}
}
private void ReadHeaders() {
NodeIndex headerTokenIndex = ContainsHeaderEndToken(_headerNode);
while (headerTokenIndex != null) {
_currentMessage = new HttpMessage();
_currentMessage.HeaderStr = _headerNode;
_headerNode = headerTokenIndex.Node.CutNew(null,
headerTokenIndex.Index+1,
headerTokenIndex.Node.Value.Length);
headerTokenIndex.Node.Cut(headerTokenIndex.Node.Start, headerTokenIndex.Index);
headerTokenIndex.Node.Next = null;
_currentMessage.InitHeaders(_lines);
_lines.Clear();
ReadBody(_currentMessage);
if (_waitParseBody)
break;
else
fireEvent();
headerTokenIndex = ContainsHeaderEndToken(_headerNode);
}
}
private void fireEvent() {
if (!_waitParseBody) {
if (_currentMessage == null) return;
if (_currentMessage.MessageType == MessageType.Request) {
HttpRequest request = _currentMessage.AsRequest();
Action<HttpRequest> temp = RequestReceived;
if (temp != null)
temp(request);
}
else {
HttpResponse response = _currentMessage.AsResponse();
Action<HttpResponse> temp = ResponseReceived;
if (temp != null)
temp(response);
}
_currentMessage = null;
}
}
private void ReadBody(HttpMessage message) {
if (message.ContentLength == 0) {
_waitParseBody = false;
return;
}
int i = 0, pos = 0;
bool first = true;
BytesNode tempNode = _headerNode;
BytesNode previousNode = tempNode;
while (tempNode != null) {
i = i + tempNode.Length;
if (i >= message.ContentLength) {
if (first)
pos = tempNode.Start + message.ContentLength;
else
pos = tempNode.Length - (i - message.ContentLength);
break;
}
first = false;
previousNode = tempNode;
tempNode = tempNode.Next;
}
if (i >= message.ContentLength) {
tempNode.Cut(tempNode.Start, pos );
message.BodyStr = _headerNode;
_headerNode = tempNode.CutNew(null, pos, tempNode.Value.Length);
tempNode.Next = null;
_waitParseBody = false;
}
else
_waitParseBody = true;
}
private void SetNodes(byte[] tempBuffer) {
BytesNode tempNode = new BytesNode(tempBuffer);
if (_headerNode == null)
_tailNode = _headerNode = tempNode;
else if (_headerNode.Next == null) {
_headerNode.Next = tempNode;
_tailNode = tempNode;
}
else if (_tailNode != null) {
_tailNode.Next = tempNode;
_tailNode = tempNode;
}
}
BytesLine _line = new BytesLine(Encoding.ASCII);
List<BytesLine> _lines = new List<BytesLine>();
internal NodeIndex ContainsHeaderEndToken(BytesNode node) {
if (_waitParseBody)
return null;
_lines.Clear();
bool secondBackslashN = false;
bool firstBackslashR = false;
byte expectNextChar = 0x0d;
BytesNode previousNode = null;
int k = -1;
while (node != null) {
int end = node.End;
byte[] nodeBytes = node.Value;
for (int i = node.Start; i < end; i++) {
k++;
byte tempByte = nodeBytes[i];
_line.Bytes[k] = tempByte;
_line.Size = k;
if(tempByte == 0x3a)
_line.Pos1 = k;
if ((secondBackslashN || firstBackslashR) && tempByte != expectNextChar) {
if (firstBackslashR && secondBackslashN) {
_lines.Add(_line);
_line = new BytesLine();
_line.Bytes[0] = tempByte;
k = 0;
}
firstBackslashR = false;
secondBackslashN = false;
}
if (tempByte != expectNextChar) {
continue;
}
if (expectNextChar == 0x0d) {
firstBackslashR = true;
expectNextChar = 0x0a;
continue;
}
if (expectNextChar == 0x0a) {
if (!secondBackslashN) {
expectNextChar = 0x0d;
secondBackslashN = true;
continue;
}
_line.Size--;
_lines.Add(_line);
_line = new BytesLine();
_line.Bytes[0] = tempByte;
k = 0;
return new NodeIndex(node, i);
}
}
previousNode = node;
node = node.Next;
}
return null;
}
public event Action<HttpRequest> RequestReceived;
public event Action<HttpResponse> ResponseReceived;
public event Action<Exception> Error;
}
}
代码下载:http://files.cnblogs.com/onlytiancai/HttpStack.zip