Skip to content

Commit 184eb11

Browse files
committed
fix #1657 node settings always string, fixes bad sniffs. Added integration tests for sniffing and fixed an issue with how global query strings are appended (e.g ?pretty=true)
1 parent 83c1a7a commit 184eb11

File tree

11 files changed

+221
-109
lines changed

11 files changed

+221
-109
lines changed

src/Elasticsearch.Net/Configuration/ConnectionConfiguration.cs

Lines changed: 67 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using System.ComponentModel;
44

55
namespace Elasticsearch.Net
6-
{
6+
{
77
/// <summary>
88
/// ConnectionConfiguration allows you to control how ElasticsearchClient behaves and where/how it connects
99
/// to elasticsearch
@@ -12,36 +12,41 @@ public class ConnectionConfiguration : ConnectionConfiguration<ConnectionConfigu
1212
{
1313
public static TimeSpan DefaultTimeout = TimeSpan.FromMinutes(1);
1414
public static TimeSpan DefaultPingTimeout = TimeSpan.FromSeconds(2);
15-
public static TimeSpan DefaultPingTimeoutOnSSL = TimeSpan.FromSeconds(5);
16-
15+
public static TimeSpan DefaultPingTimeoutOnSSL = TimeSpan.FromSeconds(5);
16+
1717
/// <summary>
1818
/// ConnectionConfiguration allows you to control how ElasticsearchClient behaves and where/how it connects
1919
/// to elasticsearch
2020
/// </summary>
2121
/// <param name="uri">The root of the elasticsearch node we want to connect to. Defaults to http://localhost:9200</param>
2222
public ConnectionConfiguration(Uri uri = null)
23-
: this(new SingleNodeConnectionPool(uri ?? new Uri("http://localhost:9200"))) { }
24-
23+
: this(new SingleNodeConnectionPool(uri ?? new Uri("http://localhost:9200")))
24+
{ }
25+
2526
/// <summary>
2627
/// ConnectionConfiguration allows you to control how ElasticsearchClient behaves and where/how it connects
2728
/// to elasticsearch
2829
/// </summary>
2930
/// <param name="connectionPool">A connection pool implementation that'll tell the client what nodes are available</param>
30-
public ConnectionConfiguration(IConnectionPool connectionPool)
31-
// ReSharper disable once IntroduceOptionalParameters.Global
32-
: this(connectionPool, null, null) { }
31+
public ConnectionConfiguration(IConnectionPool connectionPool)
32+
// ReSharper disable once IntroduceOptionalParameters.Global
33+
: this(connectionPool, null, null)
34+
{ }
3335

34-
public ConnectionConfiguration(IConnectionPool connectionPool, IConnection connection)
35-
// ReSharper disable once IntroduceOptionalParameters.Global
36-
: this(connectionPool, connection, null) { }
36+
public ConnectionConfiguration(IConnectionPool connectionPool, IConnection connection)
37+
// ReSharper disable once IntroduceOptionalParameters.Global
38+
: this(connectionPool, connection, null)
39+
{ }
3740

3841
public ConnectionConfiguration(IConnectionPool connectionPool, IElasticsearchSerializer serializer)
39-
: this(connectionPool, null, serializer) { }
40-
41-
// ReSharper disable once MemberCanBePrivate.Global
42-
// eventhough we use don't use this we very much would like to expose this constructor
42+
: this(connectionPool, null, serializer)
43+
{ }
44+
45+
// ReSharper disable once MemberCanBePrivate.Global
46+
// eventhough we use don't use this we very much would like to expose this constructor
4347
public ConnectionConfiguration(IConnectionPool connectionPool, IConnection connection, IElasticsearchSerializer serializer)
44-
: base(connectionPool, connection, serializer) { }
48+
: base(connectionPool, connection, serializer)
49+
{ }
4550
}
4651

4752
[Browsable(false)]
@@ -73,7 +78,7 @@ public abstract class ConnectionConfiguration<T> : IConnectionConfigurationValue
7378
private string _proxyUsername;
7479
string IConnectionConfigurationValues.ProxyUsername => _proxyUsername;
7580

76-
private string _proxyPassword;
81+
private string _proxyPassword;
7782
string IConnectionConfigurationValues.ProxyPassword => _proxyPassword;
7883

7984
private bool _disablePings;
@@ -112,7 +117,7 @@ public abstract class ConnectionConfiguration<T> : IConnectionConfigurationValue
112117
private bool _throwExceptions;
113118
bool IConnectionConfigurationValues.ThrowExceptions => _throwExceptions;
114119

115-
private static void DefaultApiCallHandler(IApiCallDetails status) {}
120+
private static void DefaultApiCallHandler(IApiCallDetails status) { }
116121
Action<IApiCallDetails> _apiCallHandler = DefaultApiCallHandler;
117122
Action<IApiCallDetails> IConnectionConfigurationValues.ApiCallHandler => _apiCallHandler;
118123

@@ -135,13 +140,13 @@ private static void DefaultApiCallHandler(IApiCallDetails status) {}
135140
IConnection IConnectionConfigurationValues.Connection => _connection;
136141

137142
[System.Diagnostics.CodeAnalysis.SuppressMessage(
138-
"Potential Code Quality Issues", "RECS0021:Warns about calls to virtual member functions occuring in the constructor",
143+
"Potential Code Quality Issues", "RECS0021:Warns about calls to virtual member functions occuring in the constructor",
139144
Justification = "We want the virtual method to run on most derived")]
140145
protected ConnectionConfiguration(IConnectionPool connectionPool, IConnection connection, IElasticsearchSerializer serializer)
141146
{
142147
this._connectionPool = connectionPool;
143-
this._connection = connection ?? new HttpConnection();
144-
// ReSharper disable once VirtualMemberCallInContructor
148+
this._connection = connection ?? new HttpConnection();
149+
// ReSharper disable once VirtualMemberCallInContructor
145150
this._serializer = serializer ?? this.DefaultSerializer();
146151

147152
this._requestTimeout = ConnectionConfiguration.DefaultTimeout;
@@ -157,95 +162,95 @@ protected ConnectionConfiguration(IConnectionPool connectionPool, IConnection co
157162
public T EnableTcpKeepAlive(TimeSpan keepAliveTime, TimeSpan keepAliveInterval) =>
158163
Assign(a => { this._keepAliveTime = keepAliveTime; this._keepAliveInterval = keepAliveInterval; });
159164

160-
public T MaximumRetries(int maxRetries) => Assign(a => a._maxRetries = maxRetries);
161-
165+
public T MaximumRetries(int maxRetries) => Assign(a => a._maxRetries = maxRetries);
166+
162167
/// <summary>
163168
/// On connection pools that support reseeding setting this to true (default) will resniff the cluster when a call fails
164169
/// </summary>
165-
public T SniffOnConnectionFault(bool sniffsOnConnectionFault = true) => Assign(a => a._sniffOnConnectionFault = sniffsOnConnectionFault);
166-
170+
public T SniffOnConnectionFault(bool sniffsOnConnectionFault = true) => Assign(a => a._sniffOnConnectionFault = sniffsOnConnectionFault);
171+
167172
/// <summary>
168173
/// Enables sniffing on first usage of a connection pool if that pool supports reseeding, defaults to true
169174
/// </summary>
170-
public T SniffOnStartup(bool sniffsOnStartup = true) => Assign(a => a._sniffOnStartup = sniffsOnStartup);
171-
175+
public T SniffOnStartup(bool sniffsOnStartup = true) => Assign(a => a._sniffOnStartup = sniffsOnStartup);
176+
172177
/// <summary>
173178
/// Set the duration after which a cluster state is considered stale and a sniff should be performed again.
174179
/// An IConnectionPool has to signal it supports reseeding otherwise sniffing will never happen.
175180
/// Defaults to 1 hour.
176181
/// Set to null to disable completely. Sniffing will only ever happen on ConnectionPools that return true for SupportsReseeding
177182
/// </summary>
178183
/// <param name="sniffLifeSpan">The duration a clusterstate is considered fresh, set to null to disable periodic sniffing</param>
179-
public T SniffLifeSpan(TimeSpan? sniffLifeSpan) => Assign(a => a._sniffLifeSpan = sniffLifeSpan);
180-
184+
public T SniffLifeSpan(TimeSpan? sniffLifeSpan) => Assign(a => a._sniffLifeSpan = sniffLifeSpan);
185+
181186
/// <summary>
182187
/// Enable gzip compressed requests and responses, do note that you need to configure elasticsearch to set this
183188
/// <para>http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/modules-http.html"</para>
184189
/// </summary>
185190
public T EnableHttpCompression(bool enabled = true) => Assign(a => a._enableHttpCompression = enabled);
186191

187-
public T DisableAutomaticProxyDetection(bool disable = true) => Assign(a => a._disableAutomaticProxyDetection = disable);
188-
192+
public T DisableAutomaticProxyDetection(bool disable = true) => Assign(a => a._disableAutomaticProxyDetection = disable);
193+
189194
/// <summary>
190195
/// Instead of following a c/go like error checking on response.IsValid always throw an exception
191196
/// on the client when a call resulted in an exception on either the client or the Elasticsearch server.
192197
/// <para>Reasons for such exceptions could be search parser errors, index missing exceptions, etc...</para>
193198
/// </summary>
194-
public T ThrowExceptions(bool alwaysThrow = true) => Assign(a => a._throwExceptions = alwaysThrow);
195-
199+
public T ThrowExceptions(bool alwaysThrow = true) => Assign(a => a._throwExceptions = alwaysThrow);
200+
196201
/// <summary>
197202
/// When a node is used for the very first time or when it's used for the first time after it has been marked dead
198203
/// a ping with a very low timeout is send to the node to make sure that when it's still dead it reports it as fast as possible.
199204
/// You can disable these pings globally here if you rather have it fail on the possible slower original request
200205
/// </summary>
201-
public T DisablePing(bool disable = true) => Assign(a => a._disablePings = disable);
202-
206+
public T DisablePing(bool disable = true) => Assign(a => a._disablePings = disable);
207+
203208
/// <summary>
204209
/// This NameValueCollection will be appended to every url NEST calls, great if you need to pass i.e an API key.
205210
/// </summary>
206-
public T GlobalQueryStringParameters(NameValueCollection queryStringParameters) => Assign(a => a._queryString.Add(queryStringParameters));
207-
211+
public T GlobalQueryStringParameters(NameValueCollection queryStringParameters) => Assign(a => a._queryString.Add(queryStringParameters));
212+
208213
/// <summary>
209214
/// a NameValueCollection that will be send as headers for each request
210215
/// </summary>
211-
public T GlobalHeaders(NameValueCollection headers) => Assign(a => a._headers.Add(headers));
212-
216+
public T GlobalHeaders(NameValueCollection headers) => Assign(a => a._headers.Add(headers));
217+
213218
/// <summary>
214219
/// Sets the default timeout in milliseconds for each request to Elasticsearch.
215220
/// NOTE: You can set this to a high value here, and specify the timeout on Elasticsearch's side.
216221
/// </summary>
217222
/// <param name="timeout">time out in milliseconds</param>
218-
public T RequestTimeout(TimeSpan timeout) => Assign(a => a._requestTimeout = timeout);
219-
223+
public T RequestTimeout(TimeSpan timeout) => Assign(a => a._requestTimeout = timeout);
224+
220225
/// <summary>
221226
/// Sets the default ping timeout in milliseconds for ping requests, which are used
222227
/// to determine whether a node is alive. Pings should fail as fast as possible.
223228
/// </summary>
224229
/// <param name="timeout">The ping timeout in milliseconds defaults to 1000, or 2000 is using SSL.</param>
225-
public T PingTimeout(TimeSpan timeout) => Assign(a => a._pingTimeout = timeout);
226-
230+
public T PingTimeout(TimeSpan timeout) => Assign(a => a._pingTimeout = timeout);
231+
227232
/// <summary>
228233
/// Sets the default dead timeout factor when a node has been marked dead.
229234
/// </summary>
230235
/// <remarks>Some connection pools may use a flat timeout whilst others take this factor and increase it exponentially</remarks>
231236
/// <param name="timeout"></param>
232-
public T DeadTimeout(TimeSpan timeout) => Assign(a => a._deadTimeout = timeout);
233-
237+
public T DeadTimeout(TimeSpan timeout) => Assign(a => a._deadTimeout = timeout);
238+
234239
/// <summary>
235240
/// Sets the maximum time a node can be marked dead.
236241
/// Different implementations of IConnectionPool may choose a different default.
237242
/// </summary>
238243
/// <param name="timeout">The timeout in milliseconds</param>
239-
public T MaxDeadTimeout(TimeSpan timeout) => Assign(a => a._maxDeadTimeout = timeout);
240-
244+
public T MaxDeadTimeout(TimeSpan timeout) => Assign(a => a._maxDeadTimeout = timeout);
245+
241246
/// <summary>
242247
/// Limits the total runtime including retries separately from <see cref="RequestTimeout"/>
243248
/// <pre>
244249
/// When not specified defaults to <see cref="RequestTimeout"/> which itself defaults to 60seconds
245250
/// </pre>
246251
/// </summary>
247-
public T MaxRetryTimeout(TimeSpan maxRetryTimeout) => Assign(a => a._maxRetryTimeout = maxRetryTimeout);
248-
252+
public T MaxRetryTimeout(TimeSpan maxRetryTimeout) => Assign(a => a._maxRetryTimeout = maxRetryTimeout);
253+
249254
/// <summary>
250255
/// If your connection has to go through proxy use this method to specify the proxy url
251256
/// </summary>
@@ -256,32 +261,33 @@ public T Proxy(Uri proxyAdress, string username, string password)
256261
this._proxyUsername = username;
257262
this._proxyPassword = password;
258263
return (T)this;
259-
}
260-
264+
}
265+
261266
/// <summary>
262267
/// Forces all requests to have ?pretty=true, causing elasticsearch to return formatted json.
263268
/// Also forces the client to send out formatted json. Defaults to false
264269
/// </summary>
265-
public T PrettyJson(bool b = true) => Assign(a => {
270+
public T PrettyJson(bool b = true) => Assign(a =>
271+
{
266272
this._prettyJson = b;
267273
if (!b && this._queryString["pretty"] != null) this._queryString.Remove("pretty");
268274
else if (b && this._queryString["pretty"] == null)
269-
this.GlobalQueryStringParameters(new NameValueCollection { { "pretty", b.ToString().ToLowerInvariant() } });
270-
});
271-
275+
this.GlobalQueryStringParameters(new NameValueCollection { { "pretty", b.ToString().ToLowerInvariant() } });
276+
});
277+
272278
/// <summary>
273279
/// Make sure the reponse bytes are always available on the ElasticsearchResponse object
274280
/// <para>Note: that depending on the registered serializer this may cause the respond to be read in memory first</para>
275281
/// </summary>
276-
public T DisableDirectStreaming(bool b = true) => Assign(a => a._disableDirectStreaming = b);
277-
282+
public T DisableDirectStreaming(bool b = true) => Assign(a => a._disableDirectStreaming = b);
283+
278284
/// <summary>
279285
/// Global callback for every response that NEST receives, useful for custom logging.
280286
/// Calling this multiple times will register multiple listeners
281287
/// </summary>
282288
public T ConnectionStatusHandler(Action<IApiCallDetails> handler) =>
283-
Assign(a => a._apiCallHandler += handler ?? DefaultApiCallHandler);
284-
289+
Assign(a => a._apiCallHandler += handler ?? DefaultApiCallHandler);
290+
285291
/// <summary>
286292
/// Basic access authentication credentials to specify with all requests.
287293
/// </summary>
@@ -293,8 +299,8 @@ public T BasicAuthentication(string userName, string password)
293299
Password = password
294300
};
295301
return (T)this;
296-
}
297-
302+
}
303+
298304
/// <summary>
299305
/// Allows for requests to be pipelined. http://en.wikipedia.org/wiki/HTTP_pipelining
300306
/// <para>Note: HTTP pipelining must also be enabled in Elasticsearch for this to work properly.</para>

