diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 330c692..c89c9bc 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -38,6 +38,7 @@
+
@@ -53,5 +54,5 @@
false
-
+
diff --git a/src/FluentRest/FluentDispatcher.cs b/src/FluentRest/FluentDispatcher.cs
index b290b82..337084b 100644
--- a/src/FluentRest/FluentDispatcher.cs
+++ b/src/FluentRest/FluentDispatcher.cs
@@ -52,7 +52,7 @@ private static async Task PrepareRequest(HttpRequestMessage
if (requestMessage.Headers.UserAgent.Count == 0)
{
// user-agent header required
- var headerValue = new ProductInfoHeaderValue("FluentRest", "5.0.0.0");
+ var headerValue = new ProductInfoHeaderValue("FluentRest", ThisAssembly.FileVersion);
requestMessage.Headers.UserAgent.Add(headerValue);
}
diff --git a/src/FluentRest/StringBuilderCache.cs b/src/FluentRest/StringBuilderCache.cs
new file mode 100644
index 0000000..27c2724
--- /dev/null
+++ b/src/FluentRest/StringBuilderCache.cs
@@ -0,0 +1,54 @@
+using System.Text;
+
+namespace FluentRest;
+
+/// Provide a cached reusable instance of StringBuilder per thread.
+internal static class StringBuilderCache
+{
+ // The value 360 was chosen in discussion with performance experts as a compromise between using
+ // as little memory per thread as possible and still covering a large part of short-lived
+ // StringBuilder creations on the startup path of VS designers.
+ internal const int MaxBuilderSize = 360;
+ private const int DefaultCapacity = 16; // == StringBuilder.DefaultCapacity
+
+ [ThreadStatic]
+ private static StringBuilder t_cachedInstance;
+
+ /// Get a StringBuilder for the specified capacity.
+ /// If a StringBuilder of an appropriate size is cached, it will be returned and the cache emptied.
+ public static StringBuilder Acquire(int capacity = DefaultCapacity)
+ {
+ if (capacity > MaxBuilderSize)
+ return new StringBuilder(capacity);
+
+ var sb = t_cachedInstance;
+ if (sb == null)
+ return new StringBuilder(capacity);
+
+ // Avoid StringBuilder block fragmentation by getting a new StringBuilder
+ // when the requested size is larger than the current capacity
+ if (capacity > sb.Capacity)
+ return new StringBuilder(capacity);
+
+ t_cachedInstance = null;
+ sb.Clear();
+
+ return sb;
+
+ }
+
+ /// Place the specified builder in the cache if it is not too big.
+ public static void Release(StringBuilder sb)
+ {
+ if (sb.Capacity <= MaxBuilderSize)
+ t_cachedInstance = sb;
+ }
+
+ /// Release StringBuilder to the cache, and return the resulting string.
+ public static string ToString(StringBuilder sb)
+ {
+ string result = sb.ToString();
+ Release(sb);
+ return result;
+ }
+}
diff --git a/src/FluentRest/UrlBuilder.cs b/src/FluentRest/UrlBuilder.cs
index 793f257..4279366 100644
--- a/src/FluentRest/UrlBuilder.cs
+++ b/src/FluentRest/UrlBuilder.cs
@@ -544,7 +544,7 @@ public Uri ToUri()
///
public override string ToString()
{
- var builder = new StringBuilder();
+ var builder = StringBuilderCache.Acquire(150);
if (!string.IsNullOrWhiteSpace(_scheme))
builder.Append(_scheme).Append(_schemeDelimiter);
@@ -553,16 +553,16 @@ public override string ToString()
{
builder.Append(_username);
if (!string.IsNullOrWhiteSpace(_password))
- builder.Append(":").Append(_password);
+ builder.Append(':').Append(_password);
- builder.Append("@");
+ builder.Append('@');
}
if (!string.IsNullOrWhiteSpace(_host))
{
builder.Append(_host);
if (_port.HasValue && !IsStandardPort())
- builder.Append(":").Append(_port);
+ builder.Append(':').Append(_port);
}
WritePath(builder);
@@ -571,7 +571,7 @@ public override string ToString()
if (!string.IsNullOrWhiteSpace(_fragment))
builder.Append(_fragment);
- return builder.ToString();
+ return StringBuilderCache.ToString(builder);
}
@@ -586,7 +586,7 @@ private bool IsStandardPort()
private void WritePath(StringBuilder builder)
{
- builder.Append("/");
+ builder.Append('/');
if (Path == null || Path.Count == 0)
return;
@@ -594,12 +594,11 @@ private void WritePath(StringBuilder builder)
foreach (var p in Path)
{
if (builder.Length > start)
- builder.Append("/");
+ builder.Append('/');
- var s = p.Replace(" ", "+");
- s = Uri.EscapeUriString(s);
+ var v = Uri.EscapeDataString(p);
- builder.Append(s);
+ builder.Append(v);
}
}
@@ -608,29 +607,27 @@ private void WriteQueryString(StringBuilder builder)
if (Query == null || Query.Count == 0)
return;
- builder.Append("?");
+ builder.Append('?');
int start = builder.Length;
foreach (var pair in Query)
{
var key = pair.Key;
key = Uri.EscapeDataString(key);
- key = key.Replace("%20", "+");
var values = pair.Value.ToList();
foreach (var value in values)
{
if (builder.Length > start)
- builder.Append("&");
+ builder.Append('&');
var v = value;
v = Uri.EscapeDataString(v);
- v = v.Replace("%20", "+");
builder
.Append(key)
- .Append("=")
+ .Append('=')
.Append(v);
}
}
@@ -763,6 +760,3 @@ private void ParsePath(string s)
}
}
-
-
-
diff --git a/test/FluentRest.Tests/UrlBuilderTests.cs b/test/FluentRest.Tests/UrlBuilderTests.cs
index db769c3..6e767c1 100644
--- a/test/FluentRest.Tests/UrlBuilderTests.cs
+++ b/test/FluentRest.Tests/UrlBuilderTests.cs
@@ -21,10 +21,10 @@ public UrlBuilderTests(ITestOutputHelper output)
[Theory]
[InlineData("http://foo/bar/baz", "date", "today", "http://foo/bar/baz?date=today")]
- [InlineData("http://foo/bar/baz", "date", "sunday afternoon", "http://foo/bar/baz?date=sunday+afternoon")]
+ [InlineData("http://foo/bar/baz", "date", "sunday afternoon", "http://foo/bar/baz?date=sunday%20afternoon")]
[InlineData("http://foo/bar/baz?date=today", "key1", "value1", "http://foo/bar/baz?date=today&key1=value1")]
- [InlineData("http://foo/bar/baz?date=today", "key1", "value 1&", "http://foo/bar/baz?date=today&key1=value+1%26")]
- [InlineData("foo/bar/baz?date=today", "key1", "value 1&", "http://foo/bar/baz?date=today&key1=value+1%26")]
+ [InlineData("http://foo/bar/baz?date=today", "key1", "value 1&", "http://foo/bar/baz?date=today&key1=value%201%26")]
+ [InlineData("foo/bar/baz?date=today", "key1", "value 1&", "http://foo/bar/baz?date=today&key1=value%201%26")]
public void AppendQuery(string url, string key, string value, string expected)
{
var builder = new UrlBuilder(url);