/// <summary>Disallows requests that would like the response in json format.</summary> /// <param name="requestKind">Type of request.</param> /// <param name="acceptHeader">Accept header value.</param> private static void DisallowJsonRequests(RequestKind requestKind, string acceptHeader) { if (HttpProcessUtils.IsJsonRequest(requestKind, acceptHeader)) { throw new DomainDataServiceException((int)HttpStatusCode.UnsupportedMediaType, Resource.HttpProcessUtility_UnsupportedMediaType); } }
/// <summary>Read a parameter for a media type/range.</summary> /// <param name="text">Text to read from.</param> /// <param name="textIndex">Pointer in text.</param> /// <param name="parameters">Array with parameters to grow as necessary.</param> private static void ReadMediaTypeParameter(string text, ref int textIndex, ref KeyValuePair <string, string>[] parameters) { int startIndex = textIndex; if (ReadToken(text, ref textIndex)) { throw HttpProcessUtils.CreateParsingException(Resource.HttpProcessUtility_MediaTypeMissingValue); } string parameterName = text.Substring(startIndex, textIndex - startIndex); if (text[textIndex] != '=') { throw HttpProcessUtils.CreateParsingException(Resource.HttpProcessUtility_MediaTypeMissingValue); } textIndex++; string parameterValue = ReadQuotedParameterValue(parameterName, text, ref textIndex); // Add the parameter name/value pair to the list. if (parameters == null) { parameters = new KeyValuePair <string, string> [1]; } else { KeyValuePair <string, string>[] grow = new KeyValuePair <string, string> [parameters.Length + 1]; Array.Copy(parameters, grow, parameters.Length); parameters = grow; } parameters[parameters.Length - 1] = new KeyValuePair <string, string>(parameterName, parameterValue); }
/// <summary>Reads the type and subtype specifications for a MIME type.</summary> /// <param name='text'>Text in which specification exists.</param> /// <param name='textIndex'>Pointer into text.</param> /// <param name='type'>Type of media found.</param> /// <param name='subType'>Subtype of media found.</param> private static void ReadMediaTypeAndSubtype(string text, ref int textIndex, out string type, out string subType) { Debug.Assert(text != null, "text != null"); int textStart = textIndex; if (ReadToken(text, ref textIndex)) { throw HttpProcessUtils.CreateParsingException(Resource.HttpProcessUtility_MediaTypeUnspecified); } if (text[textIndex] != '/') { throw HttpProcessUtils.CreateParsingException(Resource.HttpProcessUtility_MediaTypeRequiresSlash); } type = text.Substring(textStart, textIndex - textStart); textIndex++; int subTypeStart = textIndex; ReadToken(text, ref textIndex); if (textIndex == subTypeStart) { throw HttpProcessUtils.CreateParsingException(Resource.HttpProcessUtility_MediaTypeRequiresSubType); } subType = text.Substring(subTypeStart, textIndex - subTypeStart); }
/// <summary> /// Reads the numeric part of a quality value substring, normalizing it to 0-1000 /// rather than the standard 0.000-1.000 ranges. /// </summary> /// <param name="text">Text to read qvalue from.</param> /// <param name="textIndex">Index into text where the qvalue starts.</param> /// <param name="qualityValue">After the method executes, the normalized qvalue.</param> /// <remarks> /// For more information, see RFC 2616.3.8. /// </remarks> private static void ReadQualityValue(string text, ref int textIndex, out int qualityValue) { char digit = text[textIndex++]; if (digit == '0') { qualityValue = 0; } else if (digit == '1') { qualityValue = 1; } else { throw HttpProcessUtils.CreateParsingException(Resource.HttpContextServiceHost_MalformedHeaderValue); } if (textIndex < text.Length && text[textIndex] == '.') { textIndex++; int adjustFactor = 1000; while (adjustFactor > 1 && textIndex < text.Length) { char c = text[textIndex]; int charValue = DigitToInt32(c); if (charValue >= 0) { textIndex++; adjustFactor /= 10; qualityValue *= 10; qualityValue += charValue; } else { break; } } qualityValue = qualityValue *= adjustFactor; if (qualityValue > 1000) { // Too high of a value in qvalue. throw HttpProcessUtils.CreateParsingException(Resource.HttpContextServiceHost_MalformedHeaderValue); } } else { qualityValue *= 1000; } }
/// <summary> /// Converts the specified character from the ASCII range to a digit. /// </summary> /// <param name="c">Character to convert.</param> /// <returns> /// The Int32 value for c, or -1 if it is an element separator. /// </returns> private static int DigitToInt32(char c) { if (c >= '0' && c <= '9') { return((int)(c - '0')); } else { if (IsHttpElementSeparator(c)) { return(-1); } else { throw HttpProcessUtils.CreateParsingException(Resource.HttpContextServiceHost_MalformedHeaderValue); } } }
/// <summary>Returns all MIME types from the specified (non-blank) <paramref name='text' />.</summary> /// <param name='text'>Non-blank text, as it appears on an HTTP Accepts header.</param> /// <returns>An enumerable object with media type descriptions.</returns> private static IEnumerable <MediaType> MimeTypesFromAcceptHeader(string text) { Debug.Assert(!String.IsNullOrEmpty(text), "!String.IsNullOrEmpty(text)"); List <MediaType> mediaTypes = new List <MediaType>(); int textIndex = 0; while (!SkipWhitespace(text, ref textIndex)) { string type; string subType; ReadMediaTypeAndSubtype(text, ref textIndex, out type, out subType); KeyValuePair <string, string>[] parameters = null; while (!SkipWhitespace(text, ref textIndex)) { if (text[textIndex] == ',') { textIndex++; break; } if (text[textIndex] != ';') { throw HttpProcessUtils.CreateParsingException(Resource.HttpProcessUtility_MediaTypeRequiresSemicolonBeforeParameter); } textIndex++; if (SkipWhitespace(text, ref textIndex)) { // ';' should be a leading separator, but we choose to be a // bit permissive and allow it as a final delimiter as well. break; } ReadMediaTypeParameter(text, ref textIndex, ref parameters); } mediaTypes.Add(new MediaType(type, subType, parameters)); } return(mediaTypes); }
/// <summary>Disallows requests that would like the response in json format.</summary> /// <param name="requestKind">Type of request.</param> /// <param name="acceptHeader">Accept header value.</param> /// <returns>True if request is accepting json response.</returns> internal static bool IsJsonRequest(RequestKind requestKind, string acceptHeader) { string mimeType = null; switch (requestKind) { case RequestKind.ServiceDocument: mimeType = HttpProcessUtils.SelectRequiredMimeType( acceptHeader, HttpProcessUtils.ServiceDocumentMimeTypes, HttpProcessUtils.MimeApplicationXml); break; case RequestKind.MetadataDocument: mimeType = HttpProcessUtils.SelectRequiredMimeType( acceptHeader, HttpProcessUtils.MetadataDocumentMimeTypes, HttpProcessUtils.MimeApplicationXml); break; case RequestKind.ResourceSet: mimeType = HttpProcessUtils.SelectRequiredMimeType( acceptHeader, HttpProcessUtils.ResourceSetMimeTypes, HttpProcessUtils.MimeApplicationAtom); break; case RequestKind.ServiceOperation: mimeType = HttpProcessUtils.SelectRequiredMimeType( acceptHeader, HttpProcessUtils.ServiceOperationMimeTypes, HttpProcessUtils.MimeApplicationXml); break; default: Debug.Assert(false, "Must never receive any other kind of request."); break; } return(HttpProcessUtils.CompareMimeType(mimeType, HttpProcessUtils.MimeApplicationJson)); }
/// <summary>Gets a number of non-* matching types, or -1 if not matching at all.</summary> /// <param name="candidate">Candidate MIME type to match.</param> /// <returns>The number of non-* matching types, or -1 if not matching at all.</returns> internal int GetMatchingParts(string candidate) { Debug.Assert(candidate != null, "candidate must not be null."); int result = -1; if (candidate.Length > 0) { if (this.type == "*") { result = 0; } else { int separatorIdx = candidate.IndexOf('/'); if (separatorIdx >= 0) { string candidateType = candidate.Substring(0, separatorIdx); if (HttpProcessUtils.CompareMimeType(this.type, candidateType)) { if (this.subType == "*") { result = 1; } else { string candidateSubType = candidate.Substring(candidateType.Length + 1); if (HttpProcessUtils.CompareMimeType(this.subType, candidateSubType)) { result = 2; } } } } } } return(result); }
/// <summary> /// Reads Mime type parameter value for a particular parameter in the Content-Type/Accept headers. /// </summary> /// <param name="parameterName">Name of parameter.</param> /// <param name="headerText">Header text.</param> /// <param name="textIndex">Parsing index in <paramref name="headerText"/>.</param> /// <returns>String representing the value of the <paramref name="parameterName"/> parameter.</returns> private static string ReadQuotedParameterValue(string parameterName, string headerText, ref int textIndex) { StringBuilder parameterValue = new StringBuilder(); // Check if the value is quoted. bool valueIsQuoted = false; if (textIndex < headerText.Length) { if (headerText[textIndex] == '\"') { textIndex++; valueIsQuoted = true; } } while (textIndex < headerText.Length) { char currentChar = headerText[textIndex]; if (currentChar == '\\' || currentChar == '\"') { if (!valueIsQuoted) { throw HttpProcessUtils.CreateParsingException(string.Format( CultureInfo.InvariantCulture, Resource.HttpProcessUtility_EscapeCharWithoutQuotes, parameterName)); } textIndex++; // End of quoted parameter value. if (currentChar == '\"') { valueIsQuoted = false; break; } if (textIndex >= headerText.Length) { throw HttpProcessUtils.CreateParsingException(string.Format( CultureInfo.InvariantCulture, Resource.HttpProcessUtility_EscapeCharAtEnd, parameterName)); } currentChar = headerText[textIndex]; } else if (!IsHttpToken(currentChar)) { // If the given character is special, we stop processing. break; } parameterValue.Append(currentChar); textIndex++; } if (valueIsQuoted) { throw HttpProcessUtils.CreateParsingException(string.Format( CultureInfo.InvariantCulture, Resource.HttpProcessUtility_ClosingQuoteNotFound, parameterName)); } return(parameterValue.ToString()); }
/// <summary>Gets the appropriate MIME type for the request, throwing if there is none.</summary> /// <param name='acceptTypesText'>Text as it appears in an HTTP Accepts header.</param> /// <param name='exactContentType'>Preferred content type to match if an exact media type is given - this is in descending order of preference.</param> /// <param name='inexactContentType'>Preferred fallback content type for inexact matches.</param> /// <returns>One of exactContentType or inexactContentType.</returns> private static string SelectRequiredMimeType( string acceptTypesText, string[] exactContentType, string inexactContentType) { Debug.Assert(exactContentType != null && exactContentType.Length != 0, "exactContentType != null && exactContentType.Length != 0"); Debug.Assert(inexactContentType != null, "inexactContentType != null"); string selectedContentType = null; int selectedMatchingParts = -1; int selectedQualityValue = 0; bool acceptable = false; bool acceptTypesEmpty = true; bool foundExactMatch = false; if (!String.IsNullOrEmpty(acceptTypesText)) { IEnumerable <MediaType> acceptTypes = MimeTypesFromAcceptHeader(acceptTypesText); foreach (MediaType acceptType in acceptTypes) { acceptTypesEmpty = false; for (int i = 0; i < exactContentType.Length; i++) { if (HttpProcessUtils.CompareMimeType(acceptType.MimeType, exactContentType[i])) { selectedContentType = exactContentType[i]; selectedQualityValue = acceptType.SelectQualityValue(); acceptable = selectedQualityValue != 0; foundExactMatch = true; break; } } if (foundExactMatch) { break; } int matchingParts = acceptType.GetMatchingParts(inexactContentType); if (matchingParts < 0) { continue; } if (matchingParts > selectedMatchingParts) { // A more specific type wins. selectedContentType = inexactContentType; selectedMatchingParts = matchingParts; selectedQualityValue = acceptType.SelectQualityValue(); acceptable = selectedQualityValue != 0; } else if (matchingParts == selectedMatchingParts) { // A type with a higher q-value wins. int candidateQualityValue = acceptType.SelectQualityValue(); if (candidateQualityValue > selectedQualityValue) { selectedContentType = inexactContentType; selectedQualityValue = candidateQualityValue; acceptable = selectedQualityValue != 0; } } } } if (!acceptable && !acceptTypesEmpty) { throw new DomainDataServiceException((int)HttpStatusCode.UnsupportedMediaType, Resource.HttpProcessUtility_UnsupportedMediaType); } if (acceptTypesEmpty) { Debug.Assert(selectedContentType == null, "selectedContentType == null - otherwise accept types were not empty"); selectedContentType = inexactContentType; } Debug.Assert(selectedContentType != null, "selectedContentType != null - otherwise no selection was made"); return(selectedContentType); }