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); }
/// <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); } } } }
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); }
/// <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>()); }
/// <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)); }
/// <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); }
/// <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)); } }
/// <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()); } }
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); }
/// <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); }
/// <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); }
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); }
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>()); }
/// <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)); }
private static bool IsQualityValueParameter(string parameterName) { return(HttpUtils.CompareMediaTypeParameterNames("q", parameterName)); }
/// <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); }
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)); } }