/// <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(MediaType mediaType, Encoding encoding) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(mediaType != null, "mediaType != null"); string mediaTypeAsText = mediaType.ToText(); if (encoding == null) { return mediaTypeAsText; } else { return string.Concat(mediaTypeAsText, ";", HttpConstants.Charset, "=", encoding.WebName); } }
/// <summary> /// Gets a number of non-* matching type name parts and parameters; returns the number of matching type name parts or -1 if not matching at all. /// The <paramref name="matchingParameters"/> represent the number of matched parameters or -1 if there are no parameters to match and the candidate /// type also has no parameters. /// </summary> /// <param name="candidate">Candidate media type to match.</param> /// <param name="matchingParameters">The number of matched parameters or -1 if there are no parameters to match and the <paramref name="candidate"/> does not have parameters either.</param> /// <param name="qualityValue">The quality value of the candidate (or -1 if none is specified).</param> /// <param name="parameterCount">The number of parameters of this type that are not accept-parameters (and thus ignored).</param> /// <returns>Returns the number of matching type name parts or -1 if not matching at all. The <paramref name="matchingParameters"/> /// represent the number of matched parameters or -1 if there are no parameters to match and the candidate /// type also has no parameters. /// </returns> internal int GetMatchingParts(MediaType candidate, out int matchingParameters, out int qualityValue, out int parameterCount) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(candidate != null, "candidate must not be null."); int matchedNameParts = -1; if (this.type == "*") { matchedNameParts = 0; } else { if (HttpUtils.CompareMediaTypeNames(this.type, candidate.type)) { if (this.subType == "*") { // only type matches matchedNameParts = 1; } else if (HttpUtils.CompareMediaTypeNames(this.subType, candidate.subType)) { // both type and subtype match matchedNameParts = 2; } } } qualityValue = DefaultQualityValue; parameterCount = 0; matchingParameters = 0; // NOTE: we know that the candidates don't have accept parameters so we can rely // on the total parameter count. IList<KeyValuePair<string, string>> candidateParameters = candidate.Parameters; bool candidateHasParams = candidateParameters != null && candidateParameters.Count > 0; bool thisHasParams = this.parameters != null && this.parameters.Count > 0; if (thisHasParams) { for (int i = 0; i < this.parameters.Count; ++i) { string parameterName = this.parameters[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. qualityValue = ParseQualityValue(this.parameters[i].Value.Trim()); break; } parameterCount = i + 1; if (candidateHasParams) { // find the current parameter name in the set of parameters of the candidate and compare the value; // if they match increase the result count string parameterValue; if (TryFindMediaTypeParameter(candidateParameters, parameterName, out parameterValue) && string.Compare(this.parameters[i].Value.Trim(), parameterValue.Trim(), StringComparison.OrdinalIgnoreCase) == 0) { matchingParameters++; } } } } // if we have no non-accept parameters special rules apply if (!thisHasParams || parameterCount == 0) { if (candidateHasParams) { if (matchedNameParts == 0 || matchedNameParts == 1) { // this is a media range specification using the '*' wildcard. // we assume that all parameters are matched for such ranges so the server // will pick the most specific one matchingParameters = candidateParameters.Count; } else { // the candidate type has parameters while this type has not; // return 0 to indicate that none of the candidate's parameters match. matchingParameters = 0; } } else { // neither this type nor the candidate have parameters; in this case we return -1 to indicate // that the candidate parameters are a perfect. matchingParameters = -1; } } return matchedNameParts; }
/// <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 specifed charset request.</returns> internal static Encoding EncodingFromAcceptableCharsets(string acceptableCharsets, MediaType mediaType, Encoding utf8Encoding, Encoding defaultEncoding) { DebugUtils.CheckNoExternalCallers(); 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. List<CharsetPart> parts = new List<CharsetPart>(AcceptCharsetParts(acceptableCharsets)); parts.Sort(delegate(CharsetPart x, CharsetPart y) { return y.Quality - x.Quality; }); foreach (CharsetPart part in parts) { 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 specifed, 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> /// Parses the accepted charsets and matches them against the supported encodings for the given <paramref name="payloadKind"/>. /// </summary> /// <param name="acceptCharsetHeader">The Accept-Charset header of the request.</param> /// <param name="payloadKind">The <see cref="ODataPayloadKind"/> for which to compute the encoding.</param> /// <param name="mediaType">The media type used to compute the default encoding for the payload.</param> /// <returns>The encoding to be used for the response.</returns> private static Encoding GetEncoding(string acceptCharsetHeader, ODataPayloadKind payloadKind, MediaType mediaType) { if (payloadKind == ODataPayloadKind.BinaryValue) { return null; } else { // TODO: we currently assume that the media type does not have a 'charset' parameter specified. This is true for // the current uses of this method. Consider making this general purpose and also include the 'charset' // parameter on the media type into the resolution process. (Currently the media type is only used to // compute the default encoding). return HttpUtils.EncodingFromAcceptableCharsets(acceptCharsetHeader, mediaType, encodingUtf8NoPreamble, encodingUtf8NoPreamble); } }
/// <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 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="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( ODataWriterSettings settings, ODataPayloadKind payloadKind, out MediaType mediaType, out Encoding encoding) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(settings != null, "settings != null"); // compute format, media type and encoding ODataFormat format; if (settings.AcceptableMediaTypes != null) { mediaType = GetMediaType(settings.AcceptableMediaTypes, payloadKind, out format); encoding = GetEncoding(settings.AcceptableCharsets, payloadKind, mediaType); } else { mediaType = GetDefaultMediaType(payloadKind, settings.Format, out format); encoding = mediaType.SelectEncoding(); } return format; }
/// <summary> /// Parses the accepted charsets and matches them against the supported encodings for the given <paramref name="payloadKind"/>. /// </summary> /// <param name="acceptCharsetHeader">The Accept-Charset header of the request.</param> /// <param name="payloadKind">The <see cref="ODataPayloadKind"/> for which to compute the encoding.</param> /// <param name="mediaType">The media type used to compute the default encoding for the payload.</param> /// <returns>The encoding to be used for the response.</returns> private static Encoding GetEncoding(string acceptCharsetHeader, ODataPayloadKind payloadKind, MediaType mediaType) { if (payloadKind == ODataPayloadKind.BinaryValue) { return(null); } else { // TODO: we currently assume that the media type does not have a 'charset' parameter specified. This is true for // the current uses of this method. Consider making this general purpose and also include the 'charset' // parameter on the media type into the resolution process. (Currently the media type is only used to // compute the default encoding). return(HttpUtils.EncodingFromAcceptableCharsets(acceptCharsetHeader, mediaType, encodingUtf8NoPreamble, encodingUtf8NoPreamble)); } }