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); } } } }