Skip to content

Commit 04e3396

Browse files
committed
Merge pull request #1912 from elastic/fix/1904
Fix #1904 invoke OnRequestCompleted when exception is thrown
2 parents 4cfdabc + 8dd2186 commit 04e3396

File tree

2 files changed

+53
-37
lines changed

2 files changed

+53
-37
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,11 @@ public void BadResponse<TReturn>(ref ElasticsearchResponse<TReturn> response, Re
459459
AuditTrail = this.AuditTrail
460460
};
461461

462-
if (_settings.ThrowExceptions) throw clientException;
462+
if (_settings.ThrowExceptions)
463+
{
464+
this._settings.OnRequestCompleted?.Invoke(clientException.Response);
465+
throw clientException;
466+
}
463467

464468
if (response == null)
465469
{

src/Tests/ClientConcepts/LowLevel/Connecting.doc.cs

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@
1010
using Newtonsoft.Json;
1111
using Tests.Framework;
1212
using Tests.Framework.MockData;
13+
using Xunit;
1314

1415
namespace Tests.ClientConcepts.LowLevel
1516
{
1617
public class Connecting
1718
{
18-
/** # Connecting
19+
/** # Connecting
1920
* Connecting to *Elasticsearch* with `Elasticsearch.Net` is quite easy but has a few toggles and options worth knowing.
20-
*
21+
*
2122
* # Choosing the right connection strategy
2223
* If you simply new an `ElasticLowLevelClient`, it will be a non-failover connection to `http://localhost:9200`
2324
*/
@@ -29,9 +30,9 @@ public void InstantiateUsingAllDefaults()
2930

3031
}
3132
/**
32-
* If your Elasticsearch node does not live at `http://localhost:9200` but i.e `http://mynode.example.com:8082/apiKey`, then
33+
* If your Elasticsearch node does not live at `http://localhost:9200` but i.e `http://mynode.example.com:8082/apiKey`, then
3334
* you will need to pass in some instance of `IConnectionConfigurationValues`.
34-
*
35+
*
3536
* The easiest way to do this is:
3637
*/
3738

@@ -42,9 +43,9 @@ public void InstantiatingASingleNodeClient()
4243
var client = new ElasticLowLevelClient(config);
4344
}
4445

45-
/**
46+
/**
4647
* This however is still a non-failover connection. Meaning if that `node` goes down the operation will not be retried on any other nodes in the cluster.
47-
*
48+
*
4849
* To get a failover connection we have to pass an `IConnectionPool` instance instead of a `Uri`.
4950
*/
5051

@@ -56,12 +57,12 @@ public void InstantiatingAConnectionPoolClient()
5657
var client = new ElasticLowLevelClient(config);
5758
}
5859

59-
/**
60+
/**
6061
* Here instead of directly passing `node`, we pass a `SniffingConnectionPool` which will use our `node` to find out the rest of the available cluster nodes.
6162
* Be sure to read more about [Connection Pooling and Cluster Failover here](/elasticsearch-net/cluster-failover.html)
62-
*
63+
*
6364
* ## Options
64-
*
65+
*
6566
* Besides either passing a `Uri` or `IConnectionPool` to `ConnectionConfiguration`, you can also fluently control many more options. For instance:
6667
*/
6768

@@ -95,22 +96,22 @@ public void AvailableOptions()
9596

9697
.EnableHttpCompression()
9798
/**
98-
* Enable compressed request and reesponses from Elasticsearch (Note that nodes need to be configured
99+
* Enable compressed request and reesponses from Elasticsearch (Note that nodes need to be configured
99100
* to allow this. See the [http module settings](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/modules-http.html) for more info).
100101
*/
101102

102103
.DisableDirectStreaming()
103104
/**
104105
* By default responses are deserialized off stream to the object you tell it to.
105-
* For debugging purposes it can be very useful to keep a copy of the raw response on the result object.
106+
* For debugging purposes it can be very useful to keep a copy of the raw response on the result object.
106107
*/;
107108

108109
var result = client.Search<SearchResponse<object>>(new { size = 12 });
109110
var raw = result.ResponseBodyInBytes;
110111
/** This will only have a value if the client configuration has ExposeRawResponse set */
111112

112-
/**
113-
* Please note that this only make sense if you need a mapped response and the raw response at the same time.
113+
/**
114+
* Please note that this only make sense if you need a mapped response and the raw response at the same time.
114115
* If you need a `string` or `byte[]` response simply call:
115116
*/
116117
var stringResult = client.Search<string>(new { });
@@ -129,23 +130,23 @@ public void AvailableOptions()
129130
.RequestTimeout(TimeSpan.FromSeconds(4))
130131
/**
131132
* Sets the global maximum time a connection may take.
132-
* Please note that this is the request timeout, the builtin .NET `WebRequest` has no way to set connection timeouts
133+
* Please note that this is the request timeout, the builtin .NET `WebRequest` has no way to set connection timeouts
133134
* (see http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.timeout(v=vs.110).aspx).
134135
*/
135136

