logstash-output-tcp 6.1.0 → 6.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d30cf25cfbc7e1cc2e72d5e1eb8ab8179d42b31413a549801a012d5e1be7e640
4
- data.tar.gz: 53632382865d4bade1e6d56e9872b1b5db690fd9b866cb4d0952dfa8d1156cee
3
+ metadata.gz: c907232f3196c96261615180d8c1fc85c7973dc94c31fbd5161bf657f9c32adb
4
+ data.tar.gz: 2787199dc0aa9904ae4387a97d7f8add642b8cbd5af37929a263604c0d15c1d1
5
5
  SHA512:
6
- metadata.gz: e48f3511c25df04edb79dfd0425c6a84d18808bce5f8d4910cabe024955e6c16f8b856a8ac7fac01cb7721e7628934101e14e7180497a7c6cc1096169290963e
7
- data.tar.gz: 94758e5adb6be529f4c70715aa0796d1a15bd8d17e91d35c63fdc1b29eccea58c0f36e4c29b0cf911a3691fade34c895922ca9b0384cd3df405f723a97ff642f
6
+ metadata.gz: e0a8adce0cb539e83d1305c5d52931710041d7f4d7b8e42098f141bfca50980f80c39bef88116de710463044b6cfb5a14e0a32d3e32c3058d76990bb5bbd5d3b
7
+ data.tar.gz: 754a94a8b98ed57b77390091ed6dc05538d28d6aea75e4e5c30a438c34696fe80f9cb56503ed1a81ee2832fccee347122fbb07afb755ea01d2070c2ce4384b21
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## 6.1.1
2
+ - Fixes an issue where payloads larger than a connection's current TCP window could be silently truncated [#49](https://github.com/logstash-plugins/logstash-output-tcp/pull/49)
3
+
1
4
  ## 6.1.0
