logstash-input-http 3.1.0-java → 3.2.0-java

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 445d3da2f390e8891b32e2090945f28677d6257018545983859c26665c21af84
4
- data.tar.gz: 585afff812773b166fd44370764bd0837958218dd994335da024216faf2cd352
3
+ metadata.gz: dd4ab63a8da778ab6b98669f405773f04defba698bef77a3430d0ea4eb778cf0
4
+ data.tar.gz: 8bc8d4d7909fa768424d63142198644bb97e6b8ea44bef779288267d48044052
5
5
  SHA512:
6
- metadata.gz: a9915f4e38147a0b97c2376b9fda49412b9c2aa995d2a6d5f4b5153a957b54baaa045375a0cbc8ef53d32cd3b1f97731729b73aee3b5cd34dbda4dfa468afc0d
7
- data.tar.gz: cf4d9aa38eda357bf7bba97c6f696221eaf2d09f990eb36ff9e1857c26f6fb96118905fc0e4aff16ce583476af9e095d9a0121e15884b7d34449e1d99cb36c1e
6
+ metadata.gz: 91ee9ad30eaf383efbf38b2f8a6720a3a9bba045ed7dda602734600dfa63b6d7c532bbee12b078a6c1444632e7edb3afe19569d72e9f2f4e94405092849d4a6c
7
+ data.tar.gz: b09f089df723d4240a6a89d8c78cb46c54a914784d8a15491604be7b31238c4f668044ea15f3cceb74e557f70678783ff0b4c9444ba8d61c56d55f0f8bd844c8
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 3.2.0
2
+ - Add `request_headers_target_field` and `remote_host_target_field` configuration options with default to `host` and `headers` respectively #68
3
+ - Sanitize content-type header with getMimeType #87
4
+ - Move most message handling code to java #85
5
+ - Fix: respond with correct http protocol version #84
6
+
1
7
  ## 3.1.0
2
8
  - Replace Puma web server with Netty
3
9
  - Support crt/key certificates
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.1.0
1
+ 3.2.0
data/docs/index.asciidoc CHANGED
@@ -184,6 +184,22 @@ Persistent Queue for the logstash pipeline.
184
184
 
185
185
  specify a custom set of response headers
186
186
 
187
+ [id="plugins-{type}s-{plugin}-remote_host_target_field"]
188
+ ===== `remote_host_target_field`
189
+
190
+ * Value type is <<string,string>>
191
+ * Default value is `"host"`
192
+
193
+ specify a target field for the client host of the http request
194
+
195
+ [id="plugins-{type}s-{plugin}-request_headers_target_field"]
196
+ ===== `request_headers_target_field`
197
+
198
+ * Value type is <<string,string>>
199
+ * Default value is `"headers"`
200
+
201
+ specify target field for the client host of the http request
202
+
187
203
  [id="plugins-{type}s-{plugin}-ssl"]
188
204
  ===== `ssl`
189
205
 
@@ -4,4 +4,4 @@ require 'jar_dependencies'
4
4
  require_jar('io.netty', 'netty-all', '4.1.18.Final')
5
5
  require_jar('io.netty', 'netty-tcnative-boringssl-static', '2.0.7.Final')
6
6
  require_jar('org.apache.logging.log4j', 'log4j-api', '2.6.2')
7
- require_jar('org.logstash.plugins.input.http', 'logstash-input-http', '3.1.0')
7
+ require_jar('org.logstash.plugins.input.http', 'logstash-input-http', '3.2.0')
@@ -4,6 +4,8 @@ require "logstash/namespace"
4
4
  require "stud/interval"
5
5
  require "logstash-input-http_jars"
6
6
 
7
+ java_import "io.netty.handler.codec.http.HttpUtil"
8
+
7
9
  # Using this input you can receive single or multiline events over http(s).
8
10
  # Applications can send a HTTP POST request with a body to the endpoint started by this
9
11
  # input and Logstash will convert it into an event for subsequent processing. Users
@@ -101,6 +103,12 @@ class LogStash::Inputs::Http < LogStash::Inputs::Base
101
103
  # specify a custom set of response headers
102
104
  config :response_headers, :validate => :hash, :default => { 'Content-Type' => 'text/plain' }
103
105
 
