Imported Upstream version 3.6.0

Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
This commit is contained in:
Jo Shields
2014-08-13 10:39:27 +01:00
commit a575963da9
50588 changed files with 8155799 additions and 0 deletions

View File

@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Net.Http
{
internal static class CloneableExtensions
{
/// <summary>
/// Convenience method for cloning objects that implement <see cref="ICloneable"/> explicitly.
/// </summary>
/// <typeparam name="T">The type of the cloneable object.</typeparam>
/// <param name="value">The cloneable object.</param>
/// <returns>The result of cloning the <paramref name="value"/>.</returns>
internal static T Clone<T>(this T value) where T : ICloneable
{
return (T)value.Clone();
}
}
}

View File

@ -0,0 +1,139 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.Contracts;
using System.IO;
using System.Net.Http.Headers;
using System.Net.Http.Internal;
using System.Threading.Tasks;
using System.Web.Http;
namespace System.Net.Http.Formatting
{
/// <summary>
/// Base class for writing a synchronous formatter on top of the asynchronous formatter infrastructure.
/// This does not guarantee non-blocking threads. The only way to guarantee that we don't block a thread on IO is
/// to use the asynchronous <see cref="MediaTypeFormatter"/>.
/// </summary>
public abstract class BufferedMediaTypeFormatter : MediaTypeFormatter
{
private const int MinBufferSize = 0;
private const int DefaultBufferSize = 16 * 1024;
private int _bufferSizeInBytes = DefaultBufferSize;
/// <summary>
/// Suggested size of buffer to use with streams, in bytes. The default size is 16K.
/// </summary>
public int BufferSize
{
get { return _bufferSizeInBytes; }
set
{
if (value < MinBufferSize)
{
throw Error.ArgumentGreaterThanOrEqualTo("value", value, MinBufferSize);
}
_bufferSizeInBytes = value;
}
}
/// <summary>
/// Writes synchronously to the buffered stream.
/// </summary>
/// <remarks>
/// An implementation of this method should close <paramref name="stream"/> upon completion.
/// </remarks>
/// <param name="type">The type of the object to write.</param>
/// <param name="value">The object value to write. It may be <c>null</c>.</param>
/// <param name="stream">The <see cref="Stream"/> to which to write.</param>
/// <param name="contentHeaders">The <see cref="HttpContentHeaders"/> if available. Note that
/// modifying the headers will have no effect on the generated HTTP message; they should only be used to guide the writing.</param>
public virtual void WriteToStream(Type type, object value, Stream stream, HttpContentHeaders contentHeaders)
{
throw new NotSupportedException(RS.Format(Properties.Resources.MediaTypeFormatterCannotWriteSync, GetType().Name));
}
/// <summary>
/// Reads synchronously from the buffered stream.
/// </summary>
/// <remarks>
/// An implementation of this method should close <paramref name="stream"/> upon completion.
/// </remarks>
/// <param name="type">The type of the object to deserialize.</param>
/// <param name="stream">The <see cref="Stream"/> to read.</param>
/// <param name="contentHeaders">The <see cref="HttpContentHeaders"/> if available.</param>
/// <param name="formatterLogger">The <see cref="IFormatterLogger"/> to log events to.</param>
/// <returns>An object of the given type.</returns>
public virtual object ReadFromStream(Type type, Stream stream, HttpContentHeaders contentHeaders, IFormatterLogger formatterLogger)
{
throw new NotSupportedException(RS.Format(Properties.Resources.MediaTypeFormatterCannotReadSync, GetType().Name));
}
// Sealed because derived classes shouldn't override the async version. Override sync version instead.
public sealed override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContentHeaders contentHeaders, TransportContext transportContext)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
if (stream == null)
{
throw new ArgumentNullException("stream");
}
return TaskHelpers.RunSynchronously(
() =>
{
using (Stream bufferedStream = GetBufferStream(stream, _bufferSizeInBytes))
{
WriteToStream(type, value, bufferedStream, contentHeaders);
}
});
}
public sealed override Task<object> ReadFromStreamAsync(Type type, Stream stream, HttpContentHeaders contentHeaders, IFormatterLogger formatterLogger)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
if (stream == null)
{
throw new ArgumentNullException("stream");
}
return TaskHelpers.RunSynchronously<object>(
() =>
{
if (contentHeaders != null && contentHeaders.ContentLength == 0)
{
return GetDefaultValueForType(type);
}
using (Stream bufferedStream = GetBufferStream(stream, _bufferSizeInBytes))
{
return ReadFromStream(type, bufferedStream, contentHeaders, formatterLogger);
}
});
}
private static Stream GetBufferStream(Stream innerStream, int bufferSize)
{
Contract.Assert(innerStream != null);
// We wrap the inner stream in a non-closing delegating stream so that we allow the user
// to use the using (...) pattern yet not break the contract of formatters not closing
// the inner stream.
Stream nonClosingStream = new NonClosingDelegatingStream(innerStream);
// This uses a naive buffering. BufferedStream() will block the thread while it drains the buffer.
// We can explore a smarter implementation that async drains the buffer.
Stream bufferedStream = new BufferedStream(nonClosingStream, bufferSize);
// We now have buffered, non-closing stream which we can pass to the user.
return bufferedStream;
}
}
}

View File

@ -0,0 +1,53 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Net.Http.Headers;
namespace System.Net.Http.Formatting
{
/// <summary>
/// Represents the result of content negotiation performed using
/// <see cref="IContentNegotiator.Negotiate(Type, HttpRequestMessage, IEnumerable{MediaTypeFormatter})"/>
/// </summary>
public class ContentNegotiationResult
{
private MediaTypeFormatter _formatter;
/// <summary>
/// Create the content negotiation result object.
/// </summary>
/// <param name="formatter">The formatter.</param>
/// <param name="mediaType">The preferred media type. Can be <c>null</c>.</param>
public ContentNegotiationResult(MediaTypeFormatter formatter, MediaTypeHeaderValue mediaType)
{
if (formatter == null)
{
throw new ArgumentNullException("formatter");
}
_formatter = formatter;
MediaType = mediaType;
}
/// <summary>
/// The formatter chosen for serialization.
/// </summary>
public MediaTypeFormatter Formatter
{
get { return _formatter; }
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
_formatter = value;
}
}
/// <summary>
/// The media type that is associated with the formatter chosen for serialization. Can be <c>null</c>.
/// </summary>
public MediaTypeHeaderValue MediaType { get; set; }
}
}

View File