2
5
  - Feat: ssl_supported_protocols (TLSv1.3) [#47](https://github.com/logstash-plugins/logstash-output-tcp/pull/47)
3
6
  - Fix: close server and client sockets on plugin close
@@ -56,18 +56,25 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
56
56
 
57
57
  class Client
58
58
 
59
- def initialize(socket, logger)
59
+ ##
60
+ # @param socket [Socket]
61
+ # @param logger_context [#log_warn&#log_error]
62
+ def initialize(socket, logger_context)
60
63
  @socket = socket
61
- @logger = logger
64
+ @logger_context = logger_context
62
65
  @queue = Queue.new
63
66
  end
64
67
 
65
68
  def run
66
69
  loop do
67
70
  begin
68
- @socket.write(@queue.pop)
71
+ remaining_payload = @queue.pop
72
+ while remaining_payload && remaining_payload.bytesize > 0
73
+ written_bytes_size = @socket.write(remaining_payload)
74
+ remaining_payload = remaining_payload.byteslice(written_bytes_size..-1)
75
+ end
69
76
  rescue => e
70
- log_warn 'socket write failed:', e, socket: (@socket ? @socket.to_s : nil)
77
+ @logger_context.log_warn 'socket write failed:', e, socket: (@socket ? @socket.to_s : nil)
71
78
  break
72
79
  end
73
80
  end
@@ -80,7 +87,7 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
80
87
  def close
81
88
  @socket.close
82
89
  rescue => e
83
- log_warn 'socket close failed:', e, socket: (@socket ? @socket.to_s : nil)
90
+ @logger_context.log_warn 'socket close failed:', e, socket: (@socket ? @socket.to_s : nil)
84
91
  end
85
92
  end # class Client
86
93
 
@@ -135,69 +142,85 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
135
142
  require "socket"
136
143
  require "stud/try"
137
144
  @closed = Concurrent::AtomicBoolean.new(false)
145
+ @thread_no = Concurrent::AtomicFixnum.new(0)
138
146
  setup_ssl if @ssl_enable
139
147
 
140
148
  if server?
141
- @logger.info("Starting tcp output listener", :address => "#{@host}:#{@port}")
142
- begin
143
- @server_socket = TCPServer.new(@host, @port)
144
- rescue Errno::EADDRINUSE
145
- @logger.error("Could not start tcp server: Address in use", host: @host, port: @port)
146
- raise
147
- end
148
- if @ssl_enable
149
- @server_socket = OpenSSL::SSL::SSLServer.new(@server_socket, @ssl_context)
150
- end # @ssl_enable
151
- @client_threads = Concurrent::Array.new
152
-
153
- @accept_thread = Thread.new(@server_socket) do |server_socket|
154
- LogStash::Util.set_thread_name("[#{pipeline_id}]|output|tcp|server_accept")
155
- loop do
156
- break if @closed.value
157
- client_socket = server_socket.accept_nonblock exception: false
158
- if client_socket == :wait_readable
159
- IO.select [ server_socket ]
160
- next
161
- end
162
- Thread.start(client_socket) do |client_socket|
163
- # monkeypatch a 'peer' method onto the socket.
164
- client_socket.instance_eval { class << self; include ::LogStash::Util::SocketPeer end }
165
- @logger.debug("accepted connection", client: client_socket.peer, server: "#{@host}:#{@port}")
166
- client = Client.new(client_socket, @logger)
167
- Thread.current[:client] = client
168
- LogStash::Util.set_thread_name("[#{pipeline_id}]|output|tcp|client_socket-#{@client_threads.size}")
169
- @client_threads << Thread.current
170
- client.run unless @closed.value
171
- end
149
+ run_as_server
150
+ else
151
+ run_as_client
152
+ end
153
+ end
154
+
155
+ def run_as_server
156
+ @logger.info("Starting tcp output listener", :address => "#{@host}:#{@port}")
157
+ begin
158
+ @server_socket = TCPServer.new(@host, @port)
159
+ rescue Errno::EADDRINUSE
160
+ @logger.error("Could not start tcp server: Address in use", host: @host, port: @port)
161
+ raise
162
+ end
163
+ if @ssl_enable
164
+ @server_socket = OpenSSL::SSL::SSLServer.new(@server_socket, @ssl_context)
165
+ end # @ssl_enable
166
+ @client_threads = Concurrent::Array.new
167
+
168
+ @accept_thread = Thread.new(@server_socket) do |server_socket|
169
+ LogStash::Util.set_thread_name("[#{pipeline_id}]|output|tcp|server_accept")
170
+ loop do
171
+ break if @closed.value
172
+ client_socket = server_socket.accept_nonblock exception: false
173
+ if client_socket == :wait_readable
174
+ IO.select [ server_socket ]
175
+ next
176
+ end
177
+ Thread.start(client_socket) do |client_socket|
178
+ # monkeypatch a 'peer' method onto the socket.
179
+ client_socket.extend(::LogStash::Util::SocketPeer)
180
+ @logger.debug("accepted connection", client: client_socket.peer, server: "#{@host}:#{@port}")
181
+ client = Client.new(client_socket, self)
182
+ Thread.current[:client] = client
183
+ LogStash::Util.set_thread_name("[#{pipeline_id}]|output|tcp|client_socket-#{@thread_no.increment}")
184
+ @client_threads << Thread.current
185
+ client.run unless @closed.value
172
186
  end
173
187
  end
188
+ end
174
189
 
175
- @codec.on_event do |event, payload|
176
- @client_threads.select!(&:alive?)
177
- @client_threads.each do |client_thread|
178
- client_thread[:client].write(payload)
179
- end
190
+ @codec.on_event do |event, payload|
191
+ @client_threads.select!(&:alive?)
192
+ @client_threads.each do |client_thread|
193
+ client_thread[:client].write(payload)
180
194
  end
181
- else
182
- client_socket = nil
183
- @codec.on_event do |event, payload|
184
- begin
185
- client_socket = connect unless client_socket
186
- r,w,e = IO.select([client_socket], [client_socket], [client_socket], nil)
195
+ end
196
+ end
197
+
198
+ def run_as_client
199
+ client_socket = nil
200
+ @codec.on_event do |event, payload|
201
+ begin
202
+ client_socket = connect unless client_socket
203
+
204
+ writable_io = nil
205
+ while writable_io.nil? || writable_io.any? == false
206
+ readable_io, writable_io, _ = IO.select([client_socket],[client_socket])
207
+
187
208
  # don't expect any reads, but a readable socket might
188
209
  # mean the remote end closed, so read it and throw it away.
189
210
  # we'll get an EOFError if it happens.
190
- client_socket.sysread(16384) if r.any?
211
+ readable_io.each { |readable| readable.sysread(16384) }
212
+ end
191
213
 
192
- # Now send the payload
193
- client_socket.syswrite(payload) if w.any?
194
- rescue => e
195
- log_warn "client socket failed:", e, host: @host, port: @port, socket: (client_socket ? client_socket.to_s : nil)
196
- client_socket.close rescue nil
197
- client_socket = nil
198
- sleep @reconnect_interval
199
- retry
214
+ while payload && payload.bytesize > 0
215
+ written_bytes_size = client_socket.syswrite(payload)
216
+ payload = payload.byteslice(written_bytes_size..-1)
200
217
  end
218
+ rescue => e
219
+ log_warn "client socket failed:", e, host: @host, port: @port, socket: (client_socket ? client_socket.to_s : nil)
220
+ client_socket.close rescue nil
221
+ client_socket = nil
222
+ sleep @reconnect_interval
223
+ retry
201
224
  end
202
225
  end
203
226
  end
@@ -219,6 +242,18 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
219
242
  end
220
243
  end
221
244
 
245
+ def log_warn(msg, e, backtrace: @logger.debug?, **details)
246
+ details = details.merge message: e.message, exception: e.class
247
+ details[:backtrace] = e.backtrace if backtrace
248
+ @logger.warn(msg, details)
249
+ end
250
+
251
+ def log_error(msg, e, backtrace: @logger.info?, **details)
252
+ details = details.merge message: e.message, exception: e.class
253
+ details[:backtrace] = e.backtrace if backtrace
254
+ @logger.error(msg, details)
255
+ end
256
+
222
257
  private
223
258
 
224
259
  def connect
@@ -235,7 +270,7 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
235
270
  raise
236
271
  end
237
272
  end
238
- client_socket.instance_eval { class << self; include ::LogStash::Util::SocketPeer end }
273
+ client_socket.extend(::LogStash::Util::SocketPeer)
239
274
  @logger.debug("opened connection", :client => client_socket.peer)
240
275
  return client_socket
241
276
  rescue => e
@@ -253,16 +288,4 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
253
288
  execution_context.pipeline_id || 'main'
254
289
  end
255
290
 
256
- def log_warn(msg, e, backtrace: @logger.debug?, **details)
257
- details = details.merge message: e.message, exception: e.class
258
- details[:backtrace] = e.backtrace if backtrace
259
- @logger.warn(msg, details)
260
- end
261
-
262
- def log_error(msg, e, backtrace: @logger.info?, **details)
263
- details = details.merge message: e.message, exception: e.class
264
- details[:backtrace] = e.backtrace if backtrace
265
- @logger.error(msg, details)
266
- end
267
-
268
291
  end # class LogStash::Outputs::Tcp
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-output-tcp'
4
- s.version = '6.1.0'
4
+ s.version = '6.1.1'
5
5
  s.licenses = ['Apache License (2.0)']
6
6
  s.summary = "Writes events over a TCP socket"
7
7
  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"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-output-tcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.1.0
4
+ version: 6.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-06-06 00:00:00.000000000 Z
11
+ date: 2022-08-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement