Beispiel #1
0
        internal static ODataFormat GetFormatFromContentType(string contentTypeHeader, ODataPayloadKind[] supportedPayloadKinds, MediaTypeResolver mediaTypeResolver, out MediaType mediaType, out Encoding encoding, out ODataPayloadKind selectedPayloadKind, out string batchBoundary)
        {
            ODataFormat format = GetFormatFromContentType(contentTypeHeader, supportedPayloadKinds, mediaTypeResolver, out mediaType, out encoding, out selectedPayloadKind);

            if (selectedPayloadKind == ODataPayloadKind.Batch)
            {
                KeyValuePair <string, string> pair = new KeyValuePair <string, string>();
                IEnumerable <KeyValuePair <string, string> > parameters = mediaType.Parameters;
                if (parameters != null)
                {
                    bool flag = false;
                    foreach (KeyValuePair <string, string> pair2 in from p in parameters
                             where HttpUtils.CompareMediaTypeParameterNames("boundary", p.Key)
                             select p)
                    {
                        if (flag)
                        {
                            throw new ODataException(Microsoft.Data.OData.Strings.MediaTypeUtils_BoundaryMustBeSpecifiedForBatchPayloads(contentTypeHeader, "boundary"));
                        }
                        pair = pair2;
                        flag = true;
                    }
                }
                if (pair.Key == null)
                {
                    throw new ODataException(Microsoft.Data.OData.Strings.MediaTypeUtils_BoundaryMustBeSpecifiedForBatchPayloads(contentTypeHeader, "boundary"));
                }
                batchBoundary = pair.Value;
                ValidationUtils.ValidateBoundaryString(batchBoundary);
                return(format);
            }
            batchBoundary = null;
            return(format);
        }
Beispiel #2
0
        /// <summary>
        /// Converts all occurrences of the 'application/json' media type to 'application/json;odata=minimalmetadata'.
        /// This is necessary because for an accept header 'application/json, application/json;odata=verbose'
        /// we want the result to be 'application/json;odata=minimalmetadata' although this is not compliant with the
        /// default conneg rules (where application/json;odata=verbose would win).
        /// </summary>
        /// <param name="specifiedTypes">The parsed acceptable media types.</param>
        private static void ConvertApplicationJsonInAcceptableMediaTypes(IList <KeyValuePair <MediaType, string> > specifiedTypes)
        {
            if (specifiedTypes == null)
            {
                return;
            }

            for (int i = 0; i < specifiedTypes.Count; ++i)
            {
                MediaType mediaType = specifiedTypes[i].Key;
                if (HttpUtils.CompareMediaTypeNames(mediaType.SubTypeName, MimeConstants.MimeJsonSubType) &&
                    HttpUtils.CompareMediaTypeNames(mediaType.TypeName, MimeConstants.MimeApplicationType))
                {
                    if (mediaType.Parameters == null ||
                        !mediaType.Parameters.Any(p => HttpUtils.CompareMediaTypeParameterNames(p.Key, MimeConstants.MimeODataParameterName)))
                    {
                        // application/json detected; convert it to Json Light
                        IList <KeyValuePair <string, string> > existingParams = mediaType.Parameters;
                        int newCount = existingParams == null ? 1 : existingParams.Count + 1;
                        List <KeyValuePair <string, string> > newParams = new List <KeyValuePair <string, string> >(newCount);
                        newParams.Add(new KeyValuePair <string, string>(MimeConstants.MimeODataParameterName, MimeConstants.MimeODataParameterValueMinimalMetadata));
                        if (existingParams != null)
                        {
                            newParams.AddRange(existingParams);
                        }

                        specifiedTypes[i] = new KeyValuePair <MediaType, string>(new MediaType(mediaType.TypeName, mediaType.SubTypeName, newParams), specifiedTypes[i].Value);
                    }
                }
            }
        }
