Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 37 additions & 33 deletions src/Elasticsearch.Net/Transport/Sniff/SniffResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,45 @@

namespace Elasticsearch.Net
{
internal class SniffResponse
public class SniffResponse
{
//internal ctor - so that only Elasticsearch.Net can instantiate it
internal SniffResponse()
{
}

private static Regex AddressRe { get; } = new Regex(@"^((?<fqdn>[^/]+)/)?(?<ip>[^:]+):(?<port>\d+)$");
public static Regex AddressRegex { get; } = new Regex(@"^((?<fqdn>[^/]+)/)?(?<ip>[^:]+|\[[\da-fA-F:\.]+\]):(?<port>\d+)$");

public string cluster_name { get; set; }
public Dictionary<string, NodeInfo> nodes { get; set; }
internal Dictionary<string, NodeInfo> nodes { get; set; }

public IEnumerable<Node> ToNodes(bool forceHttp = false)
{
foreach (var kv in nodes.Where(n => n.Value.HttpEnabled))
foreach (var kv in nodes.Where(n => n.Value.HttpEnabled))
{
yield return new Node(this.ParseToUri(kv.Value.http?.bound_address.FirstOrDefault(), forceHttp))
{
Name = kv.Value.name,
Id = kv.Key,
MasterEligible = kv.Value.MasterEligible,
HoldsData = kv.Value.HoldsData,
};
HoldsData = kv.Value.HoldsData,
};
}
}

private Uri ParseToUri(string boundAddress, bool forceHttp)
{
if (boundAddress.IsNullOrEmpty()) return null;
var suffix = forceHttp ? "s" : string.Empty;
var match = AddressRe.Match(boundAddress);
if (!match.Success) throw new Exception($"Can not parse bound_address: {boundAddress} to Uri");
var fqdn = match.Groups["fqdn"].Value?.Trim();
var ip = match.Groups["ip"].Value?.Trim();
var port = match.Groups["port"].Value?.Trim();
var host = !fqdn.IsNullOrEmpty() ? fqdn : ip;
return new Uri($"http{suffix}://{host}:{port}");
private Uri ParseToUri(string boundAddress, bool forceHttp)
{
if (boundAddress.IsNullOrEmpty()) return null;
var suffix = forceHttp ? "s" : string.Empty;
var match = AddressRegex.Match(boundAddress);
if (!match.Success) throw new Exception($"Can not parse bound_address: {boundAddress} to Uri");

var fqdn = match.Groups["fqdn"].Value?.Trim();
var ip = match.Groups["ip"].Value?.Trim();
var port = match.Groups["port"].Value?.Trim();
var host = !fqdn.IsNullOrEmpty() ? fqdn : ip;

return new Uri($"http{suffix}://{host}:{port}");
}
}

Expand All @@ -55,21 +59,21 @@ internal class NodeInfo
public NodeInfoHttp http { get; set; }
public IDictionary<string, string> settings { get; set; }

internal bool MasterEligible => this.roles?.Contains("master") ?? false;
internal bool HoldsData => this.roles?.Contains("data") ?? false;
internal bool HttpEnabled
{
get
{
if (this.settings != null && this.settings.ContainsKey("http.enabled"))
return Convert.ToBoolean(this.settings["http.enabled"]);
return http != null;
}
}
internal bool MasterEligible => this.roles?.Contains("master") ?? false;
internal bool HoldsData => this.roles?.Contains("data") ?? false;
internal bool HttpEnabled
{
get
{
if (this.settings != null && this.settings.ContainsKey("http.enabled"))
return Convert.ToBoolean(this.settings["http.enabled"]);
return http != null;
}
}
}

internal class NodeInfoHttp
{
public IList<string> bound_address { get; set; }
internal class NodeInfoHttp
{
public IList<string> bound_address { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using FluentAssertions;
using Tests.Framework;

namespace Tests.ClientConcepts.ConnectionPooling.Sniffing
{
public class AddressParsing
{
[U]
public void IsMatched()
{
//based on examples from http://www.ietf.org/rfc/rfc2732.txt
var testcases = new[,]
{
{"[::1]:9200", "[::1]", "9200"},
{"192.168.2.1:231", "192.168.2.1", "231"},
{"[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80", "[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]", "80"},
{"[1080:0:0:0:8:800:200C:417A]:1234", "[1080:0:0:0:8:800:200C:417A]", "1234"},
{"[3ffe:2a00:100:7031::1]:1", "[3ffe:2a00:100:7031::1]", "1"},
{"[1080::8:800:200C:417A]:123", "[1080::8:800:200C:417A]", "123"},
{"[::192.9.5.5]:12", "[::192.9.5.5]", "12"},
{"[::FFFF:129.144.52.38]:80", "[::FFFF:129.144.52.38]", "80"},
{"[2010:836B:4179::836B:4179]:34533", "[2010:836B:4179::836B:4179]", "34533"}
};

for (var i = 0; i < testcases.GetLength(0); i++)
{
var address = testcases[i, 0];
var ip = testcases[i, 1];
var port = testcases[i, 2];

var match = Elasticsearch.Net.SniffResponse.AddressRegex.Match(address);

match.Success.Should().BeTrue();

match.Groups["ip"].Value.ShouldBeEquivalentTo(ip);
match.Groups["port"].Value.ShouldBeEquivalentTo(port);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public override ElasticsearchResponse<TReturn> Request<TReturn>(RequestData requ
this._cluster.SniffingRules,
requestData.RequestTimeout,
(r) => this.UpdateCluster(r.NewClusterState),
(r) => SniffResponse.Create(this._cluster.Nodes, this._cluster.SniffShouldReturnFqnd)
(r) => MockResponses.SniffResponse.Create(this._cluster.Nodes, this._cluster.SniffShouldReturnFqnd)
);
}
if (IsPingRequest(requestData))
Expand Down
1 change: 1 addition & 0 deletions src/Tests/Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@
<Compile Include="Cat\CatThreadPool\CatThreadpoolApiTests.cs" />
<Compile Include="Cat\CatThreadPool\CatThreadPoolUrlTests.cs" />
<Compile Include="ClientConcepts\ConnectionPooling\Dispose\ResponseBuilderDisposeTests.cs" />
<Compile Include="ClientConcepts\ConnectionPooling\Sniffing\AddressParsing.doc.cs" />
<Compile Include="Cluster\TaskManagement\GetTask\GetTaskApiTests.cs" />
<Compile Include="Cluster\TaskManagement\GetTask\GetTaskUrlTests.cs" />
<Compile Include="Document\Multiple\Bulk\BulkResponseParsingTests.cs" />
Expand Down