@ -0,0 +1,184 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Net.Http.Headers;
namespace System.Net.Http.Formatting
{
/// <summary>
/// Class that selects a <see cref="MediaTypeFormatter"/> for an <see cref="HttpRequestMessage"/>
/// or <see cref="HttpResponseMessage"/>.
/// </summary>
public class DefaultContentNegotiator : IContentNegotiator
{
/// <summary>
/// Performs content negotiating by selecting the most appropriate <see cref="MediaTypeFormatter"/> out of the passed in
/// <paramref name="formatters"/> for the given <paramref name="request"/> that can serialize an object of the given
/// <paramref name="type"/>.
/// </summary>
/// <param name="type">The type to be serialized.</param>
/// <param name="request">The request.</param>
/// <param name="formatters">The set of <see cref="MediaTypeFormatter"/> objects from which to choose.</param>
/// <returns>The result of the negotiation containing the most appropriate <see cref="MediaTypeFormatter"/> instance,
/// or <c>null</c> if there is no appropriate formatter.</returns>
public virtual ContentNegotiationResult Negotiate(Type type, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
if (request == null)
{
throw new ArgumentNullException("request");
}
if (formatters == null)
{
throw new ArgumentNullException("formatters");
}
MediaTypeHeaderValue mediaType;
MediaTypeFormatter formatter = RunNegotiation(type, request, formatters, out mediaType);
if (formatter != null)
{
formatter = formatter.GetPerRequestFormatterInstance(type, request, mediaType);
return new ContentNegotiationResult(formatter, mediaType);
}
return null;
}
private static MediaTypeFormatter RunNegotiation(Type type, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters, out MediaTypeHeaderValue mediaType)
{
// Asking to serialize a response. This is the nominal code path.
// We ask all formatters for their best kind of match, and then we
// choose the best among those.
MediaTypeFormatter formatterMatchOnType = null;
ResponseMediaTypeMatch mediaTypeMatchOnType = null;
MediaTypeFormatter formatterMatchOnAcceptHeader = null;
ResponseMediaTypeMatch mediaTypeMatchOnAcceptHeader = null;
MediaTypeFormatter formatterMatchWithMapping = null;
ResponseMediaTypeMatch mediaTypeMatchWithMapping = null;
MediaTypeFormatter formatterMatchOnRequestContentType = null;
ResponseMediaTypeMatch mediaTypeMatchOnRequestContentType = null;
foreach (MediaTypeFormatter formatter in formatters)
{
ResponseMediaTypeMatch match = formatter.SelectResponseMediaType(type, request);
if (match == null)
{
// Null signifies no match
continue;
}
ResponseFormatterSelectionResult matchResult = match.ResponseFormatterSelectionResult;
switch (matchResult)
{
case ResponseFormatterSelectionResult.MatchOnCanWriteType:
// First match by type trumps all other type matches
if (formatterMatchOnType == null)
{
formatterMatchOnType = formatter;
mediaTypeMatchOnType = match;
}
break;
case ResponseFormatterSelectionResult.MatchOnResponseContentType:
// Match on response content trumps all other choices
mediaType = match.MediaTypeMatch.MediaType;
return formatter;
case ResponseFormatterSelectionResult.MatchOnRequestAcceptHeader:
// Matches on accept headers must choose the highest quality match
double thisQuality = match.MediaTypeMatch.Quality;
if (formatterMatchOnAcceptHeader != null)
{
double bestQualitySeen = mediaTypeMatchOnAcceptHeader.MediaTypeMatch.Quality;
if (thisQuality <= bestQualitySeen)
{
continue;
}
}
formatterMatchOnAcceptHeader = formatter;
mediaTypeMatchOnAcceptHeader = match;
break;
case ResponseFormatterSelectionResult.MatchOnRequestWithMediaTypeMapping:
// Matches on accept headers using mappings must choose the highest quality match
double thisMappingQuality = match.MediaTypeMatch.Quality;
if (mediaTypeMatchWithMapping != null)
{
double bestMappingQualitySeen = mediaTypeMatchWithMapping.MediaTypeMatch.Quality;
if (thisMappingQuality <= bestMappingQualitySeen)
{
continue;
}
}
formatterMatchWithMapping = formatter;
mediaTypeMatchWithMapping = match;
break;
case ResponseFormatterSelectionResult.MatchOnRequestContentType:
// First match on request content type trumps other request content matches
if (formatterMatchOnRequestContentType == null)
{
formatterMatchOnRequestContentType = formatter;
mediaTypeMatchOnRequestContentType = match;
}
break;
}
}
// If we received matches based on both supported media types and from media type mappings,
// we want to give precedence to the media type mappings, but only if their quality is >= that of the supported media type.
// We do this because media type mappings are the user's extensibility point and must take precedence over normal
// supported media types in the case of a tie. The 99% case is where both have quality 1.0.
if (mediaTypeMatchWithMapping != null && mediaTypeMatchOnAcceptHeader != null)
{
if (mediaTypeMatchOnAcceptHeader.MediaTypeMatch.Quality > mediaTypeMatchWithMapping.MediaTypeMatch.Quality)
{
formatterMatchWithMapping = null;
}
}
// now select the formatter and media type
// A MediaTypeMapping is highest precedence -- it is an extensibility point
// allowing the user to override normal accept header matching
if (formatterMatchWithMapping != null)
{
mediaType = mediaTypeMatchWithMapping.MediaTypeMatch.MediaType;
return formatterMatchWithMapping;
}
else if (formatterMatchOnAcceptHeader != null)
{
mediaType = mediaTypeMatchOnAcceptHeader.MediaTypeMatch.MediaType;
return formatterMatchOnAcceptHeader;
}
else if (formatterMatchOnRequestContentType != null)
{
mediaType = mediaTypeMatchOnRequestContentType.MediaTypeMatch.MediaType;
return formatterMatchOnRequestContentType;
}
else if (formatterMatchOnType != null)
{
mediaType = mediaTypeMatchOnType.MediaTypeMatch.MediaType;
return formatterMatchOnType;
}
mediaType = null;
return null;
}
}
}

View File

@ -0,0 +1,68 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace System.Net.Http.Formatting
{
/// <summary>
/// Helper class to serialize <see cref="IEnumerable{T}"/> types by delegating them through a concrete implementation."/>.
/// </summary>
/// <typeparam name="T">The interface implementing <see cref="IEnumerable{T}"/> to proxy.</typeparam>
[SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "Enumerable conveys the meaning of collection")]
public sealed class DelegatingEnumerable<T> : IEnumerable<T>
{
private IEnumerable<T> _source;
/// <summary>
/// Initialize a DelegatingEnumerable. This constructor is necessary for <see cref="System.Runtime.Serialization.DataContractSerializer"/> to work.
/// </summary>
public DelegatingEnumerable()
{
_source = Enumerable.Empty<T>();
}
/// <summary>
/// Initialize a DelegatingEnumerable with an <see cref="IEnumerable{T}"/>. This is a helper class to proxy <see cref="IEnumerable{T}"/> interfaces for <see cref="System.Xml.Serialization.XmlSerializer"/>.
/// </summary>
/// <param name="source">The <see cref="IEnumerable{T}"/> instance to get the enumerator from.</param>
public DelegatingEnumerable(IEnumerable<T> source)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
_source = source;
}
/// <summary>
/// Get the enumerator of the associated <see cref="IEnumerable{T}"/>.
/// </summary>
/// <returns>The enumerator of the <see cref="IEnumerable{T}"/> source.</returns>
public IEnumerator<T> GetEnumerator()
{
return _source.GetEnumerator();
}
/// <summary>
/// This method is not implemented but is required method for serialization to work. Do not use.
/// </summary>
/// <param name="item">The item to add. Unused.</param>
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Justification = "Required by XmlSerializer, never used.")]
public void Add(object item)
{
throw new NotImplementedException();
}
/// <summary>
/// Get the enumerator of the associated <see cref="IEnumerable{T}"/>.
/// </summary>
/// <returns>The enumerator of the <see cref="IEnumerable{T}"/> source.</returns>
IEnumerator IEnumerable.GetEnumerator()
{
return _source.GetEnumerator();
}
}
}

View File

