Skip to content

Commit f95b720

Browse files
feat: support passing HttpClient (#174)
1 parent 3676b9b commit f95b720

File tree

8 files changed

+110
-17
lines changed

8 files changed

+110
-17
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
## 1.4.0 [unreleased]
22

3+
### Features
4+
5+
1. [#174](https://github.com/InfluxCommunity/influxdb3-csharp/pull/174): Support passing HttpClient to InfluxDBClient.
6+
37
## 1.3.0 [2025-08-12]
48

59
### Features

Client.Test/InfluxDBClientWriteTest.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Linq;
44
using System.Net;
5+
using System.Net.Http;
56
using System.Threading.Tasks;
67
using InfluxDB3.Client.Config;
78
using InfluxDB3.Client.Write;
@@ -490,4 +491,57 @@ public void WriteNoSyncTrueNotSupported()
490491
"Server doesn't support write with NoSync=true (supported by InfluxDB 3 Core/Enterprise servers only)."));
491492
});
492493
}
494+
495+
[Test]
496+
public async Task TestSetHttpClient()
497+
{
498+
MockServer
499+
.Given(Request.Create().WithPath("/api/v2/write").UsingPost())
500+
.RespondWith(Response.Create().WithStatusCode(HttpStatusCode.OK));
501+
502+
var httpClient = new HttpClient();
503+
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("my-user-agent");
504+
httpClient.DefaultRequestHeaders.Add("X-Client-ID", "123");
505+
506+
_client = new InfluxDBClient(new ClientConfig
507+
{
508+
Host = MockServerUrl,
509+
Token = "my-token",
510+
Database = "my-database",
511+
HttpClient = httpClient
512+
});
513+
514+
await _client.WriteRecordAsync("mem,tag=a field=1");
515+
var requests = MockServer.LogEntries.ToList();
516+
using (Assert.EnterMultipleScope())
517+
{
518+
Assert.That(requests[0].RequestMessage.Headers?["User-Agent"].First(), Is.EqualTo("my-user-agent"));
519+
Assert.That(requests[0].RequestMessage.Headers["X-Client-ID"].First(), Is.EqualTo("123"));
520+
}
521+
Assert.Pass();
522+
}
523+
524+
[Test]
525+
public void TestCheckHttpClientStillOpen()
526+
{
527+
MockServer
528+
.Given(Request.Create().WithPath("/test").UsingGet())
529+
.RespondWith(
530+
Response.Create()
531+
.WithStatusCode(HttpStatusCode.OK)
532+
.WithBody("Still ok"));
533+
534+
var httpClient = new HttpClient(new HttpClientHandler());
535+
_client = new InfluxDBClient(new ClientConfig
536+
{
537+
Host = MockServerUrl,
538+
Token = "my-token",
539+
Database = "my-database",
540+
HttpClient = httpClient
541+
});
542+
_client.Dispose();
543+
544+
var httpResponseMessage = httpClient.Send(new HttpRequestMessage(HttpMethod.Get, "test"));
545+
Assert.That(httpResponseMessage.Content.ReadAsStringAsync().Result, Is.EqualTo("Still ok"));
546+
}
493547
}

Client.Test/Internal/FlightSqlClientTest.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public void SetUp()
2222
Timeout = TimeSpan.FromSeconds(45)
2323
};
2424

25-
_flightSqlClient = new FlightSqlClient(config, InfluxDBClient.CreateAndConfigureHttpClient(config));
25+
_flightSqlClient = new FlightSqlClient(config, InfluxDBClient.CreateOrGetHttpClient(config));
2626
}
2727

2828
[TearDown]
@@ -108,7 +108,7 @@ public void HeadersMetadataFromConfig()
108108
}
109109
};
110110

111-
_flightSqlClient = new FlightSqlClient(config, InfluxDBClient.CreateAndConfigureHttpClient(config));
111+
_flightSqlClient = new FlightSqlClient(config, InfluxDBClient.CreateOrGetHttpClient(config));
112112

113113
var prepareHeadersMetadata =
114114
_flightSqlClient.PrepareHeadersMetadata(new Dictionary<string, string>());
@@ -139,7 +139,7 @@ public void HeadersMetadataFromRequestArePreferred()
139139
}
140140
};
141141

142-
_flightSqlClient = new FlightSqlClient(config, InfluxDBClient.CreateAndConfigureHttpClient(config));
142+
_flightSqlClient = new FlightSqlClient(config, InfluxDBClient.CreateOrGetHttpClient(config));
143143

144144
var prepareHeadersMetadata =
145145
_flightSqlClient.PrepareHeadersMetadata(new Dictionary<string, string> { { "X-Tracing-Id", "258" } });
@@ -170,7 +170,7 @@ public void UserAgentHeaderNotChanged()
170170
}
171171
};
172172

173-
_flightSqlClient = new FlightSqlClient(config, InfluxDBClient.CreateAndConfigureHttpClient(config));
173+
_flightSqlClient = new FlightSqlClient(config, InfluxDBClient.CreateOrGetHttpClient(config));
174174

175175
var prepareHeadersMetadata =
176176
_flightSqlClient.PrepareHeadersMetadata(new Dictionary<string, string> { { "user-agent", "another/user-agent" } });
@@ -199,7 +199,7 @@ public void TestGrpcCallOptions()
199199
};
200200

201201
Assert.DoesNotThrow(() =>
202-
_flightSqlClient = new FlightSqlClient(config, InfluxDBClient.CreateAndConfigureHttpClient(config)));
202+
_flightSqlClient = new FlightSqlClient(config, InfluxDBClient.CreateOrGetHttpClient(config)));
203203
}
204204