Beispiel #3
0
 internal Encoding SelectEncoding()
 {
     if (this.parameters != null)
     {
         foreach (string str in from parameter in this.parameters
                  where HttpUtils.CompareMediaTypeParameterNames("charset", parameter.Key)
                  select parameter.Value.Trim() into encodingName
                  where encodingName.Length > 0
                  select encodingName)
         {
             return(EncodingFromName(str));
         }
     }
     if (HttpUtils.CompareMediaTypeNames("text", this.type))
     {
         if (!HttpUtils.CompareMediaTypeNames("xml", this.subType))
         {
             return(MissingEncoding);
         }
         return(null);
     }
     if (HttpUtils.CompareMediaTypeNames("application", this.type) && HttpUtils.CompareMediaTypeNames("json", this.subType))
     {
         return(FallbackEncoding);
     }
     return(null);
 }
Beispiel #4
0
        /// <summary>
        /// Detects the payload kind(s) from the message stream.
        /// </summary>
        /// <param name="contentType">The content type of the message.</param>
        /// <returns>An enumerable of zero, one or more payload kinds that were detected from looking at the payload in the message stream.</returns>
        private static IEnumerable <ODataPayloadKind> DetectPayloadKindImplementation(MediaType contentType)
        {
            // NOTE: for batch payloads we only use the content type header of the message to detect the payload kind.
            //       We assume a valid batch payload if the content type is multipart/mixed and a boundary parameter exists
            // Require 'multipart/mixed' content type with a boundary parameter to be considered batch.
            if (HttpUtils.CompareMediaTypeNames(MimeConstants.MimeMultipartType, contentType.TypeName) &&
                HttpUtils.CompareMediaTypeNames(MimeConstants.MimeMixedSubType, contentType.SubTypeName) &&
                contentType.Parameters != null &&
                contentType.Parameters.Any(kvp => HttpUtils.CompareMediaTypeParameterNames(ODataConstants.HttpMultipartBoundary, kvp.Key)))
            {
                return(new ODataPayloadKind[] { ODataPayloadKind.Batch });
            }

            return(Enumerable.Empty <ODataPayloadKind>());
        }
Beispiel #5
0
        /// <summary>
        /// Checks whether the specified media type has a parameter with the expected value.
        /// </summary>
        /// <param name="mediaType">The media type to check the parameters for.</param>
        /// <param name="parameterName">The name of the expected parameter.</param>
        /// <param name="parameterValue">The value of the expected parameter.</param>
        /// <returns>true if the <paramref name="mediaType"/> has a parameter called <paramref name="parameterName"/>
        /// with value <paramref name="parameterValue"/>; otherwise false.</returns>
        internal static bool MediaTypeHasParameterWithValue(this MediaType mediaType, string parameterName, string parameterValue)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(mediaType != null, "mediaType != null");
            Debug.Assert(parameterName != null, "parameterName != null");

            if (mediaType.Parameters == null)
            {
                return(false);
            }

            return(mediaType.Parameters.Any(p =>
                                            HttpUtils.CompareMediaTypeParameterNames(p.Key, parameterName) &&
                                            String.Compare(p.Value, parameterValue, StringComparison.OrdinalIgnoreCase) == 0));
        }
Beispiel #6
0
        /// <summary>
        /// Removes the application/json media type from the set of specified types since it is not supported in V3 initially
        /// and must not be matched to application/json;odata=verbose.
        /// If no other media types are left, the method will throw.
        /// </summary>
        /// <param name="specifiedTypes">The parsed acceptable media types.</param>
        /// <param name="supportedMediaTypes">The set of supported media types.</param>
        /// <param name="acceptableMediaTypes">The string version of the acceptable media types (for error reporting).</param>
        /// <returns>If nothing was removed the unmodified <paramref name="specifiedTypes"/>; otherwise a cloned list with the unsupported types removed.</returns>
        private static IList <KeyValuePair <MediaType, string> > RemoveApplicationJsonFromAcceptableMediaTypes(
            IList <KeyValuePair <MediaType, string> > specifiedTypes,
            MediaTypeWithFormat[] supportedMediaTypes,
            string acceptableMediaTypes)
        {
            if (specifiedTypes == null)
            {
                return(null);
            }

            List <KeyValuePair <MediaType, string> > filteredTypes = null;

            for (int i = specifiedTypes.Count - 1; i >= 0; --i)
            {
                MediaType mediaType = specifiedTypes[i].Key;
                if (HttpUtils.CompareMediaTypeNames(mediaType.SubTypeName, MimeConstants.MimeJsonSubType) &&
                    HttpUtils.CompareMediaTypeNames(mediaType.TypeName, MimeConstants.MimeApplicationType))
                {
                    if (mediaType.Parameters == null ||
                        !mediaType.Parameters.Where(p => HttpUtils.CompareMediaTypeParameterNames(p.Key, MimeConstants.MimeODataParameterName)).Any())
                    {
                        // application/json detected; remove it from the list
                        if (filteredTypes == null)
                        {
                            filteredTypes = new List <KeyValuePair <MediaType, string> >(specifiedTypes);
                        }

                        filteredTypes.RemoveAt(i);
                    }
                }
            }

            if (filteredTypes == null)
            {
                return(specifiedTypes);
            }

            if (filteredTypes.Count == 0)
            {
                // If we modified the list make sure we still have acceptable media types left; otherwise fail.
                // We're calling the ToArray here since not all platforms support the string.Join which takes IEnumerable.
                string supportedTypesAsString = string.Join(", ", supportedMediaTypes.Select(mt => mt.MediaType.ToText()).ToArray());
                throw new ODataContentTypeException(Strings.MediaTypeUtils_DidNotFindMatchingMediaType(supportedTypesAsString, acceptableMediaTypes));
            }

            return(filteredTypes);
        }