@ -0,0 +1,138 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http.Formatting.Internal;
using System.Net.Http.Formatting.Parsers;
using System.Threading;
namespace System.Net.Http.Formatting
{
/// <summary>
/// Represent the form data.
/// - This has 100% fidelity (including ordering, which is important for deserializing ordered array).
/// - using interfaces allows us to optimize the implementation. Eg, we can avoid eagerly string-splitting a 10gb file.
/// - This also provides a convenient place to put extension methods.
/// </summary>
public class FormDataCollection : IEnumerable<KeyValuePair<string, string>>
{
private readonly IEnumerable<KeyValuePair<string, string>> _pairs;
private NameValueCollection _nameValueCollection;
/// <summary>
/// Initialize a form collection around incoming data.
/// The key value enumeration should be immutable.
/// </summary>
/// <param name="pairs">incoming set of key value pairs. Ordering is preserved.</param>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is the convention for representing FormData")]
public FormDataCollection(IEnumerable<KeyValuePair<string, string>> pairs)
{
if (pairs == null)
{
throw new ArgumentNullException("pairs");
}
_pairs = pairs;
}
/// <summary>
/// Initialize a form collection from a query string.
/// Uri and FormURl body have the same schema.
/// </summary>
public FormDataCollection(Uri uri)
{
if (uri == null)
{
throw new ArgumentNullException("uri");
}
string query = uri.Query;
_pairs = ParseQueryString(query);
}
/// <summary>
/// Initialize a form collection from a query string.
/// This should be just the query string and not the full URI.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads", Justification = "string is a querystring, not a URI")]
public FormDataCollection(string query)
{
_pairs = ParseQueryString(query);
}
// Helper to invoke parser around a query string
private static IEnumerable<KeyValuePair<string, string>> ParseQueryString(string query)
{
List<KeyValuePair<string, string>> result = new List<KeyValuePair<string, string>>();
if (String.IsNullOrWhiteSpace(query))
{
return result;
}
if (query.Length > 0 && query[0] == '?')
{
query = query.Substring(1);
}
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(query);
FormUrlEncodedParser parser = new FormUrlEncodedParser(result, Int64.MaxValue);
int bytesConsumed = 0;
ParserState state = parser.ParseBuffer(bytes, bytes.Length, ref bytesConsumed, isFinal: true);
if (state != ParserState.Done)
{
throw new InvalidOperationException(RS.Format(Properties.Resources.FormUrlEncodedParseError, bytesConsumed));
}
return result;
}
/// <summary>
/// Get the collection as a NameValueCollection.
/// Beware this loses some ordering. Values are ordered within a key,
/// but keys are no longer ordered against each other.
/// </summary>
public NameValueCollection ReadAsNameValueCollection()
{
if (_nameValueCollection == null)
{
// Initialize in a private collection to be thread-safe, and swap the finished object.
// Ok to double initialize this.
NameValueCollection newCollection = HttpValueCollection.Create(this);
Interlocked.Exchange(ref _nameValueCollection, newCollection);
}
return _nameValueCollection;
}
/// <summary>
/// Get values associated with a given key. If there are multiple values, they're concatenated.
/// </summary>
public string Get(string key)
{
return ReadAsNameValueCollection().Get(key);
}
/// <summary>
/// Get a value associated with a given key.
/// </summary>
public string[] GetValues(string key)
{
return ReadAsNameValueCollection().GetValues(key);
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return _pairs.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
IEnumerable ie = _pairs;
return ie.GetEnumerator();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,215 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
using System.Net.Http.Formatting.Parsers;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace System.Net.Http.Formatting
{
/// <summary>
/// <see cref="MediaTypeFormatter"/> class for handling HTML form URL-ended data, also known as <c>application/x-www-form-urlencoded</c>.
/// </summary>
public class FormUrlEncodedMediaTypeFormatter : MediaTypeFormatter
{
private const int MinBufferSize = 256;
private const int DefaultBufferSize = 32 * 1024;
private static readonly MediaTypeHeaderValue[] _supportedMediaTypes = new MediaTypeHeaderValue[]
{
MediaTypeConstants.ApplicationFormUrlEncodedMediaType
};
private int _readBufferSize = DefaultBufferSize;
private int _maxDepth = FormattingUtilities.DefaultMaxDepth;
/// <summary>
/// Initializes a new instance of the <see cref="FormUrlEncodedMediaTypeFormatter"/> class.
/// </summary>
public FormUrlEncodedMediaTypeFormatter()
{
foreach (MediaTypeHeaderValue value in _supportedMediaTypes)
{
SupportedMediaTypes.Add(value);
}
}
/// <summary>
/// Gets the default media type for HTML Form URL encoded data, namely <c>application/x-www-form-urlencoded</c>.
/// </summary>
/// <value>
/// Because <see cref="MediaTypeHeaderValue"/> is mutable, the value
/// returned will be a new instance every time.
/// </value>
public static MediaTypeHeaderValue DefaultMediaType
{
get { return MediaTypeConstants.ApplicationFormUrlEncodedMediaType; }
}
/// <summary>
/// Gets or sets the maximum depth allowed by this formatter.
/// </summary>
public int MaxDepth
{
get
{
return _maxDepth;
}
set
{
if (value < FormattingUtilities.DefaultMinDepth)
{
throw new ArgumentOutOfRangeException("value", value, RS.Format(Properties.Resources.ArgumentMustBeGreaterThanOrEqualTo, FormattingUtilities.DefaultMinDepth));
}
_maxDepth = value;
}
}
/// <summary>
/// Gets or sets the size of the buffer when reading the incoming stream.
/// </summary>
/// <value>
/// The size of the read buffer.
/// </value>
public int ReadBufferSize
{
get { return _readBufferSize; }
set
{
if (value < MinBufferSize)
{
throw new ArgumentOutOfRangeException("value", value, RS.Format(Properties.Resources.ArgumentMustBeGreaterThanOrEqualTo, MinBufferSize));
}
_readBufferSize = value;
}
}
/// <summary>
/// Determines whether this <see cref="FormUrlEncodedMediaTypeFormatter"/> can read objects
/// of the specified <paramref name="type"/>.
/// </summary>
/// <param name="type">The type of object that will be read.</param>
/// <returns><c>true</c> if objects of this <paramref name="type"/> can be read, otherwise <c>false</c>.</returns>
public override bool CanReadType(Type type)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
// Can't read arbitrary types.
return type == typeof(FormDataCollection) || FormattingUtilities.IsJTokenType(type);
}
/// <summary>
/// Determines whether this <see cref="FormUrlEncodedMediaTypeFormatter"/> can write objects
/// of the specified <paramref name="type"/>.
/// </summary>
/// <param name="type">The type of object that will be written.</param>
/// <returns><c>true</c> if objects of this <paramref name="type"/> can be written, otherwise <c>false</c>.</returns>
public override bool CanWriteType(Type type)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
return false;
}
/// <summary>
/// Called during deserialization to read an object of the specified <paramref name="type"/>
/// from the specified <paramref name="stream"/>.
/// </summary>
/// <param name="type">The type of object to read.</param>
/// <param name="stream">The <see cref="Stream"/> from which to read.</param>
/// <param name="contentHeaders">The <see cref="HttpContentHeaders"/> for the content being read.</param>
/// <param name="formatterLogger">The <see cref="IFormatterLogger"/> to log events to.</param>
/// <returns>A <see cref="Task"/> whose result will be the object instance that has been read.</returns>
public override Task<object> ReadFromStreamAsync(Type type, Stream stream, HttpContentHeaders contentHeaders, IFormatterLogger formatterLogger)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
if (stream == null)
{
throw new ArgumentNullException("stream");
}
return TaskHelpers.RunSynchronously<object>(() =>
{
IEnumerable<KeyValuePair<string, string>> nameValuePairs = ReadFormUrlEncoded(stream, ReadBufferSize);
if (type == typeof(FormDataCollection))
{
return new FormDataCollection(nameValuePairs);
}
if (FormattingUtilities.IsJTokenType(type))
{
return FormUrlEncodedJson.Parse(nameValuePairs, _maxDepth);
}
// Passed us an unsupported type. Should have called CanReadType() first.
throw new InvalidOperationException(
RS.Format(Properties.Resources.SerializerCannotSerializeType, GetType().Name, type.Name));
});
}
/// <summary>
/// Reads all name-value pairs encoded as HTML Form URL encoded data and add them to
/// a collection as UNescaped URI strings.
/// </summary>
/// <param name="input">Stream to read from.</param>
/// <param name="bufferSize">Size of the buffer used to read the contents.</param>
/// <returns>Collection of name-value pairs.</returns>
private static IEnumerable<KeyValuePair<string, string>> ReadFormUrlEncoded(Stream input, int bufferSize)
{
Contract.Assert(input != null, "input stream cannot be null");
Contract.Assert(bufferSize >= MinBufferSize, "buffer size cannot be less than MinBufferSize");
byte[] data = new byte[bufferSize];
int bytesRead;
bool isFinal = false;
List<KeyValuePair<string, string>> result = new List<KeyValuePair<string, string>>();
FormUrlEncodedParser parser = new FormUrlEncodedParser(result, Int64.MaxValue);
ParserState state;
while (true)
{
try
{
bytesRead = input.Read(data, 0, data.Length);
if (bytesRead == 0)
{
isFinal = true;
}
}
catch (Exception e)
{
throw new IOException(Properties.Resources.ErrorReadingFormUrlEncodedStream, e);
}
int bytesConsumed = 0;
state = parser.ParseBuffer(data, bytesRead, ref bytesConsumed, isFinal);
if (state != ParserState.NeedMoreData && state != ParserState.Done)
{
throw new IOException(RS.Format(Properties.Resources.FormUrlEncodedParseError, bytesConsumed));
}
if (isFinal)
{
return result;
}
}
}
}
}

