diff options
Diffstat (limited to 'src/DotNetOpenAuth.Core')
20 files changed, 638 insertions, 404 deletions
diff --git a/src/DotNetOpenAuth.Core/Assumes.cs b/src/DotNetOpenAuth.Core/Assumes.cs index b61fbd4..f29f09f 100644 --- a/src/DotNetOpenAuth.Core/Assumes.cs +++ b/src/DotNetOpenAuth.Core/Assumes.cs @@ -12,6 +12,7 @@ namespace DotNetOpenAuth { using System.Diagnostics.Contracts; using System.Globalization; using System.Linq; + using System.Runtime.Serialization; using System.Text; /// <summary> @@ -39,7 +40,7 @@ namespace DotNetOpenAuth { [Pure, DebuggerStepThrough] internal static void True(bool condition, string unformattedMessage, params object[] args) { if (!condition) { - Fail(String.Format(CultureInfo.CurrentCulture, unformattedMessage, args)); + Fail(string.Format(CultureInfo.CurrentCulture, unformattedMessage, args)); } } @@ -84,8 +85,8 @@ namespace DotNetOpenAuth { /// <exception cref="T:System.ArgumentNullException">The <paramref name="info"/> parameter is null. </exception> /// <exception cref="T:System.Runtime.Serialization.SerializationException">The class name is null or <see cref="P:System.Exception.HResult"/> is zero (0). </exception> protected InternalErrorException( - System.Runtime.Serialization.SerializationInfo info, - System.Runtime.Serialization.StreamingContext context) + SerializationInfo info, + StreamingContext context) : base(info, context) { } } diff --git a/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj b/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj index 9b8a613..c46e6b8 100644 --- a/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj +++ b/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj @@ -28,10 +28,11 @@ <Compile Include="Messaging\CachedDirectWebResponse.cs" /> <Compile Include="Messaging\ChannelContract.cs" /> <Compile Include="Messaging\DataBagFormatterBase.cs" /> + <Compile Include="Messaging\HttpRequestHeaders.cs" /> <Compile Include="Messaging\IHttpIndirectResponse.cs" /> <Compile Include="Messaging\IMessageOriginalPayload.cs" /> <Compile Include="Messaging\DirectWebRequestOptions.cs" /> - <Compile Include="Messaging\EnumerableCache.cs" /> + <Compile Include="Messaging\EnumerableCacheExtensions.cs" /> <Compile Include="Messaging\HostErrorException.cs" /> <Compile Include="Messaging\IHttpDirectResponse.cs" /> <Compile Include="Messaging\IExtensionMessage.cs" /> @@ -127,6 +128,7 @@ <Compile Include="Loggers\NoOpLogger.cs" /> <Compile Include="Loggers\TraceLogger.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="Messaging\ReadOnlyDictionary.cs" /> <Compile Include="Reporting.cs" /> <Compile Include="Requires.cs" /> <Compile Include="Strings.Designer.cs"> diff --git a/src/DotNetOpenAuth.Core/Loggers/ILog.cs b/src/DotNetOpenAuth.Core/Loggers/ILog.cs index 8094296..e801b2a 100644 --- a/src/DotNetOpenAuth.Core/Loggers/ILog.cs +++ b/src/DotNetOpenAuth.Core/Loggers/ILog.cs @@ -122,8 +122,8 @@ namespace DotNetOpenAuth.Loggers /// <param name="args">An Object array containing zero or more objects to format</param> /// <remarks> /// <para> - /// The message is formatted using the <c>String.Format</c> method. See - /// <see cref="String.Format(string, object[])"/> for details of the syntax of the format string and the behavior + /// The message is formatted using the <c>string.Format</c> method. See + /// <see cref="string.Format(string, object[])"/> for details of the syntax of the format string and the behavior /// of the formatting. /// </para> /// <para> @@ -143,8 +143,8 @@ namespace DotNetOpenAuth.Loggers /// <param name="arg0">An Object to format</param> /// <remarks> /// <para> - /// The message is formatted using the <c>String.Format</c> method. See - /// <see cref="String.Format(string, object[])"/> for details of the syntax of the format string and the behavior + /// The message is formatted using the <c>string.Format</c> method. See + /// <see cref="string.Format(string, object[])"/> for details of the syntax of the format string and the behavior /// of the formatting. /// </para> /// <para> @@ -165,8 +165,8 @@ namespace DotNetOpenAuth.Loggers /// <param name="arg1">An Object to format</param> /// <remarks> /// <para> - /// The message is formatted using the <c>String.Format</c> method. See - /// <see cref="String.Format(string, object[])"/> for details of the syntax of the format string and the behavior + /// The message is formatted using the <c>string.Format</c> method. See + /// <see cref="string.Format(string, object[])"/> for details of the syntax of the format string and the behavior /// of the formatting. /// </para> /// <para> @@ -188,8 +188,8 @@ namespace DotNetOpenAuth.Loggers /// <param name="arg2">An Object to format</param> /// <remarks> /// <para> - /// The message is formatted using the <c>String.Format</c> method. See - /// <see cref="String.Format(string, object[])"/> for details of the syntax of the format string and the behavior + /// The message is formatted using the <c>string.Format</c> method. See + /// <see cref="string.Format(string, object[])"/> for details of the syntax of the format string and the behavior /// of the formatting. /// </para> /// <para> @@ -253,8 +253,8 @@ namespace DotNetOpenAuth.Loggers /// <param name="args">An Object array containing zero or more objects to format</param> /// <remarks> /// <para> - /// The message is formatted using the <c>String.Format</c> method. See - /// <see cref="String.Format(string, object[])"/> for details of the syntax of the format string and the behavior + /// The message is formatted using the <c>string.Format</c> method. See + /// <see cref="string.Format(string, object[])"/> for details of the syntax of the format string and the behavior /// of the formatting. /// </para> /// <para> @@ -274,8 +274,8 @@ namespace DotNetOpenAuth.Loggers /// <param name="arg0">An Object to format</param> /// <remarks> /// <para> - /// The message is formatted using the <c>String.Format</c> method. See - /// <see cref="String.Format(string, object[])"/> for details of the syntax of the format string and the behavior + /// The message is formatted using the <c>string.Format</c> method. See + /// <see cref="string.Format(string, object[])"/> for details of the syntax of the format string and the behavior /// of the formatting. /// </para> /// <para> @@ -296,8 +296,8 @@ namespace DotNetOpenAuth.Loggers /// <param name="arg1">An Object to format</param> /// <remarks> /// <para> - /// The message is formatted using the <c>String.Format</c> method. See - /// <see cref="String.Format(string, object[])"/> for details of the syntax of the format string and the behavior + /// The message is formatted using the <c>string.Format</c> method. See + /// <see cref="string.Format(string, object[])"/> for details of the syntax of the format string and the behavior /// of the formatting. /// </para> /// <para> @@ -319,8 +319,8 @@ namespace DotNetOpenAuth.Loggers /// <param name="arg2">An Object to format</param> /// <remarks> /// <para> - /// The message is formatted using the <c>String.Format</c> method. See - /// <see cref="String.Format(string, object[])"/> for details of the syntax of the format string and the behavior + /// The message is formatted using the <c>string.Format</c> method. See + /// <see cref="string.Format(string, object[])"/> for details of the syntax of the format string and the behavior /// of the formatting. /// </para> /// <para> @@ -384,8 +384,8 @@ namespace DotNetOpenAuth.Loggers /// <param name="args">An Object array containing zero or more objects to format</param> /// <remarks> /// <para> - /// The message is formatted using the <c>String.Format</c> method. See - /// <see cref="String.Format(string, object[])"/> for details of the syntax of the format string and the behavior + /// The message is formatted using the <c>string.Format</c> method. See + /// <see cref="string.Format(string, object[])"/> for details of the syntax of the format string and the behavior /// of the formatting. /// </para> /// <para> @@ -405,8 +405,8 @@ namespace DotNetOpenAuth.Loggers /// <param name="arg0">An Object to format</param> /// <remarks> /// <para> - /// The message is formatted using the <c>String.Format</c> method. See - /// <see cref="String.Format(string, object[])"/> for details of the syntax of the format string and the behavior + /// The message is formatted using the <c>string.Format</c> method. See + /// <see cref="string.Format(string, object[])"/> for details of the syntax of the format string and the behavior /// of the formatting. /// </para> /// <para> @@ -427,8 +427,8 @@ namespace DotNetOpenAuth.Loggers /// <param name="arg1">An Object to format</param> /// <remarks> /// <para> - /// The message is formatted using the <c>String.Format</c> method. See - /// <see cref="String.Format(string, object[])"/> for details of the syntax of the format string and the behavior + /// The message is formatted using the <c>string.Format</c> method. See + /// <see cref="string.Format(string, object[])"/> for details of the syntax of the format string and the behavior /// of the formatting. /// </para> /// <para> @@ -450,8 +450,8 @@ namespace DotNetOpenAuth.Loggers /// <param name="arg2">An Object to format</param> /// <remarks> /// <para> - /// The message is formatted using the <c>String.Format</c> method. See - /// <see cref="String.Format(string, object[])"/> for details of the syntax of the format string and the behavior + /// The message is formatted using the <c>string.Format</c> method. See + /// <see cref="string.Format(string, object[])"/> for details of the syntax of the format string and the behavior /// of the formatting. /// </para> /// <para> @@ -515,8 +515,8 @@ namespace DotNetOpenAuth.Loggers /// <param name="args">An Object array containing zero or more objects to format</param> /// <remarks> /// <para> - /// The message is formatted using the <c>String.Format</c> method. See - /// <see cref="String.Format(string, object[])"/> for details of the syntax of the format string and the behavior + /// The message is formatted using the <c>string.Format</c> method. See + /// <see cref="string.Format(string, object[])"/> for details of the syntax of the format string and the behavior /// of the formatting. /// </para> /// <para> @@ -536,8 +536,8 @@ namespace DotNetOpenAuth.Loggers /// <param name="arg0">An Object to format</param> /// <remarks> /// <para> - /// The message is formatted using the <c>String.Format</c> method. See - /// <see cref="String.Format(string, object[])"/> for details of the syntax of the format string and the behavior + /// The message is formatted using the <c>string.Format</c> method. See + /// <see cref="string.Format(string, object[])"/> for details of the syntax of the format string and the behavior /// of the formatting. /// </para> /// <para> @@ -558,8 +558,8 @@ namespace DotNetOpenAuth.Loggers /// <param name="arg1">An Object to format</param> /// <remarks> /// <para> - /// The message is formatted using the <c>String.Format</c> method. See - /// <see cref="String.Format(string, object[])"/> for details of the syntax of the format string and the behavior + /// The message is formatted using the <c>string.Format</c> method. See + /// <see cref="string.Format(string, object[])"/> for details of the syntax of the format string and the behavior /// of the formatting. /// </para> /// <para> @@ -581,8 +581,8 @@ namespace DotNetOpenAuth.Loggers /// <param name="arg2">An Object to format</param> /// <remarks> /// <para> - /// The message is formatted using the <c>String.Format</c> method. See - /// <see cref="String.Format(string, object[])"/> for details of the syntax of the format string and the behavior + /// The message is formatted using the <c>string.Format</c> method. See + /// <see cref="string.Format(string, object[])"/> for details of the syntax of the format string and the behavior /// of the formatting. /// </para> /// <para> @@ -646,8 +646,8 @@ namespace DotNetOpenAuth.Loggers /// <param name="args">An Object array containing zero or more objects to format</param> /// <remarks> /// <para> - /// The message is formatted using the <c>String.Format</c> method. See - /// <see cref="String.Format(string, object[])"/> for details of the syntax of the format string and the behavior + /// The message is formatted using the <c>string.Format</c> method. See + /// <see cref="string.Format(string, object[])"/> for details of the syntax of the format string and the behavior /// of the formatting. /// </para> /// <para> @@ -667,8 +667,8 @@ namespace DotNetOpenAuth.Loggers /// <param name="arg0">An Object to format</param> /// <remarks> /// <para> - /// The message is formatted using the <c>String.Format</c> method. See - /// <see cref="String.Format(string, object[])"/> for details of the syntax of the format string and the behavior + /// The message is formatted using the <c>string.Format</c> method. See + /// <see cref="string.Format(string, object[])"/> for details of the syntax of the format string and the behavior /// of the formatting. /// </para> /// <para> @@ -689,8 +689,8 @@ namespace DotNetOpenAuth.Loggers /// <param name="arg1">An Object to format</param> /// <remarks> /// <para> - /// The message is formatted using the <c>String.Format</c> method. See - /// <see cref="String.Format(string, object[])"/> for details of the syntax of the format string and the behavior + /// The message is formatted using the <c>string.Format</c> method. See + /// <see cref="string.Format(string, object[])"/> for details of the syntax of the format string and the behavior /// of the formatting. /// </para> /// <para> @@ -712,8 +712,8 @@ namespace DotNetOpenAuth.Loggers /// <param name="arg2">An Object to format</param> /// <remarks> /// <para> - /// The message is formatted using the <c>String.Format</c> method. See - /// <see cref="String.Format(string, object[])"/> for details of the syntax of the format string and the behavior + /// The message is formatted using the <c>string.Format</c> method. See + /// <see cref="string.Format(string, object[])"/> for details of the syntax of the format string and the behavior /// of the formatting. /// </para> /// <para> diff --git a/src/DotNetOpenAuth.Core/Messaging/Channel.cs b/src/DotNetOpenAuth.Core/Messaging/Channel.cs index 26a8179..16e39d3 100644 --- a/src/DotNetOpenAuth.Core/Messaging/Channel.cs +++ b/src/DotNetOpenAuth.Core/Messaging/Channel.cs @@ -69,7 +69,7 @@ namespace DotNetOpenAuth.Messaging { /// <summary> /// The HTML that should be returned to the user agent as part of a 301 Redirect. /// </summary> - /// <value>A string that should be used as the first argument to String.Format, where the {0} should be replaced with the URL to redirect to.</value> + /// <value>A string that should be used as the first argument to string.Format, where the {0} should be replaced with the URL to redirect to.</value> private const string RedirectResponseBodyFormat = @"<html><head><title>Object moved</title></head><body> <h2>Object moved to <a href=""{0}"">here</a>.</h2> </body></html>"; @@ -409,7 +409,7 @@ namespace DotNetOpenAuth.Messaging { /// <returns>True if the expected message was recognized and deserialized. False otherwise.</returns> /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception> /// <exception cref="ProtocolException">Thrown when a request message of an unexpected type is received.</exception> - public bool TryReadFromRequest<TRequest>(HttpRequestInfo httpRequest, out TRequest request) + public bool TryReadFromRequest<TRequest>(HttpRequestBase httpRequest, out TRequest request) where TRequest : class, IProtocolMessage { Requires.NotNull(httpRequest, "httpRequest"); Contract.Ensures(Contract.Result<bool>() == (Contract.ValueAtReturn<TRequest>(out request) != null)); @@ -450,7 +450,7 @@ namespace DotNetOpenAuth.Messaging { /// <returns>The deserialized message. Never null.</returns> /// <exception cref="ProtocolException">Thrown if the expected message was not recognized in the response.</exception> [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")] - public TRequest ReadFromRequest<TRequest>(HttpRequestInfo httpRequest) + public TRequest ReadFromRequest<TRequest>(HttpRequestBase httpRequest) where TRequest : class, IProtocolMessage { Requires.NotNull(httpRequest, "httpRequest"); TRequest request; @@ -466,11 +466,11 @@ namespace DotNetOpenAuth.Messaging { /// </summary> /// <param name="httpRequest">The request to search for an embedded message.</param> /// <returns>The deserialized message, if one is found. Null otherwise.</returns> - public IDirectedProtocolMessage ReadFromRequest(HttpRequestInfo httpRequest) { + public IDirectedProtocolMessage ReadFromRequest(HttpRequestBase httpRequest) { Requires.NotNull(httpRequest, "httpRequest"); - if (Logger.Channel.IsInfoEnabled && httpRequest.UrlBeforeRewriting != null) { - Logger.Channel.InfoFormat("Scanning incoming request for messages: {0}", httpRequest.UrlBeforeRewriting.AbsoluteUri); + if (Logger.Channel.IsInfoEnabled && httpRequest.GetPublicFacingUrl() != null) { + Logger.Channel.InfoFormat("Scanning incoming request for messages: {0}", httpRequest.GetPublicFacingUrl().AbsoluteUri); } IDirectedProtocolMessage requestMessage = this.ReadFromRequestCore(httpRequest); if (requestMessage != null) { @@ -599,6 +599,16 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Gets the HTTP context for the current HTTP request. + /// </summary> + /// <returns>An HttpContextBase instance.</returns> + [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Allocates memory")] + protected internal virtual HttpContextBase GetHttpContext() { + Requires.ValidState(HttpContext.Current != null, MessagingStrings.HttpContextRequired); + return new HttpContextWrapper(HttpContext.Current); + } + + /// <summary> /// Gets the current HTTP request being processed. /// </summary> /// <returns>The HttpRequestInfo for the current request.</returns> @@ -607,16 +617,13 @@ namespace DotNetOpenAuth.Messaging { /// </remarks> /// <exception cref="InvalidOperationException">Thrown if <see cref="HttpContext.Current">HttpContext.Current</see> == <c>null</c>.</exception> [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Costly call should not be a property.")] - protected internal virtual HttpRequestInfo GetRequestFromContext() { + protected internal virtual HttpRequestBase GetRequestFromContext() { Requires.ValidState(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired); - Contract.Ensures(Contract.Result<HttpRequestInfo>() != null); - Contract.Ensures(Contract.Result<HttpRequestInfo>().Url != null); - Contract.Ensures(Contract.Result<HttpRequestInfo>().RawUrl != null); - Contract.Ensures(Contract.Result<HttpRequestInfo>().UrlBeforeRewriting != null); + Contract.Ensures(Contract.Result<HttpRequestBase>() != null); Contract.Assume(HttpContext.Current.Request.Url != null); Contract.Assume(HttpContext.Current.Request.RawUrl != null); - return new HttpRequestInfo(HttpContext.Current.Request); + return new HttpRequestWrapper(HttpContext.Current.Request); } /// <summary> @@ -638,6 +645,22 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Applies message prescribed HTTP response headers to an outgoing web response. + /// </summary> + /// <param name="message">The message.</param> + /// <param name="response">The HTTP response.</param> + protected static void ApplyMessageTemplate(IMessage message, OutgoingWebResponse response) { + Requires.NotNull(message, "message"); + var httpMessage = message as IHttpDirectResponse; + if (httpMessage != null) { + response.Status = httpMessage.HttpStatusCode; + foreach (string headerName in httpMessage.Headers) { + response.Headers.Add(headerName, httpMessage.Headers[headerName]); + } + } + } + + /// <summary> /// Releases unmanaged and - optionally - managed resources /// </summary> /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> @@ -731,16 +754,16 @@ namespace DotNetOpenAuth.Messaging { /// </summary> /// <param name="request">The request to search for an embedded message.</param> /// <returns>The deserialized message, if one is found. Null otherwise.</returns> - protected virtual IDirectedProtocolMessage ReadFromRequestCore(HttpRequestInfo request) { + protected virtual IDirectedProtocolMessage ReadFromRequestCore(HttpRequestBase request) { Requires.NotNull(request, "request"); - Logger.Channel.DebugFormat("Incoming HTTP request: {0} {1}", request.HttpMethod, request.UrlBeforeRewriting.AbsoluteUri); + Logger.Channel.DebugFormat("Incoming HTTP request: {0} {1}", request.HttpMethod, request.GetPublicFacingUrl().AbsoluteUri); // Search Form data first, and if nothing is there search the QueryString - Contract.Assume(request.Form != null && request.QueryStringBeforeRewriting != null); + Contract.Assume(request.Form != null && request.GetQueryStringBeforeRewriting() != null); var fields = request.Form.ToDictionary(); if (fields.Count == 0 && request.HttpMethod != "POST") { // OpenID 2.0 section 4.1.2 - fields = request.QueryStringBeforeRewriting.ToDictionary(); + fields = request.GetQueryStringBeforeRewriting().ToDictionary(); } MessageReceivingEndpoint recipient; @@ -1038,22 +1061,6 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> - /// Applies message prescribed HTTP response headers to an outgoing web response. - /// </summary> - /// <param name="message">The message.</param> - /// <param name="response">The HTTP response.</param> - protected void ApplyMessageTemplate(IMessage message, OutgoingWebResponse response) { - Requires.NotNull(message, "message"); - var httpMessage = message as IHttpDirectResponse; - if (httpMessage != null) { - response.Status = httpMessage.HttpStatusCode; - foreach (string headerName in httpMessage.Headers) { - response.Headers.Add(headerName, httpMessage.Headers[headerName]); - } - } - } - - /// <summary> /// Prepares to send a request to the Service Provider as the query string in a GET request. /// </summary> /// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param> diff --git a/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs b/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs index 43df1f5..9d4b93e 100644 --- a/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs +++ b/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs @@ -110,7 +110,7 @@ namespace DotNetOpenAuth.Messaging { /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param> protected DataBagFormatterBase(ICryptoKeyStore cryptoKeyStore = null, string bucket = null, bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? minimumAge = null, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null) : this(signed, encrypted, compressed, maximumAge, decodeOnceOnly) { - Requires.True(!String.IsNullOrEmpty(bucket) || cryptoKeyStore == null, null); + Requires.True(!string.IsNullOrEmpty(bucket) || cryptoKeyStore == null, null); Requires.True(cryptoKeyStore != null || (!signed && !encrypted), null); this.cryptoKeyStore = cryptoKeyStore; @@ -190,15 +190,22 @@ namespace DotNetOpenAuth.Messaging { /// <summary> /// Deserializes a <see cref="DataBag"/>, including decompression, decryption, signature and nonce validation where applicable. /// </summary> - /// <param name="containingMessage">The message that contains the <see cref="DataBag"/> serialized value. Must not be nulll.</param> + /// <param name="containingMessage">The message that contains the <see cref="DataBag"/> serialized value. Must not be null.</param> /// <param name="value">The serialized form of the <see cref="DataBag"/> to deserialize. Must not be null or empty.</param> - /// <returns>The deserialized value. Never null.</returns> + /// <param name="messagePartName">The name of the parameter whose value is to be deserialized. Used for error message generation.</param> + /// <returns> + /// The deserialized value. Never null. + /// </returns> [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No apparent problem. False positive?")] - public T Deserialize(IProtocolMessage containingMessage, string value) { + public T Deserialize(IProtocolMessage containingMessage, string value, string messagePartName) { + Requires.NotNull(containingMessage, "containingMessage"); + Requires.NotNullOrEmpty(value, "value"); + Requires.NotNullOrEmpty(messagePartName, "messagePartName"); + string symmetricSecretHandle = null; if (this.encrypted && this.cryptoKeyStore != null) { string valueWithoutHandle; - MessagingUtilities.ExtractKeyHandleAndPayload(containingMessage, "<TODO>", value, out symmetricSecretHandle, out valueWithoutHandle); + MessagingUtilities.ExtractKeyHandleAndPayload(containingMessage, messagePartName, value, out symmetricSecretHandle, out valueWithoutHandle); value = valueWithoutHandle; } diff --git a/src/DotNetOpenAuth.Core/Messaging/EmptyDictionary.cs b/src/DotNetOpenAuth.Core/Messaging/EmptyDictionary.cs index a686d45..f21be78 100644 --- a/src/DotNetOpenAuth.Core/Messaging/EmptyDictionary.cs +++ b/src/DotNetOpenAuth.Core/Messaging/EmptyDictionary.cs @@ -75,6 +75,7 @@ namespace DotNetOpenAuth.Messaging { /// Gets or sets the value with the specified key. /// </summary> /// <param name="key">The key being read or written.</param> + /// <returns>Nothing. It always throws.</returns> public TValue this[TKey key] { get { throw new KeyNotFoundException(); } set { throw new NotSupportedException(); } diff --git a/src/DotNetOpenAuth.Core/Messaging/EmptyList.cs b/src/DotNetOpenAuth.Core/Messaging/EmptyList.cs index 6790e19..5d64118 100644 --- a/src/DotNetOpenAuth.Core/Messaging/EmptyList.cs +++ b/src/DotNetOpenAuth.Core/Messaging/EmptyList.cs @@ -53,6 +53,7 @@ namespace DotNetOpenAuth.Messaging { /// Gets or sets the <typeparamref name="T"/> at the specified index. /// </summary> /// <param name="index">The index of the element in the list to change.</param> + /// <returns>Nothing. It always throws.</returns> public T this[int index] { get { throw new ArgumentOutOfRangeException("index"); diff --git a/src/DotNetOpenAuth.Core/Messaging/EnumerableCache.cs b/src/DotNetOpenAuth.Core/Messaging/EnumerableCacheExtensions.cs index afbdf30..5e9cf93 100644 --- a/src/DotNetOpenAuth.Core/Messaging/EnumerableCache.cs +++ b/src/DotNetOpenAuth.Core/Messaging/EnumerableCacheExtensions.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------- -// <copyright file="EnumerableCache.cs" company="Outercurve Foundation"> +// <copyright file="EnumerableCacheExtensions.cs" company="Outercurve Foundation"> // Copyright (c) Outercurve Foundation. All rights reserved. // This code is released under the Microsoft Public License (Ms-PL). // </copyright> diff --git a/src/DotNetOpenAuth.Core/Messaging/HttpRequestHeaders.cs b/src/DotNetOpenAuth.Core/Messaging/HttpRequestHeaders.cs new file mode 100644 index 0000000..9579a81 --- /dev/null +++ b/src/DotNetOpenAuth.Core/Messaging/HttpRequestHeaders.cs @@ -0,0 +1,27 @@ +//----------------------------------------------------------------------- +// <copyright file="HttpRequestHeaders.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Messaging { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + + /// <summary> + /// Well known HTTP headers. + /// </summary> + internal static class HttpRequestHeaders { + /// <summary> + /// The Authorization header, which specifies the credentials that the client presents in order to authenticate itself to the server. + /// </summary> + internal const string Authorization = "Authorization"; + + /// <summary> + /// The Content-Type header, which specifies the MIME type of the accompanying body data. + /// </summary> + internal const string ContentType = "Content-Type"; + } +} diff --git a/src/DotNetOpenAuth.Core/Messaging/HttpRequestInfo.cs b/src/DotNetOpenAuth.Core/Messaging/HttpRequestInfo.cs index 49ecb36..ed948ce 100644 --- a/src/DotNetOpenAuth.Core/Messaging/HttpRequestInfo.cs +++ b/src/DotNetOpenAuth.Core/Messaging/HttpRequestInfo.cs @@ -25,101 +25,51 @@ namespace DotNetOpenAuth.Messaging { /// ASP.NET does not let us fully initialize that class, so we have to write one /// of our one. /// </remarks> - public class HttpRequestInfo { + public class HttpRequestInfo : HttpRequestBase { /// <summary> - /// The key/value pairs found in the entity of a POST request. + /// The HTTP verb in the request. /// </summary> - private NameValueCollection form; + private readonly string httpMethod; /// <summary> - /// The key/value pairs found in the querystring of the incoming request. + /// The full request URL. /// </summary> - private NameValueCollection queryString; + private readonly Uri requestUri; /// <summary> - /// Backing field for the <see cref="QueryStringBeforeRewriting"/> property. + /// The HTTP headers. /// </summary> - private NameValueCollection queryStringBeforeRewriting; + private readonly NameValueCollection headers; /// <summary> - /// Backing field for the <see cref="Message"/> property. + /// The variables defined in the query part of the URL. /// </summary> - private IDirectedProtocolMessage message; + private readonly NameValueCollection queryString; /// <summary> - /// Initializes a new instance of the <see cref="HttpRequestInfo"/> class. + /// The POSTed form variables. /// </summary> - /// <param name="request">The ASP.NET structure to copy from.</param> - public HttpRequestInfo(HttpRequest request) { - Requires.NotNull(request, "request"); - Contract.Ensures(this.HttpMethod == request.HttpMethod); - Contract.Ensures(this.Url == request.Url); - Contract.Ensures(this.RawUrl == request.RawUrl); - Contract.Ensures(this.UrlBeforeRewriting != null); - Contract.Ensures(this.Headers != null); - Contract.Ensures(this.InputStream == request.InputStream); - Contract.Ensures(this.form == request.Form); - Contract.Ensures(this.queryString == request.QueryString); - - this.HttpMethod = request.HttpMethod; - this.Url = request.Url; - this.UrlBeforeRewriting = GetPublicFacingUrl(request); - this.RawUrl = request.RawUrl; - this.Headers = GetHeaderCollection(request.Headers); - this.InputStream = request.InputStream; - - // These values would normally be calculated, but we'll reuse them from - // HttpRequest since they're already calculated, and there's a chance (<g>) - // that ASP.NET does a better job of being comprehensive about gathering - // these as well. - this.form = request.Form; - this.queryString = request.QueryString; - - Reporting.RecordRequestStatistics(this); - } + private readonly NameValueCollection form; /// <summary> - /// Initializes a new instance of the <see cref="HttpRequestInfo"/> class. + /// The server variables collection. /// </summary> - /// <param name="httpMethod">The HTTP method (i.e. GET or POST) of the incoming request.</param> - /// <param name="requestUrl">The URL being requested.</param> - /// <param name="rawUrl">The raw URL that appears immediately following the HTTP verb in the request, - /// before any URL rewriting takes place.</param> - /// <param name="headers">Headers in the HTTP request.</param> - /// <param name="inputStream">The entity stream, if any. (POST requests typically have these). Use <c>null</c> for GET requests.</param> - public HttpRequestInfo(string httpMethod, Uri requestUrl, string rawUrl, WebHeaderCollection headers, Stream inputStream) { - Requires.NotNullOrEmpty(httpMethod, "httpMethod"); - Requires.NotNull(requestUrl, "requestUrl"); - Requires.NotNull(rawUrl, "rawUrl"); - Requires.NotNull(headers, "headers"); - - this.HttpMethod = httpMethod; - this.Url = requestUrl; - this.UrlBeforeRewriting = requestUrl; - this.RawUrl = rawUrl; - this.Headers = headers; - this.InputStream = inputStream; - - Reporting.RecordRequestStatistics(this); - } + private readonly NameValueCollection serverVariables; /// <summary> /// Initializes a new instance of the <see cref="HttpRequestInfo"/> class. /// </summary> - /// <param name="listenerRequest">Details on the incoming HTTP request.</param> - public HttpRequestInfo(HttpListenerRequest listenerRequest) { - Requires.NotNull(listenerRequest, "listenerRequest"); - - this.HttpMethod = listenerRequest.HttpMethod; - this.Url = listenerRequest.Url; - this.UrlBeforeRewriting = listenerRequest.Url; - this.RawUrl = listenerRequest.RawUrl; - this.Headers = new WebHeaderCollection(); - foreach (string key in listenerRequest.Headers) { - this.Headers[key] = listenerRequest.Headers[key]; - } + /// <param name="request">The request.</param> + /// <param name="requestUri">The request URI.</param> + internal HttpRequestInfo(HttpRequestMessageProperty request, Uri requestUri) { + Requires.NotNull(request, "request"); + Requires.NotNull(requestUri, "requestUri"); - this.InputStream = listenerRequest.InputStream; + this.httpMethod = request.Method; + this.headers = request.Headers; + this.requestUri = requestUri; + this.form = new NameValueCollection(); + this.serverVariables = new NameValueCollection(); Reporting.RecordRequestStatistics(this); } @@ -127,297 +77,179 @@ namespace DotNetOpenAuth.Messaging { /// <summary> /// Initializes a new instance of the <see cref="HttpRequestInfo"/> class. /// </summary> - /// <param name="request">The WCF incoming request structure to get the HTTP information from.</param> - /// <param name="requestUri">The URI of the service endpoint.</param> - public HttpRequestInfo(HttpRequestMessageProperty request, Uri requestUri) { - Requires.NotNull(request, "request"); + /// <param name="httpMethod">The HTTP method.</param> + /// <param name="requestUri">The request URI.</param> + /// <param name="form">The form variables.</param> + /// <param name="headers">The HTTP headers.</param> + internal HttpRequestInfo(string httpMethod, Uri requestUri, NameValueCollection form = null, NameValueCollection headers = null) { + Requires.NotNullOrEmpty(httpMethod, "httpMethod"); Requires.NotNull(requestUri, "requestUri"); - this.HttpMethod = request.Method; - this.Headers = request.Headers; - this.Url = requestUri; - this.UrlBeforeRewriting = requestUri; - this.RawUrl = MakeUpRawUrlFromUrl(requestUri); - - Reporting.RecordRequestStatistics(this); + this.httpMethod = httpMethod; + this.requestUri = requestUri; + this.form = form ?? new NameValueCollection(); + this.queryString = HttpUtility.ParseQueryString(requestUri.Query); + this.headers = headers ?? new NameValueCollection(); + this.serverVariables = new NameValueCollection(); } /// <summary> /// Initializes a new instance of the <see cref="HttpRequestInfo"/> class. /// </summary> - internal HttpRequestInfo() { - Contract.Ensures(this.HttpMethod == "GET"); - Contract.Ensures(this.Headers != null); + /// <param name="listenerRequest">Details on the incoming HTTP request.</param> + internal HttpRequestInfo(HttpListenerRequest listenerRequest) { + Requires.NotNull(listenerRequest, "listenerRequest"); - this.HttpMethod = "GET"; - this.Headers = new WebHeaderCollection(); + this.httpMethod = listenerRequest.HttpMethod; + this.requestUri = listenerRequest.Url; + this.queryString = listenerRequest.QueryString; + this.headers = listenerRequest.Headers; + this.form = ParseFormData(listenerRequest.HttpMethod, listenerRequest.Headers, listenerRequest.InputStream); + this.serverVariables = new NameValueCollection(); + + Reporting.RecordRequestStatistics(this); } /// <summary> /// Initializes a new instance of the <see cref="HttpRequestInfo"/> class. /// </summary> - /// <param name="request">The HttpWebRequest (that was never used) to copy from.</param> - internal HttpRequestInfo(WebRequest request) { - Requires.NotNull(request, "request"); + /// <param name="httpMethod">The HTTP method.</param> + /// <param name="requestUri">The request URI.</param> + /// <param name="headers">The headers.</param> + /// <param name="inputStream">The input stream.</param> + internal HttpRequestInfo(string httpMethod, Uri requestUri, NameValueCollection headers, Stream inputStream) { + Requires.NotNullOrEmpty(httpMethod, "httpMethod"); + Requires.NotNull(requestUri, "requestUri"); - this.HttpMethod = request.Method; - this.Url = request.RequestUri; - this.UrlBeforeRewriting = request.RequestUri; - this.RawUrl = MakeUpRawUrlFromUrl(request.RequestUri); - this.Headers = GetHeaderCollection(request.Headers); - this.InputStream = null; + this.httpMethod = httpMethod; + this.requestUri = requestUri; + this.headers = headers; + this.queryString = HttpUtility.ParseQueryString(requestUri.Query); + this.form = ParseFormData(httpMethod, headers, inputStream); + this.serverVariables = new NameValueCollection(); Reporting.RecordRequestStatistics(this); } /// <summary> - /// Initializes a new instance of the <see cref="HttpRequestInfo"/> class. + /// Gets the HTTP method. /// </summary> - /// <param name="message">The message being passed in through a mock transport. May be null.</param> - /// <param name="httpMethod">The HTTP method that the incoming request came in on, whether or not <paramref name="message"/> is null.</param> - internal HttpRequestInfo(IDirectedProtocolMessage message, HttpDeliveryMethods httpMethod) { - this.message = message; - this.HttpMethod = MessagingUtilities.GetHttpVerb(httpMethod); + public override string HttpMethod { + get { return this.httpMethod; } } /// <summary> - /// Gets or sets the message that is being sent over a mock transport (for testing). + /// Gets the headers. /// </summary> - internal virtual IDirectedProtocolMessage Message { - get { return this.message; } - set { this.message = value; } + public override NameValueCollection Headers { + get { return this.headers; } } /// <summary> - /// Gets or sets the verb in the request (i.e. GET, POST, etc.) - /// </summary> - internal string HttpMethod { get; set; } - - /// <summary> - /// Gets or sets the entire URL of the request, after any URL rewriting. - /// </summary> - internal Uri Url { get; set; } - - /// <summary> - /// Gets or sets the raw URL that appears immediately following the HTTP verb in the request, - /// before any URL rewriting takes place. + /// Gets the URL. /// </summary> - internal string RawUrl { get; set; } - - /// <summary> - /// Gets or sets the full public URL used by the remote client to initiate this request, - /// before any URL rewriting and before any changes made by web farm load distributors. - /// </summary> - internal Uri UrlBeforeRewriting { get; set; } - - /// <summary> - /// Gets the query part of the URL (The ? and everything after it), after URL rewriting. - /// </summary> - internal string Query { - get { return this.Url != null ? this.Url.Query : null; } + public override Uri Url { + get { return this.requestUri; } } /// <summary> - /// Gets or sets the collection of headers that came in with the request. - /// </summary> - internal WebHeaderCollection Headers { get; set; } - - /// <summary> - /// Gets or sets the entity, or body of the request, if any. + /// Gets the raw URL. /// </summary> - internal Stream InputStream { get; set; } - - /// <summary> - /// Gets the key/value pairs found in the entity of a POST request. - /// </summary> - internal NameValueCollection Form { - get { - Contract.Ensures(Contract.Result<NameValueCollection>() != null); - if (this.form == null) { - ContentType contentType = string.IsNullOrEmpty(this.Headers[HttpRequestHeader.ContentType]) ? null : new ContentType(this.Headers[HttpRequestHeader.ContentType]); - if (this.HttpMethod == "POST" && contentType != null && string.Equals(contentType.MediaType, Channel.HttpFormUrlEncoded, StringComparison.Ordinal)) { - StreamReader reader = new StreamReader(this.InputStream); - long originalPosition = 0; - if (this.InputStream.CanSeek) { - originalPosition = this.InputStream.Position; - } - this.form = HttpUtility.ParseQueryString(reader.ReadToEnd()); - if (this.InputStream.CanSeek) { - this.InputStream.Seek(originalPosition, SeekOrigin.Begin); - } - } else { - this.form = new NameValueCollection(); - } - } - - return this.form; - } + public override string RawUrl { + get { return this.requestUri.AbsolutePath + this.requestUri.Query; } } /// <summary> - /// Gets the key/value pairs found in the querystring of the incoming request. + /// Gets the form. /// </summary> - internal NameValueCollection QueryString { - get { - if (this.queryString == null) { - this.queryString = this.Query != null ? HttpUtility.ParseQueryString(this.Query) : new NameValueCollection(); - } - - return this.queryString; - } + public override NameValueCollection Form { + get { return this.form; } } /// <summary> - /// Gets the query data from the original request (before any URL rewriting has occurred.) + /// Gets the query string. /// </summary> - /// <returns>A <see cref="NameValueCollection"/> containing all the parameters in the query string.</returns> - internal NameValueCollection QueryStringBeforeRewriting { - get { - if (this.queryStringBeforeRewriting == null) { - // This request URL may have been rewritten by the host site. - // For openid protocol purposes, we really need to look at - // the original query parameters before any rewriting took place. - if (!this.IsUrlRewritten) { - // No rewriting has taken place. - this.queryStringBeforeRewriting = this.QueryString; - } else { - // Rewriting detected! Recover the original request URI. - ErrorUtilities.VerifyInternal(this.UrlBeforeRewriting != null, "UrlBeforeRewriting is null, so the query string cannot be determined."); - this.queryStringBeforeRewriting = HttpUtility.ParseQueryString(this.UrlBeforeRewriting.Query); - } - } - - return this.queryStringBeforeRewriting; - } + public override NameValueCollection QueryString { + get { return this.queryString; } } /// <summary> - /// Gets a value indicating whether the request's URL was rewritten by ASP.NET - /// or some other module. + /// Gets the server variables. /// </summary> - /// <value> - /// <c>true</c> if this request's URL was rewritten; otherwise, <c>false</c>. - /// </value> - internal bool IsUrlRewritten { - get { return this.Url != this.UrlBeforeRewriting; } + public override NameValueCollection ServerVariables { + get { return this.serverVariables; } } /// <summary> - /// Gets the public facing URL for the given incoming HTTP request. + /// Creates an <see cref="HttpRequestBase"/> instance that describes the specified HTTP request. /// </summary> /// <param name="request">The request.</param> - /// <param name="serverVariables">The server variables to consider part of the request.</param> - /// <returns> - /// The URI that the outside world used to create this request. - /// </returns> - /// <remarks> - /// Although the <paramref name="serverVariables"/> value can be obtained from - /// <see cref="HttpRequest.ServerVariables"/>, it's useful to be able to pass them - /// in so we can simulate injected values from our unit tests since the actual property - /// is a read-only kind of <see cref="NameValueCollection"/>. - /// </remarks> - internal static Uri GetPublicFacingUrl(HttpRequest request, NameValueCollection serverVariables) { - Requires.NotNull(request, "request"); - Requires.NotNull(serverVariables, "serverVariables"); - - // Due to URL rewriting, cloud computing (i.e. Azure) - // and web farms, etc., we have to be VERY careful about what - // we consider the incoming URL. We want to see the URL as it would - // appear on the public-facing side of the hosting web site. - // HttpRequest.Url gives us the internal URL in a cloud environment, - // So we use a variable that (at least from what I can tell) gives us - // the public URL: - if (serverVariables["HTTP_HOST"] != null) { - ErrorUtilities.VerifySupported(request.Url.Scheme == Uri.UriSchemeHttps || request.Url.Scheme == Uri.UriSchemeHttp, "Only HTTP and HTTPS are supported protocols."); - string scheme = serverVariables["HTTP_X_FORWARDED_PROTO"] ?? request.Url.Scheme; - Uri hostAndPort = new Uri(scheme + Uri.SchemeDelimiter + serverVariables["HTTP_HOST"]); - UriBuilder publicRequestUri = new UriBuilder(request.Url); - publicRequestUri.Scheme = scheme; - publicRequestUri.Host = hostAndPort.Host; - publicRequestUri.Port = hostAndPort.Port; // CC missing Uri.Port contract that's on UriBuilder.Port - return publicRequestUri.Uri; - } else { - // Failover to the method that works for non-web farm enviroments. - // We use Request.Url for the full path to the server, and modify it - // with Request.RawUrl to capture both the cookieless session "directory" if it exists - // and the original path in case URL rewriting is going on. We don't want to be - // fooled by URL rewriting because we're comparing the actual URL with what's in - // the return_to parameter in some cases. - // Response.ApplyAppPathModifier(builder.Path) would have worked for the cookieless - // session, but not the URL rewriting problem. - return new Uri(request.Url, request.RawUrl); - } + /// <param name="requestUri">The request URI.</param> + /// <returns>An instance of <see cref="HttpRequestBase"/>.</returns> + public static HttpRequestBase Create(HttpRequestMessageProperty request, Uri requestUri) { + return new HttpRequestInfo(request, requestUri); } /// <summary> - /// Gets the query or form data from the original request (before any URL rewriting has occurred.) + /// Creates an <see cref="HttpRequestBase"/> instance that describes the specified HTTP request. /// </summary> - /// <returns>A set of name=value pairs.</returns> - [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Expensive call")] - internal NameValueCollection GetQueryOrFormFromContext() { - NameValueCollection query; - if (this.HttpMethod == "GET") { - query = this.QueryStringBeforeRewriting; - } else { - query = this.Form; - } - return query; + /// <param name="listenerRequest">The listener request.</param> + /// <returns>An instance of <see cref="HttpRequestBase"/>.</returns> + public static HttpRequestBase Create(HttpListenerRequest listenerRequest) { + return new HttpRequestInfo(listenerRequest); } /// <summary> - /// Gets the public facing URL for the given incoming HTTP request. + /// Creates an <see cref="HttpRequestBase"/> instance that describes the specified HTTP request. /// </summary> - /// <param name="request">The request.</param> - /// <returns>The URI that the outside world used to create this request.</returns> - private static Uri GetPublicFacingUrl(HttpRequest request) { - Requires.NotNull(request, "request"); - return GetPublicFacingUrl(request, request.ServerVariables); + /// <param name="httpMethod">The HTTP method.</param> + /// <param name="requestUri">The request URI.</param> + /// <param name="form">The form variables.</param> + /// <param name="headers">The HTTP headers.</param> + /// <returns>An instance of <see cref="HttpRequestBase"/>.</returns> + public static HttpRequestBase Create(string httpMethod, Uri requestUri, NameValueCollection form = null, NameValueCollection headers = null) { + return new HttpRequestInfo(httpMethod, requestUri, form, headers); } /// <summary> - /// Makes up a reasonable guess at the raw URL from the possibly rewritten URL. + /// Creates an <see cref="HttpRequestBase"/> instance that describes the specified HTTP request. /// </summary> - /// <param name="url">A full URL.</param> - /// <returns>A raw URL that might have come in on the HTTP verb.</returns> - private static string MakeUpRawUrlFromUrl(Uri url) { - Requires.NotNull(url, "url"); - return url.AbsolutePath + url.Query + url.Fragment; + /// <param name="httpMethod">The HTTP method.</param> + /// <param name="requestUri">The request URI.</param> + /// <param name="headers">The headers.</param> + /// <param name="inputStream">The input stream.</param> + /// <returns>An instance of <see cref="HttpRequestBase"/>.</returns> + public static HttpRequestBase Create(string httpMethod, Uri requestUri, NameValueCollection headers, Stream inputStream) { + return new HttpRequestInfo(httpMethod, requestUri, headers, inputStream); } /// <summary> - /// Converts a NameValueCollection to a WebHeaderCollection. + /// Reads name=value pairs from the POSTed form entity when the HTTP headers indicate that that is the payload of the entity. /// </summary> - /// <param name="pairs">The collection a HTTP headers.</param> - /// <returns>A new collection of the given headers.</returns> - private static WebHeaderCollection GetHeaderCollection(NameValueCollection pairs) { - Requires.NotNull(pairs, "pairs"); - - WebHeaderCollection headers = new WebHeaderCollection(); - foreach (string key in pairs) { - try { - headers.Add(key, pairs[key]); - } catch (ArgumentException ex) { - Logger.Messaging.WarnFormat( - "{0} thrown when trying to add web header \"{1}: {2}\". {3}", - ex.GetType().Name, - key, - pairs[key], - ex.Message); + /// <param name="httpMethod">The HTTP method.</param> + /// <param name="headers">The headers.</param> + /// <param name="inputStream">The input stream.</param> + /// <returns>The non-null collection of form variables.</returns> + private static NameValueCollection ParseFormData(string httpMethod, NameValueCollection headers, Stream inputStream) { + Requires.NotNullOrEmpty(httpMethod, "httpMethod"); + Requires.NotNull(headers, "headers"); + + ContentType contentType = string.IsNullOrEmpty(headers[HttpRequestHeaders.ContentType]) ? null : new ContentType(headers[HttpRequestHeaders.ContentType]); + if (inputStream != null && httpMethod == "POST" && contentType != null && string.Equals(contentType.MediaType, Channel.HttpFormUrlEncoded, StringComparison.Ordinal)) { + var reader = new StreamReader(inputStream); + long originalPosition = 0; + if (inputStream.CanSeek) { + originalPosition = inputStream.Position; + } + string postEntity = reader.ReadToEnd(); + if (inputStream.CanSeek) { + inputStream.Seek(originalPosition, SeekOrigin.Begin); } - } - return headers; - } + return HttpUtility.ParseQueryString(postEntity); + } -#if CONTRACTS_FULL - /// <summary> - /// Verifies conditions that should be true for any valid state of this object. - /// </summary> - [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")] - [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")] - [ContractInvariantMethod] - private void ObjectInvariant() { + return new NameValueCollection(); } -#endif } } diff --git a/src/DotNetOpenAuth.Core/Messaging/IDataBagFormatter.cs b/src/DotNetOpenAuth.Core/Messaging/IDataBagFormatter.cs index f911264..9086ee9 100644 --- a/src/DotNetOpenAuth.Core/Messaging/IDataBagFormatter.cs +++ b/src/DotNetOpenAuth.Core/Messaging/IDataBagFormatter.cs @@ -24,10 +24,13 @@ namespace DotNetOpenAuth.Messaging { /// <summary> /// Deserializes a <see cref="DataBag"/>. /// </summary> - /// <param name="containingMessage">The message that contains the <see cref="DataBag"/> serialized value. Must not be nulll.</param> + /// <param name="containingMessage">The message that contains the <see cref="DataBag"/> serialized value. Must not be null.</param> /// <param name="data">The serialized form of the <see cref="DataBag"/> to deserialize. Must not be null or empty.</param> - /// <returns>The deserialized value. Never null.</returns> - T Deserialize(IProtocolMessage containingMessage, string data); + /// <param name="messagePartName">The name of the parameter whose value is to be deserialized. Used for error message generation.</param> + /// <returns> + /// The deserialized value. Never null. + /// </returns> + T Deserialize(IProtocolMessage containingMessage, string data, string messagePartName); } /// <summary> @@ -51,7 +54,7 @@ namespace DotNetOpenAuth.Messaging { /// <returns>A non-null, non-empty value.</returns> string IDataBagFormatter<T>.Serialize(T message) { Requires.NotNull(message, "message"); - Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<string>())); + Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>())); throw new System.NotImplementedException(); } @@ -61,10 +64,14 @@ namespace DotNetOpenAuth.Messaging { /// </summary> /// <param name="containingMessage">The message that contains the <see cref="DataBag"/> serialized value. Must not be nulll.</param> /// <param name="data">The serialized form of the <see cref="DataBag"/> to deserialize. Must not be null or empty.</param> - /// <returns>The deserialized value. Never null.</returns> - T IDataBagFormatter<T>.Deserialize(IProtocolMessage containingMessage, string data) { + /// <param name="messagePartName">Name of the message part whose value is to be deserialized. Used for exception messages.</param> + /// <returns> + /// The deserialized value. Never null. + /// </returns> + T IDataBagFormatter<T>.Deserialize(IProtocolMessage containingMessage, string data, string messagePartName) { Requires.NotNull(containingMessage, "containingMessage"); Requires.NotNullOrEmpty(data, "data"); + Requires.NotNullOrEmpty(messagePartName, "messagePartName"); Contract.Ensures(Contract.Result<T>() != null); throw new System.NotImplementedException(); diff --git a/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs index 1305620..b26deeb 100644 --- a/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs +++ b/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs @@ -153,9 +153,7 @@ namespace DotNetOpenAuth.Messaging { [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Expensive call should not be a property.")] public static Uri GetRequestUrlFromContext() { Requires.ValidState(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired); - HttpContext context = HttpContext.Current; - - return HttpRequestInfo.GetPublicFacingUrl(context.Request, context.Request.ServerVariables); + return new HttpRequestWrapper(HttpContext.Current.Request).GetPublicFacingUrl(); } /// <summary> @@ -448,7 +446,7 @@ namespace DotNetOpenAuth.Messaging { internal static string CombineKeyHandleAndPayload(string handle, string payload) { Requires.NotNullOrEmpty(handle, "handle"); Requires.NotNullOrEmpty(payload, "payload"); - Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<string>())); + Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>())); return handle + "!" + payload; } @@ -1248,7 +1246,7 @@ namespace DotNetOpenAuth.Messaging { Requires.NotNull(args, "args"); Contract.Ensures(Contract.Result<string>() != null); - if (args.Count() == 0) { + if (!args.Any()) { return string.Empty; } StringBuilder sb = new StringBuilder(args.Count() * 10); @@ -1352,8 +1350,8 @@ namespace DotNetOpenAuth.Messaging { /// <param name="request">The request to get recipient information from.</param> /// <returns>The recipient.</returns> /// <exception cref="ArgumentException">Thrown if the HTTP request is something we can't handle.</exception> - internal static MessageReceivingEndpoint GetRecipient(this HttpRequestInfo request) { - return new MessageReceivingEndpoint(request.UrlBeforeRewriting, GetHttpDeliveryMethod(request.HttpMethod)); + internal static MessageReceivingEndpoint GetRecipient(this HttpRequestBase request) { + return new MessageReceivingEndpoint(request.GetPublicFacingUrl(), GetHttpDeliveryMethod(request.HttpMethod)); } /// <summary> @@ -1484,6 +1482,22 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Converts a dictionary to a <see cref="NameValueCollection"/> + /// </summary> + /// <param name="data">The existing dictionary.</param> + /// <returns>The new collection.</returns> + internal static NameValueCollection ToNameValueCollection(this IDictionary<string, string> data) { + Requires.NotNull(data, "data"); + + var nvc = new NameValueCollection(); + foreach (var entry in data) { + nvc.Add(entry.Key, entry.Value); + } + + return nvc; + } + + /// <summary> /// Sorts the elements of a sequence in ascending order by using a specified comparer. /// </summary> /// <typeparam name="TSource">The type of the elements of source.</typeparam> @@ -1663,6 +1677,111 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Gets the query data from the original request (before any URL rewriting has occurred.) + /// </summary> + /// <param name="request">The request.</param> + /// <returns> + /// A <see cref="NameValueCollection"/> containing all the parameters in the query string. + /// </returns> + internal static NameValueCollection GetQueryStringBeforeRewriting(this HttpRequestBase request) { + // This request URL may have been rewritten by the host site. + // For openid protocol purposes, we really need to look at + // the original query parameters before any rewriting took place. + Uri beforeRewriting = GetPublicFacingUrl(request); + if (beforeRewriting == request.Url) { + // No rewriting has taken place. + return request.QueryString; + } else { + // Rewriting detected! Recover the original request URI. + ErrorUtilities.VerifyInternal(beforeRewriting != null, "UrlBeforeRewriting is null, so the query string cannot be determined."); + return HttpUtility.ParseQueryString(beforeRewriting.Query); + } + } + + /// <summary> + /// Gets a value indicating whether the request's URL was rewritten by ASP.NET + /// or some other module. + /// </summary> + /// <param name="request">The request.</param> + /// <returns>A value indicating whether there is evidence that the URL of the request has been changed to some internal server (farm) representation.</returns> + /// <value> + /// <c>true</c> if this request's URL was rewritten; otherwise, <c>false</c>. + /// </value> + internal static bool GetIsUrlRewritten(this HttpRequestBase request) { + return request.Url != GetPublicFacingUrl(request); + } + + /// <summary> + /// Gets the public facing URL for the given incoming HTTP request. + /// </summary> + /// <param name="request">The request.</param> + /// <param name="serverVariables">The server variables to consider part of the request.</param> + /// <returns> + /// The URI that the outside world used to create this request. + /// </returns> + /// <remarks> + /// Although the <paramref name="serverVariables"/> value can be obtained from + /// <see cref="HttpRequest.ServerVariables"/>, it's useful to be able to pass them + /// in so we can simulate injected values from our unit tests since the actual property + /// is a read-only kind of <see cref="NameValueCollection"/>. + /// </remarks> + internal static Uri GetPublicFacingUrl(this HttpRequestBase request, NameValueCollection serverVariables) { + Requires.NotNull(request, "request"); + Requires.NotNull(serverVariables, "serverVariables"); + + // Due to URL rewriting, cloud computing (i.e. Azure) + // and web farms, etc., we have to be VERY careful about what + // we consider the incoming URL. We want to see the URL as it would + // appear on the public-facing side of the hosting web site. + // HttpRequest.Url gives us the internal URL in a cloud environment, + // So we use a variable that (at least from what I can tell) gives us + // the public URL: + if (serverVariables["HTTP_HOST"] != null) { + ErrorUtilities.VerifySupported(request.Url.Scheme == Uri.UriSchemeHttps || request.Url.Scheme == Uri.UriSchemeHttp, "Only HTTP and HTTPS are supported protocols."); + string scheme = serverVariables["HTTP_X_FORWARDED_PROTO"] ?? request.Url.Scheme; + Uri hostAndPort = new Uri(scheme + Uri.SchemeDelimiter + serverVariables["HTTP_HOST"]); + UriBuilder publicRequestUri = new UriBuilder(request.Url); + publicRequestUri.Scheme = scheme; + publicRequestUri.Host = hostAndPort.Host; + publicRequestUri.Port = hostAndPort.Port; // CC missing Uri.Port contract that's on UriBuilder.Port + return publicRequestUri.Uri; + } else { + // Failover to the method that works for non-web farm enviroments. + // We use Request.Url for the full path to the server, and modify it + // with Request.RawUrl to capture both the cookieless session "directory" if it exists + // and the original path in case URL rewriting is going on. We don't want to be + // fooled by URL rewriting because we're comparing the actual URL with what's in + // the return_to parameter in some cases. + // Response.ApplyAppPathModifier(builder.Path) would have worked for the cookieless + // session, but not the URL rewriting problem. + return new Uri(request.Url, request.RawUrl); + } + } + + /// <summary> + /// Gets the public facing URL for the given incoming HTTP request. + /// </summary> + /// <param name="request">The request.</param> + /// <returns>The URI that the outside world used to create this request.</returns> + internal static Uri GetPublicFacingUrl(this HttpRequestBase request) { + Requires.NotNull(request, "request"); + return GetPublicFacingUrl(request, request.ServerVariables); + } + + /// <summary> + /// Gets the query or form data from the original request (before any URL rewriting has occurred.) + /// </summary> + /// <param name="request">The request.</param> + /// <returns> + /// A set of name=value pairs. + /// </returns> + [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Expensive call")] + internal static NameValueCollection GetQueryOrForm(this HttpRequestBase request) { + Requires.NotNull(request, "request"); + return request.HttpMethod == "GET" ? GetQueryStringBeforeRewriting(request) : request.Form; + } + + /// <summary> /// Creates a symmetric algorithm for use in encryption/decryption. /// </summary> /// <param name="key">The symmetric key to use for encryption/decryption.</param> diff --git a/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponseActionResult.cs b/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponseActionResult.cs index 9569e34..a5fe782 100644 --- a/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponseActionResult.cs +++ b/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponseActionResult.cs @@ -34,7 +34,7 @@ namespace DotNetOpenAuth.Messaging { /// </summary> /// <param name="context">The context in which to set the response.</param> public override void ExecuteResult(ControllerContext context) { - this.response.Respond(); + this.response.Respond(context.HttpContext); } } } diff --git a/src/DotNetOpenAuth.Core/Messaging/ReadOnlyDictionary.cs b/src/DotNetOpenAuth.Core/Messaging/ReadOnlyDictionary.cs new file mode 100644 index 0000000..0ba1ff5 --- /dev/null +++ b/src/DotNetOpenAuth.Core/Messaging/ReadOnlyDictionary.cs @@ -0,0 +1,224 @@ +//----------------------------------------------------------------------- +// <copyright file="ReadOnlyDictionary.cs" company="Microsoft Corporation"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Messaging { + using System; + using System.Collections.Generic; + + /// <summary> + /// Represents a read-only dictionary. + /// </summary> + /// <typeparam name="K">The type of the key.</typeparam> + /// <typeparam name="V">The type of the value.</typeparam> + internal class ReadOnlyDictionary<K, V> : IDictionary<K, V> { + /// <summary> + /// Contains base dictionary. + /// </summary> + private readonly IDictionary<K, V> baseDictionary; + + /// <summary> + /// Initializes a new instance of the <see cref="ReadOnlyDictionary<K, V>"/> class. + /// </summary> + /// <param name="baseDictionary">The base dictionary.</param> + public ReadOnlyDictionary(IDictionary<K, V> baseDictionary) { + this.baseDictionary = baseDictionary; + } + + /// <summary> + /// Gets an <see cref="T:System.Collections.Generic.ICollection`1"/> containing the keys of the <see cref="T:System.Collections.Generic.IDictionary`2"/>. + /// </summary> + /// <returns> + /// An <see cref="T:System.Collections.Generic.ICollection`1"/> containing the keys of the object that implements <see cref="T:System.Collections.Generic.IDictionary`2"/>. + /// </returns> + public ICollection<K> Keys { + get { return this.baseDictionary.Keys; } + } + + /// <summary> + /// Gets an <see cref="T:System.Collections.Generic.ICollection`1"/> containing the values in the <see cref="T:System.Collections.Generic.IDictionary`2"/>. + /// </summary> + /// <returns> + /// An <see cref="T:System.Collections.Generic.ICollection`1"/> containing the values in the object that implements <see cref="T:System.Collections.Generic.IDictionary`2"/>. + /// </returns> + public ICollection<V> Values { + get { return this.baseDictionary.Values; } + } + + /// <summary> + /// Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>. + /// </summary> + /// <returns> + /// The number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>. + /// </returns> + public int Count { + get { return this.baseDictionary.Count; } + } + + /// <summary> + /// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only. + /// </summary> + /// <returns>true if the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only; otherwise, false. + /// </returns> + public bool IsReadOnly { + get { return true; } + } + + /// <summary> + /// Gets or sets the element with the specified key. + /// </summary> + /// <param name="key">The key being read or written.</param> + /// <returns> + /// The element with the specified key. + /// </returns> + /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null. + /// </exception> + /// <exception cref="T:System.Collections.Generic.KeyNotFoundException"> + /// The property is retrieved and <paramref name="key"/> is not found. + /// </exception> + /// <exception cref="T:System.NotSupportedException"> + /// The property is set and the <see cref="T:System.Collections.Generic.IDictionary`2"/> is read-only. + /// </exception> + public V this[K key] { + get { return this.baseDictionary[key]; } + set { throw new NotSupportedException(); } + } + + /// <summary> + /// Adds an element with the provided key and value to the <see cref="T:System.Collections.Generic.IDictionary`2"/>. + /// </summary> + /// <param name="key">The object to use as the key of the element to add.</param> + /// <param name="value">The object to use as the value of the element to add.</param> + /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null. + /// </exception> + /// <exception cref="T:System.ArgumentException"> + /// An element with the same key already exists in the <see cref="T:System.Collections.Generic.IDictionary`2"/>. + /// </exception> + /// <exception cref="T:System.NotSupportedException"> + /// The <see cref="T:System.Collections.Generic.IDictionary`2"/> is read-only. + /// </exception> + public void Add(K key, V value) { + throw new NotSupportedException(); + } + + /// <summary> + /// Determines whether the <see cref="T:System.Collections.Generic.IDictionary`2"/> contains an element with the specified key. + /// </summary> + /// <param name="key">The key to locate in the <see cref="T:System.Collections.Generic.IDictionary`2"/>.</param> + /// <returns> + /// true if the <see cref="T:System.Collections.Generic.IDictionary`2"/> contains an element with the key; otherwise, false. + /// </returns> + /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null. + /// </exception> + public bool ContainsKey(K key) { + return this.baseDictionary.ContainsKey(key); + } + + /// <summary> + /// Removes the element with the specified key from the <see cref="T:System.Collections.Generic.IDictionary`2"/>. + /// </summary> + /// <param name="key">The key of the element to remove.</param> + /// <returns> + /// true if the element is successfully removed; otherwise, false. This method also returns false if <paramref name="key"/> was not found in the original <see cref="T:System.Collections.Generic.IDictionary`2"/>. + /// </returns> + /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null. + /// </exception> + /// <exception cref="T:System.NotSupportedException"> + /// The <see cref="T:System.Collections.Generic.IDictionary`2"/> is read-only. + /// </exception> + public bool Remove(K key) { + throw new NotSupportedException(); + } + + /// <summary> + /// Gets the value associated with the specified key. + /// </summary> + /// <param name="key">The key whose value to get.</param> + /// <param name="value">When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the <paramref name="value"/> parameter. This parameter is passed uninitialized.</param> + /// <returns> + /// true if the object that implements <see cref="T:System.Collections.Generic.IDictionary`2"/> contains an element with the specified key; otherwise, false. + /// </returns> + /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null. + /// </exception> + public bool TryGetValue(K key, out V value) { + return this.baseDictionary.TryGetValue(key, out value); + } + + /// <summary> + /// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1"/>. + /// </summary> + /// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param> + /// <exception cref="T:System.NotSupportedException"> + /// The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only. + /// </exception> + public void Add(KeyValuePair<K, V> item) { + throw new NotSupportedException(); + } + + /// <summary> + /// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"/>. + /// </summary> + /// <exception cref="T:System.NotSupportedException"> + /// The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only. + /// </exception> + public void Clear() { + throw new NotSupportedException(); + } + + /// <summary> + /// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1"/> contains a specific value. + /// </summary> + /// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param> + /// <returns> + /// true if <paramref name="item"/> is found in the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false. + /// </returns> + public bool Contains(KeyValuePair<K, V> item) { + return this.baseDictionary.Contains(item); + } + + /// <summary> + /// Copies to. + /// </summary> + /// <param name="array">The array.</param> + /// <param name="arrayIndex">Index of the array.</param> + public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex) { + this.baseDictionary.CopyTo(array, arrayIndex); + } + + /// <summary> + /// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"/>. + /// </summary> + /// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param> + /// <returns> + /// true if <paramref name="item"/> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false. This method also returns false if <paramref name="item"/> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"/>. + /// </returns> + /// <exception cref="T:System.NotSupportedException"> + /// The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only. + /// </exception> + public bool Remove(KeyValuePair<K, V> item) { + throw new NotSupportedException(); + } + + /// <summary> + /// Returns an enumerator that iterates through the collection. + /// </summary> + /// <returns> + /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection. + /// </returns> + public IEnumerator<KeyValuePair<K, V>> GetEnumerator() { + return this.baseDictionary.GetEnumerator(); + } + + /// <summary> + /// Returns an enumerator that iterates through a collection. + /// </summary> + /// <returns> + /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection. + /// </returns> + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return this.baseDictionary.GetEnumerator(); + } + } +}
\ No newline at end of file diff --git a/src/DotNetOpenAuth.Core/Messaging/UntrustedWebRequestHandler.cs b/src/DotNetOpenAuth.Core/Messaging/UntrustedWebRequestHandler.cs index c490127..25a7bbb 100644 --- a/src/DotNetOpenAuth.Core/Messaging/UntrustedWebRequestHandler.cs +++ b/src/DotNetOpenAuth.Core/Messaging/UntrustedWebRequestHandler.cs @@ -383,7 +383,7 @@ namespace DotNetOpenAuth.Messaging { /// <exception cref="ProtocolException">Thrown when the URI is disallowed for security reasons.</exception> private void EnsureAllowableRequestUri(Uri requestUri, bool requireSsl) { ErrorUtilities.VerifyProtocol(this.IsUriAllowable(requestUri), MessagingStrings.UnsafeWebRequestDetected, requestUri); - ErrorUtilities.VerifyProtocol(!requireSsl || String.Equals(requestUri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase), MessagingStrings.InsecureWebRequestWithSslRequired, requestUri); + ErrorUtilities.VerifyProtocol(!requireSsl || string.Equals(requestUri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase), MessagingStrings.InsecureWebRequestWithSslRequired, requestUri); } /// <summary> diff --git a/src/DotNetOpenAuth.Core/Messaging/UriStyleMessageFormatter.cs b/src/DotNetOpenAuth.Core/Messaging/UriStyleMessageFormatter.cs index 84e75b0..92b1928 100644 --- a/src/DotNetOpenAuth.Core/Messaging/UriStyleMessageFormatter.cs +++ b/src/DotNetOpenAuth.Core/Messaging/UriStyleMessageFormatter.cs @@ -46,7 +46,7 @@ namespace DotNetOpenAuth.Messaging { /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param> protected internal UriStyleMessageFormatter(ICryptoKeyStore cryptoKeyStore = null, string bucket = null, bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? minimumAge = null, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null) : base(cryptoKeyStore, bucket, signed, encrypted, compressed, minimumAge, maximumAge, decodeOnceOnly) { - Requires.True((cryptoKeyStore != null && !String.IsNullOrEmpty(bucket)) || (!signed && !encrypted), null); + Requires.True((cryptoKeyStore != null && !string.IsNullOrEmpty(bucket)) || (!signed && !encrypted), null); } /// <summary> diff --git a/src/DotNetOpenAuth.Core/Properties/AssemblyInfo.cs b/src/DotNetOpenAuth.Core/Properties/AssemblyInfo.cs index 950a7cd..e57b211 100644 --- a/src/DotNetOpenAuth.Core/Properties/AssemblyInfo.cs +++ b/src/DotNetOpenAuth.Core/Properties/AssemblyInfo.cs @@ -67,6 +67,8 @@ using System.Web.UI; [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.ResourceServer, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.Client, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.Client.UI, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] +[assembly: InternalsVisibleTo("DotNetOpenAuth.AspNet.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] +[assembly: InternalsVisibleTo("DotNetOpenAuth.AspNet, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] #else [assembly: InternalsVisibleTo("DotNetOpenAuth.Test")] @@ -88,5 +90,7 @@ using System.Web.UI; [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.AuthorizationServer")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.ResourceServer")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.Client")] +[assembly: InternalsVisibleTo("DotNetOpenAuth.AspNet.Test")] +[assembly: InternalsVisibleTo("DotNetOpenAuth.AspNet")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] #endif diff --git a/src/DotNetOpenAuth.Core/Reporting.cs b/src/DotNetOpenAuth.Core/Reporting.cs index a7940b6..951bb7c 100644 --- a/src/DotNetOpenAuth.Core/Reporting.cs +++ b/src/DotNetOpenAuth.Core/Reporting.cs @@ -170,7 +170,7 @@ namespace DotNetOpenAuth { /// <param name="category">The category within the event. Null and empty strings are allowed, but considered the same.</param> [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "PersistentCounter instances are stored in a table for later use.")] internal static void RecordEventOccurrence(string eventName, string category) { - Contract.Requires(!String.IsNullOrEmpty(eventName)); + Contract.Requires(!string.IsNullOrEmpty(eventName)); // In release builds, just quietly return. if (string.IsNullOrEmpty(eventName)) { @@ -213,7 +213,7 @@ namespace DotNetOpenAuth { /// </summary> /// <param name="feature">The feature.</param> internal static void RecordFeatureUse(string feature) { - Contract.Requires(!String.IsNullOrEmpty(feature)); + Contract.Requires(!string.IsNullOrEmpty(feature)); // In release builds, just quietly return. if (string.IsNullOrEmpty(feature)) { @@ -297,7 +297,7 @@ namespace DotNetOpenAuth { /// Records statistics collected from incoming requests. /// </summary> /// <param name="request">The request.</param> - internal static void RecordRequestStatistics(HttpRequestInfo request) { + internal static void RecordRequestStatistics(HttpRequestBase request) { Contract.Requires(request != null); // In release builds, just quietly return. @@ -311,7 +311,7 @@ namespace DotNetOpenAuth { } if (Configuration.IncludeLocalRequestUris && !observedRequests.IsFull) { - var requestBuilder = new UriBuilder(request.UrlBeforeRewriting); + var requestBuilder = new UriBuilder(request.GetPublicFacingUrl()); requestBuilder.Query = null; requestBuilder.Fragment = null; observedRequests.Add(requestBuilder.Uri.AbsoluteUri); diff --git a/src/DotNetOpenAuth.Core/Requires.cs b/src/DotNetOpenAuth.Core/Requires.cs index 8aa15dd..7a196a3 100644 --- a/src/DotNetOpenAuth.Core/Requires.cs +++ b/src/DotNetOpenAuth.Core/Requires.cs @@ -24,16 +24,18 @@ namespace DotNetOpenAuth { /// <typeparam name="T">The type of the parameter</typeparam> /// <param name="value">The value.</param> /// <param name="parameterName">Name of the parameter.</param> + /// <returns>The tested value, guaranteed to not be null.</returns> #if !CLR4 [ContractArgumentValidator] #endif [Pure, DebuggerStepThrough] - internal static void NotNull<T>(T value, string parameterName) where T : class { + internal static T NotNull<T>(T value, string parameterName) where T : class { if (value == null) { throw new ArgumentNullException(parameterName); } Contract.EndContractBlock(); + return value; } /// <summary> @@ -134,7 +136,7 @@ namespace DotNetOpenAuth { [Pure, DebuggerStepThrough] internal static void True(bool condition, string parameterName, string unformattedMessage, params object[] args) { if (!condition) { - throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, unformattedMessage, args), parameterName); + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, unformattedMessage, args), parameterName); } Contract.EndContractBlock(); @@ -185,7 +187,7 @@ namespace DotNetOpenAuth { [Pure, DebuggerStepThrough] internal static void ValidState(bool condition, string unformattedMessage, params object[] args) { if (!condition) { - throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, unformattedMessage, args)); + throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, unformattedMessage, args)); } Contract.EndContractBlock(); diff --git a/src/DotNetOpenAuth.Core/Settings.StyleCop b/src/DotNetOpenAuth.Core/Settings.StyleCop index 017d610..ac744c0 100644 --- a/src/DotNetOpenAuth.Core/Settings.StyleCop +++ b/src/DotNetOpenAuth.Core/Settings.StyleCop @@ -1,6 +1,6 @@ <StyleCopSettings Version="4.3"> <Analyzers> - <Analyzer AnalyzerId="Microsoft.StyleCop.CSharp.DocumentationRules"> + <Analyzer AnalyzerId="StyleCop.CSharp.DocumentationRules"> <Rules> <Rule Name="ElementDocumentationMustNotBeCopiedAndPasted"> <RuleSettings> @@ -10,7 +10,7 @@ </Rules> <AnalyzerSettings /> </Analyzer> - <Analyzer AnalyzerId="Microsoft.StyleCop.CSharp.MaintainabilityRules"> + <Analyzer AnalyzerId="StyleCop.CSharp.MaintainabilityRules"> <Rules> <Rule Name="StatementMustNotUseUnnecessaryParenthesis"> <RuleSettings> @@ -20,7 +20,7 @@ </Rules> <AnalyzerSettings /> </Analyzer> - <Analyzer AnalyzerId="Microsoft.StyleCop.CSharp.ReadabilityRules"> + <Analyzer AnalyzerId="StyleCop.CSharp.ReadabilityRules"> <Rules> <Rule Name="UseStringEmptyForEmptyStrings"> <RuleSettings> |