/// <summary> /// Converts all occurrences of the 'application/json' media type to 'application/json;odata.metadata=minimal'. /// This is necessary because for an accept header 'application/json /// we want the result to be 'application/json;odata.metadata=minimal' /// </summary> /// <param name="specifiedTypes">The parsed acceptable media types.</param> private static void ConvertApplicationJsonInAcceptableMediaTypes(IList <KeyValuePair <ODataMediaType, string> > specifiedTypes) { if (specifiedTypes == null) { return; } for (int i = 0; i < specifiedTypes.Count; ++i) { ODataMediaType mediaType = specifiedTypes[i].Key; if (HttpUtils.CompareMediaTypeNames(mediaType.SubType, MimeConstants.MimeJsonSubType) && HttpUtils.CompareMediaTypeNames(mediaType.Type, MimeConstants.MimeApplicationType)) { if (mediaType.Parameters == null || !mediaType.Parameters.Any(p => HttpUtils.CompareMediaTypeParameterNames(p.Key, MimeConstants.MimeMetadataParameterName))) { // application/json detected; convert it to Json Light IList <KeyValuePair <string, string> > existingParams = mediaType.Parameters != null?mediaType.Parameters.ToList() : null; 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.MimeMetadataParameterName, MimeConstants.MimeMetadataParameterValueMinimal)); if (existingParams != null) { newParams.AddRange(existingParams); } specifiedTypes[i] = new KeyValuePair <ODataMediaType, string>(new ODataMediaType(mediaType.Type, mediaType.SubType, newParams), specifiedTypes[i].Value); } } } }
/// <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) { ExceptionUtils.CheckArgumentNotNull(firstTypeAndSubtype, "firstTypeAndSubtype"); ExceptionUtils.CheckArgumentNotNull(secondTypeAndSubtype, "secondTypeAndSubtype"); return(HttpUtils.CompareMediaTypeNames(firstTypeAndSubtype, secondTypeAndSubtype)); }
/// <summary> /// Checks for wildcard characters in the <see cref="ODataMediaType"/>. /// </summary> /// <param name="mediaType">The <see cref="ODataMediaType"/> to check.</param> internal static void CheckMediaTypeForWildCards(ODataMediaType mediaType) { Debug.Assert(mediaType != null, "mediaType != null"); if (HttpUtils.CompareMediaTypeNames(MimeConstants.MimeStar, mediaType.Type) || HttpUtils.CompareMediaTypeNames(MimeConstants.MimeStar, mediaType.SubType)) { throw new ODataContentTypeException(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(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> /// 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) { // 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.Type) && HttpUtils.CompareMediaTypeNames(MimeConstants.MimeMixedSubType, contentType.SubType) && contentType.Parameters != null && contentType.Parameters.Any(kvp => HttpUtils.CompareMediaTypeParameterNames(ODataConstants.HttpMultipartBoundary, kvp.Key))) { return(new ODataPayloadKind[] { ODataPayloadKind.Batch }); } return(Enumerable.Empty <ODataPayloadKind>()); }
private static bool IsJsonMetadata(ODataMediaType contentType) { // by default, it's XML metadata if (contentType == null) { return(false); } if (HttpUtils.CompareMediaTypeNames(MimeConstants.MimeApplicationType, contentType.Type) && HttpUtils.CompareMediaTypeNames(MimeConstants.MimeJsonSubType, contentType.SubType)) { return(true); } return(false); }
/// <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."); ODataMediaType mediaType; ODataPayloadKind readerPayloadKind; MediaTypeUtils.GetFormatFromContentType( contentType, new ODataPayloadKind[] { ODataPayloadKind.Batch }, this.mediaTypeResolver, 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> /// 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() { 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 : MediaTypeUtils.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(MediaTypeUtils.FallbackEncoding); } return(null); }
/// <summary> /// Matches the source type against the media type. /// </summary> /// <param name="sourceType">The source <see cref="ODataMediaType"/> to match against the target type.</param> /// <param name="targetType">The target <see cref="ODataMediaType"/> to match against the source type.</param> private void MatchTypes(ODataMediaType sourceType, ODataMediaType targetType) { this.MatchingTypeNamePartCount = -1; if (sourceType.Type == "*") { this.MatchingTypeNamePartCount = 0; } else { if (HttpUtils.CompareMediaTypeNames(sourceType.Type, targetType.Type)) { if (sourceType.SubType == "*") { // only type matches this.MatchingTypeNamePartCount = 1; } else if (HttpUtils.CompareMediaTypeNames(sourceType.SubType, targetType.SubType)) { // both type and subtype match this.MatchingTypeNamePartCount = 2; } } } this.QualityValue = DefaultQualityValue; this.SourceTypeParameterCountForMatching = 0; this.MatchingParameterCount = 0; IList <KeyValuePair <string, string> > sourceParameters = sourceType.Parameters != null?sourceType.Parameters.ToList() : null; IList <KeyValuePair <string, string> > targetParameters = targetType.Parameters != null?targetType.Parameters.ToList() : null; 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 parameters we // have a perfect parameter match. if (!sourceHasParams || this.SourceTypeParameterCountForMatching == 0 || this.MatchingParameterCount == this.SourceTypeParameterCountForMatching) { this.MatchingParameterCount = -1; } }