Esempio n. 1
0
        internal bool Read(Stream stream, IPEndPoint endPoint)
        {
            byte[] buffer = new byte[1024];
            RequestParserState state = RequestParserState.ReadMethod;
            string key = "";
            string value = "";
            MemoryStream ms = null;

            DateTime begin = DateTime.Now;
            DateTime lastByteReceived = begin;

            if (endPoint != null)
                UserHostAddress = endPoint.Address.ToString();

            while (true)
            {
                if (state == RequestParserState.ReadDone)
                    return true;

                int bytesRead = 0;
                int idx = 0;

#if(MF)
                // set all bytes to null byte (strings are ending with null byte in MF)
                Array.Clear(buffer, 0, buffer.Length);
#endif

                try
                {
                    bytesRead = stream.Read(buffer, 0, buffer.Length);

                    if (bytesRead > 0)
                        lastByteReceived = DateTime.Now;
                }
                catch (IOException)
                {
                    break;
                }
                catch (Exception)
                {
                    DateTime nd = DateTime.Now;
#if(MF)
                    if((nd.Ticks - lastByteReceived.Ticks) / TimeSpan.TicksPerMillisecond > MAX_REQUEST_SLIDING_TIMEOUT)
                        break;
#else
                    if ((nd - lastByteReceived).TotalMilliseconds > MAX_REQUEST_SLIDING_TIMEOUT)
                        break;
#endif

                    if (HttpMethod == "POST" && (_body == null || _body.Length < ContentLength))
                        continue;

#if(MF)
                    if((nd.Ticks - begin.Ticks) / TimeSpan.TicksPerMillisecond < MAX_REQUEST_TIMEOUT)
                        continue;
#else
                    if ((nd - begin).TotalMilliseconds < MAX_REQUEST_TIMEOUT)
                        continue;
#endif

                    break;
                }

                if (bytesRead == 0)     // should never happen
                    break;

                totalBytes += bytesRead;

#if(FILELOG && !MF && !WindowsCE)
                File.AppendAllText("loghttp-" + socket.RemoteEndPoint.ToString().Replace(":", "-") + ".txt", Encoding.UTF8.GetString(buffer, 0, bytesRead) + "\r\n");
#endif

                if (totalBytes <= idx)
                    continue;

                do
                {
                    switch (state)
                    {
                        case RequestParserState.ReadMethod:
                            if (buffer[idx] != ' ')
                                HttpMethod += (char)buffer[idx++];
                            else
                            {
                                // TODO: add a allowed methods list

                                //if (HttpMethod != "POST" && HttpMethod != "GET" && HttpMethod != "OPTIONS")
                                //    throw new HttpException(HttpStatusCode.MethodNotAllowed);

                                idx++;
                                state = RequestParserState.ReadUrl;
                            }
                            break;

                        case RequestParserState.ReadUrl:
                            if (buffer[idx] == '?')
                            {
                                idx++;
                                key = "";
                                _params = new NameValueCollection();
                                state = RequestParserState.ReadParamKey;
                            }
                            else if (buffer[idx] != ' ')
                            {
                                RawUrl += (char)buffer[idx++];
                            }
                            else
                            {
                                idx++;
                                RawUrl = HttpServerUtility.UrlDecode(RawUrl);
                                state = RequestParserState.ReadVersion;
                            }
                            break;

                        case RequestParserState.ReadParamKey:
                            if (buffer[idx] == '=')
                            {
                                idx++;
                                value = "";
                                state = RequestParserState.ReadParamValue;
                            }
                            else if (buffer[idx] == ' ')
                            {
                                idx++;
                                RawUrl = HttpServerUtility.UrlDecode(RawUrl);
                                state = RequestParserState.ReadVersion;
                            }
                            else
                            {
                                key += (char)buffer[idx++];
                            }
                            break;

                        case RequestParserState.ReadParamValue:
                            if (buffer[idx] == '&')
                            {
                                idx++;
                                key = HttpServerUtility.UrlDecode(key);
                                value = HttpServerUtility.UrlDecode(value);

                                Params[key] = (Params[key] != null ? Params[key] + ", " + value : value);

                                key = "";
                                value = "";

                                state = RequestParserState.ReadParamKey;
                            }
                            else if (buffer[idx] == ' ')
                            {
                                idx++;
                                key = HttpServerUtility.UrlDecode(key);
                                value = HttpServerUtility.UrlDecode(value);

                                Params[key] = (Params[key] != null ? Params[key] + ", " + value : value);

                                RawUrl = HttpServerUtility.UrlDecode(RawUrl);

                                state = RequestParserState.ReadVersion;
                            }
                            else
                            {
                                value += (char)buffer[idx++];
                            }
                            break;

                        case RequestParserState.ReadVersion:
                            if (buffer[idx] == '\r')
                                idx++;
                            else if (buffer[idx] != '\n')
                                HttpVersion += (char)buffer[idx++];
                            else
                            {
                                if (HttpVersion != "HTTP/1.1" && HttpVersion != "HTTP/1.0")
                                    throw new HttpException(HttpStatusCode.HttpVersionNotSupported);

                                idx++;
                                key = "";
                                Headers = new NameValueCollection();
                                state = RequestParserState.ReadHeaderKey;
                            }
                            break;

                        case RequestParserState.ReadHeaderKey:
                            if (buffer[idx] == '\r')
                                idx++;
                            else if (buffer[idx] == '\n')
                            {
                                idx++;
                                if (HttpMethod == "POST")
                                    state = RequestParserState.ReadBody;
                                else
                                {
                                    state = RequestParserState.ReadDone;    // well, we don't really need this
                                    return true;
                                }
                            }
                            else if (buffer[idx] == ':')
                                idx++;
                            else if (buffer[idx] != ' ')
                                key += (char)buffer[idx++];
                            else
                            {
                                idx++;
                                value = "";
                                state = RequestParserState.ReadHeaderValue;
                            }
                            break;

                        case RequestParserState.ReadHeaderValue:
                            if (buffer[idx] == '\r')
                                idx++;
                            else if (buffer[idx] != '\n')
                                value += (char)buffer[idx++];
                            else
                            {
                                idx++;
                                Headers.Add(key, value);
                                key = "";
                                state = RequestParserState.ReadHeaderKey;
                            }
                            break;

                        case RequestParserState.ReadBody:

                            if (ContentLength > MAX_CONTENT_LENGTH)
                            {
                                // TODO: how can I stop the client to cancel http request
                                //throw new HttpException(HttpStatusCode.RequestEntitiyTooLarge);
                            }

                            if (ms == null)
                                ms = new MemoryStream();

                            ms.Write(buffer, idx, bytesRead - idx);
                            idx = bytesRead;
                            
                            if (ms.Length >= ContentLength)
                            {
                                _body = ms.ToArray();

                                // if using a <form/> tag with POST check if it is urlencoded or multipart boundary

                                if (ContentType.IndexOf("application/x-www-form-urlencoded") != -1)
                                {
                                    _form = new NameValueCollection();
                                    key = "";
                                    value = null;

                                    for (int i = 0; i < _body.Length; i++)
                                    {
                                        if (_body[i] == '=')
                                            value = "";
                                        else if (_body[i] == '&')
                                        {
                                            _form.Add(key, value != null ? HttpServerUtility.UrlDecode(value) : "");
                                            key = "";
                                            value = null;
                                        }
                                        else if (value == null)
                                            key += (char)_body[i];
                                        else if (value != null)
                                            value += (char)_body[i];
                                    }

                                    if (key != null && key.Length > 0)
                                    {
                                        _form.Add(key, value != null ? HttpServerUtility.UrlDecode(value) : "");
                                    }
                                }
                                else if (ContentType != null && ContentType.Length > "multipart/form-data; boundary=".Length && ContentType.Substring(0, "multipart/form-data; boundary=".Length) == "multipart/form-data; boundary=")
                                {
                                    string boundary = ContentType.Substring("multipart/form-data; boundary=".Length);

                                    _mime = new MimeContentCollection();

                                    MimeParser mp = new MimeParser(_body, boundary);

                                    MimeContent mime = mp.GetNextContent();
                                    while (mime != null)
                                    {
                                        _mime.Add(mime.Name, mime);

                                        if (mime.Headers["Content-Disposition"] != null && mime.Headers["Content-Disposition"].IndexOf("form-data") >= 0)
                                        {
                                            if (_form == null)
                                                _form = new NameValueCollection();

                                            _form.Add(mime.Name, (mime.Content != null && mime.Content.Length > 0 ? new string(Encoding.UTF8.GetChars(mime.Content)) : ""));
                                        }

                                        mime = mp.GetNextContent();
                                    }
                                }
                                state = RequestParserState.ReadDone;        // well, we don't really need this
                                return true;
                            }
                            break;

                        case RequestParserState.ReadDone:                   // well, we don't really need this
                            return true;

                        default:
                            //idx++;
                            break;

                    }
                }
                while (idx < bytesRead);
            }

            return false;
        }