Beispiel #7
0
 /// <summary>
 /// Fails early on unsupported content types that cannot be caught by content negotiation/resolution.
 /// </summary>
 /// <param name="contentType">The parsed media type from the <paramref name="contentTypeName"/>.</param>
 /// <param name="contentTypeName">The name of the content type to be checked (for error reporting only).</param>
 /// <param name="supportedPayloadKinds">All possible kinds of payload that can be read with this content type (for error reporting only).</param>
 /// <param name="mediaTypeResolver">The media type resolver to use when interpreting the content type (for error reporting only).</param>
 private static void FailOnUnsupportedMediaTypes(MediaType contentType, string contentTypeName, ODataPayloadKind[] supportedPayloadKinds, MediaTypeResolver mediaTypeResolver)
 {
     // We currently fail for application/json;odata=light for all version.
     // Once we support Json Light, we will only fail for V1 and V2 where Json Light continues to not be supported.
     // NOTE: once we support custom formats and custom content negotiation, we will have to move this logic into
     //       the format itself (i.e., also register Json Light for V1 and V2 but make it fail).
     if (HttpUtils.CompareMediaTypeNames(contentType.SubTypeName, MimeConstants.MimeJsonSubType) &&
         HttpUtils.CompareMediaTypeNames(contentType.TypeName, MimeConstants.MimeApplicationType) &&
         contentType.Parameters != null &&
         contentType.Parameters.Where(p =>
                                      HttpUtils.CompareMediaTypeParameterNames(p.Key, MimeConstants.MimeODataParameterName) &&
                                      string.Compare(MimeConstants.MimeODataParameterValueLight, p.Value, StringComparison.OrdinalIgnoreCase) == 0).Any())
     {
         string supportedTypesAsString = string.Join(", ", supportedPayloadKinds.SelectMany(pk => mediaTypeResolver.GetMediaTypesForPayloadKind(pk).Select(mt => mt.MediaType.ToText())).ToArray());
         throw new ODataContentTypeException(Strings.MediaTypeUtils_CannotDetermineFormatFromContentType(supportedTypesAsString, contentTypeName));
     }
 }
