/// <summary> /// Try add the specified bytes to the request. /// Returns the number of bytes read from the buffer. /// </summary> internal unsafe int TryAdd(byte[] buffer, int index, int count) { // is the request complete? if (_complete) { return(0); } // has the header been completely read? if (_section == Section.Body) { // does the current buffer contain the required bytes? if (_contentLength <= count) { // yes, write the remaining content length to the stream _stream.Write(buffer, index, _contentLength); count = _contentLength; _contentLength = 0; // the request is complete _complete = true; // move back to the start of the content body - this flushes the byte stream _stream.Position = _bodyIndex; // TODO : Auto decompression return(count); } // no, write the buffer _stream.Write(buffer, index, count); // decrement the remaining content length _contentLength -= count; return(count); } // persist the starting index of the stream long startIndex = _stream.WriteEnd; // write the buffer to the request _stream.Write(buffer, index, count); // return to the start of the buffer _stream.Position = startIndex; char c; // while characters can be read from the stream while ((c = _stream.ReadChar()) != Chars.Null) { // should the character be skipped? yes, continue reading if (c == Chars.CarriageReturn) { continue; } // determine the state of the request method switch (_section) { case Section.Method: // read until a space is encountered if (c == Chars.Space) { // try determine the type of web request if (_chars.EndsWith(Chars.G, Chars.E, Chars.T)) { Method = HttpMethod.Get; } else if (_chars.EndsWith(Chars.P, Chars.O, Chars.S, Chars.T)) { Method = HttpMethod.Post; } else if (_chars.EndsWith(Chars.P, Chars.U, Chars.T, Chars.Space)) { Method = HttpMethod.Put; } else if (_chars.EndsWith(Chars.D, Chars.E, Chars.L, Chars.E, Chars.T, Chars.E)) { Method = HttpMethod.Delete; } else if (_chars.EndsWith(Chars.U, Chars.P, Chars.D, Chars.A, Chars.T, Chars.E)) { Method = HttpMethod.Update; } _section = Section.Path; _chars.Reset(); } else { // add the current character to the collection _chars.Add(c); } break; case Section.Path: // yes, does the current character equal a new line? if (c == Chars.NewLine) { // yes, derive the request path and the http version index = _chars.Count; bool versionFound = false; // while the indices haven't been explored while (--index >= 0) { // does the current character equal a space? if (_chars[index] == Chars.Space) { // no, derive the request version Headers[HttpRequestHeader.HttpVersion] = new string(_chars.Array, index + 1, _chars.Count - index - 1); versionFound = true; break; } } // yes, has the version been derived? yes, derive the request path if (versionFound) { RequestPath = new string(_chars.Array, 0, index); } else { RequestPath = "/"; } _section = Section.HeaderKey; _chars.Reset(); } else { // add the current character to the collection _chars.Add(c); } break; case Section.HeaderKey: // is the current character a separator between header names and values? if (c == Chars.Colon) { // get the name of the chracter header _headerKey = new string(_chars.Array, 0, _chars.Count); // read the header value _section = Section.HeaderValue; // clear the characters _chars.Reset(); } else if (c == Chars.NewLine && _chars.Count == 0) { // yes, the request header has been completed _chars.Dispose(); // determine whether to continue the request switch (Method) { case HttpMethod.Put: case HttpMethod.Post: case HttpMethod.Update: // set the start of the body index _bodyIndex = (int)_stream.Position; // get the content length - was it able to be parsed? if (!int.TryParse(Headers[HttpRequestHeader.ContentLength], out _contentLength)) { // the content length wasn't able to be parsed Log.Warning("Invalid content length parameter."); // set the content length to '0'. _contentLength = 0; _complete = true; return(count); } // the request contains a body // should this request claim the remaining bytes written to the stream? if (_stream.WriteEnd - _stream.Position >= _contentLength) { // no, get the excess number of bytes from the request count = (int)(_stream.WriteEnd - startIndex); _contentLength = 0; _complete = true; } else { // decrement the content length _contentLength -= (int)(_stream.WriteEnd - _stream.Position); // flag the header as complete _section = Section.Body; // move the stream to the end of the written section _stream.Position = _stream.WriteEnd; } // return the number of bytes that contribute to the request return(count); default: // the request doesn't contain a body, end reading the request _complete = true; // return the number of bytes read from the buffer return((int)(_stream.Position - startIndex)); } } else { // add the current character to the collection _chars.Add(c); } break; case Section.HeaderValue: // is the current character a new line? if (c == Chars.NewLine) { // add the header to the collection Headers[_headerKey] = new string(_chars.Array, 0, _chars.Count); _chars.Reset(); _section = Section.HeaderKey; } else if (c != Chars.Space || _chars.Count != 0) { // add the current character to the collection _chars.Add(c); } break; } } // return the number of bytes read from the buffer return(count); }