From de8da5191dc4fd7b33f1a843a30a2e778f961a0a Mon Sep 17 00:00:00 2001 From: Arthur Kepler <610274+excalq@users.noreply.github.com> Date: Mon, 23 Jan 2023 19:19:12 -0500 Subject: [PATCH 1/3] Adds response_body to configuration options --- lib/logstash/inputs/http.rb | 14 +++++++++----- .../plugins/inputs/http/HttpInitializer.java | 6 ++++-- .../plugins/inputs/http/HttpServerHandler.java | 6 ++++-- .../plugins/inputs/http/MessageProcessor.java | 12 +++++++++--- .../plugins/inputs/http/NettyHttpServer.java | 6 ++++-- 5 files changed, 30 insertions(+), 14 deletions(-) diff --git a/lib/logstash/inputs/http.rb b/lib/logstash/inputs/http.rb index 5045a6cd..c3ba03b2 100644 --- a/lib/logstash/inputs/http.rb +++ b/lib/logstash/inputs/http.rb @@ -98,8 +98,14 @@ class LogStash::Inputs::Http < LogStash::Inputs::Base # and no codec for the request's content-type is found config :additional_codecs, :validate => :hash, :default => { "application/json" => "json" } - # specify a custom set of response headers - config :response_headers, :validate => :hash, :default => { 'Content-Type' => 'text/plain' } + # Send reponses with this HTTP code. 204 is `no content`, and forces an empty response body + config :response_code, :validate => [200, 201, 202, 204], :default => 200 + + # Send this as the body to each HTTP POST. A JSON example: `"{\"ok\": true}"`. + config :response_body, :default => "ok" + + # specify a custom set of response headers (use lowercase keys, per netty.io) + config :response_headers, :validate => :hash, :default => { 'content-type' => 'text/plain' } # target field for the client host of the http request config :remote_host_target_field, :validate => :string @@ -113,8 +119,6 @@ class LogStash::Inputs::Http < LogStash::Inputs::Base config :max_content_length, :validate => :number, :required => false, :default => 100 * 1024 * 1024 - config :response_code, :validate => [200, 201, 202, 204], :default => 200 - # Deprecated options # The JKS keystore to validate the client's certificates @@ -282,7 +286,7 @@ def validate_ssl_settings! def create_http_server(message_handler) org.logstash.plugins.inputs.http.NettyHttpServer.new( - @host, @port, message_handler, build_ssl_params(), @threads, @max_pending_requests, @max_content_length, @response_code) + @host, @port, message_handler, build_ssl_params(), @threads, @max_pending_requests, @max_content_length, @response_code, @response_body) end def build_ssl_params diff --git a/src/main/java/org/logstash/plugins/inputs/http/HttpInitializer.java b/src/main/java/org/logstash/plugins/inputs/http/HttpInitializer.java index 27f21500..135d25ef 100644 --- a/src/main/java/org/logstash/plugins/inputs/http/HttpInitializer.java +++ b/src/main/java/org/logstash/plugins/inputs/http/HttpInitializer.java @@ -20,14 +20,16 @@ public class HttpInitializer extends ChannelInitializer { private SslHandlerProvider sslHandlerProvider; private final int maxContentLength; private final HttpResponseStatus responseStatus; + private final String responseBody; private final ThreadPoolExecutor executorGroup; public HttpInitializer(IMessageHandler messageHandler, ThreadPoolExecutor executorGroup, - int maxContentLength, HttpResponseStatus responseStatus) { + int maxContentLength, HttpResponseStatus responseStatus, String responseBody) { this.messageHandler = messageHandler; this.executorGroup = executorGroup; this.maxContentLength = maxContentLength; this.responseStatus = responseStatus; + this.responseBody = responseBody; } protected void initChannel(SocketChannel socketChannel) throws Exception { @@ -40,7 +42,7 @@ protected void initChannel(SocketChannel socketChannel) throws Exception { pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new HttpContentDecompressor()); pipeline.addLast(new HttpObjectAggregator(maxContentLength)); - pipeline.addLast(new HttpServerHandler(messageHandler.copy(), executorGroup, responseStatus)); + pipeline.addLast(new HttpServerHandler(messageHandler.copy(), executorGroup, responseStatus, responseBody)); } public void enableSSL(SslHandlerProvider sslHandlerProvider) { diff --git a/src/main/java/org/logstash/plugins/inputs/http/HttpServerHandler.java b/src/main/java/org/logstash/plugins/inputs/http/HttpServerHandler.java index 36c1b291..6f6b603c 100644 --- a/src/main/java/org/logstash/plugins/inputs/http/HttpServerHandler.java +++ b/src/main/java/org/logstash/plugins/inputs/http/HttpServerHandler.java @@ -23,19 +23,21 @@ public class HttpServerHandler extends SimpleChannelInboundHandler stringHeaders) { req.protocolVersion(), responseStatus); final DefaultHttpHeaders headers = new DefaultHttpHeaders(); + bool hasContentTypeHeader = false; for(String key : stringHeaders.keySet()) { headers.set(key, stringHeaders.get(key)); + hasContentTypeHeader = (key.toLowerCase() == HttpHeaderNames.CONTENT_TYPE); + } + if (!hasContentTypeHeader) { + headers.set(HttpHeaderNames.CONTENT_TYPE, "text/plain"); } response.headers().set(headers); if (responseStatus != HttpResponseStatus.NO_CONTENT) { - final ByteBuf payload = Unpooled.wrappedBuffer("ok".getBytes(UTF8_CHARSET)); + final ByteBuf payload = Unpooled.wrappedBuffer(responseBody.getBytes(UTF8_CHARSET)); response.headers().set(HttpHeaderNames.CONTENT_LENGTH, payload.readableBytes()); - response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain"); response.content().writeBytes(payload); } diff --git a/src/main/java/org/logstash/plugins/inputs/http/NettyHttpServer.java b/src/main/java/org/logstash/plugins/inputs/http/NettyHttpServer.java index 12eb7945..3b63eb1e 100644 --- a/src/main/java/org/logstash/plugins/inputs/http/NettyHttpServer.java +++ b/src/main/java/org/logstash/plugins/inputs/http/NettyHttpServer.java @@ -32,10 +32,12 @@ public class NettyHttpServer implements Runnable, Closeable { public NettyHttpServer(String host, int port, IMessageHandler messageHandler, SslHandlerProvider sslHandlerProvider, int threads, - int maxPendingRequests, int maxContentLength, int responseCode) + int maxPendingRequests, int maxContentLength, + int responseCode, String responseBody) { this.host = host; this.port = port; + this.responseStatus = HttpResponseStatus.valueOf(responseCode); processorGroup = new NioEventLoopGroup(threads, daemonThreadFactory("http-input-processor")); @@ -44,7 +46,7 @@ public NettyHttpServer(String host, int port, IMessageHandler messageHandler, new CustomRejectedExecutionHandler()); final HttpInitializer httpInitializer = new HttpInitializer(messageHandler, executorGroup, - maxContentLength, responseStatus); + maxContentLength, responseStatus, responseBody); if (sslHandlerProvider != null) { httpInitializer.enableSSL(sslHandlerProvider); From 525a5b2155e5d9a0eb3eb301884fbba5506b8b67 Mon Sep 17 00:00:00 2001 From: Arthur Kepler <610274+excalq@users.noreply.github.com> Date: Mon, 23 Jan 2023 20:19:27 -0500 Subject: [PATCH 2/3] Fix Java syntax error in header comparison --- .../org/logstash/plugins/inputs/http/MessageProcessor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/logstash/plugins/inputs/http/MessageProcessor.java b/src/main/java/org/logstash/plugins/inputs/http/MessageProcessor.java index 2fd867de..340bf11c 100644 --- a/src/main/java/org/logstash/plugins/inputs/http/MessageProcessor.java +++ b/src/main/java/org/logstash/plugins/inputs/http/MessageProcessor.java @@ -102,10 +102,10 @@ private FullHttpResponse generateResponse(Map stringHeaders) { req.protocolVersion(), responseStatus); final DefaultHttpHeaders headers = new DefaultHttpHeaders(); - bool hasContentTypeHeader = false; + boolean hasContentTypeHeader = false; for(String key : stringHeaders.keySet()) { headers.set(key, stringHeaders.get(key)); - hasContentTypeHeader = (key.toLowerCase() == HttpHeaderNames.CONTENT_TYPE); + hasContentTypeHeader = (HttpHeaderNames.CONTENT_TYPE.contentEqualsIgnoreCase(key)); } if (!hasContentTypeHeader) { headers.set(HttpHeaderNames.CONTENT_TYPE, "text/plain"); From b5cd6b3fc609b75912a7d9c772afa0523499c554 Mon Sep 17 00:00:00 2001 From: Arthur Kepler <610274+excalq@users.noreply.github.com> Date: Mon, 23 Jan 2023 20:53:49 -0500 Subject: [PATCH 3/3] Updates docs with `response_body` --- docs/index.asciidoc | 16 ++++++++++++++-- lib/logstash/inputs/http.rb | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/index.asciidoc b/docs/index.asciidoc index 0d21f4e8..8ee1fc98 100644 --- a/docs/index.asciidoc +++ b/docs/index.asciidoc @@ -25,7 +25,7 @@ Using this input you can receive single or multiline events over http(s). Applications can send an HTTP request to the endpoint started by this input and Logstash will convert it into an event for subsequent processing. Users can pass plain text, JSON, or any formatted data and use a corresponding codec with this -input. For Content-Type `application/json` the `json` codec is used, but for all other +input. For content-type `application/json` the `json` codec is used, but for all other data formats, `plain` codec is used. This input can also be used to receive webhook requests to integrate with other services @@ -101,6 +101,7 @@ This plugin supports the following configuration options plus the <> |<>|No | <> |<>|No | <> |<>, one of `[200, 201, 202, 204]`|No +| <> |<>|No | <> |<>|No | <> |a valid filesystem path|No | <> |<>|No @@ -280,11 +281,22 @@ invalid credentials (401), internal errors (503) or backpressure (429). If 204 (No Content) is set, the response body will not be sent in the response. +[id="plugins-{type}s-{plugin}-response_body"] +===== `response_body` + + * Value type is <> + * Default value is `ok` + +The text body of the response returned to clients on successful POSTs. +The default `ok` can be overriden with any string content such as JSON. + +If 204 (No Content) is set, the `response_body` option is ignored. + [id="plugins-{type}s-{plugin}-response_headers"] ===== `response_headers` * Value type is <> - * Default value is `{"Content-Type"=>"text/plain"}` + * Default value is `{"content-type"=>"text/plain"}` specify a custom set of response headers diff --git a/lib/logstash/inputs/http.rb b/lib/logstash/inputs/http.rb index c3ba03b2..e7f535c0 100644 --- a/lib/logstash/inputs/http.rb +++ b/lib/logstash/inputs/http.rb @@ -101,7 +101,7 @@ class LogStash::Inputs::Http < LogStash::Inputs::Base # Send reponses with this HTTP code. 204 is `no content`, and forces an empty response body config :response_code, :validate => [200, 201, 202, 204], :default => 200 - # Send this as the body to each HTTP POST. A JSON example: `"{\"ok\": true}"`. + # Send this as the body to each HTTP POST. A JSON example: `'{"ok": true}'`. config :response_body, :default => "ok" # specify a custom set of response headers (use lowercase keys, per netty.io)