205205
}

Client.Test/Internal/RestClientTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ public void Timeout()
294294

295295
private void CreateAndConfigureRestClient(ClientConfig config)
296296
{
297-
_httpClient = InfluxDBClient.CreateAndConfigureHttpClient(config);
297+
_httpClient = InfluxDBClient.CreateOrGetHttpClient(config);
298298
_client = new RestClient(config, _httpClient);
299299
}
300300

Client.Test/MockHttpsServerTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace InfluxDB3.Client.Test;
66

7-
public class MockHttpsServerTest
7+
public abstract class MockHttpsServerTest
88
{
99
internal WireMockServer MockHttpsServer;
1010
internal string MockHttpsServerUrl;

Client.Test/MockServerTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
namespace InfluxDB3.Client.Test;
55

6-
public class MockServerTest
6+
public abstract class MockServerTest
77
{
88
internal WireMockServer MockServer, MockProxy;
99
internal string MockServerUrl, MockProxyUrl;

Client/Config/ClientConfig.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections;
33
using System.Collections.Generic;
44
using System.Net;
5+
using System.Net.Http;
56
using System.Web;
67
using InfluxDB3.Client.Write;
78

@@ -26,6 +27,7 @@ namespace InfluxDB3.Client.Config;
2627
/// <item>- Proxy: The HTTP proxy URL. Default is not set.</item>
2728
/// <item>- WriteOptions: Write options.</item>
2829
/// <item>- QueryOptions Query options.</item>
30+
/// <item>- HttpClient: The HttpClient will be used for Write and Query apis.</item>
2931
/// </list>
3032
///
3133
/// <para>If you want create client with custom options, you can use the following code:</para>
@@ -200,6 +202,13 @@ public string Host
200202
/// </summary>
201203
public QueryOptions QueryOptions { get; set; }
202204

205+
/// <summary>
206+
/// User-defined HttpClient.
207+
/// Influxdb client will add an authentication header and base url to HttpClient. The rest is up to the users.
208+
/// Users will be responsible for closing the HttpClient.
209+
/// </summary>
210+
public HttpClient? HttpClient { get; set; }
211+
203212
internal void Validate()
204213
{
205214
if (string.IsNullOrEmpty(Host))

Client/InfluxDBClient.cs

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ public InfluxDBClient(ClientConfig config)
343343
config.Validate();
344344

345345
_config = config;
346-
_httpClient = CreateAndConfigureHttpClient(_config);
346+
_httpClient = CreateOrGetHttpClient(_config);
347347
FlightSqlClient = new FlightSqlClient(config: _config, httpClient: _httpClient);
348348
_restClient = new RestClient(config: _config, httpClient: _httpClient);
349349
_gzipHandler = new GzipHandler(config.WriteOptions != null ? config.WriteOptions.GzipThreshold : 0);
@@ -840,7 +840,12 @@ await _restClient
840840

841841
public void Dispose()
842842
{
843-
_httpClient.Dispose();
843+
// _config.HttpClient == null means HttpClient is created by the library, not from the user.
844+
// so the client will be responsible for disposing of the HttpClient.
845+
if (_config.HttpClient == null)
846+
{
847+
_httpClient.Dispose();
848+
}
844849
FlightSqlClient.Dispose();
845850
_disposed = true;
846851
}
@@ -876,7 +881,28 @@ private static StringBuilder ToLineProtocolBody(IEnumerable<object?> data, Write
876881
return sb;
877882
}
878883

879-
internal static HttpClient CreateAndConfigureHttpClient(ClientConfig config)
884+
internal static HttpClient CreateOrGetHttpClient(ClientConfig config)
885+
{
886+
var httpClient = config.HttpClient;
887+
if (httpClient == null)
888+
{
889+
httpClient = CreateHttpClient(config);
890+
}
891+
892+
if (httpClient.BaseAddress == null)
893+
{
894+
httpClient.BaseAddress = new Uri(config.Host);
895+
}
896+
897+
if (!string.IsNullOrEmpty(config.Token))
898+
{
899+
_setAuthenticationHeader(httpClient, config);
900+
}
901+
902+
return httpClient;
903+
}
904+
905+
private static HttpClient CreateHttpClient(ClientConfig config)
880906
{
881907
var handler = new HttpClientHandler();
882908
if (handler.SupportsRedirectConfiguration)
@@ -916,17 +942,17 @@ internal static HttpClient CreateAndConfigureHttpClient(ClientConfig config)
916942
{
917943
Timeout = config.Timeout
918944
};
919-
920945
client.DefaultRequestHeaders.UserAgent.ParseAdd(AssemblyHelper.GetUserAgent());
921-
if (!string.IsNullOrEmpty(config.Token))
922-
{
923-
string authScheme = string.IsNullOrEmpty(config.AuthScheme) ? "Token" : config.AuthScheme!;
924-
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(authScheme, config.Token);
925-
}
926946

927947
return client;
928948
}
929949

950+
private static void _setAuthenticationHeader(HttpClient httpClient, ClientConfig config)
951+
{
952+
var authScheme = string.IsNullOrEmpty(config.AuthScheme) ? "Token" : config.AuthScheme!;
953+
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(authScheme, config.Token);
954+
}
955+
930956
private static string OptionMessage(string property)
931957
{
932958
return $"Please specify the '{property}' as a method parameter or use default configuration " +

0 commit comments

Comments
 (0)