Beispiel #1
0
 /// <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);
        }