Beispiel #8
0
        /// <summary>
        /// Converts the given <see cref="MediaType"/> to a string representation suitable for use in a content-type header.
        /// </summary>
        /// <param name="mediaType">The media type to convert to text.</param>
        /// <param name="encoding">The encoding to use when converting the <paramref name="mediaType"/> into text.</param>
        /// <returns>The string representation of the provided <paramref name="mediaType"/>.</returns>
        internal static string ToText(this MediaType mediaType, Encoding encoding)
        {
            DebugUtils.CheckNoExternalCallers();

            // TODO: for now we include all the parameters since we know that we will not have accept parameters (after the quality value)
            //       that needed to be ignored.
            if (mediaType.Parameters == null || mediaType.Parameters.Count == 0)
            {
                string typeName = mediaType.FullTypeName;
                if (encoding != null)
                {
                    typeName = string.Concat(typeName, ";", ODataConstants.Charset, "=", encoding.WebName);
                }

                return(typeName);
            }
            else
            {
                StringBuilder builder = new StringBuilder(mediaType.FullTypeName);
                for (int i = 0; i < mediaType.Parameters.Count; ++i)
                {
                    KeyValuePair <string, string> parameter = mediaType.Parameters[i];

                    // ignore the char set if specified in the parameters; we write the one from the encoding
                    if (!HttpUtils.CompareMediaTypeParameterNames(ODataConstants.Charset, parameter.Key))
                    {
                        builder.Append(";");
                        builder.Append(parameter.Key);
                        builder.Append("=");
                        builder.Append(parameter.Value);
                    }
                }

                // write the encoding (if any)
                if (encoding != null)
                {
                    builder.Append(";");
                    builder.Append(ODataConstants.Charset);
                    builder.Append("=");
                    builder.Append(encoding.WebName);
                }

                return(builder.ToString());
            }
        }
Beispiel #9
0
 private static bool TryFindMediaTypeParameter(IList <KeyValuePair <string, string> > parameters, string parameterName, out string parameterValue)
 {
     parameterValue = null;
     if (parameters != null)
     {
         for (int i = 0; i < parameters.Count; i++)
         {
             KeyValuePair <string, string> pair = parameters[i];
             string key = pair.Key;
             if (HttpUtils.CompareMediaTypeParameterNames(parameterName, key))
             {
                 KeyValuePair <string, string> pair2 = parameters[i];
                 parameterValue = pair2.Value;
                 return(true);
             }
         }
     }
     return(false);
 }
Beispiel #10
0
            /// <summary>
            /// Tries to find a parameter with the specified <paramref name="parameterName"/> in the given list <paramref name="parameters"/> of parameters.
            /// Does not include accept extensions (i.e., parameters after the q quality value parameter)
            /// </summary>
            /// <param name="parameters">The list of parameters to search.</param>
            /// <param name="parameterName">The name of the parameter to find.</param>
            /// <param name="parameterValue">The parameter value of the parameter with the specified <paramref name="parameterName"/>.</param>
            /// <returns>True if a parameter with the specified <paramref name="parameterName"/> was found; otherwise false.</returns>
            private static bool TryFindMediaTypeParameter(IList <KeyValuePair <string, string> > parameters, string parameterName, out string parameterValue)
            {
                Debug.Assert(!string.IsNullOrEmpty(parameterName), "!string.IsNullOrEmpty(parameterName)");

                parameterValue = null;

                if (parameters != null)
                {
                    for (int i = 0; i < parameters.Count; ++i)
                    {
                        string candidateParameterName = parameters[i].Key;

                        if (HttpUtils.CompareMediaTypeParameterNames(parameterName, candidateParameterName))
                        {
                            parameterValue = parameters[i].Value;
                            return(true);
                        }
                    }
                }

                return(false);
            }
Beispiel #11
0
        /// <summary>
        /// Selects the encoding appropriate for this media type specification
        /// (possibly null).
        /// </summary>
        /// <returns>
        /// The encoding explicitly defined on the media type specification, or
        /// the default encoding for well-known media types.
        /// </returns>
        /// <remarks>
        /// As per http://tools.ietf.org/html/rfc2616#section-3.7, the type,
        /// subtype and parameter name attributes are case-insensitive.
        /// </remarks>
        internal Encoding SelectEncoding()
        {
            DebugUtils.CheckNoExternalCallers();
            if (this.parameters != null)
            {
                foreach (string encodingName in
                         this.parameters.Where(parameter =>
                                               HttpUtils.CompareMediaTypeParameterNames(ODataConstants.Charset, parameter.Key))
                         .Select(parameter => parameter.Value.Trim())
                         .Where(encodingName => encodingName.Length > 0))
                {
                    return(EncodingFromName(encodingName));
                }
            }

            // Select the default encoding for this media type.
            if (HttpUtils.CompareMediaTypeNames(MimeConstants.MimeTextType, this.type))
            {
                // HTTP 3.7.1 Canonicalization and Text Defaults
                // "text" subtypes default to ISO-8859-1
                //
                // Unless the subtype is XML, in which case we should default
                // to us-ascii. Instead we return null, to let the encoding
                // in the <?xml ...?> PI win (http://tools.ietf.org/html/rfc3023#section-3.1)
                return(HttpUtils.CompareMediaTypeNames(MimeConstants.MimeXmlSubType, this.subType) ? null : MissingEncoding);
            }

            if (HttpUtils.CompareMediaTypeNames(MimeConstants.MimeApplicationType, this.type) &&
                HttpUtils.CompareMediaTypeNames(MimeConstants.MimeJsonSubType, this.subType))
            {
                // http://tools.ietf.org/html/rfc4627#section-3
                // The default encoding is UTF-8.
                return(FallbackEncoding);
            }

            return(null);
        }