Esempio n. 2
0
        /// <summary>
        /// Parse a message
        /// </summary>
        /// <param name="buffer">bytes to parse.</param>
        /// <param name="offset">where in buffer that parsing should start</param>
        /// <param name="count">number of bytes to parse, starting on <paramref name="offset"/>.</param>
        /// <returns>offset (where to start parsing next).</returns>
        /// <exception cref="BadRequestException"><c>BadRequestException</c>.</exception>
        public int Parse(byte[] buffer, int offset, int count)
        {
            // add body bytes
            if (CurrentState == RequestParserState.Body)
            {
				// copy all remaining bytes to the beginning of the buffer.
				//Buffer.BlockCopy(buffer, offset + bytesUsed, buffer, 0, count - bytesUsed);


                return AddToBody(buffer, 0, count);
            }

#if DEBUG
            string temp = Encoding.ASCII.GetString(buffer, offset, count);
            _log.Write(this, LogPrio.Trace, "\r\n\r\n HTTP MESSAGE: " + temp + "\r\n");
#endif

            int currentLine = 1;
            int startPos = -1;

            // set start pos since this is from an partial request
            if (CurrentState == RequestParserState.HeaderValue)
                startPos = 0;

            int endOfBufferPos = offset + count;

            //<summary>
            // Handled bytes are used to keep track of the number of bytes processed.
            // We do this since we can handle partial requests (to be able to check headers and abort
            // invalid requests directly without having to process the whole header / body).
            // </summary>
            int handledBytes = 0;


            for (int currentPos = offset; currentPos < endOfBufferPos; ++currentPos)
            {
                var ch = (char) buffer[currentPos];
                char nextCh = endOfBufferPos > currentPos + 1 ? (char) buffer[currentPos + 1] : char.MinValue;

                if (ch == '\r')
                    ++currentLine;

                switch (CurrentState)
                {
                    case RequestParserState.FirstLine:
                        if (currentPos > 4196)
                        {
                            _log.Write(this, LogPrio.Warning, "HTTP Request is too large.");
                            throw new BadRequestException("Too large request line.");
                        }
                        if (char.IsLetterOrDigit(ch) && startPos == -1)
                            startPos = currentPos;
                        if (startPos == -1 && (ch != '\r' || nextCh != '\n'))
                        {
                            _log.Write(this, LogPrio.Warning, "Request line is not found.");
                            throw new BadRequestException("Invalid request line.");
                        }
                        if (startPos != -1 && (ch == '\r' || ch == '\n'))
                        {
                        	int size = GetLineBreakSize(buffer, currentPos);
                            OnFirstLine(Encoding.UTF8.GetString(buffer, startPos, currentPos - startPos));
                            CurrentState = CurrentState + 1;
                        	currentPos += size - 1;
                            handledBytes = currentPos + size - 1;
                            startPos = -1;
                        }
                        break;
                    case RequestParserState.HeaderName:
                        if (ch == '\r' || ch == '\n')
                        {
                        	currentPos += GetLineBreakSize(buffer, currentPos);
                            if (_bodyBytesLeft == 0)
                            {
                                CurrentState = RequestParserState.FirstLine;
                                _log.Write(this, LogPrio.Trace, "Request parsed successfully (no content).");
                                OnRequestCompleted();
                                Clear();
                                return currentPos;
                            }

                            CurrentState = RequestParserState.Body;
                            if (currentPos + 1 < endOfBufferPos)
                            {
                                _log.Write(this, LogPrio.Trace, "Adding bytes to the body");
                                return AddToBody(buffer, currentPos, endOfBufferPos - currentPos);
                            }

                            return currentPos;
                        }
                        if (char.IsWhiteSpace(ch) || ch == ':')
                        {
                            if (startPos == -1)
                            {
                                _log.Write(this, LogPrio.Warning,
                                           "Expected header name, got colon on line " + currentLine);
                                throw new BadRequestException("Expected header name, got colon on line " + currentLine);
                            }
                            _curHeaderName = Encoding.UTF8.GetString(buffer, startPos, currentPos - startPos);
                            handledBytes = currentPos + 1;
                            startPos = -1;
                            CurrentState = CurrentState + 1;
                            if (ch == ':')
                                CurrentState = CurrentState + 1;
                        }
                        else if (startPos == -1)
                            startPos = currentPos;
                        else if (!char.IsLetterOrDigit(ch) && ch != '-' && ch != '_' && ch != '.')
                        {
                            _log.Write(this, LogPrio.Warning, "Invalid character in header name on line " + currentLine);
                            throw new BadRequestException("Invalid character in header name on line " + currentLine);
                        }
                        if (startPos != -1 && currentPos - startPos > 200)
                        {
                            _log.Write(this, LogPrio.Warning, "Invalid header name on line " + currentLine);
                            throw new BadRequestException("Invalid header name on line " + currentLine);
                        }
                        break;
                    case RequestParserState.AfterName:
                        if (ch == ':')
                        {
                            handledBytes = currentPos + 1;
                            CurrentState = CurrentState + 1;
                        }
                        break;
					case RequestParserState.Between:
                		{
                			if (ch == ' ' || ch == '\t')
                				continue;
                			int newLineSize = GetLineBreakSize(buffer, currentPos);
							if (newLineSize > 0 && currentPos + newLineSize < endOfBufferPos &&
                			    char.IsWhiteSpace((char) buffer[currentPos + newLineSize]))
                			{
                				++currentPos;
                				continue;
                			}
                			startPos = currentPos;
                			CurrentState = CurrentState + 1;
                			handledBytes = currentPos;
                			continue;
                		}
                	case RequestParserState.HeaderValue:
                        {
                            if (ch != '\r' && ch != '\n')
                                continue;
                        	int newLineSize = GetLineBreakSize(buffer, currentPos);
                            if (startPos == -1)
                                continue; // allow new lines before start of value

                            if (_curHeaderName == string.Empty)
                                throw new BadRequestException("Missing header on line " + currentLine);
                            if (startPos == -1)
                            {
                                _log.Write(this, LogPrio.Warning, "Missing header value for '" + _curHeaderName);
                                throw new BadRequestException("Missing header value for '" + _curHeaderName);
                            }
                            if (currentPos - startPos > 4096)
                            {
                                _log.Write(this, LogPrio.Warning, "Too large header value on line " + currentLine);
                                throw new BadRequestException("Too large header value on line " + currentLine);
                            }

                            // Header fields can be extended over multiple lines by preceding each extra line with at
                            // least one SP or HT.
                            if (endOfBufferPos > currentPos + newLineSize 
								&& (buffer[currentPos + newLineSize] == ' ' || buffer[currentPos + newLineSize] == '\t'))
                            {
                                if (startPos != -1)
                                    _curHeaderValue = Encoding.UTF8.GetString(buffer, startPos, currentPos - startPos);

                                _log.Write(this, LogPrio.Trace, "Header value is on multiple lines.");
                                CurrentState = RequestParserState.Between;
                                startPos = -1;
                                currentPos += newLineSize - 1;
                                handledBytes = currentPos + newLineSize - 1;
                                continue;
                            }

                            _curHeaderValue += Encoding.UTF8.GetString(buffer, startPos, currentPos - startPos);
                            _log.Write(this, LogPrio.Trace, "Header [" + _curHeaderName + ": " + _curHeaderValue + "]");
                            OnHeader(_curHeaderName, _curHeaderValue);

                            startPos = -1;
                            CurrentState = RequestParserState.HeaderName;
                            _curHeaderValue = string.Empty;
                            _curHeaderName = string.Empty;
                            ++currentPos;
                            handledBytes = currentPos + 1;

                            // Check if we got a colon so we can cut header name, or crlf for end of header.
                            bool canContinue = false;
                            for (int j = currentPos; j < endOfBufferPos; ++j)
                            {
                                if (buffer[j] != ':' && buffer[j] != '\r' && buffer[j] != '\n') continue;
                                canContinue = true;
                                break;
                            }
                            if (!canContinue)
                            {
                                _log.Write(this, LogPrio.Trace, "Cant continue, no colon.");
                                return currentPos + 1;
                            }
                        }
                        break;
                }
            }

            return handledBytes;
        }
        /// <summary>
        /// Reades and creates a http request object from the given stream.
        /// </summary>
        /// <param name="inputStream">the stream to read the http request from</param>
        /// <returns></returns>
        public static async Task <HttpRequest> ParseFromStream(Stream inputStream)
        {
            Debug.Assert(inputStream != null);

            byte[]             myReadBuffer = new byte[BufferSize];
            RequestParserState parserState  = RequestParserState.Method;
            HttpRequest        httpRequest  = new HttpRequest();

            String myCompleteMessage = "";
            int    numberOfBytesRead = 0;

            string hValue = "";
            string hKey   = "";


            // binary data buffer index
            int bfndx = 0;

            // Incoming message may be larger than the buffer size.
            do
            {
                numberOfBytesRead = await inputStream.ReadAsync(myReadBuffer, 0, myReadBuffer.Length);

                myCompleteMessage =
                    String.Concat(myCompleteMessage, Encoding.UTF8.GetString(myReadBuffer.ToArray(), 0, numberOfBytesRead));

                // read buffer index
                int ndx = 0;
                do
                {
                    switch (parserState)
                    {
                    case RequestParserState.Method:
                        if (myReadBuffer[ndx] != ' ')
                        {
                            httpRequest.Method += (char)myReadBuffer[ndx++];
                        }
                        else
                        {
                            ndx++;
                            parserState = RequestParserState.Url;
                        }
                        break;

                    case RequestParserState.Url:
                        if (myReadBuffer[ndx] == '?')
                        {
                            ndx++;
                            hKey        = "";
                            parserState = RequestParserState.Urlparm;
                        }
                        else if (myReadBuffer[ndx] != ' ')
                        {
                            httpRequest.Url += (char)myReadBuffer[ndx++];
                        }
                        else
                        {
                            ndx++;
                            httpRequest.Url = HttpUtility.UrlDecode(httpRequest.Url);
                            parserState     = RequestParserState.Version;
                        }
                        break;

                    case RequestParserState.Urlparm:
                        if (myReadBuffer[ndx] == '=')
                        {
                            ndx++;
                            hValue      = "";
                            parserState = RequestParserState.Urlvalue;
                        }
                        else if (myReadBuffer[ndx] == ' ')
                        {
                            ndx++;

                            httpRequest.Url = HttpUtility.UrlDecode(httpRequest.Url);
                            parserState     = RequestParserState.Version;
                        }
                        else
                        {
                            hKey += (char)myReadBuffer[ndx++];
                        }
                        break;

                    case RequestParserState.Urlvalue:
                        if (myReadBuffer[ndx] == '&')
                        {
                            ndx++;
                            hKey   = HttpUtility.UrlDecode(hKey);
                            hValue = HttpUtility.UrlDecode(hValue);
                            httpRequest.Args[hKey] = httpRequest.Args[hKey] != null ? httpRequest.Args[hKey] + ", " + hValue : hValue;
                            hKey        = "";
                            parserState = RequestParserState.Urlparm;
                        }
                        else if (myReadBuffer[ndx] == ' ')
                        {
                            ndx++;
                            hKey   = HttpUtility.UrlDecode(hKey);
                            hValue = HttpUtility.UrlDecode(hValue);
                            httpRequest.Args[hKey] = httpRequest.Args[hKey] != null ? httpRequest.Args[hKey] + ", " + hValue : hValue;

                            httpRequest.Url = HttpUtility.UrlDecode(httpRequest.Url);
                            parserState     = RequestParserState.Version;
                        }
                        else
                        {
                            hValue += (char)myReadBuffer[ndx++];
                        }
                        break;

                    case RequestParserState.Version:
                        if (myReadBuffer[ndx] == '\r')
                        {
                            ndx++;
                        }
                        else if (myReadBuffer[ndx] != '\n')
                        {
                            httpRequest.Version += (char)myReadBuffer[ndx++];
                        }
                        else
                        {
                            ndx++;
                            hKey        = "";
                            parserState = RequestParserState.Headerkey;
                        }
                        break;

                    case RequestParserState.Headerkey:
                        if (myReadBuffer[ndx] == '\r')
                        {
                            ndx++;
                        }
                        else if (myReadBuffer[ndx] == '\n')
                        {
                            ndx++;
                            if (httpRequest.Headers["Content-Length"] != null)
                            {
                                httpRequest.BodySize = Convert.ToInt32(httpRequest.Headers["Content-Length"]);
                                httpRequest.BodyData = new byte[httpRequest.BodySize];
                                parserState          = RequestParserState.Body;
                            }
                            else
                            {
                                parserState = RequestParserState.Ok;
                            }
                        }
                        else if (myReadBuffer[ndx] == ':')
                        {
                            ndx++;
                        }
                        else if (myReadBuffer[ndx] != ' ')
                        {
                            hKey += (char)myReadBuffer[ndx++];
                        }
                        else
                        {
                            ndx++;
                            hValue      = "";
                            parserState = RequestParserState.Headervalue;
                        }
                        break;

                    case RequestParserState.Headervalue:
                        if (myReadBuffer[ndx] == '\r')
                        {
                            ndx++;
                        }
                        else if (myReadBuffer[ndx] != '\n')
                        {
                            hValue += (char)myReadBuffer[ndx++];
                        }
                        else
                        {
                            ndx++;
                            httpRequest.Headers.Add(hKey, hValue);
                            hKey        = "";
                            parserState = RequestParserState.Headerkey;
                        }
                        break;

                    case RequestParserState.Body:
                        // Append to request BodyData
                        Array.Copy(myReadBuffer, ndx, httpRequest.BodyData, bfndx, numberOfBytesRead - ndx);
                        bfndx += numberOfBytesRead - ndx;
                        ndx    = numberOfBytesRead;
                        if (httpRequest.BodySize <= bfndx)
                        {
                            parserState = RequestParserState.Ok;
                        }
                        break;
                        //default:
                        //	ndx++;
                        //	break;
                    }
                }while (ndx < numberOfBytesRead);
            }while (numberOfBytesRead == BufferSize);

            if (httpRequest.BodyData == null)
            {
                httpRequest.BodyData = new byte[0];
            }

            if (parserState == RequestParserState.Ok)
            {
                httpRequest.IsValid = true;
            }

            return(httpRequest);
        }
