/// <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 static void CheckMediaTypeForWildCards(MediaType mediaType) { if (HttpUtils.CompareMediaTypeNames("*", mediaType.TypeName) || HttpUtils.CompareMediaTypeNames("*", mediaType.SubTypeName)) { throw new ODataException(Microsoft.Data.OData.Strings.ODataMessageReader_WildcardInContentType(mediaType.FullTypeName)); } }
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); }
private static IEnumerable <ODataPayloadKind> DetectPayloadKindImplementation(MediaType contentType) { if (HttpUtils.CompareMediaTypeNames("text", contentType.TypeName) && HttpUtils.CompareMediaTypeNames("text/plain", contentType.SubTypeName)) { return(new ODataPayloadKind[] { ODataPayloadKind.Value }); } return(new ODataPayloadKind[] { ODataPayloadKind.BinaryValue }); }
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> /// Checks whether two media types with subtypes (but without parameters) are equal. /// </summary> /// <param name="firstTypeAndSubtype">The first media type and subtype.</param> /// <param name="secondTypeAndSubtype">The second media type and subtype.</param> /// <returns>true if the <paramref name="firstTypeAndSubtype"/> is equal to <paramref name="secondTypeAndSubtype"/>; otherwise false.</returns> internal static bool MediaTypeAndSubtypeAreEqual(string firstTypeAndSubtype, string secondTypeAndSubtype) { DebugUtils.CheckNoExternalCallers(); ExceptionUtils.CheckArgumentNotNull(firstTypeAndSubtype, "firstTypeAndSubtype"); ExceptionUtils.CheckArgumentNotNull(secondTypeAndSubtype, "secondTypeAndSubtype"); return(HttpUtils.CompareMediaTypeNames(firstTypeAndSubtype, secondTypeAndSubtype)); }
private void MatchTypes(MediaType sourceType, MediaType targetType) { this.MatchingTypeNamePartCount = -1; if (sourceType.TypeName == "*") { this.MatchingTypeNamePartCount = 0; } else if (HttpUtils.CompareMediaTypeNames(sourceType.TypeName, targetType.TypeName)) { if (sourceType.SubTypeName == "*") { this.MatchingTypeNamePartCount = 1; } else if (HttpUtils.CompareMediaTypeNames(sourceType.SubTypeName, targetType.SubTypeName)) { this.MatchingTypeNamePartCount = 2; } } this.QualityValue = 0x3e8; this.SourceTypeParameterCountForMatching = 0; this.MatchingParameterCount = 0; IList <KeyValuePair <string, string> > parameters = sourceType.Parameters; IList <KeyValuePair <string, string> > list2 = targetType.Parameters; bool flag = (list2 != null) && (list2.Count > 0); bool flag2 = (parameters != null) && (parameters.Count > 0); if (flag2) { for (int i = 0; i < parameters.Count; i++) { string str2; KeyValuePair <string, string> pair = parameters[i]; string key = pair.Key; if (IsQualityValueParameter(key)) { KeyValuePair <string, string> pair2 = parameters[i]; this.QualityValue = ParseQualityValue(pair2.Value.Trim()); break; } this.SourceTypeParameterCountForMatching = i + 1; if (flag && TryFindMediaTypeParameter(list2, key, out str2)) { KeyValuePair <string, string> pair3 = parameters[i]; if (string.Compare(pair3.Value.Trim(), str2.Trim(), StringComparison.OrdinalIgnoreCase) == 0) { this.MatchingParameterCount++; } } } } if ((!flag2 || (this.SourceTypeParameterCountForMatching == 0)) || (this.MatchingParameterCount == this.SourceTypeParameterCountForMatching)) { this.MatchingParameterCount = -1; } }
/// <summary> /// Checks for wildcard characters in the <see cref="MediaType"/>. /// </summary> /// <param name="mediaType">The <see cref="MediaType"/> to check.</param> internal static void CheckMediaTypeForWildCards(MediaType mediaType) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(mediaType != null, "mediaType != null"); if (HttpUtils.CompareMediaTypeNames(MimeConstants.MimeStar, mediaType.TypeName) || HttpUtils.CompareMediaTypeNames(MimeConstants.MimeStar, mediaType.SubTypeName)) { throw new ODataException(Strings.ODataMessageReader_WildcardInContentType(mediaType.FullTypeName)); } }
/// <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) { Debug.Assert(contentType != null, "contentType != null"); if (HttpUtils.CompareMediaTypeNames(MimeConstants.MimeTextType, contentType.TypeName) && HttpUtils.CompareMediaTypeNames(MimeConstants.MimeTextPlain, contentType.SubTypeName)) { return(new ODataPayloadKind[] { ODataPayloadKind.Value }); } return(new ODataPayloadKind[] { ODataPayloadKind.BinaryValue }); }
/// <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>()); }
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)); } }
/// <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> /// Parse the content type header value to retrieve the boundary and encoding of a changeset. /// </summary> /// <param name="contentType">The content type to parse.</param> private void DetermineChangesetBoundaryAndEncoding(string contentType) { Debug.Assert(!string.IsNullOrEmpty(contentType), "Should have validated that non-null, non-empty content type header exists."); MediaType mediaType; ODataPayloadKind readerPayloadKind; MediaTypeUtils.GetFormatFromContentType( contentType, new ODataPayloadKind[] { ODataPayloadKind.Batch }, MediaTypeResolver.DefaultMediaTypeResolver, out mediaType, out this.changesetEncoding, out readerPayloadKind, out this.changesetBoundary); Debug.Assert(readerPayloadKind == ODataPayloadKind.Batch, "Must find batch payload kind."); Debug.Assert(this.changesetBoundary != null && this.changesetBoundary.Length > 0, "Boundary string should have been validated by now."); Debug.Assert(HttpUtils.CompareMediaTypeNames(MimeConstants.MimeMultipartMixed, mediaType.FullTypeName), "Must be multipart/mixed media type."); }
/// <summary> /// Checks for an illegal media type that cannot be caught during content negotiation/resolution /// since it would match an unsupported media type. /// </summary> /// <param name="mediaType">The parsed media type to check.</param> /// <returns>true if the media type is illegal (and we should fail); otherwise false.</returns> internal bool IsIllegalMediaType(MediaType mediaType) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(mediaType != null, "mediaType != null"); // We fail for JSON-Light for all versions < V3. // 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 (this.version < ODataVersion.V3 && HttpUtils.CompareMediaTypeNames(mediaType.SubTypeName, MimeConstants.MimeJsonSubType) && HttpUtils.CompareMediaTypeNames(mediaType.TypeName, MimeConstants.MimeApplicationType) && (mediaType.MediaTypeHasParameterWithValue(MimeConstants.MimeODataParameterName, MimeConstants.MimeODataParameterValueMinimalMetadata) || mediaType.MediaTypeHasParameterWithValue(MimeConstants.MimeODataParameterName, MimeConstants.MimeODataParameterValueFullMetadata) || mediaType.MediaTypeHasParameterWithValue(MimeConstants.MimeODataParameterName, MimeConstants.MimeODataParameterValueNoMetadata))) { 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); }
/// <summary> /// Matches the source type against the media type. /// </summary> /// <param name="sourceType">The source <see cref="MediaType"/> to match against the target type.</param> /// <param name="targetType">The target <see cref="MediaType"/> to match against the source type.</param> private void MatchTypes(MediaType sourceType, MediaType targetType) { this.MatchingTypeNamePartCount = -1; if (sourceType.TypeName == "*") { this.MatchingTypeNamePartCount = 0; } else { if (HttpUtils.CompareMediaTypeNames(sourceType.TypeName, targetType.TypeName)) { if (sourceType.SubTypeName == "*") { // only type matches this.MatchingTypeNamePartCount = 1; } else if (HttpUtils.CompareMediaTypeNames(sourceType.SubTypeName, targetType.SubTypeName)) { // both type and subtype match this.MatchingTypeNamePartCount = 2; } } } this.QualityValue = DefaultQualityValue; this.SourceTypeParameterCountForMatching = 0; this.MatchingParameterCount = 0; IList <KeyValuePair <string, string> > sourceParameters = sourceType.Parameters; IList <KeyValuePair <string, string> > targetParameters = targetType.Parameters; bool targetHasParams = targetParameters != null && targetParameters.Count > 0; bool sourceHasParams = sourceParameters != null && sourceParameters.Count > 0; if (sourceHasParams) { for (int i = 0; i < sourceParameters.Count; ++i) { string parameterName = sourceParameters[i].Key; if (IsQualityValueParameter(parameterName)) { // once we hit the q-value in the parameters we know that only accept-params will follow // that don't contribute to the matching. Parse the quality value but don't continue processing // parameters. this.QualityValue = ParseQualityValue(sourceParameters[i].Value.Trim()); break; } this.SourceTypeParameterCountForMatching = i + 1; if (targetHasParams) { // find the current parameter name in the set of parameters of the candidate and compare the value; // if they match increase the result count // NOTE: according to RFC 2045, Section 2, parameter values are case sensitive per default (while // type values, subtype values and parameter names are case-insensitive); however, we // are more relaxed in ODL and allow case insensitive values. string parameterValue; if (TryFindMediaTypeParameter(targetParameters, parameterName, out parameterValue) && string.Compare(sourceParameters[i].Value.Trim(), parameterValue.Trim(), StringComparison.OrdinalIgnoreCase) == 0) { this.MatchingParameterCount++; } } } } // if the source does not have parameters or it only has accept extensions // (parameters after the q value) or we match all the paramters we // have a perfect parameter match. if (!sourceHasParams || this.SourceTypeParameterCountForMatching == 0 || this.MatchingParameterCount == this.SourceTypeParameterCountForMatching) { this.MatchingParameterCount = -1; } }