/// <summary> /// Determines whether the media type has a 'IEEE754Compatible' parameter with the value 'true'. /// </summary> /// <param name="mediaType">The media type to check</param> /// <returns> /// <c>true</c> if the media type has a 'IEEE754Compatible' parameter with the value 'true'; otherwise, <c>false</c>. /// </returns> internal static bool HasIeee754CompatibleSetToTrue(this ODataMediaType mediaType) { return(mediaType.MediaTypeHasParameterWithValue(MimeConstants.MimeIeee754CompatibleParameterName, MimeConstants.MimeParameterValueTrue)); }
/// <summary> /// Gets all payload kinds and their corresponding formats that match the specified content type header. /// </summary> /// <param name="contentTypeHeader">The content type header to get the payload kinds for.</param> /// <param name="mediaTypeResolver">The media type resolver to use when interpreting the content type.</param> /// <param name="contentType">The parsed content type as <see cref="ODataMediaType"/>.</param> /// <param name="encoding">The encoding from the content type or the default encoding from <see cref="ODataMediaType" />.</param> /// <returns>The list of payload kinds and formats supported for the specified <paramref name="contentTypeHeader"/>.</returns> internal static IList <ODataPayloadKindDetectionResult> GetPayloadKindsForContentType(string contentTypeHeader, ODataMediaTypeResolver mediaTypeResolver, out ODataMediaType contentType, out Encoding encoding) { Debug.Assert(!String.IsNullOrEmpty(contentTypeHeader), "Content-Type header must not be null or empty."); string charset; encoding = null; contentType = ParseContentType(contentTypeHeader, out charset); ODataMediaType[] targetTypes = new ODataMediaType[] { contentType }; List <ODataPayloadKindDetectionResult> payloadKinds = new List <ODataPayloadKindDetectionResult>(); IList <ODataMediaTypeFormat> mediaTypesForKind = null; for (int i = 0; i < allSupportedPayloadKinds.Length; ++i) { // get the supported and default media types for the current payload kind ODataPayloadKind payloadKind = allSupportedPayloadKinds[i]; mediaTypesForKind = mediaTypeResolver.GetMediaTypeFormats(payloadKind).ToList(); // match the specified media types against the supported/default ones // and get the format MediaTypeMatchInfo matchInfo = MatchMediaTypes(mediaTypesForKind.Select(smt => smt.MediaType), targetTypes); if (matchInfo != null) { Debug.Assert(matchInfo.TargetTypeIndex == 0, "Invalid target type index detected."); payloadKinds.Add(new ODataPayloadKindDetectionResult(payloadKind, mediaTypesForKind[matchInfo.SourceTypeIndex].Format)); } } if (!String.IsNullOrEmpty(charset)) { encoding = HttpUtils.GetEncodingFromCharsetName(charset); } return(payloadKinds); }
/// <summary> /// Determines whether the media type has a 'streaming' parameter with the value 'true'. /// </summary> /// <param name="mediaType">The media type to check.</param> /// <returns> /// <c>true</c> if the media type has a 'streaming' parameter with the value 'true'; otherwise, <c>false</c>. /// </returns> internal static bool HasStreamingSetToTrue(this ODataMediaType mediaType) { return(mediaType.MediaTypeHasParameterWithValue(MimeConstants.MimeStreamingParameterName, MimeConstants.MimeParameterValueTrue)); }
/// <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(ODataMediaType contentType) { Debug.Assert(contentType != null, "contentType != null"); if (HttpUtils.CompareMediaTypeNames(MimeConstants.MimeTextType, contentType.Type) && HttpUtils.CompareMediaTypeNames(MimeConstants.MimeTextPlain, contentType.SubType)) { return(new ODataPayloadKind[] { ODataPayloadKind.Value }); } return(new ODataPayloadKind[] { ODataPayloadKind.BinaryValue }); }
/// <summary> /// Given the Accept and the Accept-Charset headers of the request message computes the media type, encoding and <see cref="ODataFormat"/> /// to be used for the response message. /// </summary> /// <param name="settings">The message writer settings to use for serializing the response payload.</param> /// <param name="payloadKind">The kind of payload to be serialized as part of the response message.</param> /// <param name="mediaTypeResolver">The media type resolver to use when interpreting the content type.</param> /// <param name="mediaType">The media type to be used in the response message.</param> /// <param name="encoding">The encoding to be used in the response message.</param> /// <returns>The <see cref="ODataFormat"/> used when serializing the response.</returns> internal static ODataFormat GetContentTypeFromSettings( ODataMessageWriterSettings settings, ODataPayloadKind payloadKind, ODataMediaTypeResolver mediaTypeResolver, out ODataMediaType mediaType, out Encoding encoding) { Debug.Assert(settings != null, "settings != null"); // compute format, media type and encoding ODataFormat format; // get the supported and default media types for the specified payload kind IList <ODataMediaTypeFormat> supportedMediaTypes = mediaTypeResolver.GetMediaTypeFormats(payloadKind).ToList(); if (supportedMediaTypes == null || supportedMediaTypes.Count == 0) { throw new ODataContentTypeException(Strings.MediaTypeUtils_DidNotFindMatchingMediaType(null, settings.AcceptableMediaTypes)); } if (settings.UseFormat == true) { Debug.Assert(settings.AcceptableMediaTypes == null, "settings.AcceptableMediaTypes == null"); Debug.Assert(settings.AcceptableCharsets == null, "settings.AcceptableCharsets == null"); mediaType = GetDefaultMediaType(supportedMediaTypes, settings.Format, out format); // NOTE the default media types don't have any parameters (in particular no 'charset' parameters) encoding = mediaType.SelectEncoding(); } else { // parse the accept header into its parts IList <KeyValuePair <ODataMediaType, string> > specifiedTypes = HttpUtils.MediaTypesFromString(settings.AcceptableMediaTypes); // Starting in V3 we replace all occurrences of application/json with application/json;odata.metadata=minimal // before handing the acceptable media types to the conneg code. This is necessary because for an accept // header 'application/json, we want to the result to be 'application/json;odata.metadata=minimal' ConvertApplicationJsonInAcceptableMediaTypes(specifiedTypes); ODataMediaTypeFormat selectedMediaTypeWithFormat; string specifiedCharset = null; if (specifiedTypes == null || specifiedTypes.Count == 0) { selectedMediaTypeWithFormat = supportedMediaTypes[0]; } else { // match the specified media types against the supported/default ones and get the format MediaTypeMatchInfo matchInfo = MatchMediaTypes(specifiedTypes.Select(kvp => kvp.Key), supportedMediaTypes.Select(smt => smt.MediaType).ToArray()); if (matchInfo == null) { // 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, settings.AcceptableMediaTypes)); } selectedMediaTypeWithFormat = supportedMediaTypes[matchInfo.TargetTypeIndex]; specifiedCharset = specifiedTypes[matchInfo.SourceTypeIndex].Value; } format = selectedMediaTypeWithFormat.Format; mediaType = selectedMediaTypeWithFormat.MediaType; // If a charset was specified with the accept header, consider it for the encoding string acceptableCharsets = settings.AcceptableCharsets; if (specifiedCharset != null) { acceptableCharsets = acceptableCharsets == null ? specifiedCharset : specifiedCharset + "," + acceptableCharsets; } encoding = GetEncoding(acceptableCharsets, payloadKind, mediaType, /*useDefaultEncoding*/ true); } return(format); }
/// <summary> /// Configure the media type tables so that Json Light is the first JSON format in the table, except for the /// case of Batch payload kind, where the JsonLight media types is added to the back of the list to preserve /// original behavior of default MIME media type. /// </summary> /// <remarks> /// This is only used in V4 and beyond. /// </remarks> private void AddJsonLightMediaTypes() { var optionalParameters = new[] { new { ParameterName = MimeConstants.MimeMetadataParameterName, Values = new[] { MimeConstants.MimeMetadataParameterValueMinimal, MimeConstants.MimeMetadataParameterValueFull, MimeConstants.MimeMetadataParameterValueNone } }, new { ParameterName = MimeConstants.MimeStreamingParameterName, Values = new[] { MimeConstants.MimeParameterValueTrue, MimeConstants.MimeParameterValueFalse } }, new { ParameterName = MimeConstants.MimeIeee754CompatibleParameterName, Values = new[] { MimeConstants.MimeParameterValueFalse, MimeConstants.MimeParameterValueTrue } }, }; // initial seed for the list will be extended in breadth-first passes over the optional parameters var mediaTypesToAdd = new LinkedList <ODataMediaType>(); mediaTypesToAdd.AddFirst(ApplicationJsonMediaType); foreach (var optionalParameter in optionalParameters) { // go through each one so far and extend it for (LinkedListNode <ODataMediaType> currentNode = mediaTypesToAdd.First; currentNode != null; currentNode = currentNode.Next) { ODataMediaType typeToExtend = currentNode.Value; foreach (string valueToAdd in optionalParameter.Values) { var extendedParameters = new List <KeyValuePair <string, string> >(typeToExtend.Parameters ?? Enumerable.Empty <KeyValuePair <string, string> >()) { new KeyValuePair <string, string>(optionalParameter.ParameterName, valueToAdd) }; var extendedMediaType = new ODataMediaType(typeToExtend.Type, typeToExtend.SubType, extendedParameters); // always match more specific things first mediaTypesToAdd.AddBefore(currentNode, extendedMediaType); } } } List <ODataMediaTypeFormat> mediaTypeWithFormat = mediaTypesToAdd.Select(mediaType => new ODataMediaTypeFormat(mediaType, ODataFormat.Json)).ToList(); foreach (ODataPayloadKind kind in JsonPayloadKinds) { if (kind == ODataPayloadKind.Batch) { // Appending the json media types AFTER the existing MIME media type(s), which is the default media type for Batch payload kind. this.mediaTypesForPayloadKind[(int)kind].AddRange(mediaTypeWithFormat); } else { // For other payload kinds, insert the json media types to the front. this.mediaTypesForPayloadKind[(int)kind].InsertRange(0, mediaTypeWithFormat); } } }
/// <summary>Builds a Content-Type header which includes media type and encoding information.</summary> /// <param name="mediaType">Media type to be used.</param> /// <param name="encoding">Encoding to be used in response, possibly null.</param> /// <returns>The value for the Content-Type header.</returns> internal static string BuildContentType(ODataMediaType mediaType, Encoding encoding) { Debug.Assert(mediaType != null, "mediaType != null"); return(mediaType.ToText(encoding)); }
/// <summary>Gets the best encoding available for the specified charset request.</summary> /// <param name="acceptableCharsets"> /// The Accept-Charset header value (eg: "iso-8859-5, unicode-1-1;q=0.8"). /// </param> /// <param name="mediaType">The media type used to compute the default encoding for the payload.</param> /// <param name="utf8Encoding">The encoding to use for UTF-8 charsets; we use the one without the BOM.</param> /// <param name="defaultEncoding">The encoding to use if no encoding could be computed from the <paramref name="acceptableCharsets"/> or <paramref name="mediaType"/>.</param> /// <returns>An Encoding object appropriate to the specified charset request.</returns> internal static Encoding EncodingFromAcceptableCharsets(string acceptableCharsets, ODataMediaType mediaType, Encoding utf8Encoding, Encoding defaultEncoding) { Debug.Assert(mediaType != null, "mediaType != null"); // Determines the appropriate encoding mapping according to // RFC 2616.14.2 (http://tools.ietf.org/html/rfc2616#section-14.2). Encoding result = null; if (!string.IsNullOrEmpty(acceptableCharsets)) { // PERF: in the future if we find that computing the encoding from the accept charsets is // too expensive we could introduce a cache of original strings to resolved encoding. CharsetPart[] parts = new List <CharsetPart>(AcceptCharsetParts(acceptableCharsets)).ToArray(); // NOTE: List<T>.Sort uses an unstable sort algorithm; if charsets have the same quality value // we want to pick the first one specified so we need a stable sort. KeyValuePair <int, CharsetPart>[] sortedParts = parts.StableSort(delegate(CharsetPart x, CharsetPart y) { return(y.Quality - x.Quality); }); foreach (KeyValuePair <int, CharsetPart> sortedPart in sortedParts) { CharsetPart part = sortedPart.Value; if (part.Quality > 0) { // When UTF-8 is specified, select the version that doesn't use the BOM. if (String.Compare("utf-8", part.Charset, StringComparison.OrdinalIgnoreCase) == 0) { result = utf8Encoding; break; } else { result = GetEncodingFromCharsetName(part.Charset); if (result != null) { break; } // If the charset is not supported it is ignored so other possible charsets are evaluated. } } } } // No Charset was specified, or if charsets were specified, no valid charset was found. // Returning a different charset is also valid. Get the default encoding for the media type. if (result == null) { result = mediaType.SelectEncoding(); if (result == null) { return(defaultEncoding); } } return(result); }
/// <summary> /// Append default values required by OData to specified HTTP header. /// /// When header name is ODataConstants.ContentTypeHeader, if header value is application/json /// append the following default values for 4.0: /// odata.metadata=minimal /// odata.streaming=true /// IEEE754Compatible=false /// append the following default values for 4.01: /// metadata=minimal /// streaming=true /// IEEE754Compatible=false /// </summary> /// <param name="headerName">The name of the header to append default values.</param> /// <param name="headerValue">The original header value string.</param> /// <param name="version">The ODataVersion for which to create the default header value</param> /// <returns>The header value string with appended default values.</returns> public static string AppendDefaultHeaderValue(string headerName, string headerValue, ODataVersion version) { if (string.CompareOrdinal(headerName, ODataConstants.ContentTypeHeader) != 0) { return(headerValue); } if (headerValue == null) { return(null); } var mediaTypeList = HttpUtils.MediaTypesFromString(headerValue); var mediaType = mediaTypeList.Single().Key; var encoding = HttpUtils.GetEncodingFromCharsetName(mediaTypeList.Single().Value); if (string.CompareOrdinal(mediaType.FullTypeName, MimeConstants.MimeApplicationJson) != 0) { return(headerValue); } var extendedParameters = new List <KeyValuePair <string, string> >(); var extendedMediaType = new ODataMediaType(mediaType.Type, mediaType.SubType, extendedParameters); var hasMetadata = false; var hasStreaming = false; var hasIeee754Compatible = false; if (mediaType.Parameters != null) { foreach (var parameter in mediaType.Parameters) { extendedParameters.Add(parameter); if (HttpUtils.IsMetadataParameter(parameter.Key)) { hasMetadata = true; } if (HttpUtils.IsStreamingParameter(parameter.Key)) { hasStreaming = true; } if (string.Compare(parameter.Key, MimeConstants.MimeIeee754CompatibleParameterName, StringComparison.OrdinalIgnoreCase) == 0) { hasIeee754Compatible = true; } } } if (!hasMetadata) { extendedParameters.Add(new KeyValuePair <string, string>( version < ODataVersion.V401 ? MimeConstants.MimeMetadataParameterName : MimeConstants.MimeShortMetadataParameterName, MimeConstants.MimeMetadataParameterValueMinimal)); } if (!hasStreaming) { extendedParameters.Add(new KeyValuePair <string, string>( version < ODataVersion.V401 ? MimeConstants.MimeStreamingParameterName : MimeConstants.MimeShortStreamingParameterName, MimeConstants.MimeParameterValueTrue)); } if (!hasIeee754Compatible) { extendedParameters.Add(new KeyValuePair <string, string>( MimeConstants.MimeIeee754CompatibleParameterName, MimeConstants.MimeParameterValueFalse)); } return(extendedMediaType.ToText(encoding)); }
/// <summary> /// Returns the appropriate content-type for this format. /// </summary> /// <param name="mediaType">The specified media type.</param> /// <param name="encoding">The specified encoding.</param> /// <param name="writingResponse">True if the message writer is being used to write a response.</param> /// <param name="mediaTypeParameters"> The resultant parameters list of the media type. Parameters list could be updated /// when getting content type and should be returned if that is the case. /// </param> /// <returns>The content-type value for the format.</returns> internal virtual string GetContentType(ODataMediaType mediaType, Encoding encoding, bool writingResponse, out IEnumerable <KeyValuePair <string, string> > mediaTypeParameters) { mediaTypeParameters = mediaType.Parameters; return(HttpUtils.BuildContentType(mediaType, encoding)); }