Esempio n. 4
0
        public ParsedHttpRequest(NetworkStream stream, MessageLogger messageLogger, IDataLogger dataLogger)
        {
            int bytesRead;

            byte[] readBuffer = new byte[2048];

            parserState = RequestParserState.Method;

            StringBuilder stringBuilder = new StringBuilder(InitialCapacityForMethod);
            String        hValue        = String.Empty;
            String        hKey          = String.Empty;
            String        temp;
            Int32         bodyIndex = 0;

            do
            {
                switch (parserState)
                {
                case RequestParserState.Method:
                    messageLogger.Log("Reading Request");
                    break;

                case RequestParserState.Body:
                    messageLogger.Log("Waiting for {0} bytes for the body", body.Length - bodyIndex);
                    break;

                default:
                    messageLogger.Log("Waiting for more data (ParserState={0})...", parserState);
                    break;
                }
                bytesRead = stream.Read(readBuffer, 0, readBuffer.Length);
                if (bytesRead <= 0)
                {
                    break;
                }

                if (dataLogger != null)
                {
                    dataLogger.LogData(readBuffer, 0, bytesRead);
                }

                int offset = 0;
                do
                {
                    switch (parserState)
                    {
                    case RequestParserState.Method:
                        if (readBuffer[offset] != ' ')
                        {
                            stringBuilder.Append((char)readBuffer[offset]);
                        }
                        else
                        {
                            method        = stringBuilder.ToString();
                            stringBuilder = new StringBuilder(InitialCapacityForUrl);
                            parserState   = RequestParserState.Url;
                        }
                        offset++;
                        break;

                    case RequestParserState.Url:
                        if (readBuffer[offset] == '?')
                        {
                            url = Http.UrlDecode(stringBuilder.ToString());

                            hKey         = String.Empty;
                            urlArguments = new Dictionary <String, String>();
                            parserState  = RequestParserState.UrlParam;
                        }
                        else if (readBuffer[offset] != ' ')
                        {
                            stringBuilder.Append((char)readBuffer[offset]);
                        }
                        else
                        {
                            url         = Http.UrlDecode(stringBuilder.ToString());
                            parserState = RequestParserState.Version;
                        }
                        offset++;
                        break;

                    case RequestParserState.UrlParam:
                        if (readBuffer[offset] == '=')
                        {
                            offset++;
                            hValue      = String.Empty;
                            parserState = RequestParserState.UrlParamValue;
                        }
                        else if (readBuffer[offset] == ' ')
                        {
                            offset++;

                            url         = Http.UrlDecode(url);
                            parserState = RequestParserState.Version;
                        }
                        else
                        {
                            hKey += (char)readBuffer[offset++];
                        }
                        break;

                    case RequestParserState.UrlParamValue:
                        if (readBuffer[offset] == '&')
                        {
                            offset++;
                            hKey   = Http.UrlDecode(hKey);
                            hValue = Http.UrlDecode(hValue);
                            if (urlArguments.TryGetValue(hKey, out temp))
                            {
                                urlArguments[hKey] = String.Format("{0},{1}", temp, hValue);
                            }
                            else
                            {
                                urlArguments[hKey] = hValue;
                            }

                            hKey        = String.Empty;
                            parserState = RequestParserState.UrlParam;
                        }
                        else if (readBuffer[offset] == ' ')
                        {
                            offset++;
                            hKey   = Http.UrlDecode(hKey);
                            hValue = Http.UrlDecode(hValue);
                            if (urlArguments.TryGetValue(hKey, out temp))
                            {
                                urlArguments[hKey] = String.Format("{0},{1}", temp, hValue);
                            }
                            else
                            {
                                urlArguments[hKey] = hValue;
                            }

                            parserState = RequestParserState.Version;
                        }
                        else
                        {
                            hValue += (char)readBuffer[offset++];
                        }
                        break;

                    case RequestParserState.Version:
                        if (readBuffer[offset] == '\r')
                        {
                            offset++;
                        }

                        if (readBuffer[offset] != '\n')
                        {
                            httpVersion += (char)readBuffer[offset++];
                        }
                        else
                        {
                            offset++;
                            hKey        = String.Empty;
                            headers     = new Dictionary <String, String>();
                            parserState = RequestParserState.HeaderKey;
                        }
                        break;

                    case RequestParserState.HeaderKey:
                        if (readBuffer[offset] == '\r')
                        {
                            offset++;
                        }

                        if (readBuffer[offset] == '\n')
                        {
                            offset++;
                            if (headers.TryGetValue("Content-Length", out temp))
                            {
                                body        = new byte[Int32.Parse(temp)];
                                parserState = RequestParserState.Body;
                            }
                            else
                            {
                                parserState = RequestParserState.Done;
                            }
                        }
                        else if (readBuffer[offset] == ':')
                        {
                            offset++;
                        }
                        else if (readBuffer[offset] != ' ')
                        {
                            hKey += (char)readBuffer[offset++];
                        }
                        else
                        {
                            offset++;
                            hValue      = "";
                            parserState = RequestParserState.HeaderValue;
                        }
                        break;

                    case RequestParserState.HeaderValue:
                        if (readBuffer[offset] == '\r')
                        {
                            offset++;
                        }

                        if (readBuffer[offset] == '\n')
                        {
                            offset++;
                            headers.Add(hKey, hValue);
                            hKey        = String.Empty;
                            parserState = RequestParserState.HeaderKey;
                        }
                        else
                        {
                            hValue += (char)readBuffer[offset++];
                        }
                        break;

                    case RequestParserState.Body:
                        // Append to request BodyData
                        Array.Copy(readBuffer, offset, body, bodyIndex, bytesRead - offset);
                        bodyIndex += bytesRead - offset;
                        offset     = bytesRead;
                        if (bodyIndex >= ((body == null) ? 0 : body.Length))
                        {
                            parserState = RequestParserState.Done;
                        }
                        break;

                    default:
                        throw new InvalidOperationException(String.Format("Unrecognized Parser State '{0}' ({1})", parserState, (int)parserState));
                    }
                }while (offset < bytesRead);
            } while ((parserState != RequestParserState.Done) && stream.DataAvailable);
        }