136137
.ThrowExceptions()
137138
/**
138-
* As an alternative to the C/go like error checking on `response.IsValid`, you can instead tell the client to throw
139-
* exceptions.
139+
* As an alternative to the C/go like error checking on `response.IsValid`, you can instead tell the client to throw
140+
* exceptions.
140141
*
141142
* There are three category of exceptions thay may be thrown:
142-
*
143+
*
143144
* 1) ElasticsearchClientException: These are known exceptions, either an exception that occurred in the request pipeline
144-
* (such as max retries or timeout reached, bad authentication, etc...) or Elasticsearch itself returned an error (could
145-
* not parse the request, bad query, missing field, etc...). If it is an Elasticsearch error, the `ServerError` property
146-
* on the response will contain the the actual error that was returned. The inner exception will always contain the
145+
* (such as max retries or timeout reached, bad authentication, etc...) or Elasticsearch itself returned an error (could
146+
* not parse the request, bad query, missing field, etc...). If it is an Elasticsearch error, the `ServerError` property
147+
* on the response will contain the the actual error that was returned. The inner exception will always contain the
147148
* root causing exception.
148-
*
149+
*
149150
* 2) UnexpectedElasticsearchClientException: These are unknown exceptions, for instance a response from Elasticsearch not
150151
* properly deserialized. These are usually bugs and should be reported. This excpetion also inherits from ElasticsearchClientException
151152
* so an additional catch block isn't necessary, but can be helpful in distinguishing between the two.
@@ -175,26 +176,37 @@ public void AvailableOptions()
175176
*/
176177
}
177178

178-
/**
179-
* You can pass a callback of type `Action&lt;IApiCallDetails&gt;` that can eaves drop every time a response (good or bad) is created.
179+
/**
180+
* You can pass a callback of type `Action&lt;IApiCallDetails&gt;` that can eaves drop every time a response (good or bad) is created.
180181
* If you have complex logging needs this is a good place to add that in.
181182
*/
182183
[U]
183184
public void OnRequestCompletedIsCalled()
184185
{
185186
var counter = 0;
186-
var connectionPool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
187-
var settings = new ConnectionSettings(connectionPool, new InMemoryConnection())
188-
.OnRequestCompleted(r => counter++);
189-
var client = new ElasticClient(settings);
187+
var client = TestClient.GetInMemoryClient(s => s.OnRequestCompleted(r => counter++));
190188
client.RootNodeInfo();
191189
counter.Should().Be(1);
192190
client.RootNodeInfoAsync();
193191
counter.Should().Be(2);
194192
}
195193

196-
/**
197-
* An example of using `OnRequestCompleted()` for complex logging. Remember, if you would also like
194+
[U]
195+
public void OnRequestCompletedIsCalledWhenExceptionIsThrown()
196+
{
197+
var counter = 0;
198+
var client = TestClient.GetFixedReturnClient(new { }, 500, s => s
199+
.ThrowExceptions()
200+
.OnRequestCompleted(r => counter++)
201+
);
202+
Assert.Throws<ElasticsearchClientException>(() => client.RootNodeInfo());
203+
counter.Should().Be(1);
204+
Assert.ThrowsAsync<ElasticsearchClientException>(() => client.RootNodeInfoAsync());
205+
counter.Should().Be(2);
206+
}
207+
208+
/**
209+
* An example of using `OnRequestCompleted()` for complex logging. Remember, if you would also like
198210
* to capture the request and/or response bytes, you also need to set `.DisableDirectStreaming()`
199211
* to `true`
200212
*/
@@ -264,19 +276,19 @@ public void ConfiguringSSL()
264276
{
265277
/**
266278
* ## Configuring SSL
267-
* SSL must be configured outside of the client using .NET's
279+
* SSL must be configured outside of the client using .NET's
268280
* [ServicePointManager](http://msdn.microsoft.com/en-us/library/system.net.servicepointmanager%28v=vs.110%29.aspx)
269281
* class and setting the [ServerCertificateValidationCallback](http://msdn.microsoft.com/en-us/library/system.net.servicepointmanager.servercertificatevalidationcallback.aspx)
270282
* property.
271-
*
283+
*
272284
* The bare minimum to make .NET accept self-signed SSL certs that are not in the Window's CA store would be to have the callback simply return `true`:
273285
*/
274286

275287
#if !DOTNETCORE
276288
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, errors) => true;
277289
#endif
278290
/**
279-
* However, this will accept all requests from the AppDomain to untrusted SSL sites,
291+
* However, this will accept all requests from the AppDomain to untrusted SSL sites,
280292
* therefore we recommend doing some minimal introspection on the passed in certificate.
281293
*/
282294
}
@@ -286,15 +298,15 @@ public void ConfiguringSSL()
286298
*
287299
* Please be advised that this is an expert behavior but if you need to get to the nitty gritty this can be really useful
288300
*
289-
* Create a subclass of the `JsonNetSerializer`
290-
301+
* Create a subclass of the `JsonNetSerializer`
302+
291303
*/
292304
public class MyJsonNetSerializer : JsonNetSerializer
293305
{
294306
public MyJsonNetSerializer(IConnectionSettingsValues settings) : base(settings) { }
295307

296308

297-
/**
309+
/**
298310
* Override ModifyJsonSerializerSettings if you need access to `JsonSerializerSettings`
299311
*/
300312
public int CallToModify { get; set; } = 0;
@@ -316,7 +328,7 @@ public MyJsonNetSerializer(IConnectionSettingsValues settings) : base(settings)
316328
}
317329

318330
/**
319-
* You can then register a factory on ConnectionSettings to create an instance of your subclass instead.
331+
* You can then register a factory on ConnectionSettings to create an instance of your subclass instead.
320332
* This is called once per instance of ConnectionSettings.
321333
*/
322334
[U]

0 commit comments

Comments
 (0)