diff --git a/src/Flurl.Http/Configuration/DefaultJsonSerializer.cs b/src/Flurl.Http/Configuration/DefaultJsonSerializer.cs
index 580217cd..0bc346b3 100644
--- a/src/Flurl.Http/Configuration/DefaultJsonSerializer.cs
+++ b/src/Flurl.Http/Configuration/DefaultJsonSerializer.cs
@@ -35,6 +35,8 @@ public DefaultJsonSerializer(JsonSerializerOptions options = null) {
/// Deserializes the specified stream to an object of type T.
///
/// The stream to deserialize.
- public T Deserialize(Stream stream) => stream.Length == 0 ? default : JsonSerializer.Deserialize(stream, _options);
+ public T Deserialize(Stream stream) => stream.CanSeek && stream.Length == 0
+ ? default
+ : JsonSerializer.Deserialize(stream, _options);
}
}
\ No newline at end of file
diff --git a/src/Flurl.Http/Flurl.Http.csproj b/src/Flurl.Http/Flurl.Http.csproj
index 773828c1..a0104431 100644
--- a/src/Flurl.Http/Flurl.Http.csproj
+++ b/src/Flurl.Http/Flurl.Http.csproj
@@ -4,7 +4,7 @@
9.0
True
Flurl.Http
- 4.0.1
+ 4.0.2
Todd Menier
A fluent, testable HTTP client library.
Copyright (c) Todd Menier 2024.
diff --git a/src/Flurl.Http/FlurlHttp.cs b/src/Flurl.Http/FlurlHttp.cs
index 7c633c5d..bfd69cba 100644
--- a/src/Flurl.Http/FlurlHttp.cs
+++ b/src/Flurl.Http/FlurlHttp.cs
@@ -48,6 +48,6 @@ public static IFlurlClientBuilder ConfigureClientForUrl(string url) {
///
/// Builds a cache key consisting of URL scheme, host, and port. This is the default client caching strategy.
///
- public static string BuildClientNameByHost(IFlurlRequest req) => $"{req.Url.Scheme}|{req.Url.Host}|{req.Url.Port}";
+ public static string BuildClientNameByHost(IFlurlRequest req) => $"{req.Url?.Scheme}|{req.Url?.Host}|{req.Url?.Port}";
}
}
\ No newline at end of file
diff --git a/src/Flurl.Http/FlurlRequest.cs b/src/Flurl.Http/FlurlRequest.cs
index ca9762b4..c98e6649 100644
--- a/src/Flurl.Http/FlurlRequest.cs
+++ b/src/Flurl.Http/FlurlRequest.cs
@@ -115,6 +115,7 @@ public IFlurlClient Client {
set {
_client = value;
Settings.Parent = _client?.Settings;
+ SyncBaseUrl(_client, this);
SyncHeaders(_client, this);
}
}
@@ -163,6 +164,19 @@ internal static void SyncHeaders(IFlurlClient client, IFlurlRequest request) {
}
}
+ ///
+ /// Prepends client.BaseUrl to this.Url, but only if this.Url isn't already a valid, absolute URL.
+ ///
+ private static void SyncBaseUrl(IFlurlClient client, IFlurlRequest request) {
+ if (string.IsNullOrEmpty(client?.BaseUrl))
+ return;
+
+ if (request.Url == null)
+ request.Url = client.BaseUrl;
+ else if (!Url.IsValid(request.Url))
+ request.Url = Url.Combine(client.BaseUrl, request.Url);
+ }
+
private void ApplyCookieJar(CookieJar jar) {
_jar = jar;
if (jar == null)
diff --git a/test/Flurl.Test/Http/GlobalConfigTests.cs b/test/Flurl.Test/Http/GlobalConfigTests.cs
index fecd033b..73ffb61d 100644
--- a/test/Flurl.Test/Http/GlobalConfigTests.cs
+++ b/test/Flurl.Test/Http/GlobalConfigTests.cs
@@ -1,6 +1,7 @@
using System;
using System.Threading.Tasks;
using Flurl.Http;
+using Flurl.Http.Testing;
using NUnit.Framework;
namespace Flurl.Test.Http
@@ -74,5 +75,23 @@ public void can_configure_client_for_url() {
Assert.AreEqual(123, cli2.Settings.Timeout.Value.TotalSeconds);
Assert.AreNotEqual(123, cli3.Settings.Timeout.Value.TotalSeconds);
}
+
+ [Test] // #803
+ public async Task can_prepend_global_base_url() {
+ FlurlHttp.Clients.WithDefaults(builder =>
+ builder.ConfigureHttpClient(cli => cli.BaseAddress = new Uri("https://api.com")));
+
+ using var test = new HttpTest();
+
+ await "".GetAsync();
+ await "/path/1".GetAsync();
+ await "path/2".GetAsync();
+ await "https://other.com/path/3".GetAsync();
+
+ test.ShouldHaveCalled("https://api.com/").Times(1);
+ test.ShouldHaveCalled("https://api.com/path/1").Times(1);
+ test.ShouldHaveCalled("https://api.com/path/2").Times(1);
+ test.ShouldHaveCalled("https://other.com/path/3").Times(1);
+ }
}
}
diff --git a/test/Flurl.Test/Http/RealHttpTests.cs b/test/Flurl.Test/Http/RealHttpTests.cs
index 8bfca19c..98763281 100644
--- a/test/Flurl.Test/Http/RealHttpTests.cs
+++ b/test/Flurl.Test/Http/RealHttpTests.cs
@@ -60,6 +60,19 @@ public async Task can_post_and_receive_json() {
Assert.AreEqual(2, result.json["b"].GetInt32());
}
+ [Test]
+ [TestCase(HttpCompletionOption.ResponseHeadersRead)]
+ [TestCase(HttpCompletionOption.ResponseContentRead)]
+ public async Task can_get_json_with_http_completion_option_headers(HttpCompletionOption completionOption)
+ {
+ var result = await "https://httpbin.org"
+ .AppendPathSegment("gzip")
+ .WithHeader("Accept-encoding", "gzip")
+ .GetJsonAsync>(completionOption);
+
+ Assert.AreEqual(true, ((JsonElement)result["gzipped"]).GetBoolean());
+ }
+
[Test]
public async Task can_get_stream() {
using (var stream = await "https://www.google.com".GetStreamAsync())