diff --git a/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer.UnitTests/ServiceBusTests/ContentTests.cs b/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer.UnitTests/HttpTests/ContentTests.cs similarity index 99% rename from microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer.UnitTests/ServiceBusTests/ContentTests.cs rename to microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer.UnitTests/HttpTests/ContentTests.cs index cb22569ae4ea..5976e0e224fd 100644 --- a/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer.UnitTests/ServiceBusTests/ContentTests.cs +++ b/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer.UnitTests/HttpTests/ContentTests.cs @@ -23,7 +23,7 @@ using Windows.Storage.Streams; using Xunit; -namespace Microsoft.WindowsAzure.ServiceLayer.UnitTests.ServiceBusTests +namespace Microsoft.WindowsAzure.ServiceLayer.UnitTests.HttpTests { /// /// Tests for the Content class. diff --git a/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer.UnitTests/HttpTests/PipelineTests.cs b/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer.UnitTests/HttpTests/PipelineTests.cs new file mode 100644 index 000000000000..3ad3a891e925 --- /dev/null +++ b/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer.UnitTests/HttpTests/PipelineTests.cs @@ -0,0 +1,53 @@ +// +// Copyright 2012 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.WindowsAzure.ServiceLayer.Http; +using Xunit; + +namespace Microsoft.WindowsAzure.ServiceLayer.UnitTests.HttpTests +{ + /// + /// HTTP pipeline tests. + /// + public class PipelineTests + { + /// + /// Tests passing invalid arguments in the request's constructor. + /// + [Fact] + public void InvalidArgsInRequestConstructor() + { + Uri validUri = new Uri("http://microsoft.com"); + Assert.Throws(() => new HttpRequest(null, validUri)); + Assert.Throws(() => new HttpRequest("PUT", null)); + } + + /// + /// Tests passing invalid arguments in the response's constructor. + /// + [Fact] + public void InvalidArgsInResponseConstructor() + { + HttpRequest validRequest = new HttpRequest("PUT", new Uri("http://microsoft.com")); + Assert.Throws(() => new HttpResponse(null, 200)); + Assert.Throws(() => new HttpResponse(validRequest, -5)); + } + } +} diff --git a/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer.UnitTests/Microsoft.WindowsAzure.ServiceLayer.UnitTests.csproj b/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer.UnitTests/Microsoft.WindowsAzure.ServiceLayer.UnitTests.csproj index 36ee264b446b..b420e14f3cbb 100644 --- a/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer.UnitTests/Microsoft.WindowsAzure.ServiceLayer.UnitTests.csproj +++ b/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer.UnitTests/Microsoft.WindowsAzure.ServiceLayer.UnitTests.csproj @@ -113,9 +113,10 @@ + - + @@ -134,6 +135,7 @@ References\xunit.dll + 11.0 diff --git a/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/Http/HttpResponse.cs b/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/Http/HttpResponse.cs new file mode 100644 index 000000000000..5fb65949c7da --- /dev/null +++ b/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/Http/HttpResponse.cs @@ -0,0 +1,105 @@ +// +// Copyright 2012 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using NetHttpResponseMessage = System.Net.Http.HttpResponseMessage; + +namespace Microsoft.WindowsAzure.ServiceLayer.Http +{ + /// + /// Represents an HTTP response message. + /// server. + /// + public sealed class HttpResponse + { + /// + /// Gets the request that lead to this response. + /// + public HttpRequest Request { get; private set; } + + /// + /// Gets the status code of the HTTP response. + /// + public int StatusCode { get; set; } + + /// + /// Tells whether the HTTP response was successful. + /// + public bool IsSuccessStatusCode { get { return StatusCode >= 200 && StatusCode < 299; } } + + /// + /// Gets or sets the reason phrase which typically is sent by servers + /// together with the status code. + /// + public string ReasonPhrase { get; set; } + + /// + /// Gets or sets the content of the response. + /// + public HttpContent Content { get; set; } + + /// + /// Gets the collection of HTTP response headers. + /// + public IDictionary Headers { get; private set; } + + /// + /// Initializes the response object. + /// + /// Request that initiated the response. + /// Status code of the HTTP response. + public HttpResponse(HttpRequest originalRequest, int statusCode) + { + if (originalRequest == null) + { + throw new ArgumentNullException("originalRequest"); + } + if (!Enum.IsDefined(typeof(System.Net.HttpStatusCode), statusCode)) + { + throw new ArgumentOutOfRangeException("statusCode"); + } + + Request = originalRequest; + StatusCode = statusCode; + Headers = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + /// + /// Initializes the response object. + /// + /// Request that initiated the response. + /// .Net HTTP response. + internal HttpResponse(HttpRequest originalRequest, NetHttpResponseMessage response) + { + Debug.Assert(originalRequest != null); + Debug.Assert(response != null); + + Request = originalRequest; + StatusCode = (int)response.StatusCode; + ReasonPhrase = response.ReasonPhrase; + Content = HttpContent.CreateFromResponse(response); + Headers = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (KeyValuePair> item in response.Headers) + { + string valueString = string.Join(string.Empty, item.Value); + Headers.Add(item.Key, valueString); + } + } + } +} diff --git a/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/Microsoft.WindowsAzure.ServiceLayer.csproj b/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/Microsoft.WindowsAzure.ServiceLayer.csproj index 2a897c911219..cdf376957496 100644 --- a/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/Microsoft.WindowsAzure.ServiceLayer.csproj +++ b/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/Microsoft.WindowsAzure.ServiceLayer.csproj @@ -108,6 +108,7 @@ + diff --git a/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/ServiceBus/BrokeredMessageInfo.cs b/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/ServiceBus/BrokeredMessageInfo.cs index 61764514361f..27017abeaac5 100644 --- a/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/ServiceBus/BrokeredMessageInfo.cs +++ b/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/ServiceBus/BrokeredMessageInfo.cs @@ -25,8 +25,6 @@ using Windows.Foundation; using Windows.Storage.Streams; -using NetHttpResponseMessage = System.Net.Http.HttpResponseMessage; - namespace Microsoft.WindowsAzure.ServiceLayer.ServiceBus { /// @@ -231,9 +229,9 @@ public IAsyncInfo CopyContentToAsync(IOutputStream stream) /// /// Response with data. /// - internal static BrokeredMessageInfo CreateFromPeekResponse(NetHttpResponseMessage response) + internal static BrokeredMessageInfo CreateFromPeekResponse(HttpResponse response) { - if (response.StatusCode == System.Net.HttpStatusCode.NoContent || response.StatusCode == System.Net.HttpStatusCode.ResetContent) + if (response.StatusCode == (int)System.Net.HttpStatusCode.NoContent || response.StatusCode == (int)System.Net.HttpStatusCode.ResetContent) { return null; } @@ -244,21 +242,16 @@ internal static BrokeredMessageInfo CreateFromPeekResponse(NetHttpResponseMessag /// Constructor. Initializes the object from the HTTP response. /// /// HTTP reponse with the data. - internal BrokeredMessageInfo(NetHttpResponseMessage response) + internal BrokeredMessageInfo(HttpResponse response) { Debug.Assert(response.IsSuccessStatusCode); - _content = HttpContent.CreateFromResponse(response); + _content = response.Content; _customProperties = new CustomPropertiesDictionary(response); DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Dictionary)); string propertiesString = null; - IEnumerable values; - - if (response.Headers.TryGetValues(Constants.BrokerPropertiesHeader, out values)) - { - propertiesString = string.Join(string.Empty, values); - } + response.Headers.TryGetValue(Constants.BrokerPropertiesHeader, out propertiesString); if (string.IsNullOrEmpty(propertiesString)) { _brokerProperties = new BrokerProperties(); diff --git a/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/ServiceBus/CustomPropertiesDictionary.cs b/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/ServiceBus/CustomPropertiesDictionary.cs index a8c32f1b7571..a7d87e10c589 100644 --- a/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/ServiceBus/CustomPropertiesDictionary.cs +++ b/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/ServiceBus/CustomPropertiesDictionary.cs @@ -44,13 +44,13 @@ internal CustomPropertiesDictionary() /// Initializes a dictionary with properties from the response. /// /// Response. - internal CustomPropertiesDictionary(NetHttpResponseMessage response) + internal CustomPropertiesDictionary(HttpResponse response) : this() { - foreach (KeyValuePair> item in response.Headers) + foreach (KeyValuePair item in response.Headers) { string key = item.Key; - string valueString = string.Join(string.Empty, item.Value); + string valueString = item.Value; JsonValue translatedValue; if (JsonValue.TryParse(valueString, out translatedValue) && IsSupportedType(translatedValue.ValueType)) diff --git a/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/ServiceBus/ServiceBusRestProxy.cs b/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/ServiceBus/ServiceBusRestProxy.cs index 4a9921dc6f82..034b2db28f86 100644 --- a/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/ServiceBus/ServiceBusRestProxy.cs +++ b/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/ServiceBus/ServiceBusRestProxy.cs @@ -16,7 +16,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Net; using System.Text; using System.Threading.Tasks; using Microsoft.WindowsAzure.ServiceLayer.Http; @@ -626,7 +625,7 @@ IAsyncOperation IServiceBusService.PeekQueueMessageAsync(st Uri uri = ServiceConfig.GetUnlockedMessageUri(queueName, lockInterval); HttpRequest request = new HttpRequest(HttpMethod.Post, uri); return SendAsync(request, CheckNoContent) - .ContinueWith((t) => BrokeredMessageInfo.CreateFromPeekResponse(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion) + .ContinueWith(t => BrokeredMessageInfo.CreateFromPeekResponse(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion) .AsAsyncOperation(); } @@ -857,11 +856,11 @@ private IAsyncOperation> GetItemsAsync( /// Initialization action. /// Extra types for deserialization. /// Collection of deserialized items. - private IEnumerable GetItems(NetHttpResponseMessage response, Action initAction, params Type[] extraTypes) + private IEnumerable GetItems(HttpResponse response, Action initAction, params Type[] extraTypes) { Debug.Assert(response.IsSuccessStatusCode); SyndicationFeed feed = new SyndicationFeed(); - feed.Load(response.Content.ReadAsStringAsync().Result); + feed.Load(response.Content.ReadAsStringAsync().AsTask().Result); return SerializationHelper.DeserializeCollection(feed, initAction, extraTypes); } @@ -892,11 +891,11 @@ private IAsyncOperation GetItemAsync(Uri itemUri, ActionInitialization action for deserialized items. /// Extra types for deserialization. /// Deserialized object. - private TInfo GetItem(NetHttpResponseMessage response, Action initAction, params Type[] extraTypes) + private TInfo GetItem(HttpResponse response, Action initAction, params Type[] extraTypes) { Debug.Assert(response.IsSuccessStatusCode); XmlDocument doc = new XmlDocument(); - doc.LoadXml(response.Content.ReadAsStringAsync().Result); + doc.LoadXml(response.Content.ReadAsStringAsync().AsTask().Result); SyndicationItem feedItem = new SyndicationItem(); feedItem.LoadFromXml(doc); @@ -935,7 +934,7 @@ private IAsyncOperation CreateItemAsync( return Task.Factory .StartNew(() => SetBody(request, itemSettings, ExtraRuleTypes)) - .ContinueWith(tr => SendAsync(request).Result, TaskContinuationOptions.OnlyOnRanToCompletion) + .ContinueWith(tr => SendAsync(request).Result, TaskContinuationOptions.OnlyOnRanToCompletion) .ContinueWith(tr => GetItem(tr.Result, initAction, ExtraRuleTypes), TaskContinuationOptions.OnlyOnRanToCompletion) .AsAsyncOperation(); } @@ -999,7 +998,7 @@ private static void InitRule(SyndicationItem feedItem, RuleInfo ruleInfo) /// Source HTTP response. /// Additional validators. /// Processed HTTP response. - private NetHttpResponseMessage CheckResponse(NetHttpResponseMessage response, IEnumerable> validators) + private HttpResponse CheckResponse(HttpResponse response, IEnumerable> validators) { if (!response.IsSuccessStatusCode) { @@ -1007,7 +1006,7 @@ private NetHttpResponseMessage CheckResponse(NetHttpResponseMessage response, IE } // Pass the response through all validators. - foreach (Func validator in validators) + foreach (Func validator in validators) { response = validator(response); } @@ -1019,11 +1018,12 @@ private NetHttpResponseMessage CheckResponse(NetHttpResponseMessage response, IE /// /// Request to send. /// HTTP response. - private Task SendAsync(HttpRequest request, params Func[] validators) + private Task SendAsync(HttpRequest request, params Func[] validators) { NetHttpRequestMessage netRequest = request.CreateNetRequest(); return Channel.SendAsync(netRequest) - .ContinueWith((task) => CheckResponse(task.Result, validators), TaskContinuationOptions.OnlyOnRanToCompletion); + .ContinueWith(t => new HttpResponse(request, t.Result), TaskContinuationOptions.OnlyOnRanToCompletion) + .ContinueWith(t => CheckResponse(t.Result, validators)); } /// @@ -1031,9 +1031,9 @@ private Task SendAsync(HttpRequest request, params Func< /// /// Source response. /// Processed HTTP response. - private NetHttpResponseMessage CheckNoContent(NetHttpResponseMessage response) + private HttpResponse CheckNoContent(HttpResponse response) { - if (response.StatusCode == System.Net.HttpStatusCode.NoContent || response.StatusCode == HttpStatusCode.ResetContent) + if (response.StatusCode == (int)System.Net.HttpStatusCode.NoContent || response.StatusCode == (int)System.Net.HttpStatusCode.ResetContent) { throw new WindowsAzureServiceException(response); } diff --git a/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/WindowsAzureServiceException.cs b/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/WindowsAzureServiceException.cs index 52761bd73a98..891f2512d782 100644 --- a/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/WindowsAzureServiceException.cs +++ b/microsoft-azure-servicelayer/Microsoft.WindowsAzure.ServiceLayer/WindowsAzureServiceException.cs @@ -16,10 +16,9 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net; -using System.Net.Http; using System.Text; using System.Threading.Tasks; +using Microsoft.WindowsAzure.ServiceLayer.Http; namespace Microsoft.WindowsAzure.ServiceLayer { @@ -31,7 +30,7 @@ internal class WindowsAzureServiceException: WindowsAzureException /// /// Gets the HTTP status code. /// - internal HttpStatusCode StatusCode { get; private set; } + internal int StatusCode { get; private set; } /// /// Gets the reason string fore th exception. @@ -42,7 +41,7 @@ internal class WindowsAzureServiceException: WindowsAzureException /// Constructor. /// /// Source HTTP response. - internal WindowsAzureServiceException(HttpResponseMessage response) + internal WindowsAzureServiceException(HttpResponse response) { StatusCode = response.StatusCode; ReasonPhrase = response.ReasonPhrase;