View File

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Net.Http.Headers;
namespace System.Net.Http.Formatting
{
/// <summary>
/// Performs content negotiation.
/// This is the process of selecting a response writer (formatter) in compliance with header values in the request.
/// </summary>
public interface IContentNegotiator
{
/// <summary>
/// Performs content negotiating by selecting the most appropriate <see cref="MediaTypeFormatter"/> out of the passed in
/// <paramref name="formatters"/> for the given <paramref name="request"/> that can serialize an object of the given
/// <paramref name="type"/>.
/// </summary>
/// <remarks>
/// Implementations of this method should call <see cref="MediaTypeFormatter.GetPerRequestFormatterInstance(Type, HttpRequestMessage, MediaTypeHeaderValue)"/>
/// on the selected <see cref="MediaTypeFormatter">formatter</see> and return the result of that method.
/// </remarks>
/// <param name="type">The type to be serialized.</param>
/// <param name="request">Request message, which contains the header values used to perform negotiation.</param>
/// <param name="formatters">The set of <see cref="MediaTypeFormatter"/> objects from which to choose.</param>
/// <returns>The result of the negotiation containing the most appropriate <see cref="MediaTypeFormatter"/> instance,
/// or <c>null</c> if there is no appropriate formatter.</returns>
ContentNegotiationResult Negotiate(Type type, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters);
}
}

View File

@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Net.Http.Formatting
{
/// <summary>
/// Interface to log events that occur during formatter reads.
/// </summary>
public interface IFormatterLogger
{
/// <summary>
/// Logs an error.
/// </summary>
/// <param name="errorPath">The path to the member for which the error is being logged.</param>
/// <param name="errorMessage">The error message to be logged.</param>
void LogError(string errorPath, string errorMessage);
}
}

View File

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Reflection;
namespace System.Net.Http.Formatting
{
/// <summary>
/// Interface to determine which data members on a particular type are required.
/// </summary>
public interface IRequiredMemberSelector
{
/// <summary>
/// Determines whether a given member is required on deserialization.
/// </summary>
/// <param name="member">The <see cref="MemberInfo"/> that will be deserialized.</param>
/// <returns><c>true</c> if <paramref name="member"/> should be treated as a required member, otherwise <c>false</c>.</returns>
bool IsRequiredMember(MemberInfo member);
}
}

View File

@ -0,0 +1,108 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace System.Net.Http.Formatting
{
// Default Contract resolver for JsonMediaTypeFormatter
// Handles types that DCJS supports, but Json.NET doesn't support out of the box (like [Serializable])
// Uses the IRequiredMemberSelector to choose required members
internal class JsonContractResolver : DefaultContractResolver
{
private const BindingFlags AllInstanceMemberFlag = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private readonly MediaTypeFormatter _formatter;
public JsonContractResolver(MediaTypeFormatter formatter)
{
_formatter = formatter;
}
protected override JsonObjectContract CreateObjectContract(Type type)
{
JsonObjectContract contract = base.CreateObjectContract(type);
// Handling [Serializable] types
if (type.IsSerializable && !IsTypeNullable(type) && !IsTypeDataContract(type) && !IsTypeJsonObject(type))
{
contract.Properties.Clear();
foreach (JsonProperty property in CreateSerializableJsonProperties(type))
{
contract.Properties.Add(property);
}
}
return contract;
}
// Determines whether a member is required or not and sets the appropriate JsonProperty settings
private void ConfigureProperty(MemberInfo member, JsonProperty property)
{
if (_formatter.RequiredMemberSelector != null && _formatter.RequiredMemberSelector.IsRequiredMember(member))
{
property.Required = Required.AllowNull;
property.DefaultValueHandling = DefaultValueHandling.Include;
property.NullValueHandling = NullValueHandling.Include;
}
else
{
property.Required = Required.Default;
property.DefaultValueHandling = DefaultValueHandling.Ignore;
property.NullValueHandling = NullValueHandling.Ignore;
}
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
ConfigureProperty(member, property);
return property;
}
private IEnumerable<JsonProperty> CreateSerializableJsonProperties(Type type)
{
return type.GetFields(AllInstanceMemberFlag)
.Where(field => !field.IsNotSerialized)
.Select(field =>
{
JsonProperty property = PrivateMemberContractResolver.Instance.CreatePrivateProperty(field, MemberSerialization.OptOut);
ConfigureProperty(field, property);
return property;
});
}
private static bool IsTypeNullable(Type type)
{
return Nullable.GetUnderlyingType(type) != null;
}
private static bool IsTypeDataContract(Type type)
{
return type.GetCustomAttributes(typeof(DataContractAttribute), false).Any();
}
private static bool IsTypeJsonObject(Type type)
{
return type.GetCustomAttributes(typeof(JsonObjectAttribute), false).Any();
}
private class PrivateMemberContractResolver : DefaultContractResolver
{
internal static PrivateMemberContractResolver Instance = new PrivateMemberContractResolver();
internal PrivateMemberContractResolver()
{
DefaultMembersSearchFlags = JsonContractResolver.AllInstanceMemberFlag;
}
internal JsonProperty CreatePrivateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
return CreateProperty(member, memberSerialization);
}
}
}
}

View File