106
+ # target field for the client host of the http request
107
+ config :remote_host_target_field, :validate => :string, :default => "host"
108
+
109
+ # target field for the client host of the http request
110
+ config :request_headers_target_field, :validate => :string, :default => "headers"
111
+
104
112
  config :threads, :validate => :number, :required => false, :default => ::LogStash::Config::CpuCoreStrategy.maximum
105
113
 
106
114
  config :max_pending_requests, :validate => :number, :required => false, :default => 200
@@ -133,7 +141,7 @@ class LogStash::Inputs::Http < LogStash::Inputs::Base
133
141
  end
134
142
 
135
143
  require "logstash/inputs/http/message_handler"
136
- message_handler = MessageHandler.new(self, @codec, @codecs)
144
+ message_handler = MessageHandler.new(self, @codec, @codecs, @auth_token)
137
145
  @http_server = create_http_server(message_handler)
138
146
  end # def register
139
147
 
@@ -151,23 +159,12 @@ class LogStash::Inputs::Http < LogStash::Inputs::Base
151
159
  @http_server.close() rescue nil
152
160
  end
153
161
 
154
- def decode_body(remote_address, http_full_request, default_codec, additional_codecs)
155
- body = read_bytes(http_full_request.content)
156
- headers = {}
157
-
158
- http_full_request.headers.each do |header|
159
- headers[header.key.downcase.tr('-', '_')] = header.value
160
- end
161
- headers["request_method"] = http_full_request.getMethod.name
162
- headers["request_path"] = http_full_request.getUri
163
- headers["http_version"] = http_full_request.getProtocolVersion.text
164
- headers["http_accept"] = headers.delete("accept")
165
- headers["http_host"] = headers.delete("host")
166
- headers["http_user_agent"] = headers.delete("user_agent")
167
- codec = additional_codecs.fetch(headers["content_type"], default_codec)
162
+ def decode_body(headers, remote_address, body, default_codec, additional_codecs)
163
+ content_type = headers.fetch("content_type", "")
164
+ codec = additional_codecs.fetch(HttpUtil.getMimeType(content_type), default_codec)
168
165
  codec.decode(body) { |event| push_decoded_event(headers, remote_address, event) }
169
166
  codec.flush { |event| push_decoded_event(headers, remote_address, event) }
170
- [200, @response_headers, 'ok']
167
+ true
171
168
  rescue => e
172
169
  @logger.error(
173
170
  "unable to process event.",
@@ -175,31 +172,16 @@ class LogStash::Inputs::Http < LogStash::Inputs::Base
175
172
  :class => e.class.name,
176
173
  :backtrace => e.backtrace
177
174
  )
178
- [500, @response_headers, 'internal error']
175
+ false
179
176
  end
180
177
 
181
178
  def push_decoded_event(headers, remote_address, event)
182
- event.set("headers", headers)
183
- event.set("host", remote_address)
179
+ event.set(@request_headers_target_field, headers)
180
+ event.set(@remote_host_target_field, remote_address)
184
181
  decorate(event)
185
182
  @queue << event
186
183
  end
187
184
 
188
- def valid_auth?(token)
189
- if @auth_token
190
- @auth_token == token
191
- else
192
- true
193
- end
194
- end
195
-
196
- private
197
- def read_bytes(bytebuffer)
198
- bytes = Java::byte[bytebuffer.readableBytes].new
199
- bytebuffer.getBytes(0, bytes)
200
- String.from_java_bytes(bytes, "ASCII-8BIT")
201
- end
202
-
203
185
  def validate_ssl_settings!
204
186
  if !@ssl
205
187
  @logger.warn("SSL Certificate will not be used") if @ssl_certificate
@@ -14,37 +14,27 @@ module LogStash module Inputs class Http
14
14
 
15
15
  attr_reader :input
16
16
 
17
- def initialize(input, default_codec, additional_codecs)
17
+ def initialize(input, default_codec, additional_codecs, auth_token)
18
18
  @input = input
19
19
  @default_codec = default_codec
20
20
  @additional_codecs = additional_codecs
21
+ @auth_token = auth_token
21
22
  end
22
23
 
23
- def onNewMessage(remote_address, message)
24
- if valid_auth?(message)
25
- message.headers.remove(HttpHeaderNames::AUTHORIZATION)
26
- status, headers, content = @input.decode_body(remote_address, message, @default_codec, @additional_codecs)
24
+ def validates_token(token)
25
+ if @auth_token
26
+ @auth_token == token
27
27
  else
