diff options
35 files changed, 321 insertions, 1368 deletions
diff --git a/src/DotNetOpenAuth.Core/Messaging/Channel.cs b/src/DotNetOpenAuth.Core/Messaging/Channel.cs index 8564e38..62de162 100644 --- a/src/DotNetOpenAuth.Core/Messaging/Channel.cs +++ b/src/DotNetOpenAuth.Core/Messaging/Channel.cs @@ -504,8 +504,8 @@ namespace DotNetOpenAuth.Messaging { /// <param name="response">The response that is anticipated to contain an protocol message.</param> /// <returns>The deserialized message parts, if found. Null otherwise.</returns> /// <exception cref="ProtocolException">Thrown when the response is not valid.</exception> - internal Task<IDictionary<string, string>> ReadFromResponseCoreAsyncTestHook(HttpResponseMessage response) { - return this.ReadFromResponseCoreAsync(response); + internal Task<IDictionary<string, string>> ReadFromResponseCoreAsyncTestHook(HttpResponseMessage response, CancellationToken cancellationToken) { + return this.ReadFromResponseCoreAsync(response, cancellationToken); } /// <summary> @@ -520,7 +520,7 @@ namespace DotNetOpenAuth.Messaging { /// This method should NOT be called by derived types /// except when sending ONE WAY request messages. /// </remarks> - internal Task ProcessOutgoingMessageTestHookAsync(IProtocolMessage message, CancellationToken cancellationToken) { + internal Task ProcessOutgoingMessageTestHookAsync(IProtocolMessage message, CancellationToken cancellationToken = default(CancellationToken)) { return this.ProcessOutgoingMessageAsync(message, cancellationToken); } @@ -685,7 +685,7 @@ namespace DotNetOpenAuth.Messaging { return null; } - var responseFields = await this.ReadFromResponseCoreAsync(response); + var responseFields = await this.ReadFromResponseCoreAsync(response, cancellationToken); if (responseFields == null) { return null; } @@ -914,9 +914,12 @@ namespace DotNetOpenAuth.Messaging { /// Gets the protocol message that may be in the given HTTP response. /// </summary> /// <param name="response">The response that is anticipated to contain an protocol message.</param> - /// <returns>The deserialized message parts, if found. Null otherwise.</returns> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The deserialized message parts, if found. Null otherwise. + /// </returns> /// <exception cref="ProtocolException">Thrown when the response is not valid.</exception> - protected abstract Task<IDictionary<string, string>> ReadFromResponseCoreAsync(HttpResponseMessage response); + protected abstract Task<IDictionary<string, string>> ReadFromResponseCoreAsync(HttpResponseMessage response, CancellationToken cancellationToken); /// <summary> /// Prepares an HTTP request that carries a given message. diff --git a/src/DotNetOpenAuth.Core/Messaging/MessageProtectionTasks.cs b/src/DotNetOpenAuth.Core/Messaging/MessageProtectionTasks.cs index 29163f7..37c86ca 100644 --- a/src/DotNetOpenAuth.Core/Messaging/MessageProtectionTasks.cs +++ b/src/DotNetOpenAuth.Core/Messaging/MessageProtectionTasks.cs @@ -31,5 +31,11 @@ namespace DotNetOpenAuth.Messaging { /// </summary> internal static readonly Task<MessageProtections?> TamperProtection = Task.FromResult<MessageProtections?>(MessageProtections.TamperProtection); + + /// <summary> + /// A task whose result is <see cref="MessageProtections.ReplayProtection"/> + /// </summary> + internal static readonly Task<MessageProtections?> ReplayProtection = + Task.FromResult<MessageProtections?>(MessageProtections.ReplayProtection); } } diff --git a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs index 3f677b8..b163d0d 100644 --- a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs +++ b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs @@ -296,7 +296,22 @@ namespace DotNetOpenAuth.OAuth { /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> public Task<UserAuthorizationRequest> ReadAuthorizationRequestAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { request = request ?? this.channel.GetRequestFromContext(); - return this.Channel.TryReadFromRequestAsync<UserAuthorizationRequest>(request.AsHttpRequestMessage(), cancellationToken); + return this.ReadAuthorizationRequestAsync(request.AsHttpRequestMessage(), cancellationToken); + } + + /// <summary> + /// Reads in a Consumer's request for the Service Provider to obtain permission from + /// the user to authorize the Consumer's access of some protected resource(s). + /// </summary> + /// <param name="request">The HTTP request to read from.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The incoming request, or null if no OAuth message was attached. + /// </returns> + /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> + public Task<UserAuthorizationRequest> ReadAuthorizationRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken)) { + Requires.NotNull(request, "request"); + return this.Channel.TryReadFromRequestAsync<UserAuthorizationRequest>(request, cancellationToken); } /// <summary> @@ -372,7 +387,21 @@ namespace DotNetOpenAuth.OAuth { /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> public Task<AuthorizedTokenRequest> ReadAccessTokenRequestAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { request = request ?? this.Channel.GetRequestFromContext(); - return this.Channel.TryReadFromRequestAsync<AuthorizedTokenRequest>(request.AsHttpRequestMessage(), cancellationToken); + return this.ReadAccessTokenRequestAsync(request.AsHttpRequestMessage(), cancellationToken); + } + + /// <summary> + /// Reads in a Consumer's request to exchange an authorized request token for an access token. + /// </summary> + /// <param name="request">The HTTP request to read from.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The incoming request, or null if no OAuth message was attached. + /// </returns> + /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> + public Task<AuthorizedTokenRequest> ReadAccessTokenRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken)) { + Requires.NotNull(request, "request"); + return this.Channel.TryReadFromRequestAsync<AuthorizedTokenRequest>(request, cancellationToken); } /// <summary> @@ -425,9 +454,26 @@ namespace DotNetOpenAuth.OAuth { /// to access the resources being requested. /// </remarks> /// <exception cref="ProtocolException">Thrown if an unexpected message is attached to the request.</exception> - public async Task<AccessProtectedResourceRequest> ReadProtectedResourceAuthorizationAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { - request = request ?? this.Channel.GetRequestFromContext(); - var accessMessage = await this.Channel.TryReadFromRequestAsync<AccessProtectedResourceRequest>(request.AsHttpRequestMessage(), cancellationToken); + public Task<AccessProtectedResourceRequest> ReadProtectedResourceAuthorizationAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { + request = request ?? this.channel.GetRequestFromContext(); + return this.ReadProtectedResourceAuthorizationAsync(request.AsHttpRequestMessage(), cancellationToken); + } + + /// <summary> + /// Gets the authorization (access token) for accessing some protected resource. + /// </summary> + /// <param name="request">The incoming HTTP request.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>The authorization message sent by the Consumer, or null if no authorization message is attached.</returns> + /// <remarks> + /// This method verifies that the access token and token secret are valid. + /// It falls on the caller to verify that the access token is actually authorized + /// to access the resources being requested. + /// </remarks> + /// <exception cref="ProtocolException">Thrown if an unexpected message is attached to the request.</exception> + public async Task<AccessProtectedResourceRequest> ReadProtectedResourceAuthorizationAsync(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken)) { + Requires.NotNull(request, "request"); + var accessMessage = await this.Channel.TryReadFromRequestAsync<AccessProtectedResourceRequest>(request, cancellationToken); if (accessMessage != null) { if (this.TokenManager.GetTokenType(accessMessage.AccessToken) != TokenType.AccessToken) { throw new ProtocolException( diff --git a/src/DotNetOpenAuth.OAuth/OAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOpenAuth.OAuth/OAuth/ChannelElements/OAuthChannel.cs index 0925402..b120344 100644 --- a/src/DotNetOpenAuth.OAuth/OAuth/ChannelElements/OAuthChannel.cs +++ b/src/DotNetOpenAuth.OAuth/OAuth/ChannelElements/OAuthChannel.cs @@ -171,7 +171,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// <returns> /// The deserialized message parts, if found. Null otherwise. /// </returns> - protected override async Task<IDictionary<string, string>> ReadFromResponseCoreAsync(HttpResponseMessage response) { + protected override async Task<IDictionary<string, string>> ReadFromResponseCoreAsync(HttpResponseMessage response, CancellationToken cancellationToken) { string body = await response.Content.ReadAsStringAsync(); return HttpUtility.ParseQueryString(body).ToDictionary(); } diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/OAuth2AuthorizationServerChannel.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/OAuth2AuthorizationServerChannel.cs index 3fcbf67..609cedb 100644 --- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/OAuth2AuthorizationServerChannel.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/OAuth2AuthorizationServerChannel.cs @@ -66,7 +66,7 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// The deserialized message parts, if found. Null otherwise. /// </returns> /// <exception cref="ProtocolException">Thrown when the response is not valid.</exception> - protected override Task<IDictionary<string, string>> ReadFromResponseCoreAsync(HttpResponseMessage response) { + protected override Task<IDictionary<string, string>> ReadFromResponseCoreAsync(HttpResponseMessage response, CancellationToken cancellationToken) { throw new NotImplementedException(); } diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs index ae1a823..a11b81b 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs @@ -92,7 +92,7 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// The deserialized message parts, if found. Null otherwise. /// </returns> /// <exception cref="ProtocolException">Thrown when the response is not valid.</exception> - protected override async Task<IDictionary<string, string>> ReadFromResponseCoreAsync(HttpResponseMessage response) { + protected override async Task<IDictionary<string, string>> ReadFromResponseCoreAsync(HttpResponseMessage response, CancellationToken cancellationToken) { // The spec says direct responses should be JSON objects, but Facebook uses HttpFormUrlEncoded instead, calling it text/plain // Others return text/javascript. Again bad. string body = await response.Content.ReadAsStringAsync(); diff --git a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ChannelElements/OAuth2ResourceServerChannel.cs b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ChannelElements/OAuth2ResourceServerChannel.cs index c645753..1d90844 100644 --- a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ChannelElements/OAuth2ResourceServerChannel.cs +++ b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ChannelElements/OAuth2ResourceServerChannel.cs @@ -90,7 +90,7 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// The deserialized message parts, if found. Null otherwise. /// </returns> /// <exception cref="ProtocolException">Thrown when the response is not valid.</exception> - protected override Task<IDictionary<string, string>> ReadFromResponseCoreAsync(HttpResponseMessage response) { + protected override Task<IDictionary<string, string>> ReadFromResponseCoreAsync(HttpResponseMessage response, CancellationToken cancellationToken) { // We never expect resource servers to send out direct requests, // and therefore won't have direct responses. throw new NotImplementedException(); diff --git a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/KeyValueFormEncoding.cs b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/KeyValueFormEncoding.cs index c74a636..9c06e6b 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/KeyValueFormEncoding.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/KeyValueFormEncoding.cs @@ -11,6 +11,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { using System.Globalization; using System.IO; using System.Text; + using System.Threading; using System.Threading.Tasks; using DotNetOpenAuth.Messaging; using Validation; @@ -132,12 +133,13 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// <param name="data">The stream of Key-Value Form encoded bytes.</param> /// <returns>The deserialized dictionary.</returns> /// <exception cref="FormatException">Thrown when the data is not in the expected format.</exception> - public async Task<IDictionary<string, string>> GetDictionaryAsync(Stream data) { + public async Task<IDictionary<string, string>> GetDictionaryAsync(Stream data, CancellationToken cancellationToken) { using (StreamReader reader = new StreamReader(data, textEncoding)) { var dict = new Dictionary<string, string>(); int line_num = 0; string line; while ((line = await reader.ReadLineAsync()) != null) { + cancellationToken.ThrowIfCancellationRequested(); line_num++; if (this.ConformanceLevel == KeyValueFormConformanceLevel.Loose) { line = line.Trim(); diff --git a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/OpenIdChannel.cs b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/OpenIdChannel.cs index 6d4df61..dcc4019 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/OpenIdChannel.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/OpenIdChannel.cs @@ -133,14 +133,15 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Gets the protocol message that may be in the given HTTP response. /// </summary> /// <param name="response">The response that is anticipated to contain an protocol message.</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns> /// The deserialized message parts, if found. Null otherwise. /// </returns> /// <exception cref="ProtocolException">Thrown when the response is not valid.</exception> - protected override async Task<IDictionary<string, string>> ReadFromResponseCoreAsync(HttpResponseMessage response) { + protected override async Task<IDictionary<string, string>> ReadFromResponseCoreAsync(HttpResponseMessage response, CancellationToken cancellationToken) { try { using (var responseStream = await response.Content.ReadAsStreamAsync()) { - return await this.keyValueForm.GetDictionaryAsync(responseStream); + return await this.keyValueForm.GetDictionaryAsync(responseStream, cancellationToken); } } catch (FormatException ex) { throw ErrorUtilities.Wrap(ex, ex.Message); diff --git a/src/DotNetOpenAuth.Test/CoordinatorBase.cs b/src/DotNetOpenAuth.Test/CoordinatorBase.cs index 48067af..6a3ad86 100644 --- a/src/DotNetOpenAuth.Test/CoordinatorBase.cs +++ b/src/DotNetOpenAuth.Test/CoordinatorBase.cs @@ -7,6 +7,8 @@ namespace DotNetOpenAuth.Test { using System; using System.Collections.Generic; + using System.Net; + using System.Net.Http; using System.Threading; using System.Threading.Tasks; using DotNetOpenAuth.Messaging; @@ -15,40 +17,80 @@ namespace DotNetOpenAuth.Test { using NUnit.Framework; using Validation; - internal abstract class CoordinatorBase<T1, T2> { - private Func<T1, CancellationToken, Task> party1Action; - private Func<T2, CancellationToken, Task> party2Action; + using System.Linq; - protected CoordinatorBase(Func<T1, CancellationToken, Task> party1Action, Func<T2, CancellationToken, Task> party2Action) { - Requires.NotNull(party1Action, "party1Action"); - Requires.NotNull(party2Action, "party2Action"); + internal class CoordinatorBase { + private Func<IHostFactories, CancellationToken, Task> driver; - this.party1Action = party1Action; - this.party2Action = party2Action; + private Handler[] handlers; + + internal CoordinatorBase(Func<IHostFactories, CancellationToken, Task> driver, params Handler[] handlers) { + Requires.NotNull(driver, "driver"); + Requires.NotNull(handlers, "handlers"); + + this.driver = driver; + this.handlers = handlers; } - protected internal Action<IProtocolMessage> IncomingMessageFilter { get; set; } + protected internal virtual async Task RunAsync(CancellationToken cancellationToken = default(CancellationToken)) { + IHostFactories hostFactories = new MyHostFactories(this.handlers); + + await this.driver(hostFactories, cancellationToken); + } - protected internal Action<IProtocolMessage> OutgoingMessageFilter { get; set; } + internal static Handler Handle(Uri uri) { + return new Handler(uri); + } - internal abstract Task RunAsync(); + internal struct Handler { + internal Handler(Uri uri) + : this() { + this.Uri = uri; + } + + public Uri Uri { get; private set; } + + public Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> MessageHandler { get; private set; } + + internal Handler By(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> handler) { + return new Handler(this.Uri) { MessageHandler = handler }; + } + } - protected async Task RunCoreAsync(T1 party1Object, T2 party2Object) { - var cts = new CancellationTokenSource(); + private class MyHostFactories : IHostFactories { + private readonly Handler[] handlers; + + public MyHostFactories(Handler[] handlers) { + this.handlers = handlers; + } + + public HttpMessageHandler CreateHttpMessageHandler() { + return new ForwardingMessageHandler(this.handlers); + } + + public HttpClient CreateHttpClient(HttpMessageHandler handler = null) { + return new HttpClient(handler ?? this.CreateHttpMessageHandler()); + } + } + + private class ForwardingMessageHandler : HttpMessageHandler { + private readonly Handler[] handlers; + + public ForwardingMessageHandler(Handler[] handlers) { + this.handlers = handlers; + } - try { - var parties = new List<Task> { - Task.Run(() => this.party1Action(party1Object, cts.Token)), - Task.Run(() => this.party2Action(party2Object, cts.Token)), - }; - var completingTask = await Task.WhenAny(parties); - await completingTask; // rethrow any exception from the first completing task. + protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + foreach (var handler in this.handlers) { + if (handler.Uri.AbsolutePath == request.RequestUri.AbsolutePath) { + var response = await handler.MessageHandler(request, cancellationToken); + if (response != null) { + return response; + } + } + } - // if no exception, then block for the second task now. - await Task.WhenAll(parties); - } catch { - cts.Cancel(); // cause the second party to terminate, if necessary. - throw; + return new HttpResponseMessage(HttpStatusCode.NotFound); } } } diff --git a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj index ba87c42..a355ab6 100644 --- a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj +++ b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj @@ -114,7 +114,6 @@ <Compile Include="Messaging\EnumerableCacheTests.cs" /> <Compile Include="Messaging\ErrorUtilitiesTests.cs" /> <Compile Include="Messaging\MessageSerializerTests.cs" /> - <Compile Include="Messaging\MultipartPostPartTests.cs" /> <Compile Include="Messaging\Reflection\MessageDescriptionTests.cs" /> <Compile Include="Messaging\Reflection\MessageDictionaryTests.cs" /> <Compile Include="Messaging\MessagingTestBase.cs" /> @@ -127,12 +126,6 @@ <Compile Include="Messaging\Reflection\ValueMappingTests.cs" /> <Compile Include="Messaging\StandardMessageFactoryTests.cs" /> <Compile Include="Mocks\AssociateUnencryptedRequestNoSslCheck.cs" /> - <Compile Include="Mocks\CoordinatingChannel.cs" /> - <Compile Include="Mocks\CoordinatingHttpRequestInfo.cs" /> - <Compile Include="Mocks\CoordinatingOAuth2AuthServerChannel.cs" /> - <Compile Include="Mocks\CoordinatingOAuth2ClientChannel.cs" /> - <Compile Include="Mocks\CoordinatingOutgoingWebResponse.cs" /> - <Compile Include="Mocks\CoordinatingOAuthConsumerChannel.cs" /> <Compile Include="Mocks\IBaseMessageExplicitMembers.cs" /> <Compile Include="Mocks\InMemoryTokenManager.cs" /> <Compile Include="Mocks\MockHttpMessageHandler.cs" /> @@ -242,10 +235,7 @@ <Compile Include="Performance\HighPerformance.cs" /> <Compile Include="Performance\PerformanceTestUtilities.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> - <Compile Include="Messaging\ResponseTests.cs" /> <Compile Include="OAuth\AppendixScenarios.cs" /> - <Compile Include="Mocks\CoordinatingOAuthServiceProviderChannel.cs" /> - <Compile Include="OAuth\OAuthCoordinator.cs" /> <Compile Include="TestBase.cs" /> <Compile Include="TestUtilities.cs" /> <Compile Include="UriUtilTests.cs" /> diff --git a/src/DotNetOpenAuth.Test/Messaging/Bindings/StandardExpirationBindingElementTests.cs b/src/DotNetOpenAuth.Test/Messaging/Bindings/StandardExpirationBindingElementTests.cs index d525766..a8eb7c1 100644 --- a/src/DotNetOpenAuth.Test/Messaging/Bindings/StandardExpirationBindingElementTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/Bindings/StandardExpirationBindingElementTests.cs @@ -27,27 +27,27 @@ namespace DotNetOpenAuth.Test.Messaging.Bindings { } [Test] - public void VerifyGoodTimestampIsAccepted() { + public async Task VerifyGoodTimestampIsAccepted() { this.Channel = CreateChannel(MessageProtections.Expiration); - this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, false); + await this.ParameterizedReceiveProtectedTestAsync(DateTime.UtcNow, false); } [Test] - public void VerifyFutureTimestampWithinClockSkewIsAccepted() { + public async Task VerifyFutureTimestampWithinClockSkewIsAccepted() { this.Channel = CreateChannel(MessageProtections.Expiration); - this.ParameterizedReceiveProtectedTest(DateTime.UtcNow + DotNetOpenAuthSection.Messaging.MaximumClockSkew - TimeSpan.FromSeconds(1), false); + await this.ParameterizedReceiveProtectedTestAsync(DateTime.UtcNow + DotNetOpenAuthSection.Messaging.MaximumClockSkew - TimeSpan.FromSeconds(1), false); } [Test, ExpectedException(typeof(ExpiredMessageException))] - public void VerifyOldTimestampIsRejected() { + public async Task VerifyOldTimestampIsRejected() { this.Channel = CreateChannel(MessageProtections.Expiration); - this.ParameterizedReceiveProtectedTest(DateTime.UtcNow - StandardExpirationBindingElement.MaximumMessageAge - TimeSpan.FromSeconds(1), false); + await this.ParameterizedReceiveProtectedTestAsync(DateTime.UtcNow - StandardExpirationBindingElement.MaximumMessageAge - TimeSpan.FromSeconds(1), false); } [Test, ExpectedException(typeof(ProtocolException))] - public void VerifyFutureTimestampIsRejected() { + public async Task VerifyFutureTimestampIsRejected() { this.Channel = CreateChannel(MessageProtections.Expiration); - this.ParameterizedReceiveProtectedTest(DateTime.UtcNow + DotNetOpenAuthSection.Messaging.MaximumClockSkew + TimeSpan.FromSeconds(2), false); + await this.ParameterizedReceiveProtectedTestAsync(DateTime.UtcNow + DotNetOpenAuthSection.Messaging.MaximumClockSkew + TimeSpan.FromSeconds(2), false); } } } diff --git a/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs b/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs index 4540373..b54c11b 100644 --- a/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs @@ -9,6 +9,7 @@ namespace DotNetOpenAuth.Test.Messaging { using System.Collections.Generic; using System.IO; using System.Net; + using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Web; @@ -27,13 +28,13 @@ namespace DotNetOpenAuth.Test.Messaging { } [Test] - public void ReadFromRequestQueryString() { - this.ParameterizedReceiveTest("GET"); + public async Task ReadFromRequestQueryString() { + await this.ParameterizedReceiveTestAsync(HttpMethod.Get); } [Test] - public void ReadFromRequestForm() { - this.ParameterizedReceiveTest("POST"); + public async Task ReadFromRequestForm() { + await this.ParameterizedReceiveTestAsync(HttpMethod.Post); } /// <summary> @@ -44,7 +45,7 @@ namespace DotNetOpenAuth.Test.Messaging { public async Task ReadFromRequestDisallowedHttpMethod() { var fields = GetStandardTestFields(FieldFill.CompleteBeforeBindings); fields["GetOnly"] = "true"; - await this.Channel.ReadFromRequestAsync(CreateHttpRequestInfo("POST", fields), CancellationToken.None); + await this.Channel.ReadFromRequestAsync(CreateHttpRequestInfo(HttpMethod.Post, fields), CancellationToken.None); } [Test, ExpectedException(typeof(ArgumentNullException))] @@ -79,14 +80,14 @@ namespace DotNetOpenAuth.Test.Messaging { var response = await this.Channel.PrepareResponseAsync(message); Assert.AreEqual(HttpStatusCode.Redirect, response.StatusCode); - Assert.AreEqual("text/html; charset=utf-8", response.Headers[HttpResponseHeader.ContentType]); - Assert.IsTrue(response.Body != null && response.Body.Length > 0); // a non-empty body helps get passed filters like WebSense - StringAssert.StartsWith("http://provider/path", response.Headers[HttpResponseHeader.Location]); + Assert.AreEqual("text/html; charset=utf-8", response.Content.Headers.ContentType.ToString()); + Assert.IsTrue(response.Content != null && response.Content.Headers.ContentLength > 0); // a non-empty body helps get passed filters like WebSense + StringAssert.StartsWith("http://provider/path", response.Headers.Location.AbsoluteUri); foreach (var pair in expected) { string key = MessagingUtilities.EscapeUriDataStringRfc3986(pair.Key); string value = MessagingUtilities.EscapeUriDataStringRfc3986(pair.Value); string substring = string.Format("{0}={1}", key, value); - StringAssert.Contains(substring, response.Headers[HttpResponseHeader.Location]); + StringAssert.Contains(substring, response.Headers.Location.AbsoluteUri); } } @@ -124,8 +125,8 @@ namespace DotNetOpenAuth.Test.Messaging { }; var response = await this.Channel.PrepareResponseAsync(message); Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, "A form redirect should be an HTTP successful response."); - Assert.IsNull(response.Headers[HttpResponseHeader.Location], "There should not be a redirection header in the response."); - string body = response.Body; + Assert.IsNull(response.Headers.Location, "There should not be a redirection header in the response."); + string body = await response.Content.ReadAsStringAsync(); StringAssert.Contains("<form ", body); StringAssert.Contains("action=\"http://provider/path\"", body); StringAssert.Contains("method=\"post\"", body); @@ -197,23 +198,24 @@ namespace DotNetOpenAuth.Test.Messaging { TestMessage expectedMessage = GetStandardTestMessage(FieldFill.AllRequired); HttpRequest request = new HttpRequest("somefile", "http://someurl", MessagingUtilities.CreateQueryString(fields)); HttpContext.Current = new HttpContext(request, new HttpResponse(new StringWriter())); - IProtocolMessage message = await this.Channel.ReadFromRequestAsync(CancellationToken.None); + var requestBase = this.Channel.GetRequestFromContext(); + IProtocolMessage message = await this.Channel.ReadFromRequestAsync(requestBase.AsHttpRequestMessage(), CancellationToken.None); Assert.IsNotNull(message); Assert.IsInstanceOf<TestMessage>(message); Assert.AreEqual(expectedMessage.Age, ((TestMessage)message).Age); } [Test, ExpectedException(typeof(InvalidOperationException))] - public void ReadFromRequestNoContext() { + public void GetRequestFromContextNoContext() { HttpContext.Current = null; TestBadChannel badChannel = new TestBadChannel(false); - badChannel.ReadFromRequest(); + badChannel.GetRequestFromContext(); } [Test, ExpectedException(typeof(ArgumentNullException))] - public void ReadFromRequestNull() { + public async Task ReadFromRequestNull() { TestBadChannel badChannel = new TestBadChannel(false); - badChannel.ReadFromRequest(null); + await badChannel.ReadFromRequestAsync(null, CancellationToken.None); } [Test] @@ -227,22 +229,22 @@ namespace DotNetOpenAuth.Test.Messaging { } [Test, ExpectedException(typeof(InvalidSignatureException))] - public void ReceivedInvalidSignature() { + public async Task ReceivedInvalidSignature() { this.Channel = CreateChannel(MessageProtections.TamperProtection); - this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, true); + await this.ParameterizedReceiveProtectedTestAsync(DateTime.UtcNow, true); } [Test] - public void ReceivedReplayProtectedMessageJustOnce() { + public async Task ReceivedReplayProtectedMessageJustOnce() { this.Channel = CreateChannel(MessageProtections.ReplayProtection); - this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, false); + await this.ParameterizedReceiveProtectedTestAsync(DateTime.UtcNow, false); } [Test, ExpectedException(typeof(ReplayedMessageException))] - public void ReceivedReplayProtectedMessageTwice() { + public async Task ReceivedReplayProtectedMessageTwice() { this.Channel = CreateChannel(MessageProtections.ReplayProtection); - this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, false); - this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, false); + await this.ParameterizedReceiveProtectedTestAsync(DateTime.UtcNow, false); + await this.ParameterizedReceiveProtectedTestAsync(DateTime.UtcNow, false); } [Test, ExpectedException(typeof(ProtocolException))] @@ -293,15 +295,15 @@ namespace DotNetOpenAuth.Test.Messaging { } [Test, ExpectedException(typeof(UnprotectedMessageException))] - public void InsufficientlyProtectedMessageReceived() { + public async Task InsufficientlyProtectedMessageReceived() { this.Channel = CreateChannel(MessageProtections.None, MessageProtections.TamperProtection); - this.ParameterizedReceiveProtectedTest(DateTime.Now, false); + await this.ParameterizedReceiveProtectedTestAsync(DateTime.Now, false); } [Test, ExpectedException(typeof(ProtocolException))] public async Task IncomingMessageMissingRequiredParameters() { var fields = GetStandardTestFields(FieldFill.IdentifiableButNotAllRequired); - await this.Channel.ReadFromRequestAsync(CreateHttpRequestInfo("GET", fields), CancellationToken.None); + await this.Channel.ReadFromRequestAsync(CreateHttpRequestInfo(HttpMethod.Get, fields), CancellationToken.None); } } } diff --git a/src/DotNetOpenAuth.Test/Messaging/MessagingTestBase.cs b/src/DotNetOpenAuth.Test/Messaging/MessagingTestBase.cs index 092b89c..9e0cc99 100644 --- a/src/DotNetOpenAuth.Test/Messaging/MessagingTestBase.cs +++ b/src/DotNetOpenAuth.Test/Messaging/MessagingTestBase.cs @@ -10,6 +10,7 @@ namespace DotNetOpenAuth.Test { using System.Collections.Specialized; using System.IO; using System.Net; + using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Xml; @@ -57,20 +58,19 @@ namespace DotNetOpenAuth.Test { this.Channel = new TestChannel(); } - internal static HttpRequestInfo CreateHttpRequestInfo(string method, IDictionary<string, string> fields) { + internal static HttpRequestMessage CreateHttpRequestInfo(HttpMethod method, IDictionary<string, string> fields) { + var result = new HttpRequestMessage() { Method = method }; var requestUri = new UriBuilder(DefaultUrlForHttpRequestInfo); - var headers = new NameValueCollection(); - NameValueCollection form = null; - if (method == "POST") { - form = fields.ToNameValueCollection(); - headers.Add(HttpRequestHeaders.ContentType, Channel.HttpFormUrlEncoded); - } else if (method == "GET") { - requestUri.Query = MessagingUtilities.CreateQueryString(fields); + if (method == HttpMethod.Post) { + result.Content = new FormUrlEncodedContent(fields); + } else if (method == HttpMethod.Get) { + requestUri.AppendQueryArgs(fields); } else { throw new ArgumentOutOfRangeException("method", method, "Expected POST or GET"); } - return new HttpRequestInfo(method, requestUri.Uri, form: form, headers: headers); + result.RequestUri = requestUri.Uri; + return result; } internal static Channel CreateChannel(MessageProtections capabilityAndRecognition) { @@ -145,7 +145,7 @@ namespace DotNetOpenAuth.Test { } } - internal async Task ParameterizedReceiveTestAsync(string method) { + internal async Task ParameterizedReceiveTestAsync(HttpMethod method) { var fields = GetStandardTestFields(FieldFill.CompleteBeforeBindings); TestMessage expectedMessage = GetStandardTestMessage(FieldFill.CompleteBeforeBindings); @@ -167,7 +167,7 @@ namespace DotNetOpenAuth.Test { utcCreatedDate = DateTime.Parse(utcCreatedDate.Value.ToUniversalTime().ToString()); // round off the milliseconds so comparisons work later fields.Add("created_on", XmlConvert.ToString(utcCreatedDate.Value, XmlDateTimeSerializationMode.Utc)); } - IProtocolMessage requestMessage = await this.Channel.ReadFromRequestAsync(CreateHttpRequestInfo("GET", fields), CancellationToken.None); + IProtocolMessage requestMessage = await this.Channel.ReadFromRequestAsync(CreateHttpRequestInfo(HttpMethod.Get, fields), CancellationToken.None); Assert.IsNotNull(requestMessage); Assert.IsInstanceOf<TestSignedDirectedMessage>(requestMessage); TestSignedDirectedMessage actualMessage = (TestSignedDirectedMessage)requestMessage; diff --git a/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs b/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs index 7bb6d5c..f5fa60b 100644 --- a/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs @@ -67,41 +67,6 @@ namespace DotNetOpenAuth.Test.Messaging { } [Test] - public void AsHttpResponseMessage() { - var responseContent = new byte[10]; - (new Random()).NextBytes(responseContent); - var responseStream = new MemoryStream(responseContent); - var outgoingResponse = new OutgoingWebResponse(); - outgoingResponse.Headers.Add("X-SOME-HEADER", "value"); - outgoingResponse.Headers.Add("Content-Length", responseContent.Length.ToString(CultureInfo.InvariantCulture)); - outgoingResponse.ResponseStream = responseStream; - - var httpResponseMessage = outgoingResponse.AsHttpResponseMessage(); - Assert.That(httpResponseMessage, Is.Not.Null); - Assert.That(httpResponseMessage.Headers.GetValues("X-SOME-HEADER").ToList(), Is.EqualTo(new[] { "value" })); - Assert.That( - httpResponseMessage.Content.Headers.GetValues("Content-Length").ToList(), - Is.EqualTo(new[] { responseContent.Length.ToString(CultureInfo.InvariantCulture) })); - var actualContent = new byte[responseContent.Length + 1]; // give the opportunity to provide a bit more data than we expect. - var bytesRead = httpResponseMessage.Content.ReadAsStreamAsync().Result.Read(actualContent, 0, actualContent.Length); - Assert.That(bytesRead, Is.EqualTo(responseContent.Length)); // verify that only the data we expected came back. - var trimmedActualContent = new byte[bytesRead]; - Array.Copy(actualContent, trimmedActualContent, bytesRead); - Assert.That(trimmedActualContent, Is.EqualTo(responseContent)); - } - - [Test] - public void AsHttpResponseMessageNoContent() { - var outgoingResponse = new OutgoingWebResponse(); - outgoingResponse.Headers.Add("X-SOME-HEADER", "value"); - - var httpResponseMessage = outgoingResponse.AsHttpResponseMessage(); - Assert.That(httpResponseMessage, Is.Not.Null); - Assert.That(httpResponseMessage.Headers.GetValues("X-SOME-HEADER").ToList(), Is.EqualTo(new[] { "value" })); - Assert.That(httpResponseMessage.Content, Is.Null); - } - - [Test] public void ToDictionary() { NameValueCollection nvc = new NameValueCollection(); nvc["a"] = "b"; @@ -178,35 +143,6 @@ namespace DotNetOpenAuth.Test.Messaging { } /// <summary> - /// Verifies the overall format of the multipart POST is correct. - /// </summary> - [Test] - public void PostMultipart() { - var httpHandler = new TestWebRequestHandler(); - bool callbackTriggered = false; - httpHandler.Callback = req => { - var m = Regex.Match(req.ContentType, "multipart/form-data; boundary=(.+)"); - Assert.IsTrue(m.Success, "Content-Type HTTP header not set correctly."); - string boundary = m.Groups[1].Value; - boundary = boundary.Substring(0, boundary.IndexOf(';')); // trim off charset - string expectedEntity = "--{0}\r\nContent-Disposition: form-data; name=\"a\"\r\n\r\nb\r\n--{0}--\r\n"; - expectedEntity = string.Format(expectedEntity, boundary); - string actualEntity = httpHandler.RequestEntityAsString; - Assert.AreEqual(expectedEntity, actualEntity); - callbackTriggered = true; - Assert.AreEqual(req.ContentLength, actualEntity.Length); - IncomingWebResponse resp = new CachedDirectWebResponse(); - return resp; - }; - var request = (HttpWebRequest)WebRequest.Create("http://someserver"); - var parts = new[] { - MultipartPostPart.CreateFormPart("a", "b"), - }; - request.PostMultipart(httpHandler, parts); - Assert.IsTrue(callbackTriggered); - } - - /// <summary> /// Verifies proper behavior of GetHttpVerb /// </summary> [Test] @@ -215,16 +151,16 @@ namespace DotNetOpenAuth.Test.Messaging { Assert.AreEqual("POST", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PostRequest)); Assert.AreEqual("HEAD", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.HeadRequest)); Assert.AreEqual("DELETE", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.DeleteRequest)); - Assert.AreEqual("PUT", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PutRequest));
- Assert.AreEqual("PATCH", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PatchRequest));
+ Assert.AreEqual("PUT", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PutRequest)); + Assert.AreEqual("PATCH", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PatchRequest)); Assert.AreEqual("OPTIONS", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.OptionsRequest)); Assert.AreEqual("GET", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest)); Assert.AreEqual("POST", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest)); Assert.AreEqual("HEAD", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.HeadRequest | HttpDeliveryMethods.AuthorizationHeaderRequest)); Assert.AreEqual("DELETE", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.DeleteRequest | HttpDeliveryMethods.AuthorizationHeaderRequest)); - Assert.AreEqual("PUT", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PutRequest | HttpDeliveryMethods.AuthorizationHeaderRequest));
- Assert.AreEqual("PATCH", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PatchRequest | HttpDeliveryMethods.AuthorizationHeaderRequest));
+ Assert.AreEqual("PUT", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PutRequest | HttpDeliveryMethods.AuthorizationHeaderRequest)); + Assert.AreEqual("PATCH", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.PatchRequest | HttpDeliveryMethods.AuthorizationHeaderRequest)); Assert.AreEqual("OPTIONS", MessagingUtilities.GetHttpVerb(HttpDeliveryMethods.OptionsRequest | HttpDeliveryMethods.AuthorizationHeaderRequest)); } @@ -245,8 +181,8 @@ namespace DotNetOpenAuth.Test.Messaging { Assert.AreEqual(HttpDeliveryMethods.PostRequest, MessagingUtilities.GetHttpDeliveryMethod("POST")); Assert.AreEqual(HttpDeliveryMethods.HeadRequest, MessagingUtilities.GetHttpDeliveryMethod("HEAD")); Assert.AreEqual(HttpDeliveryMethods.PutRequest, MessagingUtilities.GetHttpDeliveryMethod("PUT")); - Assert.AreEqual(HttpDeliveryMethods.DeleteRequest, MessagingUtilities.GetHttpDeliveryMethod("DELETE"));
- Assert.AreEqual(HttpDeliveryMethods.PatchRequest, MessagingUtilities.GetHttpDeliveryMethod("PATCH"));
+ Assert.AreEqual(HttpDeliveryMethods.DeleteRequest, MessagingUtilities.GetHttpDeliveryMethod("DELETE")); + Assert.AreEqual(HttpDeliveryMethods.PatchRequest, MessagingUtilities.GetHttpDeliveryMethod("PATCH")); Assert.AreEqual(HttpDeliveryMethods.OptionsRequest, MessagingUtilities.GetHttpDeliveryMethod("OPTIONS")); } diff --git a/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs b/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs deleted file mode 100644 index e9ac5aa..0000000 --- a/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs +++ /dev/null @@ -1,108 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="MultipartPostPartTests.cs" company="Outercurve Foundation"> -// Copyright (c) Outercurve Foundation. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.Test.Messaging { - using System.CodeDom.Compiler; - using System.Collections.Generic; - using System.IO; - using System.Net; - using DotNetOpenAuth.Messaging; - using NUnit.Framework; - using Validation; - - [TestFixture] - public class MultipartPostPartTests : TestBase { - /// <summary> - /// Verifies that the Length property matches the length actually serialized. - /// </summary> - [Test] - public void FormDataSerializeMatchesLength() { - var part = MultipartPostPart.CreateFormPart("a", "b"); - VerifyLength(part); - } - - /// <summary> - /// Verifies that the length property matches the length actually serialized. - /// </summary> - [Test] - public void FileSerializeMatchesLength() { - using (TempFileCollection tfc = new TempFileCollection()) { - string file = tfc.AddExtension(".txt"); - File.WriteAllText(file, "sometext"); - var part = MultipartPostPart.CreateFormFilePart("someformname", file, "text/plain"); - VerifyLength(part); - } - } - - /// <summary> - /// Verifies file multiparts identify themselves as files and not merely form-data. - /// </summary> - [Test] - public void FilePartAsFile() { - var part = MultipartPostPart.CreateFormFilePart("somename", "somefile", "plain/text", new MemoryStream()); - Assert.AreEqual("file", part.ContentDisposition); - } - - /// <summary> - /// Verifies MultiPartPost sends the right number of bytes. - /// </summary> - [Test] - public void MultiPartPostAscii() { - using (TempFileCollection tfc = new TempFileCollection()) { - string file = tfc.AddExtension("txt"); - File.WriteAllText(file, "sometext"); - this.VerifyFullPost(new List<MultipartPostPart> { - MultipartPostPart.CreateFormPart("a", "b"), - MultipartPostPart.CreateFormFilePart("SomeFormField", file, "text/plain"), - }); - } - } - - /// <summary> - /// Verifies MultiPartPost sends the right number of bytes. - /// </summary> - [Test] - public void MultiPartPostMultiByteCharacters() { - using (TempFileCollection tfc = new TempFileCollection()) { - string file = tfc.AddExtension("txt"); - File.WriteAllText(file, "\x1020\x818"); - this.VerifyFullPost(new List<MultipartPostPart> { - MultipartPostPart.CreateFormPart("a", "\x987"), - MultipartPostPart.CreateFormFilePart("SomeFormField", file, "text/plain"), - }); - } - } - - private static void VerifyLength(MultipartPostPart part) { - Requires.NotNull(part, "part"); - - var expectedLength = part.Length; - var ms = new MemoryStream(); - var sw = new StreamWriter(ms); - part.Serialize(sw); - sw.Flush(); - var actualLength = ms.Length; - Assert.AreEqual(expectedLength, actualLength); - } - - private void VerifyFullPost(List<MultipartPostPart> parts) { - var request = (HttpWebRequest)WebRequest.Create("http://localhost"); - var handler = new Mocks.TestWebRequestHandler(); - bool posted = false; - handler.Callback = req => { - foreach (string header in req.Headers) { - TestUtilities.TestLogger.InfoFormat("{0}: {1}", header, req.Headers[header]); - } - TestUtilities.TestLogger.InfoFormat(handler.RequestEntityAsString); - Assert.AreEqual(req.ContentLength, handler.RequestEntityStream.Length); - posted = true; - return null; - }; - request.PostMultipart(handler, parts); - Assert.IsTrue(posted, "HTTP POST never sent."); - } - } -} diff --git a/src/DotNetOpenAuth.Test/Messaging/ResponseTests.cs b/src/DotNetOpenAuth.Test/Messaging/ResponseTests.cs deleted file mode 100644 index 52f031e..0000000 --- a/src/DotNetOpenAuth.Test/Messaging/ResponseTests.cs +++ /dev/null @@ -1,40 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="ResponseTests.cs" company="Outercurve Foundation"> -// Copyright (c) Outercurve Foundation. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.Test.Messaging { - using System; - using System.IO; - using System.Web; - using DotNetOpenAuth.Messaging; - using NUnit.Framework; - - [TestFixture] - public class ResponseTests : TestBase { - [Test, ExpectedException(typeof(InvalidOperationException))] - public void RespondWithoutAspNetContext() { - HttpContext.Current = null; - new OutgoingWebResponse().Respond(); - } - - [Test] - public void Respond() { - StringWriter writer = new StringWriter(); - HttpRequest httpRequest = new HttpRequest("file", "http://server", string.Empty); - HttpResponse httpResponse = new HttpResponse(writer); - HttpContext context = new HttpContext(httpRequest, httpResponse); - HttpContext.Current = context; - - OutgoingWebResponse response = new OutgoingWebResponse(); - response.Status = System.Net.HttpStatusCode.OK; - response.Headers["someHeaderName"] = "someHeaderValue"; - response.Body = "some body"; - response.Respond(); - string results = writer.ToString(); - // For some reason the only output in test is the body... the headers require a web host - Assert.AreEqual(response.Body, results); - } - } -} diff --git a/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs b/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs deleted file mode 100644 index 475f4b5..0000000 --- a/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs +++ /dev/null @@ -1,348 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="CoordinatingChannel.cs" company="Outercurve Foundation"> -// Copyright (c) Outercurve Foundation. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.Test.Mocks { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Net; - using System.Text; - using System.Threading; - using System.Web; - using DotNetOpenAuth.Messaging; - using DotNetOpenAuth.Messaging.Reflection; - using DotNetOpenAuth.Test.OpenId; - using NUnit.Framework; - using Validation; - - internal class CoordinatingChannel : Channel { - /// <summary> - /// A lock to use when checking and setting the <see cref="waitingForMessage"/> - /// or the <see cref="simulationCompleted"/> fields. - /// </summary> - /// <remarks> - /// This is a static member so that all coordinating channels share a lock - /// since they peak at each others fields. - /// </remarks> - private static readonly object waitingForMessageCoordinationLock = new object(); - - /// <summary> - /// The original product channel whose behavior is being modified to work - /// better in automated testing. - /// </summary> - private Channel wrappedChannel; - - /// <summary> - /// A flag set to true when this party in a two-party test has completed - /// its part of the testing. - /// </summary> - private bool simulationCompleted; - - /// <summary> - /// A thread-coordinating signal that is set when another thread has a - /// message ready for this channel to receive. - /// </summary> - private EventWaitHandle incomingMessageSignal = new AutoResetEvent(false); - - /// <summary> - /// A thread-coordinating signal that is set briefly by this thread whenever - /// a message is picked up. - /// </summary> - private EventWaitHandle messageReceivedSignal = new AutoResetEvent(false); - - /// <summary> - /// A flag used to indicate when this channel is waiting for a message - /// to arrive. - /// </summary> - private bool waitingForMessage; - - /// <summary> - /// An incoming message that has been posted by a remote channel and - /// is waiting for receipt by this channel. - /// </summary> - private IDictionary<string, string> incomingMessage; - - /// <summary> - /// The recipient URL of the <see cref="incomingMessage"/>, where applicable. - /// </summary> - private MessageReceivingEndpoint incomingMessageRecipient; - - /// <summary> - /// The headers of the <see cref="incomingMessage"/>, where applicable. - /// </summary> - private WebHeaderCollection incomingMessageHttpHeaders; - - /// <summary> - /// A delegate that gets a chance to peak at and fiddle with all - /// incoming messages. - /// </summary> - private Action<IProtocolMessage> incomingMessageFilter; - - /// <summary> - /// A delegate that gets a chance to peak at and fiddle with all - /// outgoing messages. - /// </summary> - private Action<IProtocolMessage> outgoingMessageFilter; - - /// <summary> - /// The simulated clients cookies. - /// </summary> - private HttpCookieCollection cookies = new HttpCookieCollection(); - - /// <summary> - /// Initializes a new instance of the <see cref="CoordinatingChannel"/> class. - /// </summary> - /// <param name="wrappedChannel">The wrapped channel. Must not be null.</param> - /// <param name="incomingMessageFilter">The incoming message filter. May be null.</param> - /// <param name="outgoingMessageFilter">The outgoing message filter. May be null.</param> - internal CoordinatingChannel(Channel wrappedChannel, Action<IProtocolMessage> incomingMessageFilter, Action<IProtocolMessage> outgoingMessageFilter) - : base(GetMessageFactory(wrappedChannel), wrappedChannel.BindingElements.ToArray()) { - Requires.NotNull(wrappedChannel, "wrappedChannel"); - - this.wrappedChannel = wrappedChannel; - this.incomingMessageFilter = incomingMessageFilter; - this.outgoingMessageFilter = outgoingMessageFilter; - - // Preserve any customized binding element ordering. - this.CustomizeBindingElementOrder(this.wrappedChannel.OutgoingBindingElements, this.wrappedChannel.IncomingBindingElements); - } - - /// <summary> - /// Gets or sets the coordinating channel used by the other party. - /// </summary> - internal CoordinatingChannel RemoteChannel { get; set; } - - /// <summary> - /// Indicates that the simulation that uses this channel has completed work. - /// </summary> - /// <remarks> - /// Calling this method is not strictly necessary, but it gives the channel - /// coordination a chance to recognize when another channel is left dangling - /// waiting for a message from another channel that may never come. - /// </remarks> - internal void Close() { - lock (waitingForMessageCoordinationLock) { - this.simulationCompleted = true; - if (this.RemoteChannel.waitingForMessage && this.RemoteChannel.incomingMessage == null) { - TestUtilities.TestLogger.Debug("CoordinatingChannel is closing while remote channel is waiting for an incoming message. Signaling channel to unblock it to receive a null message."); - this.RemoteChannel.incomingMessageSignal.Set(); - } - - this.Dispose(); - } - } - - /// <summary> - /// Replays the specified message as if it were received again. - /// </summary> - /// <param name="message">The message to replay.</param> - internal void Replay(IProtocolMessage message) { - this.ProcessIncomingMessage(this.CloneSerializedParts(message)); - } - - /// <summary> - /// Called from a remote party's thread to post a message to this channel for processing. - /// </summary> - /// <param name="message">The message that this channel should receive. This message will be cloned.</param> - internal void PostMessage(IProtocolMessage message) { - if (this.incomingMessage != null) { - // The remote party hasn't picked up the last message we sent them. - // Wait for a short period for them to pick it up before failing. - TestBase.TestLogger.Warn("We're blocked waiting to send a message to the remote party and they haven't processed the last message we sent them."); - this.RemoteChannel.messageReceivedSignal.WaitOne(500); - } - ErrorUtilities.VerifyInternal(this.incomingMessage == null, "Oops, a message is already waiting for the remote party!"); - this.incomingMessage = this.MessageDescriptions.GetAccessor(message).Serialize(); - var directedMessage = message as IDirectedProtocolMessage; - this.incomingMessageRecipient = (directedMessage != null && directedMessage.Recipient != null) ? new MessageReceivingEndpoint(directedMessage.Recipient, directedMessage.HttpMethods) : null; - var httpMessage = message as IHttpDirectRequest; - this.incomingMessageHttpHeaders = (httpMessage != null) ? httpMessage.Headers.Clone() : null; - this.incomingMessageSignal.Set(); - } - - internal void SaveCookies(HttpCookieCollection cookies) { - Requires.NotNull(cookies, "cookies"); - foreach (string cookieName in cookies) { - var cookie = cookies[cookieName]; - this.cookies.Set(cookie); - } - } - - protected internal override HttpRequestBase GetRequestFromContext() { - MessageReceivingEndpoint recipient; - WebHeaderCollection headers; - var messageData = this.AwaitIncomingMessage(out recipient, out headers); - CoordinatingHttpRequestInfo result; - if (messageData != null) { - result = new CoordinatingHttpRequestInfo(this, this.MessageFactory, messageData, recipient, this.cookies); - } else { - result = new CoordinatingHttpRequestInfo(recipient, this.cookies); - } - - if (headers != null) { - headers.ApplyTo(result.Headers); - } - - return result; - } - - protected override IProtocolMessage RequestCore(IDirectedProtocolMessage request) { - this.ProcessMessageFilter(request, true); - - // Drop the outgoing message in the other channel's in-slot and let them know it's there. - this.RemoteChannel.PostMessage(request); - - // Now wait for a response... - MessageReceivingEndpoint recipient; - WebHeaderCollection headers; - IDictionary<string, string> responseData = this.AwaitIncomingMessage(out recipient, out headers); - ErrorUtilities.VerifyInternal(recipient == null, "The recipient is expected to be null for direct responses."); - - // And deserialize it. - IDirectResponseProtocolMessage responseMessage = this.MessageFactory.GetNewResponseMessage(request, responseData); - if (responseMessage == null) { - return null; - } - - var responseAccessor = this.MessageDescriptions.GetAccessor(responseMessage); - responseAccessor.Deserialize(responseData); - var responseMessageHttpRequest = responseMessage as IHttpDirectRequest; - if (headers != null && responseMessageHttpRequest != null) { - headers.ApplyTo(responseMessageHttpRequest.Headers); - } - - this.ProcessMessageFilter(responseMessage, false); - return responseMessage; - } - - protected override OutgoingWebResponse PrepareDirectResponse(IProtocolMessage response) { - this.ProcessMessageFilter(response, true); - return new CoordinatingOutgoingWebResponse(response, this.RemoteChannel, this); - } - - protected override OutgoingWebResponse PrepareIndirectResponse(IDirectedProtocolMessage message) { - this.ProcessMessageFilter(message, true); - // In this mock transport, direct and indirect messages are the same. - return this.PrepareDirectResponse(message); - } - - protected override IDirectedProtocolMessage ReadFromRequestCore(HttpRequestBase request) { - var mockRequest = (CoordinatingHttpRequestInfo)request; - if (mockRequest.Message != null) { - this.ProcessMessageFilter(mockRequest.Message, false); - } - - return mockRequest.Message; - } - - protected override IDictionary<string, string> ReadFromResponseCore(IncomingWebResponse response) { - return this.wrappedChannel.ReadFromResponseCoreTestHook(response); - } - - protected override void ProcessIncomingMessage(IProtocolMessage message) { - this.wrappedChannel.ProcessIncomingMessageTestHook(message); - } - - /// <summary> - /// Clones a message, instantiating the new instance using <i>this</i> channel's - /// message factory. - /// </summary> - /// <typeparam name="T">The type of message to clone.</typeparam> - /// <param name="message">The message to clone.</param> - /// <returns>The new instance of the message.</returns> - /// <remarks> - /// This Clone method should <i>not</i> be used to send message clones to the remote - /// channel since their message factory is not used. - /// </remarks> - protected virtual T CloneSerializedParts<T>(T message) where T : class, IProtocolMessage { - Requires.NotNull(message, "message"); - - IProtocolMessage clonedMessage; - var messageAccessor = this.MessageDescriptions.GetAccessor(message); - var fields = messageAccessor.Serialize(); - - MessageReceivingEndpoint recipient = null; - var directedMessage = message as IDirectedProtocolMessage; - var directResponse = message as IDirectResponseProtocolMessage; - if (directedMessage != null && directedMessage.IsRequest()) { - if (directedMessage.Recipient != null) { - recipient = new MessageReceivingEndpoint(directedMessage.Recipient, directedMessage.HttpMethods); - } - - clonedMessage = this.MessageFactory.GetNewRequestMessage(recipient, fields); - } else if (directResponse != null && directResponse.IsDirectResponse()) { - clonedMessage = this.MessageFactory.GetNewResponseMessage(directResponse.OriginatingRequest, fields); - } else { - throw new InvalidOperationException("Totally expected a message to implement one of the two derived interface types."); - } - - ErrorUtilities.VerifyInternal(clonedMessage != null, "Message factory did not generate a message instance for " + message.GetType().Name); - - // Fill the cloned message with data. - var clonedMessageAccessor = this.MessageDescriptions.GetAccessor(clonedMessage); - clonedMessageAccessor.Deserialize(fields); - - return (T)clonedMessage; - } - - private static IMessageFactory GetMessageFactory(Channel channel) { - Requires.NotNull(channel, "channel"); - - return channel.MessageFactoryTestHook; - } - - private IDictionary<string, string> AwaitIncomingMessage(out MessageReceivingEndpoint recipient, out WebHeaderCollection headers) { - // Special care should be taken so that we don't indefinitely - // wait for a message that may never come due to a bug in the product - // or the test. - // There are two scenarios that we need to watch out for: - // 1. Two channels are waiting to receive messages from each other. - // 2. One channel is waiting for a message that will never come because - // the remote party has already finished executing. - lock (waitingForMessageCoordinationLock) { - // It's possible that a message was just barely transmitted either to this - // or the remote channel. So it's ok for the remote channel to be waiting - // if either it or we are already about to receive a message. - ErrorUtilities.VerifyInternal(!this.RemoteChannel.waitingForMessage || this.RemoteChannel.incomingMessage != null || this.incomingMessage != null, "This channel is expecting an incoming message from another channel that is also blocked waiting for an incoming message from us!"); - - // It's permissible that the remote channel has already closed if it left a message - // for us already. - ErrorUtilities.VerifyInternal(!this.RemoteChannel.simulationCompleted || this.incomingMessage != null, "This channel is expecting an incoming message from another channel that has already been closed."); - this.waitingForMessage = true; - } - - this.incomingMessageSignal.WaitOne(); - - lock (waitingForMessageCoordinationLock) { - this.waitingForMessage = false; - var response = this.incomingMessage; - recipient = this.incomingMessageRecipient; - headers = this.incomingMessageHttpHeaders; - this.incomingMessage = null; - this.incomingMessageRecipient = null; - this.incomingMessageHttpHeaders = null; - - // Briefly signal to another thread that might be waiting for our inbox to be empty - this.messageReceivedSignal.Set(); - this.messageReceivedSignal.Reset(); - - return response; - } - } - - private void ProcessMessageFilter(IProtocolMessage message, bool outgoing) { - if (outgoing) { - if (this.outgoingMessageFilter != null) { - this.outgoingMessageFilter(message); - } - } else { - if (this.incomingMessageFilter != null) { - this.incomingMessageFilter(message); - } - } - } - } -} diff --git a/src/DotNetOpenAuth.Test/Mocks/CoordinatingHttpRequestInfo.cs b/src/DotNetOpenAuth.Test/Mocks/CoordinatingHttpRequestInfo.cs deleted file mode 100644 index 497503c..0000000 --- a/src/DotNetOpenAuth.Test/Mocks/CoordinatingHttpRequestInfo.cs +++ /dev/null @@ -1,109 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="CoordinatingHttpRequestInfo.cs" company="Outercurve Foundation"> -// Copyright (c) Outercurve Foundation. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.Test.Mocks { - using System; - using System.Collections.Generic; - using System.Net; - using System.Web; - - using DotNetOpenAuth.Messaging; - using Validation; - - internal class CoordinatingHttpRequestInfo : HttpRequestInfo { - private readonly Channel channel; - - private readonly IDictionary<string, string> messageData; - - private readonly IMessageFactory messageFactory; - - private readonly MessageReceivingEndpoint recipient; - - private IDirectedProtocolMessage message; - - /// <summary> - /// Initializes a new instance of the <see cref="CoordinatingHttpRequestInfo"/> class - /// that will generate a message when the <see cref="Message"/> property getter is called. - /// </summary> - /// <param name="channel">The channel.</param> - /// <param name="messageFactory">The message factory.</param> - /// <param name="messageData">The message data.</param> - /// <param name="recipient">The recipient.</param> - /// <param name="cookies">Cookies included in the incoming request.</param> - internal CoordinatingHttpRequestInfo( - Channel channel, - IMessageFactory messageFactory, - IDictionary<string, string> messageData, - MessageReceivingEndpoint recipient, - HttpCookieCollection cookies) - : this(recipient, cookies) { - Requires.NotNull(channel, "channel"); - Requires.NotNull(messageFactory, "messageFactory"); - Requires.NotNull(messageData, "messageData"); - this.channel = channel; - this.messageData = messageData; - this.messageFactory = messageFactory; - } - - /// <summary> - /// Initializes a new instance of the <see cref="CoordinatingHttpRequestInfo"/> class - /// that will not generate any message. - /// </summary> - /// <param name="recipient">The recipient.</param> - /// <param name="cookies">Cookies included in the incoming request.</param> - internal CoordinatingHttpRequestInfo(MessageReceivingEndpoint recipient, HttpCookieCollection cookies) - : base(GetHttpVerb(recipient), recipient != null ? recipient.Location : new Uri("http://host/path"), cookies: cookies) { - this.recipient = recipient; - } - - /// <summary> - /// Initializes a new instance of the <see cref="CoordinatingHttpRequestInfo"/> class. - /// </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 CoordinatingHttpRequestInfo(IDirectedProtocolMessage message, HttpDeliveryMethods httpMethod) - : base(GetHttpVerb(httpMethod), message.Recipient) { - this.message = message; - } - - /// <summary> - /// Gets the message deserialized from the remote channel. - /// </summary> - internal IDirectedProtocolMessage Message { - get { - if (this.message == null && this.messageData != null) { - var message = this.messageFactory.GetNewRequestMessage(this.recipient, this.messageData); - if (message != null) { - this.channel.MessageDescriptions.GetAccessor(message).Deserialize(this.messageData); - this.message = message; - } - } - - return this.message; - } - } - - private static string GetHttpVerb(MessageReceivingEndpoint recipient) { - if (recipient == null) { - return "GET"; - } - - return GetHttpVerb(recipient.AllowedMethods); - } - - private static string GetHttpVerb(HttpDeliveryMethods httpMethod) { - if ((httpMethod & HttpDeliveryMethods.GetRequest) != 0) { - return "GET"; - } - - if ((httpMethod & HttpDeliveryMethods.PostRequest) != 0) { - return "POST"; - } - - throw new ArgumentOutOfRangeException(); - } - } -} diff --git a/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuth2AuthServerChannel.cs b/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuth2AuthServerChannel.cs deleted file mode 100644 index 463b149..0000000 --- a/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuth2AuthServerChannel.cs +++ /dev/null @@ -1,33 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="CoordinatingOAuth2AuthServerChannel.cs" company="Outercurve Foundation"> -// Copyright (c) Outercurve Foundation. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.Test.Mocks { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using DotNetOpenAuth.Messaging; - using DotNetOpenAuth.OAuth2; - using DotNetOpenAuth.OAuth2.ChannelElements; - - internal class CoordinatingOAuth2AuthServerChannel : CoordinatingChannel, IOAuth2ChannelWithAuthorizationServer { - private OAuth2AuthorizationServerChannel wrappedChannel; - - internal CoordinatingOAuth2AuthServerChannel(Channel wrappedChannel, Action<IProtocolMessage> incomingMessageFilter, Action<IProtocolMessage> outgoingMessageFilter) - : base(wrappedChannel, incomingMessageFilter, outgoingMessageFilter) { - this.wrappedChannel = (OAuth2AuthorizationServerChannel)wrappedChannel; - } - - public IAuthorizationServerHost AuthorizationServer { - get { return this.wrappedChannel.AuthorizationServer; } - } - - public IScopeSatisfiedCheck ScopeSatisfiedCheck { - get { return this.wrappedChannel.ScopeSatisfiedCheck; } - set { this.wrappedChannel.ScopeSatisfiedCheck = value; } - } - } -} diff --git a/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuth2ClientChannel.cs b/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuth2ClientChannel.cs deleted file mode 100644 index 96091ac..0000000 --- a/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuth2ClientChannel.cs +++ /dev/null @@ -1,37 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="CoordinatingOAuth2ClientChannel.cs" company="Andrew Arnott"> -// Copyright (c) Andrew Arnott. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.Test.Mocks { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using DotNetOpenAuth.Messaging; - using DotNetOpenAuth.OAuth2.ChannelElements; - - internal class CoordinatingOAuth2ClientChannel : CoordinatingChannel, IOAuth2ChannelWithClient { - private OAuth2ClientChannel wrappedChannel; - - internal CoordinatingOAuth2ClientChannel(Channel wrappedChannel, Action<IProtocolMessage> incomingMessageFilter, Action<IProtocolMessage> outgoingMessageFilter) - : base(wrappedChannel, incomingMessageFilter, outgoingMessageFilter) { - this.wrappedChannel = (OAuth2ClientChannel)wrappedChannel; - } - - public string ClientIdentifier { - get { return this.wrappedChannel.ClientIdentifier; } - set { this.wrappedChannel.ClientIdentifier = value; } - } - - public DotNetOpenAuth.OAuth2.ClientCredentialApplicator ClientCredentialApplicator { - get { return this.wrappedChannel.ClientCredentialApplicator; } - set { this.wrappedChannel.ClientCredentialApplicator = value; } - } - - public System.Xml.XmlDictionaryReaderQuotas JsonReaderQuotas { - get { return this.XmlDictionaryReaderQuotas; } - } - } -}
\ No newline at end of file diff --git a/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuthConsumerChannel.cs b/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuthConsumerChannel.cs deleted file mode 100644 index 9b552d3..0000000 --- a/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuthConsumerChannel.cs +++ /dev/null @@ -1,155 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="CoordinatingOAuthConsumerChannel.cs" company="Outercurve Foundation"> -// Copyright (c) Outercurve Foundation. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.Test.Mocks { - using System; - using System.Threading; - using System.Web; - - using DotNetOpenAuth.Messaging; - using DotNetOpenAuth.Messaging.Bindings; - using DotNetOpenAuth.OAuth.ChannelElements; - using DotNetOpenAuth.OAuth.Messages; - using Validation; - - /// <summary> - /// A special channel used in test simulations to pass messages directly between two parties. - /// </summary> - internal class CoordinatingOAuthConsumerChannel : OAuthConsumerChannel { - private EventWaitHandle incomingMessageSignal = new AutoResetEvent(false); - - /// <summary> - /// Initializes a new instance of the <see cref="CoordinatingOAuthConsumerChannel"/> class. - /// </summary> - /// <param name="signingBindingElement">The signing element for the Consumer to use. Null for the Service Provider.</param> - /// <param name="tokenManager">The token manager to use.</param> - /// <param name="securitySettings">The security settings.</param> - internal CoordinatingOAuthConsumerChannel(ITamperProtectionChannelBindingElement signingBindingElement, IConsumerTokenManager tokenManager, DotNetOpenAuth.OAuth.ConsumerSecuritySettings securitySettings) - : base( - signingBindingElement, - new NonceMemoryStore(StandardExpirationBindingElement.MaximumMessageAge), - tokenManager, - securitySettings) { - } - - internal EventWaitHandle IncomingMessageSignal { - get { return this.incomingMessageSignal; } - } - - internal IProtocolMessage IncomingMessage { get; set; } - - internal OutgoingWebResponse IncomingRawResponse { get; set; } - - /// <summary> - /// Gets or sets the coordinating channel used by the other party. - /// </summary> - internal CoordinatingOAuthServiceProviderChannel RemoteChannel { get; set; } - - internal OutgoingWebResponse RequestProtectedResource(AccessProtectedResourceRequest request) { - ((ITamperResistantOAuthMessage)request).HttpMethod = this.GetHttpMethod(((ITamperResistantOAuthMessage)request).HttpMethods); - this.ProcessOutgoingMessage(request); - var requestInfo = this.SpoofHttpMethod(request); - TestBase.TestLogger.InfoFormat("Sending protected resource request: {0}", requestInfo.Message); - // Drop the outgoing message in the other channel's in-slot and let them know it's there. - this.RemoteChannel.IncomingMessage = requestInfo.Message; - this.RemoteChannel.IncomingMessageSignal.Set(); - return this.AwaitIncomingRawResponse(); - } - - protected internal override HttpRequestBase GetRequestFromContext() { - var directedMessage = (IDirectedProtocolMessage)this.AwaitIncomingMessage(); - return new CoordinatingHttpRequestInfo(directedMessage, directedMessage.HttpMethods); - } - - protected override IProtocolMessage RequestCore(IDirectedProtocolMessage request) { - var requestInfo = this.SpoofHttpMethod(request); - // Drop the outgoing message in the other channel's in-slot and let them know it's there. - this.RemoteChannel.IncomingMessage = requestInfo.Message; - this.RemoteChannel.IncomingMessageSignal.Set(); - // Now wait for a response... - return this.AwaitIncomingMessage(); - } - - protected override OutgoingWebResponse PrepareDirectResponse(IProtocolMessage response) { - this.RemoteChannel.IncomingMessage = this.CloneSerializedParts(response); - this.RemoteChannel.IncomingMessageSignal.Set(); - return new OutgoingWebResponse(); // not used, but returning null is not allowed - } - - protected override OutgoingWebResponse PrepareIndirectResponse(IDirectedProtocolMessage message) { - // In this mock transport, direct and indirect messages are the same. - return this.PrepareDirectResponse(message); - } - - protected override IDirectedProtocolMessage ReadFromRequestCore(HttpRequestBase request) { - var mockRequest = (CoordinatingHttpRequestInfo)request; - return mockRequest.Message; - } - - /// <summary> - /// Spoof HTTP request information for signing/verification purposes. - /// </summary> - /// <param name="message">The message to add a pretend HTTP method to.</param> - /// <returns>A spoofed HttpRequestInfo that wraps the new message.</returns> - private CoordinatingHttpRequestInfo SpoofHttpMethod(IDirectedProtocolMessage message) { - var signedMessage = message as ITamperResistantOAuthMessage; - if (signedMessage != null) { - string httpMethod = this.GetHttpMethod(signedMessage.HttpMethods); - signedMessage.HttpMethod = httpMethod; - } - - var requestInfo = new CoordinatingHttpRequestInfo(this.CloneSerializedParts(message), message.HttpMethods); - return requestInfo; - } - - private IProtocolMessage AwaitIncomingMessage() { - this.incomingMessageSignal.WaitOne(); - IProtocolMessage response = this.IncomingMessage; - this.IncomingMessage = null; - return response; - } - - private OutgoingWebResponse AwaitIncomingRawResponse() { - this.incomingMessageSignal.WaitOne(); - OutgoingWebResponse response = this.IncomingRawResponse; - this.IncomingRawResponse = null; - return response; - } - - private T CloneSerializedParts<T>(T message) where T : class, IProtocolMessage { - Requires.NotNull(message, "message"); - - IProtocolMessage clonedMessage; - var messageAccessor = this.MessageDescriptions.GetAccessor(message); - var fields = messageAccessor.Serialize(); - - MessageReceivingEndpoint recipient = null; - var directedMessage = message as IDirectedProtocolMessage; - var directResponse = message as IDirectResponseProtocolMessage; - if (directedMessage != null && directedMessage.IsRequest()) { - if (directedMessage.Recipient != null) { - recipient = new MessageReceivingEndpoint(directedMessage.Recipient, directedMessage.HttpMethods); - } - - clonedMessage = this.RemoteChannel.MessageFactoryTestHook.GetNewRequestMessage(recipient, fields); - } else if (directResponse != null && directResponse.IsDirectResponse()) { - clonedMessage = this.RemoteChannel.MessageFactoryTestHook.GetNewResponseMessage(directResponse.OriginatingRequest, fields); - } else { - throw new InvalidOperationException("Totally expected a message to implement one of the two derived interface types."); - } - - // Fill the cloned message with data. - var clonedMessageAccessor = this.MessageDescriptions.GetAccessor(clonedMessage); - clonedMessageAccessor.Deserialize(fields); - - return (T)clonedMessage; - } - - private string GetHttpMethod(HttpDeliveryMethods methods) { - return (methods & HttpDeliveryMethods.PostRequest) != 0 ? "POST" : "GET"; - } - } -} diff --git a/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuthServiceProviderChannel.cs b/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuthServiceProviderChannel.cs deleted file mode 100644 index a6f2a7f..0000000 --- a/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuthServiceProviderChannel.cs +++ /dev/null @@ -1,163 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="CoordinatingOAuthServiceProviderChannel.cs" company="Outercurve Foundation"> -// Copyright (c) Outercurve Foundation. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.Test.Mocks { - using System; - using System.Threading; - using System.Web; - - using DotNetOpenAuth.Messaging; - using DotNetOpenAuth.Messaging.Bindings; - using DotNetOpenAuth.OAuth.ChannelElements; - using DotNetOpenAuth.OAuth.Messages; - using NUnit.Framework; - using Validation; - - /// <summary> - /// A special channel used in test simulations to pass messages directly between two parties. - /// </summary> - internal class CoordinatingOAuthServiceProviderChannel : OAuthServiceProviderChannel { - private EventWaitHandle incomingMessageSignal = new AutoResetEvent(false); - - /// <summary> - /// Initializes a new instance of the <see cref="CoordinatingOAuthServiceProviderChannel"/> class. - /// </summary> - /// <param name="signingBindingElement">The signing element for the Consumer to use. Null for the Service Provider.</param> - /// <param name="tokenManager">The token manager to use.</param> - /// <param name="securitySettings">The security settings.</param> - internal CoordinatingOAuthServiceProviderChannel(ITamperProtectionChannelBindingElement signingBindingElement, IServiceProviderTokenManager tokenManager, DotNetOpenAuth.OAuth.ServiceProviderSecuritySettings securitySettings) - : base( - signingBindingElement, - new NonceMemoryStore(StandardExpirationBindingElement.MaximumMessageAge), - tokenManager, - securitySettings, - new OAuthServiceProviderMessageFactory(tokenManager)) { - } - - internal EventWaitHandle IncomingMessageSignal { - get { return this.incomingMessageSignal; } - } - - internal IProtocolMessage IncomingMessage { get; set; } - - internal OutgoingWebResponse IncomingRawResponse { get; set; } - - /// <summary> - /// Gets or sets the coordinating channel used by the other party. - /// </summary> - internal CoordinatingOAuthConsumerChannel RemoteChannel { get; set; } - - internal OutgoingWebResponse RequestProtectedResource(AccessProtectedResourceRequest request) { - ((ITamperResistantOAuthMessage)request).HttpMethod = GetHttpMethod(((ITamperResistantOAuthMessage)request).HttpMethods); - this.ProcessOutgoingMessage(request); - var requestInfo = this.SpoofHttpMethod(request); - TestBase.TestLogger.InfoFormat("Sending protected resource request: {0}", requestInfo.Message); - // Drop the outgoing message in the other channel's in-slot and let them know it's there. - this.RemoteChannel.IncomingMessage = requestInfo.Message; - this.RemoteChannel.IncomingMessageSignal.Set(); - return this.AwaitIncomingRawResponse(); - } - - internal void SendDirectRawResponse(OutgoingWebResponse response) { - this.RemoteChannel.IncomingRawResponse = response; - this.RemoteChannel.IncomingMessageSignal.Set(); - } - - protected internal override HttpRequestBase GetRequestFromContext() { - var directedMessage = (IDirectedProtocolMessage)this.AwaitIncomingMessage(); - return new CoordinatingHttpRequestInfo(directedMessage, directedMessage.HttpMethods); - } - - protected override IProtocolMessage RequestCore(IDirectedProtocolMessage request) { - var requestInfo = this.SpoofHttpMethod(request); - // Drop the outgoing message in the other channel's in-slot and let them know it's there. - this.RemoteChannel.IncomingMessage = requestInfo.Message; - this.RemoteChannel.IncomingMessageSignal.Set(); - // Now wait for a response... - return this.AwaitIncomingMessage(); - } - - protected override OutgoingWebResponse PrepareDirectResponse(IProtocolMessage response) { - this.RemoteChannel.IncomingMessage = this.CloneSerializedParts(response); - this.RemoteChannel.IncomingMessageSignal.Set(); - return new OutgoingWebResponse(); // not used, but returning null is not allowed - } - - protected override OutgoingWebResponse PrepareIndirectResponse(IDirectedProtocolMessage message) { - // In this mock transport, direct and indirect messages are the same. - return this.PrepareDirectResponse(message); - } - - protected override IDirectedProtocolMessage ReadFromRequestCore(HttpRequestBase request) { - var mockRequest = (CoordinatingHttpRequestInfo)request; - return mockRequest.Message; - } - - private static string GetHttpMethod(HttpDeliveryMethods methods) { - return (methods & HttpDeliveryMethods.PostRequest) != 0 ? "POST" : "GET"; - } - - /// <summary> - /// Spoof HTTP request information for signing/verification purposes. - /// </summary> - /// <param name="message">The message to add a pretend HTTP method to.</param> - /// <returns>A spoofed HttpRequestInfo that wraps the new message.</returns> - private CoordinatingHttpRequestInfo SpoofHttpMethod(IDirectedProtocolMessage message) { - var signedMessage = message as ITamperResistantOAuthMessage; - if (signedMessage != null) { - string httpMethod = GetHttpMethod(signedMessage.HttpMethods); - signedMessage.HttpMethod = httpMethod; - } - - var requestInfo = new CoordinatingHttpRequestInfo(this.CloneSerializedParts(message), message.HttpMethods); - return requestInfo; - } - - private IProtocolMessage AwaitIncomingMessage() { - this.IncomingMessageSignal.WaitOne(); - Assert.That(this.IncomingMessage, Is.Not.Null, "Incoming message signaled, but none supplied."); - IProtocolMessage response = this.IncomingMessage; - this.IncomingMessage = null; - return response; - } - - private OutgoingWebResponse AwaitIncomingRawResponse() { - this.IncomingMessageSignal.WaitOne(); - OutgoingWebResponse response = this.IncomingRawResponse; - this.IncomingRawResponse = null; - return response; - } - - private T CloneSerializedParts<T>(T message) where T : class, IProtocolMessage { - Requires.NotNull(message, "message"); - - IProtocolMessage clonedMessage; - var messageAccessor = this.MessageDescriptions.GetAccessor(message); - var fields = messageAccessor.Serialize(); - - MessageReceivingEndpoint recipient = null; - var directedMessage = message as IDirectedProtocolMessage; - var directResponse = message as IDirectResponseProtocolMessage; - if (directedMessage != null && directedMessage.IsRequest()) { - if (directedMessage.Recipient != null) { - recipient = new MessageReceivingEndpoint(directedMessage.Recipient, directedMessage.HttpMethods); - } - - clonedMessage = this.RemoteChannel.MessageFactoryTestHook.GetNewRequestMessage(recipient, fields); - } else if (directResponse != null && directResponse.IsDirectResponse()) { - clonedMessage = this.RemoteChannel.MessageFactoryTestHook.GetNewResponseMessage(directResponse.OriginatingRequest, fields); - } else { - throw new InvalidOperationException("Totally expected a message to implement one of the two derived interface types."); - } - - // Fill the cloned message with data. - var clonedMessageAccessor = this.MessageDescriptions.GetAccessor(clonedMessage); - clonedMessageAccessor.Deserialize(fields); - - return (T)clonedMessage; - } - } -} diff --git a/src/DotNetOpenAuth.Test/Mocks/CoordinatingOutgoingWebResponse.cs b/src/DotNetOpenAuth.Test/Mocks/CoordinatingOutgoingWebResponse.cs deleted file mode 100644 index 9df791c..0000000 --- a/src/DotNetOpenAuth.Test/Mocks/CoordinatingOutgoingWebResponse.cs +++ /dev/null @@ -1,47 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="CoordinatingOutgoingWebResponse.cs" company="Outercurve Foundation"> -// Copyright (c) Outercurve Foundation. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.Test.Mocks { - using System; - using System.Collections.Generic; - using System.ComponentModel; - using System.Linq; - using System.Text; - using DotNetOpenAuth.Messaging; - using Validation; - - internal class CoordinatingOutgoingWebResponse : OutgoingWebResponse { - private CoordinatingChannel receivingChannel; - - private CoordinatingChannel sendingChannel; - - /// <summary> - /// Initializes a new instance of the <see cref="CoordinatingOutgoingWebResponse"/> class. - /// </summary> - /// <param name="message">The direct response message to send to the remote channel. This message will be cloned.</param> - /// <param name="receivingChannel">The receiving channel.</param> - /// <param name="sendingChannel">The sending channel.</param> - internal CoordinatingOutgoingWebResponse(IProtocolMessage message, CoordinatingChannel receivingChannel, CoordinatingChannel sendingChannel) { - Requires.NotNull(message, "message"); - Requires.NotNull(receivingChannel, "receivingChannel"); - Requires.NotNull(sendingChannel, "sendingChannel"); - - this.receivingChannel = receivingChannel; - this.sendingChannel = sendingChannel; - this.OriginalMessage = message; - } - - [EditorBrowsable(EditorBrowsableState.Never)] - public override void Send() { - this.Respond(); - } - - public override void Respond() { - this.sendingChannel.SaveCookies(this.Cookies); - this.receivingChannel.PostMessage(this.OriginalMessage); - } - } -} diff --git a/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs b/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs index 494a1c1..7fac125 100644 --- a/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs +++ b/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs @@ -14,7 +14,7 @@ namespace DotNetOpenAuth.Test.Mocks { using DotNetOpenAuth.OAuth.Messages; using DotNetOpenAuth.Test.OAuth; - internal class InMemoryTokenManager : IConsumerTokenManager, IServiceProviderTokenManager { + internal class InMemoryTokenManager : IServiceProviderTokenManager { private KeyedCollectionDelegate<string, ConsumerInfo> consumers = new KeyedCollectionDelegate<string, ConsumerInfo>(c => c.Key); private KeyedCollectionDelegate<string, TokenInfo> tokens = new KeyedCollectionDelegate<string, TokenInfo>(t => t.Token); @@ -28,18 +28,6 @@ namespace DotNetOpenAuth.Test.Mocks { /// </summary> private List<string> accessTokens = new List<string>(); - #region IConsumerTokenManager Members - - public string ConsumerKey { - get { return this.consumers.Single().Key; } - } - - public string ConsumerSecret { - get { return this.consumers.Single().Secret; } - } - - #endregion - #region ITokenManager Members public string GetTokenSecret(string token) { diff --git a/src/DotNetOpenAuth.Test/Mocks/MockIdentifierDiscoveryService.cs b/src/DotNetOpenAuth.Test/Mocks/MockIdentifierDiscoveryService.cs index 0118851..3fd7517 100644 --- a/src/DotNetOpenAuth.Test/Mocks/MockIdentifierDiscoveryService.cs +++ b/src/DotNetOpenAuth.Test/Mocks/MockIdentifierDiscoveryService.cs @@ -9,6 +9,7 @@ namespace DotNetOpenAuth.Test.Mocks { using System.Collections.Generic; using System.Linq; using System.Text; + using System.Threading.Tasks; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.RelyingParty; @@ -26,20 +27,17 @@ namespace DotNetOpenAuth.Test.Mocks { /// Performs discovery on the specified identifier. /// </summary> /// <param name="identifier">The identifier to perform discovery on.</param> - /// <param name="requestHandler">The means to place outgoing HTTP requests.</param> - /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns> /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty. /// </returns> - public IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain) { + public Task<IdentifierDiscoveryServiceResult> DiscoverAsync(Identifier identifier, System.Threading.CancellationToken cancellationToken) { var mockIdentifier = identifier as MockIdentifier; if (mockIdentifier == null) { - abortDiscoveryChain = false; - return Enumerable.Empty<IdentifierDiscoveryResult>(); + return Task.FromResult(new IdentifierDiscoveryServiceResult(Enumerable.Empty<IdentifierDiscoveryResult>(), abortDiscoveryChain: false)); } - abortDiscoveryChain = true; - return mockIdentifier.DiscoveryEndpoints; + return Task.FromResult(new IdentifierDiscoveryServiceResult(mockIdentifier.DiscoveryEndpoints, abortDiscoveryChain: true)); } #endregion diff --git a/src/DotNetOpenAuth.Test/Mocks/MockRealm.cs b/src/DotNetOpenAuth.Test/Mocks/MockRealm.cs index 8509c03..38f9daf 100644 --- a/src/DotNetOpenAuth.Test/Mocks/MockRealm.cs +++ b/src/DotNetOpenAuth.Test/Mocks/MockRealm.cs @@ -7,6 +7,9 @@ namespace DotNetOpenAuth.Test.Mocks { using System; using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId; using Validation; @@ -30,15 +33,16 @@ namespace DotNetOpenAuth.Test.Mocks { /// Searches for an XRDS document at the realm URL, and if found, searches /// for a description of a relying party endpoints (OpenId login pages). /// </summary> - /// <param name="requestHandler">The mechanism to use for sending HTTP requests.</param> + /// <param name="hostFactories">The host factories.</param> /// <param name="allowRedirects">Whether redirects may be followed when discovering the Realm. /// This may be true when creating an unsolicited assertion, but must be /// false when performing return URL verification per 2.0 spec section 9.2.1.</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns> /// The details of the endpoints if found, otherwise null. /// </returns> - internal override IEnumerable<RelyingPartyEndpointDescription> DiscoverReturnToEndpoints(IDirectWebRequestHandler requestHandler, bool allowRedirects) { - return this.relyingPartyDescriptions; + internal override Task<IEnumerable<RelyingPartyEndpointDescription>> DiscoverReturnToEndpointsAsync(IHostFactories hostFactories, bool allowRedirects, CancellationToken cancellationToken) { + return Task.FromResult<IEnumerable<RelyingPartyEndpointDescription>>(this.relyingPartyDescriptions); } } } diff --git a/src/DotNetOpenAuth.Test/Mocks/MockReplayProtectionBindingElement.cs b/src/DotNetOpenAuth.Test/Mocks/MockReplayProtectionBindingElement.cs index 1733f17..58a2367 100644 --- a/src/DotNetOpenAuth.Test/Mocks/MockReplayProtectionBindingElement.cs +++ b/src/DotNetOpenAuth.Test/Mocks/MockReplayProtectionBindingElement.cs @@ -5,6 +5,9 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.Test.Mocks { + using System.Threading; + using System.Threading.Tasks; + using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; using NUnit.Framework; @@ -23,17 +26,17 @@ namespace DotNetOpenAuth.Test.Mocks { /// </summary> public Channel Channel { get; set; } - MessageProtections? IChannelBindingElement.ProcessOutgoingMessage(IProtocolMessage message) { + Task<MessageProtections?> IChannelBindingElement.ProcessOutgoingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { var replayMessage = message as IReplayProtectedProtocolMessage; if (replayMessage != null) { replayMessage.Nonce = "someNonce"; - return MessageProtections.ReplayProtection; + return MessageProtectionTasks.ReplayProtection; } - return null; + return MessageProtectionTasks.Null; } - MessageProtections? IChannelBindingElement.ProcessIncomingMessage(IProtocolMessage message) { + Task<MessageProtections?> IChannelBindingElement.ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { var replayMessage = message as IReplayProtectedProtocolMessage; if (replayMessage != null) { Assert.AreEqual("someNonce", replayMessage.Nonce, "The nonce didn't serialize correctly, or something"); @@ -42,10 +45,10 @@ namespace DotNetOpenAuth.Test.Mocks { throw new ReplayedMessageException(message); } this.messageReceived = true; - return MessageProtections.ReplayProtection; + return MessageProtectionTasks.ReplayProtection; } - return null; + return MessageProtectionTasks.Null; } #endregion diff --git a/src/DotNetOpenAuth.Test/Mocks/MockSigningBindingElement.cs b/src/DotNetOpenAuth.Test/Mocks/MockSigningBindingElement.cs index aa68b0b..f0b2bfc 100644 --- a/src/DotNetOpenAuth.Test/Mocks/MockSigningBindingElement.cs +++ b/src/DotNetOpenAuth.Test/Mocks/MockSigningBindingElement.cs @@ -9,6 +9,9 @@ namespace DotNetOpenAuth.Test.Mocks { using System.Collections.Generic; using System.Linq; using System.Text; + using System.Threading; + using System.Threading.Tasks; + using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; @@ -26,26 +29,26 @@ namespace DotNetOpenAuth.Test.Mocks { /// </summary> public Channel Channel { get; set; } - MessageProtections? IChannelBindingElement.ProcessOutgoingMessage(IProtocolMessage message) { - ITamperResistantProtocolMessage signedMessage = message as ITamperResistantProtocolMessage; + Task<MessageProtections?> IChannelBindingElement.ProcessOutgoingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { + var signedMessage = message as ITamperResistantProtocolMessage; if (signedMessage != null) { signedMessage.Signature = MessageSignature; - return MessageProtections.TamperProtection; + return MessageProtectionTasks.TamperProtection; } - return null; + return MessageProtectionTasks.Null; } - MessageProtections? IChannelBindingElement.ProcessIncomingMessage(IProtocolMessage message) { - ITamperResistantProtocolMessage signedMessage = message as ITamperResistantProtocolMessage; + Task<MessageProtections?> IChannelBindingElement.ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { + var signedMessage = message as ITamperResistantProtocolMessage; if (signedMessage != null) { if (signedMessage.Signature != MessageSignature) { throw new InvalidSignatureException(message); } - return MessageProtections.TamperProtection; + return MessageProtectionTasks.TamperProtection; } - return null; + return MessageProtectionTasks.Null; } #endregion diff --git a/src/DotNetOpenAuth.Test/Mocks/MockTransformationBindingElement.cs b/src/DotNetOpenAuth.Test/Mocks/MockTransformationBindingElement.cs index 2b3249f..35d7f1b 100644 --- a/src/DotNetOpenAuth.Test/Mocks/MockTransformationBindingElement.cs +++ b/src/DotNetOpenAuth.Test/Mocks/MockTransformationBindingElement.cs @@ -9,11 +9,14 @@ namespace DotNetOpenAuth.Test.Mocks { using System.Collections.Generic; using System.Linq; using System.Text; + using System.Threading; + using System.Threading.Tasks; + using DotNetOpenAuth.Messaging; using NUnit.Framework; internal class MockTransformationBindingElement : IChannelBindingElement { - private string transform; + private readonly string transform; internal MockTransformationBindingElement(string transform) { if (transform == null) { @@ -34,25 +37,25 @@ namespace DotNetOpenAuth.Test.Mocks { /// </summary> public Channel Channel { get; set; } - MessageProtections? IChannelBindingElement.ProcessOutgoingMessage(IProtocolMessage message) { + Task<MessageProtections?> IChannelBindingElement.ProcessOutgoingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { var testMessage = message as TestMessage; if (testMessage != null) { testMessage.Name = this.transform + testMessage.Name; - return MessageProtections.None; + return MessageProtectionTasks.None; } - return null; + return MessageProtectionTasks.Null; } - MessageProtections? IChannelBindingElement.ProcessIncomingMessage(IProtocolMessage message) { + Task<MessageProtections?> IChannelBindingElement.ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { var testMessage = message as TestMessage; if (testMessage != null) { StringAssert.StartsWith(this.transform, testMessage.Name); testMessage.Name = testMessage.Name.Substring(this.transform.Length); - return MessageProtections.None; + return MessageProtectionTasks.None; } - return null; + return MessageProtectionTasks.Null; } #endregion diff --git a/src/DotNetOpenAuth.Test/Mocks/TestBadChannel.cs b/src/DotNetOpenAuth.Test/Mocks/TestBadChannel.cs index 263f0fd..2a8e7b9 100644 --- a/src/DotNetOpenAuth.Test/Mocks/TestBadChannel.cs +++ b/src/DotNetOpenAuth.Test/Mocks/TestBadChannel.cs @@ -7,6 +7,9 @@ namespace DotNetOpenAuth.Test.Mocks { using System; using System.Collections.Generic; + using System.Net.Http; + using System.Threading; + using System.Threading.Tasks; using System.Web; using DotNetOpenAuth.Messaging; @@ -34,15 +37,15 @@ namespace DotNetOpenAuth.Test.Mocks { return base.Receive(fields, recipient); } - internal new IProtocolMessage ReadFromRequest(HttpRequestBase request) { - return base.ReadFromRequest(request); + internal new Task<IDirectedProtocolMessage> ReadFromRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + return base.ReadFromRequestAsync(request, cancellationToken); } - protected override IDictionary<string, string> ReadFromResponseCore(IncomingWebResponse response) { + protected override Task<IDictionary<string, string>> ReadFromResponseCoreAsync(HttpResponseMessage response, CancellationToken cancellationToken) { throw new NotImplementedException(); } - protected override OutgoingWebResponse PrepareDirectResponse(IProtocolMessage response) { + protected override HttpResponseMessage PrepareDirectResponse(IProtocolMessage response) { throw new NotImplementedException(); } } diff --git a/src/DotNetOpenAuth.Test/OAuth/AppendixScenarios.cs b/src/DotNetOpenAuth.Test/OAuth/AppendixScenarios.cs index a295732..88e461c 100644 --- a/src/DotNetOpenAuth.Test/OAuth/AppendixScenarios.cs +++ b/src/DotNetOpenAuth.Test/OAuth/AppendixScenarios.cs @@ -8,6 +8,9 @@ namespace DotNetOpenAuth.Test.OAuth { using System; using System.IO; using System.Net; + using System.Net.Http; + using System.Net.Http.Headers; + using System.Threading.Tasks; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth; using DotNetOpenAuth.OAuth.ChannelElements; @@ -17,50 +20,79 @@ namespace DotNetOpenAuth.Test.OAuth { [TestFixture] public class AppendixScenarios : TestBase { [Test] - public void SpecAppendixAExample() { - ServiceProviderDescription serviceDescription = new ServiceProviderDescription() { - RequestTokenEndpoint = new MessageReceivingEndpoint("https://photos.example.net/request_token", HttpDeliveryMethods.PostRequest), - UserAuthorizationEndpoint = new MessageReceivingEndpoint("http://photos.example.net/authorize", HttpDeliveryMethods.GetRequest), - AccessTokenEndpoint = new MessageReceivingEndpoint("https://photos.example.net/access_token", HttpDeliveryMethods.PostRequest), - TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { - new PlaintextSigningBindingElement(), - new HmacSha1SigningBindingElement(), - }, + public async Task SpecAppendixAExample() { + var serviceDescription = new ServiceProviderDescription( + "https://photos.example.net/request_token", + "http://photos.example.net/authorize", + "https://photos.example.net/access_token"); + var serviceHostDescription = new ServiceProviderHostDescription { + RequestTokenEndpoint = new MessageReceivingEndpoint(serviceDescription.TemporaryCredentialsRequestEndpoint, HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest), + UserAuthorizationEndpoint = new MessageReceivingEndpoint(serviceDescription.ResourceOwnerAuthorizationEndpoint, HttpDeliveryMethods.GetRequest), + AccessTokenEndpoint = new MessageReceivingEndpoint(serviceDescription.TokenRequestEndpoint, HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest), + TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement(), }, }; - MessageReceivingEndpoint accessPhotoEndpoint = new MessageReceivingEndpoint("http://photos.example.net/photos?file=vacation.jpg&size=original", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest); - ConsumerDescription consumerDescription = new ConsumerDescription("dpf43f3p2l4k3l03", "kd94hf93k423kf44"); + var accessPhotoEndpoint = new Uri("http://photos.example.net/photos?file=vacation.jpg&size=original"); + var consumerDescription = new ConsumerDescription("dpf43f3p2l4k3l03", "kd94hf93k423kf44"); + + var tokenManager = new InMemoryTokenManager(); + tokenManager.AddConsumer(consumerDescription); + var sp = new ServiceProvider(serviceHostDescription, tokenManager); + + var coordinator = new CoordinatorBase( + async (hostFactories, ct) => { + var consumer = new Consumer( + consumerDescription.ConsumerKey, + consumerDescription.ConsumerSecret, + serviceDescription, + new MemoryTemporaryCredentialStorage()); + consumer.HostFactories = hostFactories; + var authorizeUrl = await consumer.RequestUserAuthorizationAsync(new Uri("http://printer.example.com/request_token_ready")); + Uri authorizeResponseUri; + using (var httpClient = hostFactories.CreateHttpClient()) { + using (var response = await httpClient.GetAsync(authorizeUrl, ct)) { + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Redirect)); + authorizeResponseUri = response.Headers.Location; + } + } + + var accessTokenResponse = await consumer.ProcessUserAuthorizationAsync(authorizeResponseUri, ct); + Assert.That(accessTokenResponse, Is.Not.Null); - OAuthCoordinator coordinator = new OAuthCoordinator( - consumerDescription, - serviceDescription, - consumer => { - consumer.Channel.PrepareResponse(consumer.PrepareRequestUserAuthorization(new Uri("http://printer.example.com/request_token_ready"), null, null)); // .Send() dropped because this is just a simulation - string accessToken = consumer.ProcessUserAuthorization().AccessToken; - var photoRequest = consumer.CreateAuthorizingMessage(accessPhotoEndpoint, accessToken); - OutgoingWebResponse protectedPhoto = ((CoordinatingOAuthConsumerChannel)consumer.Channel).RequestProtectedResource(photoRequest); - Assert.IsNotNull(protectedPhoto); - Assert.AreEqual(HttpStatusCode.OK, protectedPhoto.Status); - Assert.AreEqual("image/jpeg", protectedPhoto.Headers[HttpResponseHeader.ContentType]); - Assert.AreNotEqual(0, protectedPhoto.ResponseStream.Length); + using (var authorizingClient = consumer.CreateHttpClient(accessTokenResponse.AccessToken)) { + using (var protectedPhoto = await authorizingClient.GetAsync(accessPhotoEndpoint, ct)) { + Assert.That(protectedPhoto, Is.Not.Null); + protectedPhoto.EnsureSuccessStatusCode(); + Assert.That("image/jpeg", Is.EqualTo(protectedPhoto.Content.Headers.ContentType.MediaType)); + Assert.That(protectedPhoto.Content.Headers.ContentLength, Is.Not.EqualTo(0)); + } + } }, - sp => { - var requestTokenMessage = sp.ReadTokenRequest(); - sp.Channel.PrepareResponse(sp.PrepareUnauthorizedTokenMessage(requestTokenMessage)); // .Send() dropped because this is just a simulation - var authRequest = sp.ReadAuthorizationRequest(); - ((InMemoryTokenManager)sp.TokenManager).AuthorizeRequestToken(authRequest.RequestToken); - sp.Channel.PrepareResponse(sp.PrepareAuthorizationResponse(authRequest)); // .Send() dropped because this is just a simulation - var accessRequest = sp.ReadAccessTokenRequest(); - sp.Channel.PrepareResponse(sp.PrepareAccessTokenMessage(accessRequest)); // .Send() dropped because this is just a simulation - string accessToken = sp.ReadProtectedResourceAuthorization().AccessToken; - ((CoordinatingOAuthServiceProviderChannel)sp.Channel).SendDirectRawResponse(new OutgoingWebResponse { - ResponseStream = new MemoryStream(new byte[] { 0x33, 0x66 }), - Headers = new WebHeaderCollection { - { HttpResponseHeader.ContentType, "image/jpeg" }, - }, - }); - }); + CoordinatorBase.Handle(serviceDescription.TemporaryCredentialsRequestEndpoint).By( + async (request, ct) => { + var requestTokenMessage = await sp.ReadTokenRequestAsync(request, ct); + return await sp.Channel.PrepareResponseAsync(sp.PrepareUnauthorizedTokenMessage(requestTokenMessage)); + }), + CoordinatorBase.Handle(serviceDescription.ResourceOwnerAuthorizationEndpoint).By( + async (request, ct) => { + var authRequest = await sp.ReadAuthorizationRequestAsync(request, ct); + ((InMemoryTokenManager)sp.TokenManager).AuthorizeRequestToken(authRequest.RequestToken); + return await sp.Channel.PrepareResponseAsync(sp.PrepareAuthorizationResponse(authRequest)); + }), + CoordinatorBase.Handle(serviceDescription.TokenRequestEndpoint).By( + async (request, ct) => { + var accessRequest = await sp.ReadAccessTokenRequestAsync(request, ct); + return await sp.Channel.PrepareResponseAsync(sp.PrepareAccessTokenMessage(accessRequest), ct); + }), + CoordinatorBase.Handle(accessPhotoEndpoint).By( + async (request, ct) => { + string accessToken = (await sp.ReadProtectedResourceAuthorizationAsync(request)).AccessToken; + Assert.That(accessToken, Is.Not.Null.And.Not.Empty); + var responseMessage = new HttpResponseMessage { Content = new ByteArrayContent(new byte[] { 0x33, 0x66 }), }; + responseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg"); + return responseMessage; + })); - coordinator.Run(); + await coordinator.RunAsync(); } } } diff --git a/src/DotNetOpenAuth.Test/OAuth/ConsumerDescription.cs b/src/DotNetOpenAuth.Test/OAuth/ConsumerDescription.cs index 74752f8..5c25d30 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ConsumerDescription.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ConsumerDescription.cs @@ -5,6 +5,8 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.Test.OAuth { + using DotNetOpenAuth.OAuth; + /// <summary> /// Information necessary to initialize a <see cref="Consumer"/>, /// and to tell a <see cref="ServiceProvider"/> about it. diff --git a/src/DotNetOpenAuth.Test/OAuth/OAuthCoordinator.cs b/src/DotNetOpenAuth.Test/OAuth/OAuthCoordinator.cs deleted file mode 100644 index 21c1775..0000000 --- a/src/DotNetOpenAuth.Test/OAuth/OAuthCoordinator.cs +++ /dev/null @@ -1,71 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="OAuthCoordinator.cs" company="Outercurve Foundation"> -// Copyright (c) Outercurve Foundation. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.Test.OAuth { - using System; - using DotNetOpenAuth.Messaging; - using DotNetOpenAuth.Messaging.Bindings; - using DotNetOpenAuth.OAuth; - using DotNetOpenAuth.OAuth.ChannelElements; - using DotNetOpenAuth.Test.Mocks; - using Validation; - - /// <summary> - /// Runs a Consumer and Service Provider simultaneously so they can interact in a full simulation. - /// </summary> - internal class OAuthCoordinator : CoordinatorBase<WebConsumer, ServiceProvider> { - private ConsumerDescription consumerDescription; - private ServiceProviderDescription serviceDescription; - private DotNetOpenAuth.OAuth.ServiceProviderSecuritySettings serviceProviderSecuritySettings = DotNetOpenAuth.Configuration.OAuthElement.Configuration.ServiceProvider.SecuritySettings.CreateSecuritySettings(); - private DotNetOpenAuth.OAuth.ConsumerSecuritySettings consumerSecuritySettings = DotNetOpenAuth.Configuration.OAuthElement.Configuration.Consumer.SecuritySettings.CreateSecuritySettings(); - - /// <summary>Initializes a new instance of the <see cref="OAuthCoordinator"/> class.</summary> - /// <param name="consumerDescription">The description of the consumer.</param> - /// <param name="serviceDescription">The service description that will be used to construct the Consumer and ServiceProvider objects.</param> - /// <param name="consumerAction">The code path of the Consumer.</param> - /// <param name="serviceProviderAction">The code path of the Service Provider.</param> - internal OAuthCoordinator(ConsumerDescription consumerDescription, ServiceProviderDescription serviceDescription, Action<WebConsumer> consumerAction, Action<ServiceProvider> serviceProviderAction) - : base(consumerAction, serviceProviderAction) { - Requires.NotNull(consumerDescription, "consumerDescription"); - Requires.NotNull(serviceDescription, "serviceDescription"); - - this.consumerDescription = consumerDescription; - this.serviceDescription = serviceDescription; - } - - /// <summary> - /// Starts the simulation. - /// </summary> - internal override void Run() { - // Clone the template signing binding element. - var signingElement = this.serviceDescription.CreateTamperProtectionElement(); - var consumerSigningElement = signingElement.Clone(); - var spSigningElement = signingElement.Clone(); - - // Prepare token managers - InMemoryTokenManager consumerTokenManager = new InMemoryTokenManager(); - InMemoryTokenManager serviceTokenManager = new InMemoryTokenManager(); - consumerTokenManager.AddConsumer(this.consumerDescription); - serviceTokenManager.AddConsumer(this.consumerDescription); - - // Prepare channels that will pass messages directly back and forth. - var consumerChannel = new CoordinatingOAuthConsumerChannel(consumerSigningElement, (IConsumerTokenManager)consumerTokenManager, this.consumerSecuritySettings); - var serviceProviderChannel = new CoordinatingOAuthServiceProviderChannel(spSigningElement, (IServiceProviderTokenManager)serviceTokenManager, this.serviceProviderSecuritySettings); - consumerChannel.RemoteChannel = serviceProviderChannel; - serviceProviderChannel.RemoteChannel = consumerChannel; - - // Prepare the Consumer and Service Provider objects - WebConsumer consumer = new WebConsumer(this.serviceDescription, consumerTokenManager) { - OAuthChannel = consumerChannel, - }; - ServiceProvider serviceProvider = new ServiceProvider(this.serviceDescription, serviceTokenManager, new NonceMemoryStore()) { - OAuthChannel = serviceProviderChannel, - }; - - this.RunCore(consumer, serviceProvider); - } - } -} diff --git a/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs b/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs index 14bcaec..acb37fc 100644 --- a/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs @@ -178,7 +178,7 @@ namespace DotNetOpenAuth.Test.OpenId { // When the RP notices the replay we get a specific ReplayMessageException. try { CoordinatingChannel channel = (CoordinatingChannel)rp.Channel; - channel.Replay(response); + await channel.ReplayAsync(response); Assert.Fail("Expected ProtocolException was not thrown."); } catch (ProtocolException ex) { Assert.IsTrue(ex is ReplayedMessageException || ex is InvalidSignatureException, "A {0} exception was thrown instead of the expected {1} or {2}.", ex.GetType(), typeof(ReplayedMessageException).Name, typeof(InvalidSignatureException).Name); |