Beispiel #12
0
        private static IList <KeyValuePair <MediaType, string> > RemoveApplicationJsonFromAcceptableMediaTypes(IList <KeyValuePair <MediaType, string> > specifiedTypes, MediaTypeWithFormat[] supportedMediaTypes, string acceptableMediaTypes)
        {
            if (specifiedTypes == null)
            {
                return(null);
            }
            List <KeyValuePair <MediaType, string> > list = null;

            for (int i = specifiedTypes.Count - 1; i >= 0; i--)
            {
                KeyValuePair <MediaType, string> pair = specifiedTypes[i];
                MediaType key = pair.Key;
                if (HttpUtils.CompareMediaTypeNames(key.SubTypeName, "json") && HttpUtils.CompareMediaTypeNames(key.TypeName, "application"))
                {
                    if ((key.Parameters != null) && (from p in key.Parameters
                                                     where HttpUtils.CompareMediaTypeParameterNames(p.Key, "odata")
                                                     select p).Any <KeyValuePair <string, string> >())
                    {
                        continue;
                    }
                    if (list == null)
                    {
                        list = new List <KeyValuePair <MediaType, string> >(specifiedTypes);
                    }
                    list.RemoveAt(i);
                }
            }
            if (list == null)
            {
                return(specifiedTypes);
            }
            if (list.Count == 0)
            {
                throw new ODataContentTypeException(Microsoft.Data.OData.Strings.MediaTypeUtils_DidNotFindMatchingMediaType(string.Join(", ", (from mt in supportedMediaTypes select mt.MediaType.ToText()).ToArray <string>()), acceptableMediaTypes));
            }
            return(list);
        }
Beispiel #13
0
 private static IEnumerable <ODataPayloadKind> DetectPayloadKindImplementation(MediaType contentType)
 {
     if (((HttpUtils.CompareMediaTypeNames("multipart", contentType.TypeName) && HttpUtils.CompareMediaTypeNames("mixed", contentType.SubTypeName)) && (contentType.Parameters != null)) && contentType.Parameters.Any <KeyValuePair <string, string> >(kvp => HttpUtils.CompareMediaTypeParameterNames("boundary", kvp.Key)))
     {
         return(new ODataPayloadKind[] { ODataPayloadKind.Batch });
     }
     return(Enumerable.Empty <ODataPayloadKind>());
 }
Beispiel #14
0
 /// <summary>
 /// Returns a flag indicating whether a given media type parameter name is the Http quality value parameter.
 /// </summary>
 /// <param name="parameterName">The parameter name to check.</param>
 /// <returns>True if the parameter name is for the quality value; otherwise false.</returns>
 private static bool IsQualityValueParameter(string parameterName)
 {
     return(HttpUtils.CompareMediaTypeParameterNames(ODataConstants.HttpQValueParameter, parameterName));
 }
Beispiel #15
0
 private static bool IsQualityValueParameter(string parameterName)
 {
     return(HttpUtils.CompareMediaTypeParameterNames("q", parameterName));
 }