28
- status, headers, content = 401, {}, 'failed to authenticate'
28
+ true
29
29
  end
30
- generate_response(status, headers, content)
31
30
  end
32
31
 
33
- private
34
- def generate_response(status, headers, content)
35
- payload = Unpooled.copiedBuffer(content.to_java_string, CharsetUtil::UTF_8)
36
- response = DefaultFullHttpResponse.new(
37
- HttpVersion::HTTP_1_1,
38
- HttpResponseStatus.valueOf(status),
39
- payload)
40
- response.headers().set(HttpHeaderNames::CONTENT_LENGTH, payload.readable_bytes());
41
- response.headers().set(HttpHeaderNames::CONTENT_TYPE, "text/plain");
42
- headers.each { |k, v| response.headers().set(k, v) }
43
- response
32
+ def onNewMessage(remote_address, headers, body)
33
+ @input.decode_body(headers, remote_address, body, @default_codec, @additional_codecs)
44
34
  end
45
35
 
46
36
  def copy
47
- MessageHandler.new(@input, @default_codec.clone, clone_additional_codecs())
37
+ MessageHandler.new(@input, @default_codec.clone, clone_additional_codecs(), @auth_token)
48
38
  end
49
39
 
50
40
  def clone_additional_codecs
@@ -55,8 +45,8 @@ module LogStash module Inputs class Http
55
45
  clone_additional_codecs
56
46
  end
57
47
 
58
- def valid_auth?(message)
59
- @input.valid_auth?(message.headers.get(HttpHeaderNames::AUTHORIZATION))
48
+ def response_headers
49
+ @input.response_headers
60
50
  end
61
51
  end
62
52
  end; end; end
@@ -29,6 +29,7 @@ describe LogStash::Inputs::Http do
29
29
 
30
30
  describe "request handling" do
31
31
  subject { LogStash::Inputs::Http.new("port" => port) }
32
+
32
33
  before :each do
33
34
  subject.register
34
35
  t = Thread.new { subject.run(logstash_queue) }
@@ -81,6 +82,57 @@ describe LogStash::Inputs::Http do
81
82
  end
82
83
  end
83
84
 
85
+ describe "remote host" do
86
+ subject { LogStash::Inputs::Http.new(config.merge("port" => port)) }
87
+ context "by default" do
88
+ let(:config) { {} }
89
+ it "is written to the \"host\" field" do
90
+ client.post("http://localhost:#{port}/meh.json",
91
+ :headers => { "content-type" => "text/plain" },
92
+ :body => "hello").call
93
+ event = logstash_queue.pop
94
+ expect(event.get("host")).to eq("127.0.0.1")
95
+ end
96
+ end
97
+
98
+ context "when using remote_host_target_field" do
99
+ let(:config) { { "remote_host_target_field" => "remote_host" } }
100
+ it "is written to the value of \"remote_host_target_field\" property" do
101
+ client.post("http://localhost:#{port}/meh.json",
102
+ :headers => { "content-type" => "text/plain" },
103
+ :body => "hello").call
104
+ event = logstash_queue.pop
105
+ expect(event.get("remote_host")).to eq("127.0.0.1")
106
+ end
107
+ end
108
+ end
109
+
110
+ describe "request headers" do
111
+ subject { LogStash::Inputs::Http.new(config.merge("port" => port)) }
112
+ context "by default" do
113
+ let(:config) { {} }
114
+ it "are written to the \"headers\" field" do
115
+ client.post("http://localhost:#{port}/meh.json",
116
+ :headers => { "content-type" => "text/plain" },
117
+ :body => "hello").call
118
+ event = logstash_queue.pop
119
+ expect(event.get("headers")).to be_a(Hash)
120
+ expect(event.get("headers")).to include("request_method" => "POST")
121
+ end
122
+ end
123
+ context "when using request_headers_target_field" do
124
+ let(:config) { { "request_headers_target_field" => "request_headers" } }
125
+ it "are written to the field set in \"request_headers_target_field\"" do
126
+ client.post("http://localhost:#{port}/meh.json",
127
+ :headers => { "content-type" => "text/plain" },
128
+ :body => "hello").call
129
+ event = logstash_queue.pop
130
+ expect(event.get("request_headers")).to be_a(Hash)
131
+ expect(event.get("request_headers")).to include("request_method" => "POST")
132
+ end
133
+ end
134
+ end
135
+
84
136
  it "should include remote host in \"host\" property" do
