logstash-output-http 5.2.5 → 5.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/docs/index.asciidoc +68 -11
- data/lib/logstash/outputs/http.rb +29 -4
- data/logstash-output-http.gemspec +2 -2
- data/spec/outputs/http_spec.rb +235 -42
- metadata +5 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6f8c174b5f5725b3dff206924edcff21ebc71899e386c1a301860dba438bbec5
|
4
|
+
data.tar.gz: dd6f158c15357a1dee6bfc3ad6e0faa4f6080c3fc2a89b6050b188bd3d016d4a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2d668381891939636b2462361fe0011d5ced8461b5a1a5e7f2662aa765e4672b6377e2dd37a05073eaa8e0602d60632496fa48ec9c42ab5063839f02214f2e9d
|
7
|
+
data.tar.gz: b3ff1de393aee131f18a1d8d75f05389f6a74642e695626b1e9b4b03eec99b7a26017cea50b97315f35594013650785a8b3e11bfcc7acd4e5d32ed118340f208
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
## 5.4.1
|
2
|
+
- Fix retry indefinitely in termination process. This feature requires Logstash 8.1 [#129](https://github.com/logstash-plugins/logstash-output-http/pull/129)
|
3
|
+
- Docs: Add retry policy description [#130](https://github.com/logstash-plugins/logstash-output-http/pull/130)
|
4
|
+
|
5
|
+
## 5.4.0
|
6
|
+
- Introduce retryable unknown exceptions for "connection reset by peer" and "timeout" [#127](https://github.com/logstash-plugins/logstash-output-http/pull/127)
|
7
|
+
|
8
|
+
## 5.3.0
|
9
|
+
- Feat: support ssl_verification_mode option [#126](https://github.com/logstash-plugins/logstash-output-http/pull/126)
|
10
|
+
|
1
11
|
## 5.2.5
|
2
12
|
- Reduce amount of default logging on a failed request [#122](https://github.com/logstash-plugins/logstash-output-http/pull/122)
|
3
13
|
|
data/docs/index.asciidoc
CHANGED
@@ -31,6 +31,40 @@ guaranteed!
|
|
31
31
|
|
32
32
|
Beware, this gem does not yet support codecs. Please use the 'format' option for now.
|
33
33
|
|
34
|
+
[id="plugins-{type}s-{plugin}-retry_policy"]
|
35
|
+
==== Retry policy
|
36
|
+
|
37
|
+
This output has two levels of retry: library and plugin.
|
38
|
+
|
39
|
+
[id="plugins-{type}s-{plugin}-library_retry"]
|
40
|
+
===== Library retry
|
41
|
+
|
42
|
+
The library retry applies to IO related failures.
|
43
|
+
Non retriable errors include SSL related problems, unresolvable hosts,
|
44
|
+
connection issues, and OS/JVM level interruptions happening during a request.
|
45
|
+
|
46
|
+
The options for library retry are:
|
47
|
+
|
48
|
+
* <<plugins-{type}s-{plugin}-automatic_retries,`automatic_retries`>>.
|
49
|
+
Controls the number of times the plugin should retry after failures at the library level.
|
50
|
+
* <<plugins-{type}s-{plugin}-retry_non_idempotent,`retry_non_idempotent`>>.
|
51
|
+
When set to `false`, GET, HEAD, PUT, DELETE, OPTIONS, and TRACE requests will be
|
52
|
+
retried.
|
53
|
+
|
54
|
+
[id="plugins-{type}s-{plugin}-plugin_retry"]
|
55
|
+
===== Plugin retry
|
56
|
+
|
57
|
+
The options for plugin level retry are:
|
58
|
+
|
59
|
+
* <<plugins-{type}s-{plugin}-retry_failed,`retry_failed`>>.
|
60
|
+
When set to `true`, the plugin retries indefinitely for HTTP error response codes defined
|
61
|
+
in the <<plugins-{type}s-{plugin}-retryable_codes,`retryable_codes`>> option
|
62
|
+
(429, 500, 502, 503, 504) and retryable exceptions (socket timeout/ error, DNS resolution failure and client protocol exception).
|
63
|
+
* <<plugins-{type}s-{plugin}-retryable_codes,`retryable_codes`>>.
|
64
|
+
Sets http response codes that trigger a retry.
|
65
|
+
|
66
|
+
NOTE: The `retry_failed` option does not control the library level retry.
|
67
|
+
|
34
68
|
[id="plugins-{type}s-{plugin}-options"]
|
35
69
|
==== Http Output Configuration Options
|
36
70
|
|
@@ -66,6 +100,7 @@ This plugin supports the following configuration options plus the <<plugins-{typ
|
|
66
100
|
| <<plugins-{type}s-{plugin}-retry_non_idempotent>> |<<boolean,boolean>>|No
|
67
101
|
| <<plugins-{type}s-{plugin}-retryable_codes>> |<<number,number>>|No
|
68
102
|
| <<plugins-{type}s-{plugin}-socket_timeout>> |<<number,number>>|No
|
103
|
+
| <<plugins-{type}s-{plugin}-ssl_verification_mode>> |<<string,string>>|No
|
69
104
|
| <<plugins-{type}s-{plugin}-truststore>> |a valid filesystem path|No
|
70
105
|
| <<plugins-{type}s-{plugin}-truststore_password>> |<<password,password>>|No
|
71
106
|
| <<plugins-{type}s-{plugin}-truststore_type>> |<<string,string>>|No
|
@@ -84,12 +119,10 @@ output plugins.
|
|
84
119
|
* Value type is <<number,number>>
|
85
120
|
* Default value is `1`
|
86
121
|
|
87
|
-
How many times should the client retry a failing URL. We
|
88
|
-
to zero if keepalive is enabled.
|
89
|
-
|
90
|
-
|
91
|
-
unless `retry_failed` is set.
|
92
|
-
Note: if `retry_non_idempotent` is NOT set only GET, HEAD, PUT, DELETE, OPTIONS, and TRACE requests will be retried.
|
122
|
+
How many times should the client retry a failing URL. We recommend setting this option
|
123
|
+
to a value other than zero if the <<plugins-{type}s-{plugin}-keepalive,`keepalive` option>> is enabled.
|
124
|
+
Some servers incorrectly end keepalives early, requiring a retry.
|
125
|
+
See <<plugins-{type}s-{plugin}-retry_policy,Retry Policy>> for more information.
|
93
126
|
|
94
127
|
[id="plugins-{type}s-{plugin}-cacert"]
|
95
128
|
===== `cacert`
|
@@ -310,7 +343,12 @@ Timeout (in seconds) for the entire request
|
|
310
343
|
* Value type is <<boolean,boolean>>
|
311
344
|
* Default value is `true`
|
312
345
|
|
313
|
-
|
346
|
+
Note that this option controls plugin-level retries only.
|
347
|
+
It has no affect on library-level retries.
|
348
|
+
|
349
|
+
Set this option to `false` if you want to disable infinite retries for HTTP error response codes defined in the <<plugins-{type}s-{plugin}-retryable_codes,`retryable_codes`>> or
|
350
|
+
retryable exceptions (Timeout, SocketException, ClientProtocolException, ResolutionFailure and SocketTimeout).
|
351
|
+
See <<plugins-{type}s-{plugin}-retry_policy,Retry policy>> for more information.
|
314
352
|
|
315
353
|
[id="plugins-{type}s-{plugin}-retry_non_idempotent"]
|
316
354
|
===== `retry_non_idempotent`
|
@@ -318,8 +356,10 @@ Set this to false if you don't want this output to retry failed requests
|
|
318
356
|
* Value type is <<boolean,boolean>>
|
319
357
|
* Default value is `false`
|
320
358
|
|
321
|
-
|
322
|
-
|
359
|
+
When this option is set to `false` and `automatic_retries` is enabled, GET, HEAD, PUT, DELETE, OPTIONS, and TRACE requests will be retried.
|
360
|
+
|
361
|
+
When set to `true` and `automatic_retries` is enabled, this will cause non-idempotent HTTP verbs (such as POST) to be retried.
|
362
|
+
See <<plugins-{type}s-{plugin}-retry_policy,Retry Policy>> for more information.
|
323
363
|
|
324
364
|
[id="plugins-{type}s-{plugin}-retryable_codes"]
|
325
365
|
===== `retryable_codes`
|
@@ -327,7 +367,8 @@ This only affects connectivity related errors (see related `automatic_retries` s
|
|
327
367
|
* Value type is <<number,number>>
|
328
368
|
* Default value is `[429, 500, 502, 503, 504]`
|
329
369
|
|
330
|
-
If
|
370
|
+
If the plugin encounters these response codes, the plugin will retry indefinitely.
|
371
|
+
See <<plugins-{type}s-{plugin}-retry_policy,Retry Policy>> for more information.
|
331
372
|
|
332
373
|
[id="plugins-{type}s-{plugin}-socket_timeout"]
|
333
374
|
===== `socket_timeout`
|
@@ -337,6 +378,22 @@ If encountered as response codes this plugin will retry these requests
|
|
337
378
|
|
338
379
|
Timeout (in seconds) to wait for data on the socket. Default is `10s`
|
339
380
|
|
381
|
+
[id="plugins-{type}s-{plugin}-ssl_verification_mode"]
|
382
|
+
===== `ssl_verification_mode`
|
383
|
+
|
384
|
+
* Value type is <<string,string>>
|
385
|
+
* Supported values are: `full`, `none`
|
386
|
+
* Default value is `full`
|
387
|
+
|
388
|
+
Controls the verification of server certificates.
|
389
|
+
The `full` option verifies that the provided certificate is signed by a trusted authority (CA)
|
390
|
+
and also that the server’s hostname (or IP address) matches the names identified within the certificate.
|
391
|
+
|
392
|
+
The `none` setting performs no verification of the server’s certificate.
|
393
|
+
This mode disables many of the security benefits of SSL/TLS and should only be used after cautious consideration.
|
394
|
+
It is primarily intended as a temporary diagnostic mechanism when attempting to resolve TLS errors.
|
395
|
+
Using `none` in production environments is strongly discouraged.
|
396
|
+
|
340
397
|
[id="plugins-{type}s-{plugin}-truststore"]
|
341
398
|
===== `truststore`
|
342
399
|
|
@@ -388,4 +445,4 @@ See https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache
|
|
388
445
|
[id="plugins-{type}s-{plugin}-common-options"]
|
389
446
|
include::{include_path}/{type}.asciidoc[]
|
390
447
|
|
391
|
-
:default_codec!:
|
448
|
+
:default_codec!:
|
@@ -23,6 +23,13 @@ class LogStash::Outputs::Http < LogStash::Outputs::Base
|
|
23
23
|
::Manticore::SocketTimeout
|
24
24
|
]
|
25
25
|
|
26
|
+
RETRYABLE_UNKNOWN_EXCEPTION_STRINGS = [
|
27
|
+
/Connection reset by peer/i,
|
28
|
+
/Read Timed out/i
|
29
|
+
]
|
30
|
+
|
31
|
+
class PluginInternalQueueLeftoverError < StandardError; end
|
32
|
+
|
26
33
|
# This output lets you send events to a
|
27
34
|
# generic HTTP(S) endpoint
|
28
35
|
#
|
@@ -138,10 +145,11 @@ class LogStash::Outputs::Http < LogStash::Outputs::Base
|
|
138
145
|
end
|
139
146
|
|
140
147
|
def log_retryable_response(response)
|
148
|
+
retry_msg = @retry_failed ? 'will retry' : "won't retry"
|
141
149
|
if (response.code == 429)
|
142
|
-
@logger.debug? && @logger.debug("Encountered a 429 response,
|
150
|
+
@logger.debug? && @logger.debug("Encountered a 429 response, #{retry_msg}. This is not serious, just flow control via HTTP")
|
143
151
|
else
|
144
|
-
@logger.warn("Encountered a retryable HTTP request in HTTP output,
|
152
|
+
@logger.warn("Encountered a retryable HTTP request in HTTP output, #{retry_msg}", :code => response.code, :body => response.body)
|
145
153
|
end
|
146
154
|
end
|
147
155
|
|
@@ -172,6 +180,9 @@ class LogStash::Outputs::Http < LogStash::Outputs::Base
|
|
172
180
|
|
173
181
|
event, attempt = popped
|
174
182
|
|
183
|
+
raise PluginInternalQueueLeftoverError.new("Received pipeline shutdown request but http output has unfinished events. " \
|
184
|
+
"If persistent queue is enabled, events will be retried.") if attempt > 2 && pipeline_shutdown_requested?
|
185
|
+
|
175
186
|
action, event, attempt = send_event(event, attempt)
|
176
187
|
begin
|
177
188
|
action = :failure if action == :retry && !@retry_failed
|
@@ -216,6 +227,11 @@ class LogStash::Outputs::Http < LogStash::Outputs::Base
|
|
216
227
|
raise e
|
217
228
|
end
|
218
229
|
|
230
|
+
def pipeline_shutdown_requested?
|
231
|
+
return super if defined?(super) # since LS 8.1.0
|
232
|
+
nil
|
233
|
+
end
|
234
|
+
|
219
235
|
def sleep_for_attempt(attempt)
|
220
236
|
sleep_for = attempt**2
|
221
237
|
sleep_for = sleep_for <= 60 ? sleep_for : 60
|
@@ -294,12 +310,21 @@ class LogStash::Outputs::Http < LogStash::Outputs::Base
|
|
294
310
|
end
|
295
311
|
|
296
312
|
def retryable_exception?(exception)
|
297
|
-
|
313
|
+
retryable_manticore_exception?(exception) || retryable_unknown_exception?(exception)
|
314
|
+
end
|
315
|
+
|
316
|
+
def retryable_manticore_exception?(exception)
|
317
|
+
RETRYABLE_MANTICORE_EXCEPTIONS.any? {|me| exception.is_a?(me)}
|
318
|
+
end
|
319
|
+
|
320
|
+
def retryable_unknown_exception?(exception)
|
321
|
+
exception.is_a?(::Manticore::UnknownException) &&
|
322
|
+
RETRYABLE_UNKNOWN_EXCEPTION_STRINGS.any? { |snippet| exception.message =~ snippet }
|
298
323
|
end
|
299
324
|
|
300
325
|
# This is split into a separate method mostly to help testing
|
301
326
|
def log_failure(message, opts)
|
302
|
-
@logger.error(
|
327
|
+
@logger.error(message, opts)
|
303
328
|
end
|
304
329
|
|
305
330
|
# Format the HTTP body
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'logstash-output-http'
|
3
|
-
s.version = '5.
|
3
|
+
s.version = '5.4.1'
|
4
4
|
s.licenses = ['Apache License (2.0)']
|
5
5
|
s.summary = "Sends events to a generic HTTP or HTTPS endpoint"
|
6
6
|
s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
|
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
|
|
20
20
|
|
21
21
|
# Gem dependencies
|
22
22
|
s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
|
23
|
-
s.add_runtime_dependency "logstash-mixin-http_client", ">=
|
23
|
+
s.add_runtime_dependency "logstash-mixin-http_client", ">= 7.1.0", "< 8.0.0"
|
24
24
|
|
25
25
|
s.add_development_dependency 'logstash-devutils'
|
26
26
|
s.add_development_dependency 'sinatra'
|
data/spec/outputs/http_spec.rb
CHANGED
@@ -3,6 +3,9 @@ require "logstash/outputs/http"
|
|
3
3
|
require "logstash/codecs/plain"
|
4
4
|
require "thread"
|
5
5
|
require "sinatra"
|
6
|
+
require "webrick"
|
7
|
+
require "webrick/https"
|
8
|
+
require 'openssl'
|
6
9
|
require_relative "../supports/compressed_requests"
|
7
10
|
|
8
11
|
PORT = rand(65535-1024) + 1025
|
@@ -22,9 +25,20 @@ class TestApp < Sinatra::Base
|
|
22
25
|
# on the fly uncompress gzip content
|
23
26
|
use CompressedRequests
|
24
27
|
|
25
|
-
|
28
|
+
set :environment, :production
|
29
|
+
set :sessions, false
|
30
|
+
|
31
|
+
@@server_settings = {
|
32
|
+
:AccessLog => [], # disable WEBrick logging
|
33
|
+
:Logger => WEBrick::BasicLog::new(nil, WEBrick::BasicLog::FATAL)
|
34
|
+
}
|
35
|
+
|
26
36
|
def self.server_settings
|
27
|
-
|
37
|
+
@@server_settings
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.server_settings=(settings)
|
41
|
+
@@server_settings = settings
|
28
42
|
end
|
29
43
|
|
30
44
|
def self.multiroute(methods, path, &block)
|
@@ -72,31 +86,22 @@ class TestApp < Sinatra::Base
|
|
72
86
|
end
|
73
87
|
end
|
74
88
|
|
75
|
-
RSpec.configure do
|
89
|
+
RSpec.configure do
|
76
90
|
#http://stackoverflow.com/questions/6557079/start-and-call-ruby-http-server-in-the-same-script
|
77
|
-
def
|
91
|
+
def start_app_and_wait(app, opts = {})
|
78
92
|
queue = Queue.new
|
79
93
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
queue.push("started")
|
85
|
-
end
|
86
|
-
rescue => e
|
87
|
-
puts "Error in webserver thread #{e}"
|
88
|
-
# ignore
|
94
|
+
Thread.start do
|
95
|
+
begin
|
96
|
+
app.start!({ server: 'WEBrick', port: PORT }.merge opts) do |server|
|
97
|
+
queue.push(server)
|
89
98
|
end
|
99
|
+
rescue => e
|
100
|
+
warn "Error starting app: #{e.inspect}" # ignore
|
90
101
|
end
|
91
|
-
|
92
|
-
t.daemon = true
|
93
|
-
t.start
|
94
|
-
queue.pop # blocks until the run! callback runs
|
95
|
-
end
|
102
|
+
end
|
96
103
|
|
97
|
-
|
98
|
-
sinatra_run_wait(TestApp, :port => PORT, :server => 'webrick')
|
99
|
-
puts "Test webserver on port #{PORT}"
|
104
|
+
queue.pop # blocks until the start! callback runs
|
100
105
|
end
|
101
106
|
end
|
102
107
|
|
@@ -104,6 +109,15 @@ describe LogStash::Outputs::Http do
|
|
104
109
|
# Wait for the async request to finish in this spinlock
|
105
110
|
# Requires pool_max to be 1
|
106
111
|
|
112
|
+
before(:all) do
|
113
|
+
@server = start_app_and_wait(TestApp)
|
114
|
+
end
|
115
|
+
|
116
|
+
after(:all) do
|
117
|
+
@server.shutdown # WEBrick::HTTPServer
|
118
|
+
TestApp.stop! rescue nil
|
119
|
+
end
|
120
|
+
|
107
121
|
let(:port) { PORT }
|
108
122
|
let(:event) {
|
109
123
|
LogStash::Event.new({"message" => "hi"})
|
@@ -112,6 +126,44 @@ describe LogStash::Outputs::Http do
|
|
112
126
|
let(:method) { "post" }
|
113
127
|
|
114
128
|
shared_examples("verb behavior") do |method|
|
129
|
+
|
130
|
+
shared_examples("failure log behaviour") do
|
131
|
+
it "logs failure" do
|
132
|
+
expect(subject).to have_received(:log_failure).with(any_args)
|
133
|
+
end
|
134
|
+
|
135
|
+
it "does not log headers" do
|
136
|
+
expect(subject).to have_received(:log_failure).with(anything, hash_not_including(:headers))
|
137
|
+
end
|
138
|
+
|
139
|
+
it "does not log the message body" do
|
140
|
+
expect(subject).to have_received(:log_failure).with(anything, hash_not_including(:body))
|
141
|
+
end
|
142
|
+
|
143
|
+
context "with debug log level" do
|
144
|
+
before :all do
|
145
|
+
@current_log_level = LogStash::Logging::Logger.get_logging_context.get_root_logger.get_level.to_s.downcase
|
146
|
+
LogStash::Logging::Logger.configure_logging "debug"
|
147
|
+
end
|
148
|
+
after :all do
|
149
|
+
LogStash::Logging::Logger.configure_logging @current_log_level
|
150
|
+
end
|
151
|
+
|
152
|
+
it "logs a failure" do
|
153
|
+
expect(subject).to have_received(:log_failure).with(any_args)
|
154
|
+
end
|
155
|
+
|
156
|
+
it "logs headers" do
|
157
|
+
expect(subject).to have_received(:log_failure).with(anything, hash_including(:headers))
|
158
|
+
end
|
159
|
+
|
160
|
+
it "logs the body" do
|
161
|
+
expect(subject).to have_received(:log_failure).with(anything, hash_including(:body))
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
115
167
|
let(:verb_behavior_config) { {"url" => url, "http_method" => method, "pool_max" => 1} }
|
116
168
|
subject { LogStash::Outputs::Http.new(verb_behavior_config) }
|
117
169
|
|
@@ -199,44 +251,96 @@ describe LogStash::Outputs::Http do
|
|
199
251
|
end
|
200
252
|
end
|
201
253
|
|
202
|
-
context "on exception" do
|
254
|
+
context "on retryable unknown exception" do
|
203
255
|
before :each do
|
204
|
-
|
256
|
+
raised = false
|
257
|
+
original_method = subject.client.method(:send)
|
258
|
+
allow(subject).to receive(:send_event).and_call_original
|
259
|
+
expect(subject.client).to receive(:send) do |*args|
|
260
|
+
unless raised
|
261
|
+
raised = true
|
262
|
+
raise ::Manticore::UnknownException.new("Read timed out")
|
263
|
+
end
|
264
|
+
original_method.call(args)
|
265
|
+
end
|
205
266
|
subject.multi_receive([event])
|
206
267
|
end
|
207
268
|
|
208
|
-
|
209
|
-
expect(subject).to have_received(:log_failure).with(anything, hash_not_including(:headers))
|
210
|
-
end
|
269
|
+
include_examples("failure log behaviour")
|
211
270
|
|
212
|
-
it "
|
213
|
-
expect(subject).to have_received(:
|
271
|
+
it "retries" do
|
272
|
+
expect(subject).to have_received(:send_event).exactly(2).times
|
214
273
|
end
|
274
|
+
end
|
215
275
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
276
|
+
context "on non-retryable unknown exception" do
|
277
|
+
before :each do
|
278
|
+
raised = false
|
279
|
+
original_method = subject.client.method(:send)
|
280
|
+
allow(subject).to receive(:send_event).and_call_original
|
281
|
+
expect(subject.client).to receive(:send) do |*args|
|
282
|
+
unless raised
|
283
|
+
raised = true
|
284
|
+
raise ::Manticore::UnknownException.new("broken")
|
285
|
+
end
|
286
|
+
original_method.call(args)
|
223
287
|
end
|
288
|
+
subject.multi_receive([event])
|
289
|
+
end
|
224
290
|
|
225
|
-
|
226
|
-
expect(subject).to have_received(:log_failure).with(any_args)
|
227
|
-
end
|
291
|
+
include_examples("failure log behaviour")
|
228
292
|
|
229
|
-
|
230
|
-
|
293
|
+
it "does not retry" do
|
294
|
+
expect(subject).to have_received(:send_event).exactly(1).times
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
context "on non-retryable exception" do
|
299
|
+
before :each do
|
300
|
+
raised = false
|
301
|
+
original_method = subject.client.method(:send)
|
302
|
+
allow(subject).to receive(:send_event).and_call_original
|
303
|
+
expect(subject.client).to receive(:send) do |*args|
|
304
|
+
unless raised
|
305
|
+
raised = true
|
306
|
+
raise RuntimeError.new("broken")
|
307
|
+
end
|
308
|
+
original_method.call(args)
|
231
309
|
end
|
310
|
+
subject.multi_receive([event])
|
311
|
+
end
|
232
312
|
|
233
|
-
|
234
|
-
|
313
|
+
include_examples("failure log behaviour")
|
314
|
+
|
315
|
+
it "does not retry" do
|
316
|
+
expect(subject).to have_received(:send_event).exactly(1).times
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
context "on retryable exception" do
|
321
|
+
before :each do
|
322
|
+
raised = false
|
323
|
+
original_method = subject.client.method(:send)
|
324
|
+
allow(subject).to receive(:send_event).and_call_original
|
325
|
+
expect(subject.client).to receive(:send) do |*args|
|
326
|
+
unless raised
|
327
|
+
raised = true
|
328
|
+
raise ::Manticore::Timeout.new("broken")
|
329
|
+
end
|
330
|
+
original_method.call(args)
|
235
331
|
end
|
332
|
+
subject.multi_receive([event])
|
236
333
|
end
|
334
|
+
|
335
|
+
it "retries" do
|
336
|
+
expect(subject).to have_received(:send_event).exactly(2).times
|
337
|
+
end
|
338
|
+
|
339
|
+
include_examples("failure log behaviour")
|
237
340
|
end
|
238
341
|
end
|
239
342
|
|
343
|
+
|
240
344
|
LogStash::Outputs::Http::VALID_METHODS.each do |method|
|
241
345
|
context "when using '#{method}'" do
|
242
346
|
include_examples("verb behavior", method)
|
@@ -397,4 +501,93 @@ describe LogStash::Outputs::Http do
|
|
397
501
|
let(:base_config) { { "http_compression" => true } }
|
398
502
|
end
|
399
503
|
end
|
504
|
+
|
505
|
+
describe "retryable error in termination" do
|
506
|
+
let(:url) { "http://localhost:#{port-1}/invalid" }
|
507
|
+
let(:events) { [event] }
|
508
|
+
let(:config) { {"url" => url, "http_method" => "get", "pool_max" => 1} }
|
509
|
+
|
510
|
+
subject { LogStash::Outputs::Http.new(config) }
|
511
|
+
|
512
|
+
before do
|
513
|
+
subject.register
|
514
|
+
allow(subject).to receive(:pipeline_shutdown_requested?).and_return(true)
|
515
|
+
end
|
516
|
+
|
517
|
+
it "raise exception to exit indefinitely retry" do
|
518
|
+
expect { subject.multi_receive(events) }.to raise_error(LogStash::Outputs::Http::PluginInternalQueueLeftoverError)
|
519
|
+
end
|
520
|
+
end
|
521
|
+
end
|
522
|
+
|
523
|
+
describe LogStash::Outputs::Http do # different block as we're starting web server with TLS
|
524
|
+
|
525
|
+
@@default_server_settings = TestApp.server_settings.dup
|
526
|
+
|
527
|
+
before do
|
528
|
+
cert, key = WEBrick::Utils.create_self_signed_cert 2048, [["CN", ssl_cert_host]], "Logstash testing"
|
529
|
+
TestApp.server_settings = @@default_server_settings.merge({
|
530
|
+
:SSLEnable => true,
|
531
|
+
:SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE,
|
532
|
+
:SSLCertificate => cert,
|
533
|
+
:SSLPrivateKey => key
|
534
|
+
})
|
535
|
+
|
536
|
+
TestApp.last_request = nil
|
537
|
+
|
538
|
+
@server = start_app_and_wait(TestApp)
|
539
|
+
end
|
540
|
+
|
541
|
+
after do
|
542
|
+
@server.shutdown # WEBrick::HTTPServer
|
543
|
+
|
544
|
+
TestApp.stop! rescue nil
|
545
|
+
TestApp.server_settings = @@default_server_settings
|
546
|
+
end
|
547
|
+
|
548
|
+
let(:ssl_cert_host) { 'localhost' }
|
549
|
+
|
550
|
+
let(:port) { PORT }
|
551
|
+
let(:url) { "https://localhost:#{port}/good" }
|
552
|
+
let(:method) { "post" }
|
553
|
+
|
554
|
+
let(:config) { { "url" => url, "http_method" => method } }
|
555
|
+
|
556
|
+
subject { LogStash::Outputs::Http.new(config) }
|
557
|
+
|
558
|
+
before { subject.register }
|
559
|
+
after { subject.close }
|
560
|
+
|
561
|
+
let(:last_request) { TestApp.last_request }
|
562
|
+
let(:last_request_body) { last_request.body.read }
|
563
|
+
|
564
|
+
let(:event) { LogStash::Event.new("message" => "hello!") }
|
565
|
+
|
566
|
+
context 'with default (full) verification' do
|
567
|
+
|
568
|
+
let(:config) { super() } # 'ssl_verification_mode' => 'full'
|
569
|
+
|
570
|
+
it "does NOT process the request (due client protocol exception)" do
|
571
|
+
# Manticore's default verification does not accept self-signed certificates!
|
572
|
+
Thread.start do
|
573
|
+
subject.multi_receive [ event ]
|
574
|
+
end
|
575
|
+
sleep 1.5
|
576
|
+
|
577
|
+
expect(last_request).to be nil
|
578
|
+
end
|
579
|
+
|
580
|
+
end
|
581
|
+
|
582
|
+
context 'with verification disabled' do
|
583
|
+
|
584
|
+
let(:config) { super().merge 'ssl_verification_mode' => 'none' }
|
585
|
+
|
586
|
+
it "should process the request" do
|
587
|
+
subject.multi_receive [ event ]
|
588
|
+
expect(last_request_body).to include '"message":"hello!"'
|
589
|
+
end
|
590
|
+
|
591
|
+
end
|
592
|
+
|
400
593
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logstash-output-http
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Elastic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-03-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -35,7 +35,7 @@ dependencies:
|
|
35
35
|
requirements:
|
36
36
|
- - ">="
|
37
37
|
- !ruby/object:Gem::Version
|
38
|
-
version:
|
38
|
+
version: 7.1.0
|
39
39
|
- - "<"
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: 8.0.0
|
@@ -46,7 +46,7 @@ dependencies:
|
|
46
46
|
requirements:
|
47
47
|
- - ">="
|
48
48
|
- !ruby/object:Gem::Version
|
49
|
-
version:
|
49
|
+
version: 7.1.0
|
50
50
|
- - "<"
|
51
51
|
- !ruby/object:Gem::Version
|
52
52
|
version: 8.0.0
|
@@ -132,8 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
132
132
|
- !ruby/object:Gem::Version
|
133
133
|
version: '0'
|
134
134
|
requirements: []
|
135
|
-
|
136
|
-
rubygems_version: 2.6.13
|
135
|
+
rubygems_version: 3.1.6
|
137
136
|
signing_key:
|
138
137
|
specification_version: 4
|
139
138
|
summary: Sends events to a generic HTTP or HTTPS endpoint
|