diff --git a/CHANGELOG.md b/CHANGELOG.md
index f935386..b77db61 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# CHANGELOG
+## 1.1.0
+
+ * Add ability to customize the HttpClient global timeout
+
## 1.0.0
* Add `netstandard1.6` target
diff --git a/src/SpeakEasy.IntegrationTests/SpeakEasy.IntegrationTests.csproj b/src/SpeakEasy.IntegrationTests/SpeakEasy.IntegrationTests.csproj
index 305ff6b..67b9d78 100644
--- a/src/SpeakEasy.IntegrationTests/SpeakEasy.IntegrationTests.csproj
+++ b/src/SpeakEasy.IntegrationTests/SpeakEasy.IntegrationTests.csproj
@@ -4,7 +4,7 @@
SpeakEasy.IntegrationTests
2.0.0
jonnii
- netcoreapp2.0
+ netcoreapp2.1
SpeakEasy.IntegrationTests
SpeakEasy.IntegrationTests
SpeakEasy.IntegrationTests
diff --git a/src/SpeakEasy.Specifications/HttpClientSettingsSpecs.cs b/src/SpeakEasy.Specifications/HttpClientSettingsSpecs.cs
index 088c3a3..e4608ea 100644
--- a/src/SpeakEasy.Specifications/HttpClientSettingsSpecs.cs
+++ b/src/SpeakEasy.Specifications/HttpClientSettingsSpecs.cs
@@ -133,13 +133,13 @@ class when_replacing_middleware_with_same_type
settings.Middleware.Replace(replacement);
It should_replace_middleware_in_place = () =>
- settings.Middleware.AtPosition(1).ShouldBeOfExactType();
+ settings.Middleware.AtPosition(2).ShouldBeOfExactType();
It should_have_correct_middleware_count = () =>
- settings.Middleware.Count.ShouldEqual(3);
+ settings.Middleware.Count.ShouldEqual(4);
It should_replace_instance = () =>
- settings.Middleware.AtPosition(1).ShouldBeTheSameAs(replacement);
+ settings.Middleware.AtPosition(2).ShouldBeTheSameAs(replacement);
}
class TestMiddleware : IHttpMiddleware
diff --git a/src/SpeakEasy.Specifications/HttpClientSpecs.cs b/src/SpeakEasy.Specifications/HttpClientSpecs.cs
index 57d0849..5e6ecc9 100644
--- a/src/SpeakEasy.Specifications/HttpClientSpecs.cs
+++ b/src/SpeakEasy.Specifications/HttpClientSpecs.cs
@@ -53,10 +53,24 @@ class when_building_system_client
static SystemHttpClient system_http_client;
Because of = () =>
- system_http_client = client.BuildSystemClient(new CookieContainer());
+ system_http_client = client.BuildSystemClient(new CookieContainer(), null);
It should_create_client = () =>
system_http_client.ShouldNotBeNull();
+
+ It should_create_with_system_default_timeout = () =>
+ system_http_client.Timeout.ShouldEqual(new SystemHttpClient().Timeout);
+ }
+
+ class when_building_system_client_with_custom_timeout
+ {
+ static SystemHttpClient system_http_client;
+
+ Because of = () =>
+ system_http_client = client.BuildSystemClient(new CookieContainer(), TimeSpan.FromMinutes(10));
+
+ It should_create_with_custom_timeout = () =>
+ system_http_client.Timeout.ShouldEqual(TimeSpan.FromMinutes(10));
}
class when_getting_collection_resource
diff --git a/src/SpeakEasy.Specifications/UserAgentSpecs.cs b/src/SpeakEasy.Specifications/UserAgentSpecs.cs
index 699b8c7..ab6a6c0 100644
--- a/src/SpeakEasy.Specifications/UserAgentSpecs.cs
+++ b/src/SpeakEasy.Specifications/UserAgentSpecs.cs
@@ -8,7 +8,7 @@ class UserAgentSpecs
class default_user_agent
{
It should_contain_app_version = () =>
- UserAgent.SpeakEasy.Name.ShouldEqual("SpeakEasy/1.0.0.0");
+ UserAgent.SpeakEasy.Name.ShouldContain("SpeakEasy/1.0");
}
}
}
diff --git a/src/SpeakEasy/HttpClient.cs b/src/SpeakEasy/HttpClient.cs
index 8181a3a..61a4daa 100644
--- a/src/SpeakEasy/HttpClient.cs
+++ b/src/SpeakEasy/HttpClient.cs
@@ -1,3 +1,4 @@
+using System;
using System.Net;
using System.Net.Http;
using System.Threading;
@@ -43,7 +44,7 @@ internal HttpClient(string rootUrl, HttpClientSettings settings)
settings.Validate();
var cookieContainer = new CookieContainer();
- var client = BuildSystemClient(cookieContainer);
+ var client = BuildSystemClient(cookieContainer, settings.DefaultTimeout);
requestRunner = new RequestRunner(
client,
@@ -75,7 +76,7 @@ internal HttpClient(
public Resource Root { get; }
- internal System.Net.Http.HttpClient BuildSystemClient(CookieContainer cookieContainer)
+ internal System.Net.Http.HttpClient BuildSystemClient(CookieContainer cookieContainer, TimeSpan? defaultTimeout)
{
var handler = new HttpClientHandler
{
@@ -89,6 +90,11 @@ internal System.Net.Http.HttpClient BuildSystemClient(CookieContainer cookieCont
var httpClient = new System.Net.Http.HttpClient(handler);
+ if (defaultTimeout != null)
+ {
+ httpClient.Timeout = defaultTimeout.Value;
+ }
+
settings.Authenticator.Authenticate(httpClient);
return httpClient;
diff --git a/src/SpeakEasy/HttpClientSettings.cs b/src/SpeakEasy/HttpClientSettings.cs
index ff64da5..37a7d68 100644
--- a/src/SpeakEasy/HttpClientSettings.cs
+++ b/src/SpeakEasy/HttpClientSettings.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading;
using SpeakEasy.ArrayFormatters;
using SpeakEasy.Authenticators;
using SpeakEasy.Middleware;
@@ -26,6 +27,7 @@ public HttpClientSettings()
Serializers.Add(new DefaultJsonSerializer());
Serializers.Add(new TextPlainSerializer());
+ Middleware.Append(new TimeoutMiddleware());
Middleware.Append(new UserAgentMiddleware());
}
@@ -69,6 +71,12 @@ public HttpClientSettings()
///
public bool IsValid => Serializers.Any() && ArrayFormatter != null;
+ ///
+ /// The default timeout for the HttpClient to 30 minutes,
+ /// to use the system default (100 seconds) set this property to null.
+ ///
+ public TimeSpan? DefaultTimeout { get; set; } = TimeSpan.FromMinutes(30);
+
///
/// Configures the give serializer
///
diff --git a/src/SpeakEasy/Middleware/TimeoutMiddleware.cs b/src/SpeakEasy/Middleware/TimeoutMiddleware.cs
new file mode 100644
index 0000000..3fa606b
--- /dev/null
+++ b/src/SpeakEasy/Middleware/TimeoutMiddleware.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace SpeakEasy.Middleware
+{
+ public class TimeoutMiddleware : IHttpMiddleware
+ {
+ public IHttpMiddleware Next { get; set; }
+
+ public async Task Invoke(IHttpRequest request, CancellationToken cancellationToken)
+ {
+ try
+ {
+ return await Next.Invoke(request, cancellationToken).ConfigureAwait(false);
+ }
+ catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested)
+ {
+ throw new TimeoutException();
+ }
+ }
+ }
+}