/// <summary> /// Called to scan the bytes of a potential line for invalid characters /// </summary> /// <param name="buffer"> /// Array containing the bytes that to can for invalid characters /// </param> /// <param name="start">Index in the array at which to begin reading</param> /// <param name="count">Number of bytes from the array to scan</param> protected override void VerifyPotentialLine(byte[] buffer, int start, int count) { // Make sure the line does not contain any characters which are considered // invalid by the RFC for (int index = start; index < count; ++index) { // First, find out whether this is a control character. All but 2 control // characters are disallowed by the RFC bool isControlCharacter = (buffer[index] < 32) || (buffer[index] == DEL); // If it Is a control character, we need to do another check to see whether // the characters is one of the two allowed control characters if (isControlCharacter) { bool isValidControlCharacter = (buffer[index] == SP) || (buffer[index] == HT); // It's not one of the two allowed control characters, let's complain if (!isValidControlCharacter) { throw Errors.BadRequest("Invalid character in request header"); } } } }
/// <summary>Parses a request header line sent from the client</summary> /// <param name="headerLine">String containing the received header line</param> private void parseHeaderLine(string headerLine) { // Find out whether this header line begins with whitespace. According to the // RFC, a message header can be broken into multiple lines by beginning the // next line with one or more whitespace characters (SP and HT) char firstCharacter = headerLine[0]; bool startsWithWhitespace = (firstCharacter == ' ') || (firstCharacter == '\t'); // If the line starts with a whitespace, it is either a continuation of the // previous line or simply a broken request (or there is no previous line) if (startsWithWhitespace) { // If this is the first header field, the request is broken if (this.currentFieldName == null) { throw Errors.BadRequest("First message header is preceded by whitespace"); } // Alright, this actually seems to be a valid field continuation parseHeaderFieldValue(headerLine, 1); } else // Line doesn't begin with a whitespace // Look for the delimiter character that ends the field name { int valueDelimiterIndex = headerLine.IndexOf(':'); if (valueDelimiterIndex == -1) // No delimiter? Invalid request! { throw Errors.BadRequest("Message header field omits value"); } // Extract the field name from the line string fieldName = headerLine.Substring(0, valueDelimiterIndex); if (fieldName == string.Empty) // Empty field name? Request broken! { throw Errors.BadRequest("Message header contains unnamed field"); } // There is no mention in the RFC that whitespace is allowed between the // header field name and the delimiter character, so we don't allow it. bool fieldNameEndsInWhitespace = (fieldName[fieldName.Length - 1] == ' ') || (fieldName[fieldName.Length - 1] == '\t'); if (fieldNameEndsInWhitespace) { throw Errors.BadRequest( "Message header field name is followed by whitespace" ); } // Now that we know where the value begins, parse it! this.currentFieldName = fieldName; parseHeaderFieldValue(headerLine, valueDelimiterIndex + 1); } }
/// <summary>Parses the request line sent from the client</summary> /// <param name="requestLine">String containing the received request line</param> private void parseRequestLine(string requestLine) { // The RFC doesn't say that the request line must not contain any additional // spaces, so in the we will assume the first space terminates the method and the // last space terminates the URI. int uriDelimiterIndex = requestLine.IndexOf(' '); if (uriDelimiterIndex == -1) { throw Errors.BadRequest("Request-line is missing an URI"); } // If there's only one space character, then the request is missing the version // of the HTTP protocol used. int versionDelimiterIndex = requestLine.LastIndexOf(' '); if (versionDelimiterIndex == uriDelimiterIndex) { throw Errors.BadRequest("Request-line does not specify HTTP version"); } // Request seems to be at least be in the right layout. Extract the individual // components and pass them to the request container builder (validation of // the actual settings takes place once we have a complete request). requestBuilder.Method = requestLine.Substring(0, uriDelimiterIndex); requestBuilder.Uri = requestLine.Substring( uriDelimiterIndex + 1, versionDelimiterIndex - uriDelimiterIndex - 1 ); requestBuilder.Version = requestLine.Substring(versionDelimiterIndex + 1); // We expect HTTP/1.* to stay compatible with the general format of the request. // Any other version of the protocol may include major changes to the request // format, thus we only accept HTTP/1.*. if (!requestBuilder.Version.StartsWith("HTTP/1.")) { throw Errors.UnsupportedProtocolVersion(); } }
/// <summary> /// Called when the message contains a carriage return without a line feed /// </summary> protected override void HandleLoneCarriageReturn() { throw Errors.BadRequest("Invalid character in request header"); }