Esempio n. 5
0
        protected override int ProcessData(byte[] data, bool writeExcess)
        {
            ThrowIfEnded();
            bool run = true; int i = 0;

            for (; i < data.Length && run; i++)
            {
                char c = (char)data[i];
                switch (State)
                {
                case RequestParserState.Method:
                    if (c != WHITESPACE)
                    {
                        StringQueue.Append(c);
                    }
                    else
                    {
                        Incoming.Method = StringQueue.Next();
                        StringQueue.New();
                        State = RequestParserState.Query;
                    }
                    break;

                case RequestParserState.Query:
                    if (c != WHITESPACE)
                    {
                        StringQueue.Append(c);
                    }
                    else
                    {
                        if (!Query.TryParse(StringQueue.Next(), out Query result))
                        {
                            End();
                            return(-1);
                        }
                        Incoming.Query = result;
                        StringQueue.New();
                        State = RequestParserState.Version;
                    }
                    break;

                case RequestParserState.Version:
                    if (c != CR)
                    {
                        StringQueue.Append(c);
                    }
                    else
                    {
                        if (!HttpVersion.TryParse(StringQueue.Next(), out HttpVersion result))
                        {
                            End();
                            return(-1);
                        }
                        Incoming.Version = result;
                        StringQueue.New();
                        State = RequestParserState.FirstLf;
                    }
                    break;

                case RequestParserState.FirstLf:
                    if (c != LF)
                    {
                        End(); return(-1);
                    }
                    State = RequestParserState.HeaderName;
                    break;

                case RequestParserState.HeaderName:
                    if (c == CR)
                    {
                        State = RequestParserState.Lf;
                    }
                    else if (c != COLON)
                    {
                        StringQueue.Append(c);
                    }
                    else
                    {
                        StringQueue.New();
                        State = RequestParserState.HeaderValue;
                    }
                    break;

                case RequestParserState.HeaderValue:
                    if (c != CR)
                    {
                        StringQueue.Append(c);
                    }
                    else
                    {
                        Incoming.Headers.Set(StringQueue.Next(), StringQueue.Next().Trim());
                        State = RequestParserState.HeaderLf;
                    }
                    break;

                case RequestParserState.HeaderLf:
                    if (c != LF)
                    {
                        End(); return(-1);
                    }
                    else
                    {
                        StringQueue.New();
                        State = RequestParserState.HeaderName;
                    }
                    break;

                case RequestParserState.Lf:
                    if (c != LF)
                    {
                        End(); return(-1);
                    }
                    run = false;
                    PushIncoming();
                    State = RequestParserState.Method;
                    break;
                }
            }
            if (writeExcess)
            {
                int    len    = data.Length - i;
                byte[] sliced = new byte[len];
                System.Buffer.BlockCopy(data, i, sliced, 0, len);
                Bwrite(sliced);
            }
            return(i);
        }