src/Elasticsearch.Net/Transport/Pipeline/RequestData.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public RequestData(HttpMethod method, string path, PostData<object> data, IConne
8484

8585
private string CreatePathWithQueryStrings(string path, IConnectionConfigurationValues global, IRequestParameters request = null)
8686
{
87+
8788
//Make sure we append global query string as well the request specific query string parameters
8889
var copy = new NameValueCollection(global.QueryStringParameters);
8990
var formatter = new UrlFormatProvider(this.ConnectionSettings);
@@ -92,7 +93,11 @@ private string CreatePathWithQueryStrings(string path, IConnectionConfigurationV
9293
if (!copy.HasKeys()) return path;
9394

9495
var queryString = copy.ToQueryString();
95-
path += queryString;
96+
var tempUri = new Uri("http://localhost:9200/" + path).Purify();
97+
if (tempUri.Query.IsNullOrEmpty())
98+
path += queryString;
99+
else
100+
path += "&" + queryString.Substring(1, queryString.Length - 1);
96101
return path;
97102
}
98103
}

src/Elasticsearch.Net/Transport/Sniff/SniffResponse.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ internal class SniffNode
3232
public string ip { get; set; }
3333
public string version { get; set; }
3434
public string build { get; set; }
35-
public IDictionary<string, object> settings { get; set; }
35+
public IDictionary<string, string> settings { get; set; }
3636

37-
internal bool MasterEligable => !((this.settings?.ContainsKey("node.master")).GetValueOrDefault(false) && ((bool)this.settings["node.master"]) == false);
38-
internal bool HoldsData => !((this.settings?.ContainsKey("node.data")).GetValueOrDefault(false) && ((bool)this.settings["node.data"]) == false);
37+
internal bool MasterEligable => !((this.settings?.ContainsKey("node.master")).GetValueOrDefault(false) && Convert.ToBoolean(this.settings["node.master"]) == false);
38+
internal bool HoldsData => !((this.settings?.ContainsKey("node.data")).GetValueOrDefault(false) && Convert.ToBoolean(this.settings["node.data"]) == false);
3939
}
4040

4141
}

src/Nest/IElasticClient.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ namespace Nest
44
{
55
public partial interface IElasticClient
66
{
7+
IConnectionSettingsValues ConnectionSettings { get; }
78
IElasticsearchSerializer Serializer { get; }
89
IElasticsearchClient Raw { get; }
910
ElasticInferrer Infer { get; }

0 commit comments

Comments
 (0)