/// <summary> /// Gets the value associated with the specified key. /// </summary> /// <param name="key">The key whose value to get.</param> /// <param name="value">The value associated with the specified key, if the key is found; otherwise, null.</param> /// <returns>true if the cache contains an element with the specified key; otherwise, false.</returns> public bool TryGetValue(MatchInfoCacheKey key, out MediaTypeMatchInfo value) { lock (this.dict) { return(dict.TryGetValue(key, out value)); } }
/// <summary> /// Matches the supported media types against the list of media types specified in the Accept header or ContentType header of the message. Matching follows the /// rules for media type matching as described in RFC 2616. /// </summary> /// <param name="sourceTypes">The set of media types to be matched against the <paramref name="targetTypes"/>.</param> /// <param name="targetTypes">The set of media types the <paramref name="sourceTypes"/> will be matched against.</param> /// <returns>The best <see cref="MediaTypeMatchInfo"/> found during the matching process or null if no match was found.</returns> private static MediaTypeMatchInfo MatchMediaTypes(IEnumerable <MediaType> sourceTypes, MediaType[] targetTypes) { Debug.Assert(sourceTypes != null, "sourceTypes != null"); Debug.Assert(targetTypes != null, "targetTypes != null"); MediaTypeMatchInfo selectedMatchInfo = null; int sourceIndex = 0; if (sourceTypes != null) { foreach (MediaType sourceType in sourceTypes) { int targetIndex = 0; foreach (MediaType targetType in targetTypes) { // match the type name parts and parameters of the media type MediaTypeMatchInfo currentMatchInfo = new MediaTypeMatchInfo(sourceType, targetType, sourceIndex, targetIndex); if (!currentMatchInfo.IsMatch) { targetIndex++; continue; } if (selectedMatchInfo == null) { selectedMatchInfo = currentMatchInfo; } else { int comparisonResult = selectedMatchInfo.CompareTo(currentMatchInfo); if (comparisonResult < 0) { // If the selected match is less specific than the current match, use the current match. selectedMatchInfo = currentMatchInfo; } } targetIndex++; } sourceIndex++; } } if (selectedMatchInfo == null) { return(null); } return(selectedMatchInfo); }
internal static ODataFormat GetContentTypeFromSettings(ODataMessageWriterSettings settings, ODataPayloadKind payloadKind, MediaTypeResolver mediaTypeResolver, out MediaType mediaType, out Encoding encoding) { ODataFormat format; MediaTypeWithFormat format2; MediaTypeWithFormat[] mediaTypesForPayloadKind = mediaTypeResolver.GetMediaTypesForPayloadKind(payloadKind); if ((mediaTypesForPayloadKind == null) || (mediaTypesForPayloadKind.Length == 0)) { throw new ODataContentTypeException(Microsoft.Data.OData.Strings.MediaTypeUtils_DidNotFindMatchingMediaType(null, settings.AcceptableMediaTypes)); } if (settings.UseFormat == true) { mediaType = GetDefaultMediaType(mediaTypesForPayloadKind, settings.Format, out format); encoding = mediaType.SelectEncoding(); return(format); } IList <KeyValuePair <MediaType, string> > specifiedTypes = HttpUtils.MediaTypesFromString(settings.AcceptableMediaTypes); if (((ODataVersion)settings.Version) == ODataVersion.V3) { specifiedTypes = RemoveApplicationJsonFromAcceptableMediaTypes(specifiedTypes, mediaTypesForPayloadKind, settings.AcceptableMediaTypes); } string str = null; if ((specifiedTypes == null) || (specifiedTypes.Count == 0)) { format2 = mediaTypesForPayloadKind[0]; } else { MediaTypeMatchInfo info = MatchMediaTypes(from kvp in specifiedTypes select kvp.Key, (from smt in mediaTypesForPayloadKind select smt.MediaType).ToArray <MediaType>()); if (info == null) { throw new ODataContentTypeException(Microsoft.Data.OData.Strings.MediaTypeUtils_DidNotFindMatchingMediaType(string.Join(", ", (from mt in mediaTypesForPayloadKind select mt.MediaType.ToText()).ToArray <string>()), settings.AcceptableMediaTypes)); } format2 = mediaTypesForPayloadKind[info.TargetTypeIndex]; KeyValuePair <MediaType, string> pair = specifiedTypes[info.SourceTypeIndex]; str = pair.Value; } format = format2.Format; mediaType = format2.MediaType; string acceptableCharsets = settings.AcceptableCharsets; if (str != null) { acceptableCharsets = (acceptableCharsets == null) ? str : (str + "," + acceptableCharsets); } encoding = GetEncoding(acceptableCharsets, payloadKind, mediaType, true); return(format); }
/// <summary> /// Adds an element with the provided key and value to the cache. /// </summary> /// <param name="key">The key of the element to add.</param> /// <param name="value">The value of the element to add.</param> public void Add(MatchInfoCacheKey key, MediaTypeMatchInfo value) { lock (this.dict) { if (!dict.ContainsKey(key)) { if (dict.Count == maxSize) { dict.Clear(); } dict.Add(key, value); } } }
/// <summary> /// Determine the <see cref="ODataFormat"/> to use for the given <paramref name="contentTypeName"/>. If no supported content type /// is found an exception is thrown. /// </summary> /// <param name="contentTypeName">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="contentTypeName"/>.</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="contentTypeName"/>. /// </param> /// <returns>The <see cref="ODataFormat"/> for the <paramref name="contentTypeName"/>.</returns> private static ODataFormat GetFormatFromContentType( string contentTypeName, ODataPayloadKind[] supportedPayloadKinds, MediaTypeResolver mediaTypeResolver, out MediaType mediaType, out Encoding encoding, out ODataPayloadKind selectedPayloadKind) { Debug.Assert(!supportedPayloadKinds.Contains(ODataPayloadKind.Unsupported), "!supportedPayloadKinds.Contains(ODataPayloadKind.Unsupported)"); string charset; mediaType = ParseContentType(contentTypeName, out charset); // Special code to handle unsupported media types that cannot be caught by conneg // since they are sub-types of valid media types (i.e., application/json;odata=light would // match application/json). // NOTE: once we start supporting Json Light we have to make this check version-aware. FailOnUnsupportedMediaTypes(mediaType, contentTypeName, supportedPayloadKinds, mediaTypeResolver); MediaTypeWithFormat[] supportedMediaTypes = null; for (int i = 0; i < supportedPayloadKinds.Length; ++i) { // get the supported and default media types for the current payload kind ODataPayloadKind supportedPayloadKind = supportedPayloadKinds[i]; supportedMediaTypes = mediaTypeResolver.GetMediaTypesForPayloadKind(supportedPayloadKind); // match the specified media types against the supported/default ones // and get the format MediaTypeMatchInfo matchInfo = MatchMediaTypes(supportedMediaTypes.Select(smt => smt.MediaType), new MediaType[] { mediaType }); if (matchInfo != null) { Debug.Assert(matchInfo.TargetTypeIndex == 0, "Invalid target type index detected."); selectedPayloadKind = supportedPayloadKind; encoding = GetEncoding(charset, selectedPayloadKind, mediaType, /*useDefaultEncoding*/ false); return(supportedMediaTypes[matchInfo.SourceTypeIndex].Format); } } // We're calling the ToArray here since not all platforms support the string.Join which takes IEnumerable. Debug.Assert(supportedMediaTypes != null, "supportedMediaTypes != null"); string supportedTypesAsString = string.Join(", ", supportedPayloadKinds.SelectMany(pk => mediaTypeResolver.GetMediaTypesForPayloadKind(pk).Select(mt => mt.MediaType.ToText())).ToArray()); throw new ODataContentTypeException(Strings.MediaTypeUtils_CannotDetermineFormatFromContentType(supportedTypesAsString, contentTypeName)); }
internal static IList <ODataPayloadKindDetectionResult> GetPayloadKindsForContentType(string contentTypeHeader, MediaTypeResolver mediaTypeResolver, out MediaType contentType) { string str; contentType = ParseContentType(contentTypeHeader, out str); MediaType[] targetTypes = new MediaType[] { contentType }; List <ODataPayloadKindDetectionResult> list = new List <ODataPayloadKindDetectionResult>(); MediaTypeWithFormat[] mediaTypesForPayloadKind = null; for (int i = 0; i < allSupportedPayloadKinds.Length; i++) { ODataPayloadKind payloadKind = allSupportedPayloadKinds[i]; mediaTypesForPayloadKind = mediaTypeResolver.GetMediaTypesForPayloadKind(payloadKind); MediaTypeMatchInfo info = MatchMediaTypes(from smt in mediaTypesForPayloadKind select smt.MediaType, targetTypes); if (info != null) { list.Add(new ODataPayloadKindDetectionResult(payloadKind, mediaTypesForPayloadKind[info.SourceTypeIndex].Format)); } } return(list); }
private static ODataFormat GetFormatFromContentType(string contentTypeName, ODataPayloadKind[] supportedPayloadKinds, MediaTypeResolver mediaTypeResolver, out MediaType mediaType, out Encoding encoding, out ODataPayloadKind selectedPayloadKind) { string str; mediaType = ParseContentType(contentTypeName, out str); FailOnUnsupportedMediaTypes(mediaType, contentTypeName, supportedPayloadKinds, mediaTypeResolver); MediaTypeWithFormat[] mediaTypesForPayloadKind = null; for (int i = 0; i < supportedPayloadKinds.Length; i++) { ODataPayloadKind payloadKind = supportedPayloadKinds[i]; mediaTypesForPayloadKind = mediaTypeResolver.GetMediaTypesForPayloadKind(payloadKind); MediaTypeMatchInfo info = MatchMediaTypes(from smt in mediaTypesForPayloadKind select smt.MediaType, new MediaType[] { mediaType }); if (info != null) { selectedPayloadKind = payloadKind; encoding = GetEncoding(str, selectedPayloadKind, mediaType, false); return(mediaTypesForPayloadKind[info.SourceTypeIndex].Format); } } throw new ODataContentTypeException(Microsoft.Data.OData.Strings.MediaTypeUtils_CannotDetermineFormatFromContentType(string.Join(", ", (from pk in supportedPayloadKinds select from mt in mediaTypeResolver.GetMediaTypesForPayloadKind(pk) select mt.MediaType.ToText()).SelectMany(x => x).ToArray <string>()), contentTypeName)); }
private static MediaTypeMatchInfo MatchMediaTypes(IEnumerable <MediaType> sourceTypes, MediaType[] targetTypes) { MediaTypeMatchInfo info = null; int sourceIndex = 0; if (sourceTypes != null) { foreach (MediaType type in sourceTypes) { int targetIndex = 0; foreach (MediaType type2 in targetTypes) { MediaTypeMatchInfo other = new MediaTypeMatchInfo(type, type2, sourceIndex, targetIndex); if (!other.IsMatch) { targetIndex++; } else { if (info == null) { info = other; } else if (info.CompareTo(other) < 0) { info = other; } targetIndex++; } } sourceIndex++; } } if (info == null) { return(null); } return(info); }
/// <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="MediaType"/>.</param> /// <param name="encoding">The encoding from the content type or the default encoding from <see cref="MediaType" />.</param> /// <returns>The list of payload kinds and formats supported for the specified <paramref name="contentTypeHeader"/>.</returns> internal static IList <ODataPayloadKindDetectionResult> GetPayloadKindsForContentType(string contentTypeHeader, MediaTypeResolver mediaTypeResolver, out MediaType contentType, out Encoding encoding) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(!String.IsNullOrEmpty(contentTypeHeader), "Content-Type header must not be null or empty."); string charset; encoding = null; contentType = ParseContentType(contentTypeHeader, out charset); MediaType[] targetTypes = new MediaType[] { contentType }; List <ODataPayloadKindDetectionResult> payloadKinds = new List <ODataPayloadKindDetectionResult>(); IList <MediaTypeWithFormat> 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.GetMediaTypesForPayloadKind(payloadKind); // 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> /// 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, MediaTypeResolver mediaTypeResolver, out MediaType mediaType, out Encoding encoding) { DebugUtils.CheckNoExternalCallers(); 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 MediaTypeWithFormat[] supportedMediaTypes = mediaTypeResolver.GetMediaTypesForPayloadKind(payloadKind); if (supportedMediaTypes == null || supportedMediaTypes.Length == 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 <MediaType, string> > specifiedTypes = HttpUtils.MediaTypesFromString(settings.AcceptableMediaTypes); if (settings.Version == ODataVersion.V3) { specifiedTypes = RemoveApplicationJsonFromAcceptableMediaTypes(specifiedTypes, supportedMediaTypes, settings.AcceptableMediaTypes); } MediaTypeWithFormat 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> /// 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, MediaTypeResolver mediaTypeResolver, out MediaType mediaType, out Encoding encoding) { DebugUtils.CheckNoExternalCallers(); 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 <MediaTypeWithFormat> supportedMediaTypes = mediaTypeResolver.GetMediaTypesForPayloadKind(payloadKind); 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 <MediaType, string> > specifiedTypes = HttpUtils.MediaTypesFromString(settings.AcceptableMediaTypes); // Starting in V3 we replace all occurrences of application/json with application/json;odata=minimalmetadata // before handing the acceptable media types to the conneg code. This is necessary because for an accept // header 'application/json, application/json;odata=verbose' we want to 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). if (settings.Version >= ODataVersion.V3) { ConvertApplicationJsonInAcceptableMediaTypes(specifiedTypes); } MediaTypeWithFormat 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); }