Esempio n. 6
0
        /// <summary>
        /// Parse a message
        /// </summary>
        /// <param name="buffer">bytes to parse.</param>
        /// <param name="offset">where in buffer that parsing should start</param>
        /// <param name="count">number of bytes to parse, starting on <paramref name="offset"/>.</param>
        /// <returns>offset (where to start parsing next).</returns>
        /// <exception cref="BadRequestException"><c>BadRequestException</c>.</exception>
        public int Parse(byte[] buffer, int offset, int count)
        {
            // add body bytes
            if (CurrentState == RequestParserState.Body)
            {
                // copy all remaining bytes to the beginning of the buffer.
                //Buffer.BlockCopy(buffer, offset + bytesUsed, buffer, 0, count - bytesUsed);


                return(AddToBody(buffer, 0, count));
            }

#if DEBUG
            string temp = Encoding.ASCII.GetString(buffer, offset, count);
            _log.Write(this, LogPrio.Trace, "\r\n\r\n HTTP MESSAGE: " + temp + "\r\n");
#endif

            int currentLine = 1;
            int startPos    = -1;

            // set start pos since this is from an partial request
            if (CurrentState == RequestParserState.HeaderValue)
            {
                startPos = 0;
            }

            int endOfBufferPos = offset + count;

            //<summary>
            // Handled bytes are used to keep track of the number of bytes processed.
            // We do this since we can handle partial requests (to be able to check headers and abort
            // invalid requests directly without having to process the whole header / body).
            // </summary>
            int handledBytes = 0;


            for (int currentPos = offset; currentPos < endOfBufferPos; ++currentPos)
            {
                var  ch     = (char)buffer[currentPos];
                char nextCh = endOfBufferPos > currentPos + 1 ? (char)buffer[currentPos + 1] : char.MinValue;

                if (ch == '\r')
                {
                    ++currentLine;
                }

                switch (CurrentState)
                {
                case RequestParserState.FirstLine:
                    if (currentPos > 4196)
                    {
                        _log.Write(this, LogPrio.Warning, "HTTP Request is too large.");
                        throw new BadRequestException("Too large request line.");
                    }
                    if (char.IsLetterOrDigit(ch) && startPos == -1)
                    {
                        startPos = currentPos;
                    }
                    if (startPos == -1 && (ch != '\r' || nextCh != '\n'))
                    {
                        _log.Write(this, LogPrio.Warning, "Request line is not found.");
                        throw new BadRequestException("Invalid request line.");
                    }
                    if (startPos != -1 && (ch == '\r' || ch == '\n'))
                    {
                        int size = GetLineBreakSize(buffer, currentPos);
                        OnFirstLine(Encoding.UTF8.GetString(buffer, startPos, currentPos - startPos));
                        CurrentState = CurrentState + 1;
                        currentPos  += size - 1;
                        handledBytes = currentPos + size - 1;
                        startPos     = -1;
                    }
                    break;

                case RequestParserState.HeaderName:
                    if (ch == '\r' || ch == '\n')
                    {
                        currentPos += GetLineBreakSize(buffer, currentPos);
                        if (_bodyBytesLeft == 0)
                        {
                            CurrentState = RequestParserState.FirstLine;
                            _log.Write(this, LogPrio.Trace, "Request parsed successfully (no content).");
                            OnRequestCompleted();
                            Clear();
                            return(currentPos);
                        }

                        CurrentState = RequestParserState.Body;
                        if (currentPos + 1 < endOfBufferPos)
                        {
                            _log.Write(this, LogPrio.Trace, "Adding bytes to the body");
                            return(AddToBody(buffer, currentPos, endOfBufferPos - currentPos));
                        }

                        return(currentPos);
                    }
                    if (char.IsWhiteSpace(ch) || ch == ':')
                    {
                        if (startPos == -1)
                        {
                            _log.Write(this, LogPrio.Warning,
                                       "Expected header name, got colon on line " + currentLine);
                            throw new BadRequestException("Expected header name, got colon on line " + currentLine);
                        }
                        _curHeaderName = Encoding.UTF8.GetString(buffer, startPos, currentPos - startPos);
                        handledBytes   = currentPos + 1;
                        startPos       = -1;
                        CurrentState   = CurrentState + 1;
                        if (ch == ':')
                        {
                            CurrentState = CurrentState + 1;
                        }
                    }
                    else if (startPos == -1)
                    {
                        startPos = currentPos;
                    }
                    else if (!char.IsLetterOrDigit(ch) && ch != '-' && ch != '_' && ch != '.')
                    {
                        _log.Write(this, LogPrio.Warning, "Invalid character in header name on line " + currentLine);
                        throw new BadRequestException("Invalid character in header name on line " + currentLine);
                    }
                    if (startPos != -1 && currentPos - startPos > 200)
                    {
                        _log.Write(this, LogPrio.Warning, "Invalid header name on line " + currentLine);
                        throw new BadRequestException("Invalid header name on line " + currentLine);
                    }
                    break;

                case RequestParserState.AfterName:
                    if (ch == ':')
                    {
                        handledBytes = currentPos + 1;
                        CurrentState = CurrentState + 1;
                    }
                    break;

                case RequestParserState.Between:
                {
                    if (ch == ' ' || ch == '\t')
                    {
                        continue;
                    }
                    int newLineSize = GetLineBreakSize(buffer, currentPos);
                    if (newLineSize > 0 && currentPos + newLineSize < endOfBufferPos &&
                        char.IsWhiteSpace((char)buffer[currentPos + newLineSize]))
                    {
                        ++currentPos;
                        continue;
                    }
                    startPos     = currentPos;
                    CurrentState = CurrentState + 1;
                    handledBytes = currentPos;
                    continue;
                }

                case RequestParserState.HeaderValue:
                {
                    if (ch != '\r' && ch != '\n')
                    {
                        continue;
                    }
                    int newLineSize = GetLineBreakSize(buffer, currentPos);
                    if (startPos == -1)
                    {
                        continue;         // allow new lines before start of value
                    }
                    if (_curHeaderName == string.Empty)
                    {
                        throw new BadRequestException("Missing header on line " + currentLine);
                    }
                    if (startPos == -1)
                    {
                        _log.Write(this, LogPrio.Warning, "Missing header value for '" + _curHeaderName);
                        throw new BadRequestException("Missing header value for '" + _curHeaderName);
                    }
                    if (currentPos - startPos > 4096)
                    {
                        _log.Write(this, LogPrio.Warning, "Too large header value on line " + currentLine);
                        throw new BadRequestException("Too large header value on line " + currentLine);
                    }

                    // Header fields can be extended over multiple lines by preceding each extra line with at
                    // least one SP or HT.
                    if (endOfBufferPos > currentPos + newLineSize &&
                        (buffer[currentPos + newLineSize] == ' ' || buffer[currentPos + newLineSize] == '\t'))
                    {
                        if (startPos != -1)
                        {
                            _curHeaderValue = Encoding.UTF8.GetString(buffer, startPos, currentPos - startPos);
                        }

                        _log.Write(this, LogPrio.Trace, "Header value is on multiple lines.");
                        CurrentState = RequestParserState.Between;
                        startPos     = -1;
                        currentPos  += newLineSize - 1;
                        handledBytes = currentPos + newLineSize - 1;
                        continue;
                    }

                    _curHeaderValue += Encoding.UTF8.GetString(buffer, startPos, currentPos - startPos);
                    _log.Write(this, LogPrio.Trace, "Header [" + _curHeaderName + ": " + _curHeaderValue + "]");
                    OnHeader(_curHeaderName, _curHeaderValue);

                    startPos        = -1;
                    CurrentState    = RequestParserState.HeaderName;
                    _curHeaderValue = string.Empty;
                    _curHeaderName  = string.Empty;
                    ++currentPos;
                    handledBytes = currentPos + 1;

                    // Check if we got a colon so we can cut header name, or crlf for end of header.
                    bool canContinue = false;
                    for (int j = currentPos; j < endOfBufferPos; ++j)
                    {
                        if (buffer[j] != ':' && buffer[j] != '\r' && buffer[j] != '\n')
                        {
                            continue;
                        }
                        canContinue = true;
                        break;
                    }
                    if (!canContinue)
                    {
                        _log.Write(this, LogPrio.Trace, "Cant continue, no colon.");
                        return(currentPos + 1);
                    }
                }
                break;
                }
            }

            return(handledBytes);
        }