@ -0,0 +1,413 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections;
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.IO;
using System.Net.Http.Headers;
using System.Net.Http.Internal;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace System.Net.Http.Formatting
{
/// <summary>
/// <see cref="MediaTypeFormatter"/> class to handle Json.
/// </summary>
public class JsonMediaTypeFormatter : MediaTypeFormatter
{
private static readonly MediaTypeHeaderValue[] _supportedMediaTypes = new MediaTypeHeaderValue[]
{
MediaTypeConstants.ApplicationJsonMediaType,
MediaTypeConstants.TextJsonMediaType
};
private JsonSerializerSettings _jsonSerializerSettings;
private readonly IContractResolver _defaultContractResolver;
private int _maxDepth = FormattingUtilities.DefaultMaxDepth;
private XmlDictionaryReaderQuotas _readerQuotas = FormattingUtilities.CreateDefaultReaderQuotas();
private ConcurrentDictionary<Type, DataContractJsonSerializer> _dataContractSerializerCache = new ConcurrentDictionary<Type, DataContractJsonSerializer>();
private RequestHeaderMapping _requestHeaderMapping;
/// <summary>
/// Initializes a new instance of the <see cref="JsonMediaTypeFormatter"/> class.
/// </summary>
public JsonMediaTypeFormatter()
{
// Set default supported media types
foreach (MediaTypeHeaderValue value in _supportedMediaTypes)
{
SupportedMediaTypes.Add(value);
}
// Initialize serializer
_defaultContractResolver = new JsonContractResolver(this);
_jsonSerializerSettings = CreateDefaultSerializerSettings();
// Set default supported character encodings
SupportedEncodings.Add(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true));
SupportedEncodings.Add(new UnicodeEncoding(bigEndian: false, byteOrderMark: true, throwOnInvalidBytes: true));
_requestHeaderMapping = new XHRRequestHeaderMapping();
MediaTypeMappings.Add(_requestHeaderMapping);
}
/// <summary>
/// Gets the default media type for Json, namely "application/json".
/// </summary>
/// <remarks>
/// The default media type does not have any <c>charset</c> parameter as
/// the <see cref="Encoding"/> can be configured on a per <see cref="JsonMediaTypeFormatter"/>
/// instance basis.
/// </remarks>
/// <value>
/// Because <see cref="MediaTypeHeaderValue"/> is mutable, the value
/// returned will be a new instance every time.
/// </value>
public static MediaTypeHeaderValue DefaultMediaType
{
get { return MediaTypeConstants.ApplicationJsonMediaType; }
}
/// <summary>
/// Gets or sets the <see cref="JsonSerializerSettings"/> used to configure the <see cref="JsonSerializer"/>.
/// </summary>
public JsonSerializerSettings SerializerSettings
{
get { return _jsonSerializerSettings; }
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
_jsonSerializerSettings = value;
}
}
/// <summary>
/// Gets or sets a value indicating whether to use <see cref="DataContractJsonSerializer"/> by default.
/// </summary>
/// <value>
/// <c>true</c> if use <see cref="DataContractJsonSerializer"/> by default; otherwise, <c>false</c>. The default is <c>false</c>.
/// </value>
public bool UseDataContractJsonSerializer { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to indent elements when writing data.
/// </summary>
public bool Indent { get; set; }
/// <summary>
/// Gets or sets the maximum depth allowed by this formatter.
/// </summary>
public int MaxDepth
{
get
{
return _maxDepth;
}
set
{
if (value < FormattingUtilities.DefaultMinDepth)
{
throw new ArgumentOutOfRangeException("value", value, RS.Format(Properties.Resources.ArgumentMustBeGreaterThanOrEqualTo, FormattingUtilities.DefaultMinDepth));
}
_maxDepth = value;
_readerQuotas.MaxDepth = value;
}
}
/// <summary>
/// Creates a <see cref="JsonSerializerSettings"/> instance with the default settings used by the <see cref="JsonMediaTypeFormatter"/>.
/// </summary>
public JsonSerializerSettings CreateDefaultSerializerSettings()
{
return new JsonSerializerSettings()
{
ContractResolver = _defaultContractResolver,
MissingMemberHandling = MissingMemberHandling.Ignore,
// Do not change this setting
// Setting this to None prevents Json.NET from loading malicious, unsafe, or security-sensitive types
TypeNameHandling = TypeNameHandling.None
};
}
internal bool ContainsSerializerForType(Type type)
{
return _dataContractSerializerCache.ContainsKey(type);
}
/// <summary>
/// Determines whether this <see cref="JsonMediaTypeFormatter"/> can read objects
/// of the specified <paramref name="type"/>.
/// </summary>
/// <param name="type">The type of object that will be read.</param>
/// <returns><c>true</c> if objects of this <paramref name="type"/> can be read, otherwise <c>false</c>.</returns>
public override bool CanReadType(Type type)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
if (UseDataContractJsonSerializer)
{
// If there is a registered non-null serializer, we can support this type.
DataContractJsonSerializer serializer =
_dataContractSerializerCache.GetOrAdd(type, (t) => CreateDataContractSerializer(t));
// Null means we tested it before and know it is not supported
return serializer != null;
}
else
{
return true;
}
}
/// <summary>
/// Determines whether this <see cref="JsonMediaTypeFormatter"/> can write objects
/// of the specified <paramref name="type"/>.
/// </summary>
/// <param name="type">The type of object that will be written.</param>
/// <returns><c>true</c> if objects of this <paramref name="type"/> can be written, otherwise <c>false</c>.</returns>
public override bool CanWriteType(Type type)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
if (UseDataContractJsonSerializer)
{
MediaTypeFormatter.TryGetDelegatingTypeForIQueryableGenericOrSame(ref type);
// If there is a registered non-null serializer, we can support this type.
object serializer =
_dataContractSerializerCache.GetOrAdd(type, (t) => CreateDataContractSerializer(t));
// Null means we tested it before and know it is not supported
return serializer != null;
}
else
{
return true;
}
}
/// <summary>
/// Called during deserialization to read an object of the specified <paramref name="type"/>
/// from the specified <paramref name="stream"/>.
/// </summary>
/// <param name="type">The type of object to read.</param>
/// <param name="stream">The <see cref="Stream"/> from which to read.</param>
/// <param name="contentHeaders">The <see cref="HttpContentHeaders"/> for the content being written.</param>
/// <param name="formatterLogger">The <see cref="IFormatterLogger"/> to log events to.</param>
/// <returns>A <see cref="Task"/> whose result will be the object instance that has been read.</returns>
public override Task<object> ReadFromStreamAsync(Type type, Stream stream, HttpContentHeaders contentHeaders, IFormatterLogger formatterLogger)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
if (stream == null)
{
throw new ArgumentNullException("stream");
}
return TaskHelpers.RunSynchronously<object>(() =>
{
// If content length is 0 then return default value for this type
if (contentHeaders != null && contentHeaders.ContentLength == 0)
{
return GetDefaultValueForType(type);
}
// Get the character encoding for the content
Encoding effectiveEncoding = SelectCharacterEncoding(contentHeaders);
try
{
if (UseDataContractJsonSerializer)
{
DataContractJsonSerializer dataContractSerializer = GetDataContractSerializer(type);
using (XmlReader reader = JsonReaderWriterFactory.CreateJsonReader(new NonClosingDelegatingStream(stream), effectiveEncoding, _readerQuotas, null))
{
return dataContractSerializer.ReadObject(reader);
}
}
else
{
using (JsonTextReader jsonTextReader = new SecureJsonTextReader(new StreamReader(stream, effectiveEncoding), _maxDepth) { CloseInput = false })
{
JsonSerializer jsonSerializer = JsonSerializer.Create(_jsonSerializerSettings);
if (formatterLogger != null)
{
// Error must always be marked as handled
// Failure to do so can cause the exception to be rethrown at every recursive level and overflow the stack for x64 CLR processes
jsonSerializer.Error += (sender, e) =>
{
Exception exception = e.ErrorContext.Error;
formatterLogger.LogError(e.ErrorContext.Path, exception.Message);
e.ErrorContext.Handled = true;
};
}
return jsonSerializer.Deserialize(jsonTextReader, type);
}
}
}
catch (Exception e)
{
if (formatterLogger == null)
{
throw;
}
formatterLogger.LogError(String.Empty, e.Message);
return GetDefaultValueForType(type);
}
});
}
/// <summary>
/// Called during serialization to write an object of the specified <paramref name="type"/>
/// to the specified <paramref name="stream"/>.
/// </summary>
/// <param name="type">The type of object to write.</param>
/// <param name="value">The object to write.</param>
/// <param name="stream">The <see cref="Stream"/> to which to write.</param>
/// <param name="contentHeaders">The <see cref="HttpContentHeaders"/> for the content being written.</param>
/// <param name="transportContext">The <see cref="TransportContext"/>.</param>
/// <returns>A <see cref="Task"/> that will write the value to the stream.</returns>
public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContentHeaders contentHeaders, TransportContext transportContext)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
if (stream == null)
{
throw new ArgumentNullException("stream");
}
if (UseDataContractJsonSerializer && Indent)
{
throw new NotSupportedException(RS.Format(Properties.Resources.UnsupportedIndent, typeof(DataContractJsonSerializer)));
}
return TaskHelpers.RunSynchronously(() =>
{
Encoding effectiveEncoding = SelectCharacterEncoding(contentHeaders);
if (!UseDataContractJsonSerializer)
{
using (JsonTextWriter jsonTextWriter = new JsonTextWriter(new StreamWriter(stream, effectiveEncoding)) { CloseOutput = false })
{
if (Indent)
{
jsonTextWriter.Formatting = Newtonsoft.Json.Formatting.Indented;
}
JsonSerializer jsonSerializer = JsonSerializer.Create(_jsonSerializerSettings);
jsonSerializer.Serialize(jsonTextWriter, value);
jsonTextWriter.Flush();
}
}
else
{
if (MediaTypeFormatter.TryGetDelegatingTypeForIQueryableGenericOrSame(ref type))
{
if (value != null)
{
value = MediaTypeFormatter.GetTypeRemappingConstructor(type).Invoke(new object[] { value });
}
}
DataContractJsonSerializer dataContractSerializer = GetDataContractSerializer(type);
using (XmlWriter writer = JsonReaderWriterFactory.CreateJsonWriter(stream, effectiveEncoding, ownsStream: false))
{
dataContractSerializer.WriteObject(writer, value);
}
}
});
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is propagated.")]
private static DataContractJsonSerializer CreateDataContractSerializer(Type type)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
DataContractJsonSerializer serializer = null;
try
{
if (IsKnownUnserializableType(type))
{
return null;
}
//// TODO: CSDMAIN 211321 -- determine the correct algorithm to know what is serializable.
serializer = new DataContractJsonSerializer(type);
}
catch (Exception)
{
//// TODO: CSDMain 232171 -- review and fix swallowed exception
}
return serializer;
}
private static bool IsKnownUnserializableType(Type type)
{
if (type.IsGenericType)
{
if (typeof(IEnumerable).IsAssignableFrom(type))
{
return IsKnownUnserializableType(type.GetGenericArguments()[0]);
}
}
if (!type.IsVisible)
{
return true;
}
if (type.HasElementType && IsKnownUnserializableType(type.GetElementType()))
{
return true;
}
return false;
}
private DataContractJsonSerializer GetDataContractSerializer(Type type)
{
Contract.Assert(type != null, "Type cannot be null");
DataContractJsonSerializer serializer =
_dataContractSerializerCache.GetOrAdd(type, (t) => CreateDataContractSerializer(type));
if (serializer == null)
{
// A null serializer means the type cannot be serialized
throw new InvalidOperationException(
RS.Format(Properties.Resources.SerializerCannotSerializeType, typeof(DataContractJsonSerializer).Name, type.Name));
}
return serializer;
}
}
}

View File

@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Runtime.Serialization;
using Newtonsoft.Json;
namespace System.Net.Http.Formatting
{
/// <summary>
/// Exception type to indicate that json reader quotas have been exceeded.
/// </summary>
[Serializable]
internal class JsonReaderQuotaException : JsonReaderException
{
public JsonReaderQuotaException()
: base()
{
}
public JsonReaderQuotaException(string message)
: base(message)
{
}
protected JsonReaderQuotaException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
public JsonReaderQuotaException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

View File

@ -0,0 +1,92 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Net.Http.Headers;
namespace System.Net.Http.Formatting
{
/// <summary>
/// Class that provides <see cref="MediaTypeHeaderValue"/>s for a request or response
/// from a media range.
/// </summary>
public class MediaRangeMapping : MediaTypeMapping
{
/// <summary>
/// Initializes a new instance of the <see cref="MediaRangeMapping"/> class.
/// </summary>
/// <param name="mediaRange">The <see cref="MediaTypeHeaderValue"/> that provides a description
/// of the media range.</param>
/// <param name="mediaType">The <see cref="MediaTypeHeaderValue"/> to return on a match.</param>
public MediaRangeMapping(MediaTypeHeaderValue mediaRange, MediaTypeHeaderValue mediaType)
: base(mediaType)
{
Initialize(mediaRange);
}
/// <summary>
/// Initializes a new instance of the <see cref="MediaRangeMapping"/> class.
/// </summary>
/// <param name="mediaRange">The description of the media range.</param>
/// <param name="mediaType">The media type to return on a match.</param>
public MediaRangeMapping(string mediaRange, string mediaType)
: base(mediaType)
{
if (String.IsNullOrWhiteSpace(mediaRange))
{
throw new ArgumentNullException("mediaRange");
}
Initialize(new MediaTypeHeaderValue(mediaRange));
}
/// <summary>
/// Gets the <see cref="MediaTypeHeaderValue"/>
/// describing the known media range.
/// </summary>
public MediaTypeHeaderValue MediaRange { get; private set; }
/// <summary>
/// Returns a value indicating whether this <see cref="MediaRangeMapping"/>
/// instance can provide a <see cref="MediaTypeHeaderValue"/> for the <paramref name="request"/>.
/// </summary>
/// <param name="request">The <see cref="HttpRequestMessage"/> to check.</param>
/// <returns>If this instance can match <paramref name="request"/>
/// it returns the quality of the match otherwise <c>0.0</c>.</returns>
public override double TryMatchMediaType(HttpRequestMessage request)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
ICollection<MediaTypeWithQualityHeaderValue> acceptHeader = request.Headers.Accept;
if (acceptHeader != null)
{
foreach (MediaTypeWithQualityHeaderValue mediaType in acceptHeader)
{
if (MediaRange.IsSubsetOf(mediaType))
{
return mediaType.Quality.HasValue ? mediaType.Quality.Value : MediaTypeMatch.Match;
}
}
}
return MediaTypeMatch.NoMatch;
}
private void Initialize(MediaTypeHeaderValue mediaRange)
{
if (mediaRange == null)
{
throw new ArgumentNullException("mediaRange");
}
if (!mediaRange.IsMediaRange())
{
throw new InvalidOperationException(RS.Format(Properties.Resources.InvalidMediaRange, mediaRange.ToString()));
}
MediaRange = mediaRange;
}
}
}

View File

@ -0,0 +1,86 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Net.Http.Headers;
using System.Text;
namespace System.Net.Http.Formatting
{
/// <summary>
/// Constants related to media types.
/// </summary>
internal static class MediaTypeConstants
{
private static readonly MediaTypeHeaderValue _defaultApplicationXmlMediaType = new MediaTypeHeaderValue("application/xml");
private static readonly MediaTypeHeaderValue _defaultTextXmlMediaType = new MediaTypeHeaderValue("text/xml");
private static readonly MediaTypeHeaderValue _defaultApplicationJsonMediaType = new MediaTypeHeaderValue("application/json");
private static readonly MediaTypeHeaderValue _defaultTextJsonMediaType = new MediaTypeHeaderValue("text/json");
private static readonly MediaTypeHeaderValue _defaultTextHtmlMediaType = new MediaTypeHeaderValue("text/html") { CharSet = Encoding.UTF8.WebName };
private static readonly MediaTypeHeaderValue _defaultApplicationFormUrlEncodedMediaType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
/// <summary>
/// Gets a <see cref="MediaTypeHeaderValue"/> instance representing <c>text/html</c>.
/// </summary>
/// <value>
/// A new <see cref="MediaTypeHeaderValue"/> instance representing <c>text/html</c>.
/// </value>
public static MediaTypeHeaderValue HtmlMediaType
{
get { return _defaultTextHtmlMediaType.Clone(); }
}
/// <summary>
/// Gets a <see cref="MediaTypeHeaderValue"/> instance representing <c>application/xml</c>.
/// </summary>
/// <value>
/// A new <see cref="MediaTypeHeaderValue"/> instance representing <c>application/xml</c>.
/// </value>
public static MediaTypeHeaderValue ApplicationXmlMediaType
{
get { return _defaultApplicationXmlMediaType.Clone(); }
}
/// <summary>
/// Gets a <see cref="MediaTypeHeaderValue"/> instance representing <c>application/json</c>.
/// </summary>
/// <value>
/// A new <see cref="MediaTypeHeaderValue"/> instance representing <c>application/json</c>.
/// </value>
public static MediaTypeHeaderValue ApplicationJsonMediaType
{
get { return _defaultApplicationJsonMediaType.Clone(); }
}
/// <summary>
/// Gets a <see cref="MediaTypeHeaderValue"/> instance representing <c>text/xml</c>.
/// </summary>
/// <value>
/// A new <see cref="MediaTypeHeaderValue"/> instance representing <c>text/xml</c>.
/// </value>
public static MediaTypeHeaderValue TextXmlMediaType
{
get { return _defaultTextXmlMediaType.Clone(); }
}
/// <summary>
/// Gets a <see cref="MediaTypeHeaderValue"/> instance representing <c>text/json</c>.
/// </summary>
/// <value>
/// A new <see cref="MediaTypeHeaderValue"/> instance representing <c>text/json</c>.
/// </value>
public static MediaTypeHeaderValue TextJsonMediaType
{
get { return _defaultTextJsonMediaType.Clone(); }
}
/// <summary>
/// Gets a <see cref="MediaTypeHeaderValue"/> instance representing <c>application/x-www-form-urlencoded</c>.
/// </summary>
/// <value>
/// A new <see cref="MediaTypeHeaderValue"/> instance representing <c>application/x-www-form-urlencoded</c>.
/// </value>
public static MediaTypeHeaderValue ApplicationFormUrlEncodedMediaType
{
get { return _defaultApplicationFormUrlEncodedMediaType.Clone(); }
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,165 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net.Http.Headers;
using System.Web.Http;
using System.Xml;
using System.Xml.Linq;
namespace System.Net.Http.Formatting
{
/// <summary>
/// Collection class that contains <see cref="MediaTypeFormatter"/> instances.
/// </summary>
public class MediaTypeFormatterCollection : Collection<MediaTypeFormatter>
{
private static readonly Type _mediaTypeFormatterType = typeof(MediaTypeFormatter);
/// <summary>
/// Initializes a new instance of the <see cref="MediaTypeFormatterCollection"/> class.
/// </summary>
/// <remarks>
/// This collection will be initialized to contain default <see cref="MediaTypeFormatter"/>
/// instances for Xml, JsonValue and Json.
/// </remarks>
public MediaTypeFormatterCollection()
: this(CreateDefaultFormatters())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="MediaTypeFormatterCollection"/> class.
/// </summary>
/// <param name="formatters">A collection of <see cref="MediaTypeFormatter"/> instances to place in the collection.</param>
public MediaTypeFormatterCollection(IEnumerable<MediaTypeFormatter> formatters)
{
VerifyAndSetFormatters(formatters);
}
/// <summary>
/// Gets the <see cref="MediaTypeFormatter"/> to use for Xml.
/// </summary>
public XmlMediaTypeFormatter XmlFormatter
{
get { return Items.OfType<XmlMediaTypeFormatter>().FirstOrDefault(); }
}
/// <summary>
/// Gets the <see cref="MediaTypeFormatter"/> to use for Json.
/// </summary>
public JsonMediaTypeFormatter JsonFormatter
{
get { return Items.OfType<JsonMediaTypeFormatter>().FirstOrDefault(); }
}
/// <summary>
/// Gets the <see cref="MediaTypeFormatter"/> to use for <c>application/x-www-form-urlencoded</c> data.
/// </summary>
public FormUrlEncodedMediaTypeFormatter FormUrlEncodedFormatter
{
get { return Items.OfType<FormUrlEncodedMediaTypeFormatter>().FirstOrDefault(); }
}
/// <summary>
/// Helper to search a collection for a formatter that can read the .NET type in the given mediaType.
/// </summary>
/// <param name="type">.NET type to read</param>
/// <param name="mediaType">media type to match on.</param>
/// <returns>Formatter that can read the type. Null if no formatter found.</returns>
public MediaTypeFormatter FindReader(Type type, MediaTypeHeaderValue mediaType)
{
if (type == null)
{
throw Error.ArgumentNull("type");
}
if (mediaType == null)
{
throw Error.ArgumentNull("mediaType");
}
foreach (MediaTypeFormatter formatter in this.Items)
{
if (formatter.CanReadAs(type, mediaType))
{
return formatter;
}
}
return null;
}
/// <summary>
/// Helper to search a collection for a formatter that can write the .NET type in the given mediaType.
/// </summary>
/// <param name="type">.NET type to read</param>
/// <param name="mediaType">media type to match on.</param>
/// <returns>Formatter that can write the type. Null if no formatter found.</returns>
public MediaTypeFormatter FindWriter(Type type, MediaTypeHeaderValue mediaType)
{
if (type == null)
{
throw Error.ArgumentNull("type");
}
if (mediaType == null)
{
throw Error.ArgumentNull("mediaType");
}
foreach (MediaTypeFormatter formatter in Items)
{
MediaTypeHeaderValue match;
if (formatter.CanWriteAs(type, mediaType, out match))
{
return formatter;
}
}
return null;
}
/// <summary>
/// Returns true if the type is one of those loosely defined types that should be excluded from validation
/// </summary>
/// <param name="type">.NET <see cref="Type"/> to validate</param>
/// <returns><c>true</c> if the type should be excluded.</returns>
public static bool IsTypeExcludedFromValidation(Type type)
{
return FormattingUtilities.IsJTokenType(type) || typeof(XObject).IsAssignableFrom(type) || typeof(XmlNode).IsAssignableFrom(type)
|| typeof(FormDataCollection).IsAssignableFrom(type);
}
/// <summary>
/// Creates a collection of new instances of the default <see cref="MediaTypeFormatter"/>s.
/// </summary>
/// <returns>The collection of default <see cref="MediaTypeFormatter"/> instances.</returns>
private static IEnumerable<MediaTypeFormatter> CreateDefaultFormatters()
{
return new MediaTypeFormatter[]
{
new JsonMediaTypeFormatter(),
new XmlMediaTypeFormatter(),
new FormUrlEncodedMediaTypeFormatter()
};
}
private void VerifyAndSetFormatters(IEnumerable<MediaTypeFormatter> formatters)
{
if (formatters == null)
{
throw new ArgumentNullException("formatters");
}
foreach (MediaTypeFormatter formatter in formatters)
{
if (formatter == null)
{
throw new ArgumentException(RS.Format(Properties.Resources.CannotHaveNullInList, _mediaTypeFormatterType.Name), "formatters");
}
Add(formatter);
}
}
}
}

View File

@ -0,0 +1,173 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.ComponentModel;
using System.Net.Http.Headers;
namespace System.Net.Http.Formatting
{
/// <summary>
/// Extensions for adding <see cref="MediaTypeMapping"/> items to a <see cref="MediaTypeFormatter"/>.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class MediaTypeFormatterExtensions
{
/// <summary>
/// Updates the given <paramref name="formatter"/>'s set of <see cref="MediaTypeMapping"/> elements
/// so that it associates the <paramref name="mediaType"/> with <see cref="Uri"/>s containing
/// a specific query parameter and value.
/// </summary>
/// <param name="formatter">The <see cref="MediaTypeFormatter"/> to receive the new <see cref="QueryStringMapping"/> item.</param>
/// <param name="queryStringParameterName">The name of the query parameter.</param>
/// <param name="queryStringParameterValue">The value assigned to that query parameter.</param>
/// <param name="mediaType">The <see cref="MediaTypeHeaderValue"/> to associate
/// with a <see cref="Uri"/> containing a query string matching <paramref name="queryStringParameterName"/>
/// and <paramref name="queryStringParameterValue"/>.</param>
public static void AddQueryStringMapping(
this MediaTypeFormatter formatter,
string queryStringParameterName,
string queryStringParameterValue,
MediaTypeHeaderValue mediaType)
{
if (formatter == null)
{
throw new ArgumentNullException("formatter");
}
QueryStringMapping mapping = new QueryStringMapping(queryStringParameterName, queryStringParameterValue, mediaType);
formatter.MediaTypeMappings.Add(mapping);
}
/// <summary>
/// Updates the given <paramref name="formatter"/>'s set of <see cref="MediaTypeMapping"/> elements
/// so that it associates the <paramref name="mediaType"/> with <see cref="Uri"/>s containing
/// a specific query parameter and value.
/// </summary>
/// <param name="formatter">The <see cref="MediaTypeFormatter"/> to receive the new <see cref="QueryStringMapping"/> item.</param>
/// <param name="queryStringParameterName">The name of the query parameter.</param>
/// <param name="queryStringParameterValue">The value assigned to that query parameter.</param>
/// <param name="mediaType">The media type to associate
/// with a <see cref="Uri"/> containing a query string matching <paramref name="queryStringParameterName"/>
/// and <paramref name="queryStringParameterValue"/>.</param>
public static void AddQueryStringMapping(
this MediaTypeFormatter formatter,
string queryStringParameterName,
string queryStringParameterValue,
string mediaType)
{
if (formatter == null)
{
throw new ArgumentNullException("formatter");
}
QueryStringMapping mapping = new QueryStringMapping(queryStringParameterName, queryStringParameterValue, mediaType);
formatter.MediaTypeMappings.Add(mapping);
}
/// <summary>
/// Updates the given <paramref name="formatter"/>'s set of <see cref="MediaTypeMapping"/> elements
/// so that it associates the <paramref name="mediaType"/> with requests or responses containing
/// <paramref name="mediaRange"/> in the content headers.
/// </summary>
/// <param name="formatter">The <see cref="MediaTypeFormatter"/> to receive the new <see cref="MediaRangeMapping"/> item.</param>
/// <param name="mediaRange">The media range that will appear in the content headers.</param>
/// <param name="mediaType">The media type to associate with that <paramref name="mediaRange"/>.</param>
public static void AddMediaRangeMapping(this MediaTypeFormatter formatter, string mediaRange, string mediaType)
{
if (formatter == null)
{
throw new ArgumentNullException("formatter");
}
MediaRangeMapping mapping = new MediaRangeMapping(mediaRange, mediaType);
formatter.MediaTypeMappings.Add(mapping);
}
/// <summary>
/// Updates the given <paramref name="formatter"/>'s set of <see cref="MediaTypeMapping"/> elements
/// so that it associates the <paramref name="mediaType"/> with requests or responses containing
/// <paramref name="mediaRange"/> in the content headers.
/// </summary>
/// <param name="formatter">The <see cref="MediaTypeFormatter"/> to receive the new <see cref="MediaRangeMapping"/> item.</param>
/// <param name="mediaRange">The media range that will appear in the content headers.</param>
/// <param name="mediaType">The media type to associate with that <paramref name="mediaRange"/>.</param>
public static void AddMediaRangeMapping(
this MediaTypeFormatter formatter,
MediaTypeHeaderValue mediaRange,
MediaTypeHeaderValue mediaType)
{
if (formatter == null)
{
throw new ArgumentNullException("formatter");
}
MediaRangeMapping mapping = new MediaRangeMapping(mediaRange, mediaType);
formatter.MediaTypeMappings.Add(mapping);
}
/// <summary>
/// Updates the given <paramref name="formatter"/>'s set of <see cref="MediaTypeMapping"/> elements
/// so that it associates the <paramref name="mediaType"/> with a specific HTTP request header field
/// with a specific value.
/// </summary>
/// <remarks><see cref="RequestHeaderMapping"/> checks header fields associated with <see cref="M:HttpRequestMessage.Headers"/> for a match. It does
/// not check header fields associated with <see cref="M:HttpResponseMessage.Headers"/> or <see cref="M:HttpContent.Headers"/> instances.</remarks>
/// <param name="formatter">The <see cref="MediaTypeFormatter"/> to receive the new <see cref="MediaRangeMapping"/> item.</param>
/// <param name="headerName">Name of the header to match.</param>
/// <param name="headerValue">The header value to match.</param>
/// <param name="valueComparison">The <see cref="StringComparison"/> to use when matching <paramref name="headerValue"/>.</param>
/// <param name="isValueSubstring">if set to <c>true</c> then <paramref name="headerValue"/> is
/// considered a match if it matches a substring of the actual header value.</param>
/// <param name="mediaType">The <see cref="MediaTypeHeaderValue"/> to associate
/// with a <see cref="M:HttpRequestMessage.Header"/> entry with a name matching <paramref name="headerName"/>
/// and a value matching <paramref name="headerValue"/>.</param>
public static void AddRequestHeaderMapping(
this MediaTypeFormatter formatter,
string headerName,
string headerValue,
StringComparison valueComparison,
bool isValueSubstring,
MediaTypeHeaderValue mediaType)
{
if (formatter == null)
{
throw new ArgumentNullException("formatter");
}
RequestHeaderMapping mapping = new RequestHeaderMapping(headerName, headerValue, valueComparison, isValueSubstring, mediaType);
formatter.MediaTypeMappings.Add(mapping);
}
/// <summary>
/// Updates the given <paramref name="formatter"/>'s set of <see cref="MediaTypeMapping"/> elements
/// so that it associates the <paramref name="mediaType"/> with a specific HTTP request header field
/// with a specific value.
/// </summary>
/// <remarks><see cref="RequestHeaderMapping"/> checks header fields associated with <see cref="M:HttpRequestMessage.Headers"/> for a match. It does
/// not check header fields associated with <see cref="M:HttpResponseMessage.Headers"/> or <see cref="M:HttpContent.Headers"/> instances.</remarks>
/// <param name="formatter">The <see cref="MediaTypeFormatter"/> to receive the new <see cref="MediaRangeMapping"/> item.</param>
/// <param name="headerName">Name of the header to match.</param>
/// <param name="headerValue">The header value to match.</param>
/// <param name="valueComparison">The <see cref="StringComparison"/> to use when matching <paramref name="headerValue"/>.</param>
/// <param name="isValueSubstring">if set to <c>true</c> then <paramref name="headerValue"/> is
/// considered a match if it matches a substring of the actual header value.</param>
/// <param name="mediaType">The media type to associate
/// with a <see cref="M:HttpRequestMessage.Header"/> entry with a name matching <paramref name="headerName"/>
/// and a value matching <paramref name="headerValue"/>.</param>
public static void AddRequestHeaderMapping(
this MediaTypeFormatter formatter,
string headerName,
string headerValue,
StringComparison valueComparison,
bool isValueSubstring,
string mediaType)
{
if (formatter == null)
{
throw new ArgumentNullException("formatter");
}
RequestHeaderMapping mapping = new RequestHeaderMapping(headerName, headerValue, valueComparison, isValueSubstring, mediaType);
formatter.MediaTypeMappings.Add(mapping);
}
}
}

View File

@ -0,0 +1,71 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.Contracts;
using System.Linq;
using System.Net.Http.Headers;
namespace System.Net.Http.Formatting
{
/// <summary>
/// Extension methods for <see cref="MediaTypeHeaderValue"/>.
/// </summary>
internal static class MediaTypeHeaderValueExtensions
{
public static bool IsMediaRange(this MediaTypeHeaderValue mediaType)
{
Contract.Assert(mediaType != null, "The 'mediaType' parameter should not be null.");
return new ParsedMediaTypeHeaderValue(mediaType).IsSubTypeMediaRange;
}
/// <summary>
/// Determines whether two <see cref="MediaTypeHeaderValue"/> instances match. The instance
/// <paramref name="mediaType1"/> is said to match <paramref name="mediaType2"/> if and only if
/// <paramref name="mediaType1"/> is a strict subset of the values and parameters of <paramref name="mediaType2"/>.
/// That is, if the media type and media type parameters of <paramref name="mediaType1"/> are all present
/// and match those of <paramref name="mediaType2"/> then it is a match even though <paramref name="mediaType2"/> may have additional
/// parameters.
/// </summary>
/// <param name="mediaType1">The first media type.</param>
/// <param name="mediaType2">The second media type.</param>
/// <returns><c>true</c> if this is a superset of <paramref name="mediaType2"/>; false otherwise.</returns>
public static bool IsSubsetOf(this MediaTypeHeaderValue mediaType1, MediaTypeHeaderValue mediaType2)
{
Contract.Assert(mediaType1 != null);
if (mediaType2 == null)
{
return false;
}
ParsedMediaTypeHeaderValue parsedMediaType1 = new ParsedMediaTypeHeaderValue(mediaType1);
ParsedMediaTypeHeaderValue parsedMediaType2 = new ParsedMediaTypeHeaderValue(mediaType2);
if (!String.Equals(parsedMediaType1.Type, parsedMediaType2.Type, StringComparison.OrdinalIgnoreCase))
{
if (!parsedMediaType2.IsAllMediaRange)
{
return false;
}
}
else if (!String.Equals(parsedMediaType1.SubType, parsedMediaType2.SubType, StringComparison.OrdinalIgnoreCase))
{
if (!parsedMediaType2.IsSubTypeMediaRange)
{
return false;
}
}
// So far we either have a full match or a subset match. Now check that all of
// mediaType1's parameters are present and equal in mediatype2
foreach (NameValueHeaderValue parameter1 in mediaType1.Parameters)
{
if (!mediaType2.Parameters.Any((parameter2) => { return parameter1.Equals(parameter2); }))
{
return false;
}
}
return true;
}
}
}

Some files were not shown because too many files have changed in this diff Show More