Skip to content

Commit 25639c8

Browse files
authored
feat: add possibility to follow HTTP redirects (#233)
1 parent 842f3c8 commit 25639c8

File tree

8 files changed

+127
-20
lines changed

8 files changed

+127
-20
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
Adds a `Type` overload for POCOs to `QueryAsync`. This will add `object ConvertToEntity(FluxRecord, Type)` to `IFluxResultMapper`
55

66
### Features
7-
1. [#232](https://github.com/influxdata/influxdb-client-csharp/pull/232): Adds a `Type` overload for POCOs to `QueryAsync`.
7+
1. [#232](https://github.com/influxdata/influxdb-client-csharp/pull/232): Add a `Type` overload for POCOs to `QueryAsync`.
8+
1. [#233](https://github.com/influxdata/influxdb-client-csharp/pull/233): Add possibility to follow HTTP redirects
89

910
## 2.1.0 [2021-08-20]
1011

Client.Core.Test/AbstractMockServerTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class AbstractMockServerTest : AbstractTest
1313
[SetUp]
1414
public new void SetUp()
1515
{
16-
if (MockServer != null && MockServer.IsStarted)
16+
if (MockServer is { IsStarted: true })
1717
{
1818
return;
1919
}

Client.Test/InfluxDbClientFactoryTest.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ public void CreateInstance()
2424
var client = InfluxDBClientFactory.Create("http://localhost:9999");
2525

2626
Assert.IsNotNull(client);
27+
28+
var options = GetDeclaredField<InfluxDBClientOptions>(client.GetType(), client, "_options");
29+
Assert.AreEqual(false, options.AllowHttpRedirects);
2730
}
2831

2932
[Test]
@@ -46,13 +49,14 @@ public void CreateInstanceToken() {
4649
public void LoadFromConnectionString()
4750
{
4851
var client = InfluxDBClientFactory.Create("http://localhost:9999?" +
49-
"timeout=1000&readWriteTimeout=3000&logLevel=HEADERS&token=my-token&bucket=my-bucket&org=my-org");
52+
"timeout=1000&readWriteTimeout=3000&logLevel=HEADERS&token=my-token&bucket=my-bucket&org=my-org&allowHttpRedirects=true");
5053

5154
var options = GetDeclaredField<InfluxDBClientOptions>(client.GetType(), client, "_options");
5255
Assert.AreEqual("http://localhost:9999/", options.Url);
5356
Assert.AreEqual("my-org", options.Org);
5457
Assert.AreEqual("my-bucket", options.Bucket);
5558
Assert.AreEqual("my-token".ToCharArray(), options.Token);
59+
Assert.AreEqual(true, options.AllowHttpRedirects);
5660
Assert.AreEqual(LogLevel.Headers, options.LogLevel);
5761
Assert.AreEqual(LogLevel.Headers, client.GetLogLevel());
5862

Client.Test/InfluxDbClientTest.cs

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
using InfluxDB.Client.Core.Test;
1111
using NUnit.Framework;
1212
using WireMock.RequestBuilders;
13+
using WireMock.ResponseBuilders;
14+
using WireMock.Server;
15+
using WireMock.Settings;
1316

1417
namespace InfluxDB.Client.Test
1518
{
@@ -105,9 +108,9 @@ public void LogLevelWithQueryString()
105108
{
106109
var writer = new StringWriter();
107110
Trace.Listeners.Add(new TextWriterTraceListener(writer));
108-
111+
109112
_client.SetLogLevel(LogLevel.Headers);
110-
113+
111114
MockServer
112115
.Given(Request.Create().WithPath("/api/v2/write").UsingPost())
113116
.RespondWith(CreateResponse("{}"));
@@ -117,20 +120,20 @@ public void LogLevelWithQueryString()
117120
writeApi.WriteRecord("b1", "org1", WritePrecision.Ns,
118121
"h2o_feet,location=coyote_creek water_level=1.0 1");
119122
}
120-
123+
121124
StringAssert.Contains("org=org1", writer.ToString());
122125
StringAssert.Contains("bucket=b1", writer.ToString());
123126
StringAssert.Contains("precision=ns", writer.ToString());
124127
}
125-
128+
126129
[Test]
127130
public void LogLevelWithoutQueryString()
128131
{
129132
var writer = new StringWriter();
130133
Trace.Listeners.Add(new TextWriterTraceListener(writer));
131-
134+
132135
_client.SetLogLevel(LogLevel.Basic);
133-
136+
134137
MockServer
135138
.Given(Request.Create().WithPath("/api/v2/write").UsingPost())
136139
.RespondWith(CreateResponse("{}"));
@@ -140,12 +143,12 @@ public void LogLevelWithoutQueryString()
140143
writeApi.WriteRecord("b1", "org1", WritePrecision.Ns,
141144
"h2o_feet,location=coyote_creek water_level=1.0 1");
142145
}
143-
146+
144147
StringAssert.DoesNotContain("org=org1", writer.ToString());
145148
StringAssert.DoesNotContain("bucket=b1", writer.ToString());
146149
StringAssert.DoesNotContain("precision=ns", writer.ToString());
147150
}
148-
151+
149152
[Test]
150153
public async Task UserAgentHeader()
151154
{
@@ -155,11 +158,11 @@ public async Task UserAgentHeader()
155158

156159
await _client.GetAuthorizationsApi().FindAuthorizationByIdAsync("id");
157160

158-
var request= MockServer.LogEntries.Last();
161+
var request = MockServer.LogEntries.Last();
159162
StringAssert.StartsWith("influxdb-client-csharp/3.", request.RequestMessage.Headers["User-Agent"].First());
160163
StringAssert.EndsWith(".0.0", request.RequestMessage.Headers["User-Agent"].First());
161164
}
162-
165+
163166
[Test]
164167
public void TrailingSlashInUrl()
165168
{
@@ -217,14 +220,14 @@ public void TrailingSlashInUrl()
217220
request = MockServer.LogEntries.Last();
218221
Assert.AreEqual(MockServerUrl + "/api/v2/write?org=org1&bucket=b1&precision=ns",
219222
request.RequestMessage.AbsoluteUrl);
220-
223+
221224
Assert.True(MockServer.LogEntries.Any());
222225
foreach (var logEntry in MockServer.LogEntries)
223226
{
224227
StringAssert.StartsWith(MockServerUrl + "/api/v2/", logEntry.RequestMessage.AbsoluteUrl);
225228
}
226229
}
227-
230+
228231
[Test]
229232
public void ProduceTypedException()
230233
{
@@ -242,9 +245,45 @@ public void ProduceTypedException()
242245
public void CreateService()
243246
{
244247
var service = _client.CreateService<DBRPsService>(typeof(DBRPsService));
245-
248+
246249
Assert.IsNotNull(service);
247250
Assert.IsInstanceOf(typeof(DBRPsService), service);
248251
}
252+
253+
[Test]
254+
public async Task RedirectToken()
255+
{
256+
_client.Dispose();
257+
_client = InfluxDBClientFactory.Create(new InfluxDBClientOptions.Builder()
258+
.Url(MockServerUrl)
259+
.AuthenticateToken("my-token")
260+
.AllowRedirects(true)
261+
.Build());
262+
263+
var anotherServer = WireMockServer.Start(new WireMockServerSettings
264+
{
265+
UseSSL = false
266+
});
267+
268+
// redirect to another server
269+
MockServer
270+
.Given(Request.Create().UsingGet())
271+
.RespondWith(Response.Create().WithStatusCode(301).WithHeader("location", anotherServer.Urls[0]));
272+
273+
274+
// success response
275+
anotherServer
276+
.Given(Request.Create().UsingGet())
277+
.RespondWith(CreateResponse("{\"status\":\"active\"}", "application/json"));
278+
279+
var authorization = await _client.GetAuthorizationsApi().FindAuthorizationByIdAsync("id");
280+
Assert.AreEqual(AuthorizationUpdateRequest.StatusEnum.Active, authorization.Status);
281+
282+
StringAssert.StartsWith("Token my-token",
283+
MockServer.LogEntries.Last().RequestMessage.Headers["Authorization"].First());
284+
Assert.False(anotherServer.LogEntries.Last().RequestMessage.Headers.ContainsKey("Authorization"));
285+
286+
anotherServer.Stop();
287+
}
249288
}
250289
}

Client/Configurations/Influx2.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,16 @@ public string Timeout
7474
get => (string) base["timeout"];
7575
set => base["timeout"] = value;
7676
}
77+
78+
/// <summary>
79+
/// Configure automatically following HTTP 3xx redirects.
80+
/// </summary>
81+
[ConfigurationProperty("allowHttpRedirects", IsKey = true, IsRequired = false)]
82+
public bool AllowHttpRedirects
83+
{
84+
get => (bool) base["allowHttpRedirects"];
85+
set => base["allowHttpRedirects"] = value;
86+
}
7787

7888
[ConfigurationProperty("tags", IsRequired = false)]
7989
public TagCollection Tags

Client/InfluxDBClientOptions.cs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public class InfluxDBClientOptions
3636
public TimeSpan ReadWriteTimeout { get; }
3737

3838
public IWebProxy WebProxy { get; }
39+
public bool AllowHttpRedirects { get; }
3940

4041
public PointSettings PointSettings { get; }
4142

@@ -57,6 +58,7 @@ private InfluxDBClientOptions(Builder builder)
5758
ReadWriteTimeout = builder.ReadWriteTimeout;
5859

5960
WebProxy = builder.WebProxy;
61+
AllowHttpRedirects = builder.AllowHttpRedirects;
6062

6163
PointSettings = builder.PointSettings;
6264
}
@@ -95,7 +97,8 @@ public sealed class Builder
9597
internal string OrgString;
9698
internal string BucketString;
9799

98-
internal IWebProxy WebProxy = null;
100+
internal IWebProxy WebProxy;
101+
internal bool AllowHttpRedirects;
99102

100103
internal PointSettings PointSettings = new PointSettings();
101104

@@ -265,6 +268,20 @@ public Builder Proxy(IWebProxy webProxy)
265268

266269
return this;
267270
}
271+
272+
/// <summary>
273+
/// Configure automatically following HTTP 3xx redirects.
274+
/// </summary>
275+
/// <param name="allowHttpRedirects">configure HTTP redirects</param>
276+
/// <returns><see cref="Builder"/></returns>
277+
public Builder AllowRedirects(bool allowHttpRedirects)
278+
{
279+
Arguments.CheckNotNull(allowHttpRedirects, nameof(allowHttpRedirects));
280+
281+
AllowHttpRedirects = allowHttpRedirects;
282+
283+
return this;
284+
}
268285

269286
/// <summary>
270287
/// Configure Builder via App.config.
@@ -291,6 +308,7 @@ internal Builder LoadConfig(string sectionName = "influx2")
291308
var logLevel = config.LogLevel;
292309
var timeout = config.Timeout;
293310
var readWriteTimeout = config.ReadWriteTimeout;
311+
var allowHttpRedirects = config.AllowHttpRedirects;
294312

295313
var tags = config.Tags;
296314
if (tags != null)
@@ -301,7 +319,7 @@ internal Builder LoadConfig(string sectionName = "influx2")
301319
}
302320
}
303321

304-
return Configure(url, org, bucket, token, logLevel, timeout, readWriteTimeout);
322+
return Configure(url, org, bucket, token, logLevel, timeout, readWriteTimeout, allowHttpRedirects);
305323
}
306324

307325
/// <summary>
@@ -324,12 +342,13 @@ internal Builder ConnectionString(string connectionString)
324342
var logLevel = query.Get("logLevel");
325343
var timeout = query.Get("timeout");
326344
var readWriteTimeout = query.Get("readWriteTimeout");
345+
var allowHttpRedirects = Convert.ToBoolean(query.Get("allowHttpRedirects"));
327346

328-
return Configure(url, org, bucket, token, logLevel, timeout, readWriteTimeout);
347+
return Configure(url, org, bucket, token, logLevel, timeout, readWriteTimeout, allowHttpRedirects);
329348
}
330349

331350
private Builder Configure(string url, string org, string bucket, string token, string logLevel,
332-
string timeout, string readWriteTimeout)
351+
string timeout, string readWriteTimeout, bool allowHttpRedirects = false)
333352
{
334353
Url(url);
335354
Org(org);
@@ -355,6 +374,8 @@ private Builder Configure(string url, string org, string bucket, string token, s
355374
ReadWriteTimeOut(ToTimeout(readWriteTimeout));
356375
}
357376

377+
AllowRedirects(allowHttpRedirects);
378+
358379
return this;
359380
}
360381

Client/Internal/ApiClient.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public ApiClient(InfluxDBClientOptions options, LoggingHandler loggingHandler, G
3131
var totalMilliseconds = (int) options.ReadWriteTimeout.TotalMilliseconds;
3232

3333
RestClient = new RestClient(options.Url);
34+
RestClient.FollowRedirects = options.AllowHttpRedirects;
3435
RestClient.AutomaticDecompression = false;
3536
Configuration = new Configuration
3637
{

Client/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ This section contains links to the client library documentation.
3838
- [Client connection string](#client-connection-string)
3939
- [Gzip support](#gzip-support)
4040
- [How to use WebProxy](#how-to-use-webproxy)
41+
- [Proxy and redirects configuration](#proxy-and-redirects-configuration)
4142

4243
## Queries
4344

@@ -1083,6 +1084,7 @@ The following options are supported:
10831084
| LogLevel | NONE | rest client verbosity level |
10841085
| ReadWriteTimeout | 10000 ms | read and write timeout |
10851086
| Timeout | 10000 ms | socket timeout |
1087+
| AllowHttpRedirects| false | Configure automatically following HTTP 3xx redirects. |
10861088

10871089
The `ReadWriteTimeout` and `Timeout` supports `ms`, `s` and `m` as unit. Default is milliseconds.
10881090

@@ -1131,6 +1133,7 @@ The following options are supported:
11311133
| logLevel | NONE | rest client verbosity level |
11321134
| readWriteTimeout | 10000 ms | read and write timeout |
11331135
| timeout | 10000 ms | socket timeout |
1136+
| allowHttpRedirects| false | Configure automatically following HTTP 3xx redirects. |
11341137

11351138
The `readWriteTimeout` and `timeout` supports `ms`, `s` and `m` as unit. Default is milliseconds.
11361139

@@ -1155,6 +1158,34 @@ var options = new InfluxDBClientOptions.Builder()
11551158
var client = InfluxDBClientFactory.Create(options);
11561159
```
11571160

1161+
### Proxy and redirects configuration
1162+
1163+
You can configure the client to tunnel requests through an HTTP proxy. To configure the proxy use `Proxy` configuration option:
1164+
1165+
```csharp
1166+
var options = new InfluxDBClientOptions.Builder()
1167+
.Url("http://localhost:8086")
1168+
.AuthenticateToken("my-token")
1169+
.Proxy(new WebProxy("http://proxyserver:80/", true))
1170+
.Build();
1171+
1172+
using var client = InfluxDBClientFactory.Create(options);
1173+
```
1174+
1175+
Client automatically **doesn't** follows HTTP redirects. You can enable redirects by `AllowRedirects` configuration option:
1176+
1177+
```csharp
1178+
var options = new InfluxDBClientOptions.Builder()
1179+
.Url("http://localhost:8086")
1180+
.AllowRedirects(true)
1181+
.Build();
1182+
1183+
using var client = InfluxDBClientFactory.Create(options);
1184+
```
1185+
1186+
> :warning: Due to a security reason `Authorization` header is not forwarded when redirect leads to a different domain.
1187+
> You can create custom `Authenticator` which change this behaviour - [see more](https://stackoverflow.com/a/28285735/1953325).
1188+
11581189
#### Log HTTP Request and Response
11591190

11601191
The Requests and Responses can be logged by changing the LogLevel. LogLevel values are None, Basic, Headers, Body. Note that

0 commit comments

Comments
 (0)