Beispiel #16
0
        /// <summary>
        /// Determine the <see cref="ODataFormat"/> to use for the given <paramref name="contentTypeHeader"/>. If no supported content type
        /// is found an exception is thrown.
        /// </summary>
        /// <param name="contentTypeHeader">The name of the content type to be checked.</param>
        /// <param name="supportedPayloadKinds">All possiblel kinds of payload that can be read with this content type.</param>
        /// <param name="mediaTypeResolver">The media type resolver to use when interpreting the content type.</param>
        /// <param name="mediaType">The media type parsed from the <paramref name="contentTypeHeader"/>.</param>
        /// <param name="encoding">The encoding from the content type or the default encoding for the <paramref name="mediaType" />.</param>
        /// <param name="selectedPayloadKind">
        /// The payload kind that was selected form the list of <paramref name="supportedPayloadKinds"/> for the
        /// specified <paramref name="contentTypeHeader"/>.
        /// </param>
        /// <param name="batchBoundary">The batch boundary read from the content type for batch payloads; otherwise null.</param>
        /// <returns>The <see cref="ODataFormat"/> for the <paramref name="contentTypeHeader"/>.</returns>
        internal static ODataFormat GetFormatFromContentType(
            string contentTypeHeader,
            ODataPayloadKind[] supportedPayloadKinds,
            MediaTypeResolver mediaTypeResolver,
            out MediaType mediaType,
            out Encoding encoding,
            out ODataPayloadKind selectedPayloadKind,
            out string batchBoundary)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(!supportedPayloadKinds.Contains(ODataPayloadKind.Unsupported), "!supportedPayloadKinds.Contains(ODataPayloadKind.Unsupported)");

            ODataFormat format = GetFormatFromContentType(contentTypeHeader, supportedPayloadKinds, mediaTypeResolver, out mediaType, out encoding, out selectedPayloadKind);

            // for batch payloads, read the batch boundary from the content type header; this is the only
            // content type parameter we support (and that is required for batch payloads)
            if (selectedPayloadKind == ODataPayloadKind.Batch)
            {
                KeyValuePair <string, string> boundaryPair = default(KeyValuePair <string, string>);
                IEnumerable <KeyValuePair <string, string> > parameters = mediaType.Parameters;
                if (parameters != null)
                {
                    bool boundaryPairFound = false;
                    foreach (KeyValuePair <string, string> pair in parameters.Where(p => HttpUtils.CompareMediaTypeParameterNames(ODataConstants.HttpMultipartBoundary, p.Key)))
                    {
                        if (boundaryPairFound)
                        {
                            throw new ODataException(Strings.MediaTypeUtils_BoundaryMustBeSpecifiedForBatchPayloads(contentTypeHeader, ODataConstants.HttpMultipartBoundary));
                        }

                        boundaryPair      = pair;
                        boundaryPairFound = true;
                    }
                }

                if (boundaryPair.Key == null)
                {
                    throw new ODataException(Strings.MediaTypeUtils_BoundaryMustBeSpecifiedForBatchPayloads(contentTypeHeader, ODataConstants.HttpMultipartBoundary));
                }

                batchBoundary = boundaryPair.Value;
                ValidationUtils.ValidateBoundaryString(batchBoundary);
            }
            else
            {
                batchBoundary = null;
            }

            return(format);
        }
Beispiel #17
0
        private static void FailOnUnsupportedMediaTypes(MediaType contentType, string contentTypeName, ODataPayloadKind[] supportedPayloadKinds, MediaTypeResolver mediaTypeResolver)
        {
            Func <ODataPayloadKind, IEnumerable <string> > selector = null;

            if (((HttpUtils.CompareMediaTypeNames(contentType.SubTypeName, "json") && HttpUtils.CompareMediaTypeNames(contentType.TypeName, "application")) && (contentType.Parameters != null)) && (from p in contentType.Parameters
                                                                                                                                                                                                     where HttpUtils.CompareMediaTypeParameterNames(p.Key, "odata") && (string.Compare("light", p.Value, StringComparison.OrdinalIgnoreCase) == 0)
                                                                                                                                                                                                     select p).Any <KeyValuePair <string, string> >())
            {
                if (selector == null)
                {
                    selector = pk => from mt in mediaTypeResolver.GetMediaTypesForPayloadKind(pk) select mt.MediaType.ToText();
                }
                throw new ODataContentTypeException(Microsoft.Data.OData.Strings.MediaTypeUtils_CannotDetermineFormatFromContentType(string.Join(", ", supportedPayloadKinds.SelectMany <ODataPayloadKind, string>(selector).ToArray <string>()), contentTypeName));
            }
        }