using System;
using System.Collections.Generic;
using System.Configuration;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text.RegularExpressions;
using System.Web;
using InfluxDB.Client.Configurations;
using InfluxDB.Client.Core;
using InfluxDB.Client.Core.Exceptions;
using InfluxDB.Client.Writes;
namespace InfluxDB.Client
{
///
/// InfluxDBClientOptions are used to configure the InfluxDB 2.x connections.
///
public class InfluxDBClientOptions
{
private static readonly Regex DurationRegex = new Regex(@"^(?\d+)(?[a-zA-Z]{0,2})$",
RegexOptions.ExplicitCapture |
RegexOptions.Compiled |
RegexOptions.CultureInvariant |
RegexOptions.RightToLeft);
private string _token;
private string _url;
private TimeSpan _timeout;
private LogLevel _logLevel;
private string _username;
private string _password;
private IWebProxy _webProxy;
private bool _allowHttpRedirects;
private bool _verifySsl;
private X509CertificateCollection _clientCertificates;
///
/// Set the url to connect the InfluxDB.
///
public string Url
{
get => _url;
private set
{
Arguments.CheckNonEmptyString(value, "Url");
_url = value;
}
}
///
/// Set the timespan to wait before the HTTP request times out.
///
public TimeSpan Timeout
{
get => _timeout;
set
{
Arguments.CheckNotNull(value, "Timeout");
_timeout = value;
}
}
///
/// Set the log level for the request and response information.
///
/// - Basic - Logs request and response lines.
/// - Body - Logs request and response lines including headers and body (if present). Note that applying the `Body` LogLevel will disable chunking while streaming and will load the whole response into memory.
/// - Headers - Logs request and response lines including headers.
/// - None - Disable logging.
///
///
public LogLevel LogLevel
{
get => _logLevel;
set
{
Arguments.CheckNotNull(value, "LogLevel");
_logLevel = value;
}
}
///
/// The scheme uses to Authentication.
///
public AuthenticationScheme AuthScheme { get; private set; }
///
/// Setup authorization by .
///
public string Token
{
get => _token;
set
{
_token = value;
Arguments.CheckNonEmptyString(_token, "token");
AuthScheme = AuthenticationScheme.Token;
}
}
///
/// Setup authorization by .
///
public string Username
{
get => _username;
set
{
Arguments.CheckNonEmptyString(value, "Username");
_username = value;
if (!string.IsNullOrEmpty(_username) && !string.IsNullOrEmpty(Password))
{
AuthScheme = AuthenticationScheme.Session;
}
}
}
///
/// Setup authorization by .
///
public string Password
{
get => _password;
set
{
Arguments.CheckNotNull(value, "Password");
_password = value;
if (!string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(_password))
{
AuthScheme = AuthenticationScheme.Session;
}
}
}
///
/// Specify the default destination organization for writes and queries.
///
public string Org { get; set; }
///
/// Specify the default destination bucket for writes.
///
public string Bucket { get; set; }
///
/// Specify the WebProxy instance to use by the WebRequest to connect to external InfluxDB.
///
public IWebProxy WebProxy
{
get => _webProxy;
set
{
Arguments.CheckNotNull(value, "WebProxy");
_webProxy = value;
}
}
///
/// Configure automatically following HTTP 3xx redirects.
///
public bool AllowHttpRedirects
{
get => _allowHttpRedirects;
set
{
Arguments.CheckNotNull(value, "AllowHttpRedirects");
_allowHttpRedirects = value;
}
}
///
/// Ignore Certificate Validation Errors when `false`.
///
public bool VerifySsl
{
get => _verifySsl;
set
{
Arguments.CheckNotNull(value, "VerifySsl");
_verifySsl = value;
}
}
///
/// Callback function for handling the remote SSL Certificate Validation.
/// The callback takes precedence over `VerifySsl`.
///
public RemoteCertificateValidationCallback VerifySslCallback { get; set; }
///
/// Set X509CertificateCollection to be sent with HTTP requests
///
public X509CertificateCollection ClientCertificates
{
get => _clientCertificates;
set
{
Arguments.CheckNotNull(value, "ClientCertificates");
_clientCertificates = value;
}
}
///
/// The setting for store data point: default values, threshold, ...
///
public PointSettings PointSettings { get; }
///
/// Default tags that will be use for writes by Point and POJO.
///
public Dictionary DefaultTags
{
get => PointSettings.DefaultTags;
set => PointSettings.DefaultTags = value;
}
///
/// Add default tag that will be use for writes by Point and POJO.
///
/// The expressions can be:
///
/// - "California Miner" - static value
/// - "${version}" - application settings
/// - "${env.hostname}" - environment property
///
///
///
/// the tag name
/// the tag value expression
public void AddDefaultTag(string tagName, string expression)
{
Arguments.CheckNotNull(tagName, nameof(tagName));
PointSettings.AddDefaultTag(tagName, expression);
}
///
/// Add default tags that will be use for writes by Point and POJO.
///
///
/// tags dictionary
public void AddDefaultTags(Dictionary tags)
{
foreach (var tag in tags)
{
Arguments.CheckNotNull(tag.Key, "TagName");
PointSettings.AddDefaultTag(tag.Key, tag.Value);
}
}
///
/// Create an instance of InfluxDBClientOptions. The url could be a connection string with various configurations.
///
/// e.g.: "http://localhost:8086?timeout=5000&logLevel=BASIC
/// The following options are supported:
///
/// - Timeout - timespan to wait before the HTTP request times out
/// - LogLevel - log level for the request and response information
/// - Token - setup authorization by
/// - Username - with Password property setup authorization by
/// - Password - with Username property setup authorization by
/// - Org - specify the default destination organization for writes and queries
/// - Bucket - specify the default destination bucket for writes
/// - WebProxy - specify the WebProxy instance to use by the WebRequest to connect to external InfluxDB.
/// - AllowHttpRedirects - configure automatically following HTTP 3xx redirects
/// - VerifySsl - ignore Certificate Validation Errors when `false`
/// - VerifySslCallback - callback function for handling the remote SSL Certificate Validation. The callback takes precedence over `VerifySsl`
/// - ClientCertificates - set X509CertificateCollection to be sent with HTTP requests
/// - DefaultTags - tags that will be use for writes by Point and POJO
///
///
///
/// url to connect the InfluxDB
public InfluxDBClientOptions(string url)
{
if (string.IsNullOrEmpty(url))
{
throw new ArgumentException("The url to connect the InfluxDB has to be defined.");
}
var uri = new Uri(url);
Url = uri.GetLeftPart(UriPartial.Path);
if (string.IsNullOrEmpty(Url))
{
throw new ArgumentException("The url to connect the InfluxDB has to be defined.");
}
var query = HttpUtility.ParseQueryString(uri.Query);
Org = query.Get("org");
Bucket = query.Get("bucket");
AllowHttpRedirects = Convert.ToBoolean(query.Get("allowHttpRedirects"));
var verifySslValue = query.Get("verifySsl");
var token = query.Get("token");
var logLevel = query.Get("logLevel");
var timeout = query.Get("timeout");
VerifySsl = Convert.ToBoolean(string.IsNullOrEmpty(verifySslValue) ? "true" : verifySslValue);
if (!string.IsNullOrWhiteSpace(token))
{
Token = token;
}
if (!string.IsNullOrWhiteSpace(logLevel))
{
Enum.TryParse(logLevel, true, out LogLevel logLevelValue);
LogLevel = logLevelValue;
}
if (!string.IsNullOrWhiteSpace(timeout))
{
Timeout = ToTimeout(timeout);
}
if (Timeout == TimeSpan.Zero || Timeout == TimeSpan.FromMilliseconds(0))
{
Timeout = TimeSpan.FromSeconds(10);
}
PointSettings = new PointSettings();
}
///
/// Configure InfluxDBClientOptions via App.config.
///
/// Name of configuration section. Useful for tests.
///
public static InfluxDBClientOptions LoadConfig(string sectionName = "influx2")
{
var config = (Influx2)ConfigurationManager.GetSection(sectionName);
if (config == null)
{
const string message = "The configuration doesn't contains a 'influx2' section. " +
"The minimal configuration should contains an url of InfluxDB. " +
"For more details see: " +
"https://github.com/influxdata/influxdb-client-csharp/blob/master/Client/README.md#client-configuration-file";
throw new ConfigurationErrorsException(message);
}
var url = config.Url;
var org = config.Org;
var bucket = config.Bucket;
var token = config.Token;
var logLevel = config.LogLevel;
var timeout = config.Timeout;
var allowHttpRedirects = config.AllowHttpRedirects;
var verifySsl = config.VerifySsl;
var influxDbClientOptions = new InfluxDBClientOptions(url)
{
Org = org,
Bucket = bucket,
AllowHttpRedirects = allowHttpRedirects,
VerifySsl = verifySsl
};
if (!string.IsNullOrWhiteSpace(token))
{
influxDbClientOptions.Token = token;
}
if (!string.IsNullOrWhiteSpace(logLevel))
{
Enum.TryParse(logLevel, true, out LogLevel logLevelValue);
influxDbClientOptions.LogLevel = logLevelValue;
}
if (!string.IsNullOrWhiteSpace(timeout))
{
influxDbClientOptions.Timeout = ToTimeout(timeout);
}
if (influxDbClientOptions.Timeout == TimeSpan.Zero ||
influxDbClientOptions.Timeout == TimeSpan.FromMilliseconds(0))
{
influxDbClientOptions.Timeout = TimeSpan.FromSeconds(10);
}
var tags = config.Tags;
if (tags != null)
{
foreach (Influx2.TagElement o in tags)
influxDbClientOptions.PointSettings.AddDefaultTag(o.Name, o.Value);
}
return influxDbClientOptions;
}
private InfluxDBClientOptions(Builder builder)
{
Arguments.CheckNotNull(builder, nameof(builder));
Url = builder.UrlString;
LogLevel = builder.LogLevelValue;
AuthScheme = builder.AuthScheme;
switch (builder.AuthScheme)
{
case AuthenticationScheme.Token:
Token = builder.Token;
break;
case AuthenticationScheme.Session:
Username = builder.Username;
Password = builder.Password;
break;
}
Org = builder.OrgString;
Bucket = builder.BucketString;
Timeout = builder.Timeout;
AllowHttpRedirects = builder.AllowHttpRedirects;
PointSettings = builder.PointSettings;
VerifySsl = builder.VerifySslCertificates;
VerifySslCallback = builder.VerifySslCallback;
if (builder.WebProxy != null)
{
WebProxy = builder.WebProxy;
}
if (builder.CertificateCollection != null)
{
ClientCertificates = builder.CertificateCollection;
}
}
private static TimeSpan ToTimeout(string value)
{
var matcher = DurationRegex.Match(value);
if (!matcher.Success)
{
throw new InfluxException($"'{value}' is not a valid duration");
}
var amount = matcher.Groups["Amount"].Value;
var unit = matcher.Groups["Unit"].Value;
TimeSpan duration;
switch (string.IsNullOrWhiteSpace(unit) ? "ms" : unit.ToLower())
{
case "ms":
duration = TimeSpan.FromMilliseconds(double.Parse(amount));
break;
case "s":
duration = TimeSpan.FromSeconds(double.Parse(amount));
break;
case "m":
duration = TimeSpan.FromMinutes(double.Parse(amount));
break;
default:
throw new InfluxException($"unknown unit for '{value}'");
}
return duration;
}
///
/// The scheme uses to Authentication.
///
public enum AuthenticationScheme
{
///
/// Anonymous.
///
Anonymous,
///
/// Basic auth.
///
Session,
///
/// Authentication token.
///
Token
}
///
/// A builder for .
///
public sealed class Builder
{
internal string UrlString;
internal LogLevel LogLevelValue;
internal AuthenticationScheme AuthScheme;
internal string Token;
internal string Username;
internal string Password;
internal TimeSpan Timeout;
internal string OrgString;
internal string BucketString;
internal IWebProxy WebProxy;
internal bool AllowHttpRedirects;
internal bool VerifySslCertificates = true;
internal RemoteCertificateValidationCallback VerifySslCallback;
internal X509CertificateCollection CertificateCollection;
internal PointSettings PointSettings = new PointSettings();
public static Builder CreateNew()
{
return new Builder();
}
///
/// Set the url to connect the InfluxDB.
///
/// the url to connect the InfluxDB. It must be defined.
///
public Builder Url(string url)
{
Arguments.CheckNonEmptyString(url, nameof(url));
UrlString = url;
return this;
}
///
/// Set the log level for the request and response information.
///
/// The log level for the request and response information.
///
public Builder LogLevel(LogLevel logLevel)
{
Arguments.CheckNotNull(logLevel, nameof(logLevel));
LogLevelValue = logLevel;
return this;
}
///
/// Set the timespan to wait before the HTTP request times out.
///
/// The timespan to wait before the HTTP request times out. It must be defined.
///
public Builder TimeOut(TimeSpan timeout)
{
Arguments.CheckNotNull(timeout, nameof(timeout));
Timeout = timeout;
return this;
}
///
/// Setup authorization by .
///
/// the username to use in the basic auth
/// the password to use in the basic auth
///
public Builder Authenticate(string username,
char[] password)
{
Arguments.CheckNonEmptyString(username, "username");
Arguments.CheckNotNull(password, "password");
AuthScheme = AuthenticationScheme.Session;
Username = username;
Password = new string(password);
return this;
}
///
/// Setup authorization by .
///
/// the token to use for the authorization
///
public Builder AuthenticateToken(char[] token)
{
Arguments.CheckNotNull(token, "token");
AuthScheme = AuthenticationScheme.Token;
Token = new string(token);
return this;
}
///
/// Setup authorization by .
///
/// the token to use for the authorization
///
public Builder AuthenticateToken(string token)
{
Arguments.CheckNonEmptyString(token, "token");
return AuthenticateToken(token.ToCharArray());
}
///
/// Specify the default destination organization for writes and queries.
///
/// the default destination organization for writes and queries
///
public Builder Org(string org)
{
OrgString = org;
return this;
}
///
/// Specify the default destination bucket for writes.
///
/// default destination bucket for writes
///
public Builder Bucket(string bucket)
{
BucketString = bucket;
return this;
}
///
/// Add default tag that will be use for writes by Point and POJO.
///
///
/// The expressions can be:
///
/// - "California Miner" - static value
/// - "${version}" - application settings
/// - "${env.hostname}" - environment property
///
///
///
/// the tag name
/// the tag value expression
///
public Builder AddDefaultTag(string tagName, string expression)
{
Arguments.CheckNotNull(tagName, nameof(tagName));
PointSettings.AddDefaultTag(tagName, expression);
return this;
}
///
/// Specify the WebProxy instance to use by the WebRequest to connect to external InfluxDB.
///
/// The WebProxy to use to access the InfluxDB.
///
public Builder Proxy(IWebProxy webProxy)
{
Arguments.CheckNotNull(webProxy, nameof(webProxy));
WebProxy = webProxy;
return this;
}
///
/// Configure automatically following HTTP 3xx redirects.
///
/// configure HTTP redirects
///
public Builder AllowRedirects(bool allowHttpRedirects)
{
Arguments.CheckNotNull(allowHttpRedirects, nameof(allowHttpRedirects));
AllowHttpRedirects = allowHttpRedirects;
return this;
}
///
/// Ignore Certificate Validation Errors when `false`.
///
/// validates Certificates
///
public Builder VerifySsl(bool verifySsl)
{
Arguments.CheckNotNull(verifySsl, nameof(verifySsl));
VerifySslCertificates = verifySsl;
return this;
}
///
/// Callback function for handling the remote SSL Certificate Validation.
/// The callback takes precedence over `VerifySsl`.
///
///
///
public Builder RemoteCertificateValidationCallback(RemoteCertificateValidationCallback callback)
{
VerifySslCallback = callback;
return this;
}
///
/// Set X509CertificateCollection to be sent with HTTP requests
///
/// certificate collections
///
public Builder ClientCertificates(X509CertificateCollection clientCertificates)
{
Arguments.CheckNotNull(clientCertificates, nameof(clientCertificates));
CertificateCollection = clientCertificates;
return this;
}
///
/// Configure Builder via App.config.
///
/// Name of configuration section. Useful for tests.
///
internal Builder LoadConfig(string sectionName = "influx2")
{
var config = (Influx2)ConfigurationManager.GetSection(sectionName);
if (config == null)
{
const string message = "The configuration doesn't contains a 'influx2' section. " +
"The minimal configuration should contains an url of InfluxDB. " +
"For more details see: " +
"https://github.com/influxdata/influxdb-client-csharp/blob/master/Client/README.md#client-configuration-file";
throw new ConfigurationErrorsException(message);
}
var url = config.Url;
var org = config.Org;
var bucket = config.Bucket;
var token = config.Token;
var logLevel = config.LogLevel;
var timeout = config.Timeout;
var allowHttpRedirects = config.AllowHttpRedirects;
var verifySsl = config.VerifySsl;
var tags = config.Tags;
if (tags != null)
{
foreach (Influx2.TagElement o in tags) AddDefaultTag(o.Name, o.Value);
}
return Configure(url, org, bucket, token, logLevel, timeout, allowHttpRedirects, verifySsl);
}
///
/// Configure Builder via connection string.
///
/// connection string with various configurations
///
public Builder ConnectionString(string connectionString)
{
Arguments.CheckNonEmptyString(connectionString, nameof(connectionString));
var uri = new Uri(connectionString);
var url = uri.GetLeftPart(UriPartial.Path);
var query = HttpUtility.ParseQueryString(uri.Query);
var org = query.Get("org");
var bucket = query.Get("bucket");
var token = query.Get("token");
var logLevel = query.Get("logLevel");
var timeout = query.Get("timeout");
var allowHttpRedirects = Convert.ToBoolean(query.Get("allowHttpRedirects"));
var verifySslValue = query.Get("verifySsl");
var verifySsl = Convert.ToBoolean(string.IsNullOrEmpty(verifySslValue) ? "true" : verifySslValue);
return Configure(url, org, bucket, token, logLevel, timeout, allowHttpRedirects, verifySsl);
}
private Builder Configure(string url, string org, string bucket, string token, string logLevel,
string timeout, bool allowHttpRedirects = false, bool verifySsl = true)
{
Url(url);
Org(org);
Bucket(bucket);
if (!string.IsNullOrWhiteSpace(token))
{
AuthenticateToken(token);
}
if (!string.IsNullOrWhiteSpace(logLevel))
{
Enum.TryParse(logLevel, true, out LogLevelValue);
}
if (!string.IsNullOrWhiteSpace(timeout))
{
TimeOut(ToTimeout(timeout));
}
AllowRedirects(allowHttpRedirects);
VerifySsl(verifySsl);
return this;
}
///
/// Build an instance of InfluxDBClientOptions.
///
///
/// If url is not defined.
public InfluxDBClientOptions Build()
{
if (string.IsNullOrEmpty(UrlString))
{
throw new InvalidOperationException("The url to connect the InfluxDB has to be defined.");
}
if (Timeout == TimeSpan.Zero || Timeout == TimeSpan.FromMilliseconds(0))
{
Timeout = TimeSpan.FromSeconds(10);
}
return new InfluxDBClientOptions(this);
}
}
}
}