85
137
  client.post("http://127.0.0.1:#{port}/meh.json",
86
138
  :headers => { "content-type" => "text/plain" },
@@ -190,6 +242,19 @@ describe LogStash::Inputs::Http do
190
242
  expect(event.get("message")).to eq(body)
191
243
  end
192
244
  end
245
+
246
+ context "when receiving a content-type with a charset" do
247
+ subject { LogStash::Inputs::Http.new("port" => port,
248
+ "additional_codecs" => { "application/json" => "plain" }) }
249
+ it "should decode the message accordingly" do
250
+ body = { "message" => "Hello" }.to_json
251
+ client.post("http://127.0.0.1:#{port}/meh.json",
252
+ :headers => { "content-type" => "application/json; charset=utf-8" },
253
+ :body => body).call
254
+ event = logstash_queue.pop
255
+ expect(event.get("message")).to eq(body)
256
+ end
257
+ end
193
258
 
194
259
  context "when using custom headers" do
195
260
  let(:custom_headers) { { 'access-control-allow-origin' => '*' } }
@@ -197,7 +262,7 @@ describe LogStash::Inputs::Http do
197
262
 
198
263
  describe "the response" do
199
264
  it "should include the custom headers" do
200
- response = client.post("http://127.0.0.1:#{port}/meh", :body => "hello")
265
+ response = client.post("http://127.0.0.1:#{port}/meh", :body => "hello").call
201
266
  expect(response.headers.to_hash).to include(custom_headers)
202
267
  end
203
268
  end
@@ -248,6 +313,32 @@ describe LogStash::Inputs::Http do
248
313
  end
249
314
  end
250
315
 
316
+ describe "HTTP Protocol Handling" do
317
+ context "when an HTTP1.1 request is made" do
318
+ let(:protocol_version) do
319
+ Java::OrgApacheHttp::HttpVersion::HTTP_1_1
320
+ end
321
+ it "responds with a HTTP1.1 response" do
322
+ response = client.post("http://127.0.0.1:#{port}", :body => "hello")
323
+ response.request.set_protocol_version(protocol_version)
324
+ response.call
325
+ response_protocol_version = response.instance_variable_get(:@response).get_protocol_version
326
+ expect(response_protocol_version).to eq(protocol_version)
327
+ end
328
+ end
329
+ context "when an HTTP1.0 request is made" do
330
+ let(:protocol_version) do
331
+ Java::OrgApacheHttp::HttpVersion::HTTP_1_0
332
+ end
333
+ it "responds with a HTTP1.0 response" do
334
+ response = client.post("http://127.0.0.1:#{port}", :body => "hello")
335
+ response.request.set_protocol_version(protocol_version)
336
+ response.call
337
+ response_protocol_version = response.instance_variable_get(:@response).get_protocol_version
338
+ expect(response_protocol_version).to eq(protocol_version)
339
+ end
340
+ end
341
+ end
251
342
  end
252
343
 
253
344
  context "with :ssl => false" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-input-http
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.2.0
5
5
  platform: java
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-26 00:00:00.000000000 Z
11
+ date: 2018-05-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -141,7 +141,7 @@ files:
141
141
  - vendor/jar-dependencies/io/netty/netty-all/4.1.18.Final/netty-all-4.1.18.Final.jar
142
142
  - vendor/jar-dependencies/io/netty/netty-tcnative-boringssl-static/2.0.7.Final/netty-tcnative-boringssl-static-2.0.7.Final.jar
143
143
  - vendor/jar-dependencies/org/apache/logging/log4j/log4j-api/2.6.2/log4j-api-2.6.2.jar
144
- - vendor/jar-dependencies/org/logstash/plugins/input/http/logstash-input-http/3.1.0/logstash-input-http-3.1.0.jar
144
+ - vendor/jar-dependencies/org/logstash/plugins/input/http/logstash-input-http/3.2.0/logstash-input-http-3.2.0.jar
145
145
  homepage: http://www.elastic.co/guide/en/logstash/current/index.html
146
146
  licenses:
147
147
  - Apache License (2.0)
@@ -165,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
165
165
  version: '0'
166
166
  requirements: []
167
167
  rubyforge_project:
168
- rubygems_version: 2.6.11
168
+ rubygems_version: 2.6.13
169
169
  signing_key:
170
170
  specification_version: 4
171
171
  summary: Receives events over HTTP or HTTPS