diff --git a/plugins/in_http/http_prot.c b/plugins/in_http/http_prot.c index ab16eb328c5..21e20614644 100644 --- a/plugins/in_http/http_prot.c +++ b/plugins/in_http/http_prot.c @@ -21,10 +21,13 @@ #include #include #include +#include #include #include +#include + #include "http.h" #include "http_conn.h" @@ -493,6 +496,19 @@ static int process_payload(struct flb_http *ctx, struct http_conn *conn, { int type = -1; struct mk_http_header *header; + struct mk_http_header *extra_headers; + char *extracted_extra_headers; + int content_encoding_header_location = -1; + char *uncompressed_data; + size_t uncompressed_size = 0; + + /* used when checking content-encoding */ + char *extracted; + int first_colon; + int first_newline; + char *content_encoding_header_value; + int content_encoding_compare = -1; + int gzip_ret; header = &session->parser.headers[MK_HEADER_CONTENT_TYPE]; if (header->key.data == NULL) { @@ -510,21 +526,71 @@ static int process_payload(struct flb_http *ctx, struct http_conn *conn, type = HTTP_CONTENT_URLENCODED; } + /* handle gzip-encoded content */ + /* + The way things come out of headers_extra is very strange; everything seems to be in + key->data of the first entry and val->data is everything minus the first header key. + The behavior of monkey seems to be pretty buggy here. + + ...so, we just grab the whole thing and then sift through it looking for content-encoding. + */ + if (session->parser.headers_extra_count > 0) { + extra_headers = &session->parser.headers_extra[0]; + const char *content_encoding = "content-encoding"; + if (extra_headers->key.data != NULL) { + extracted_extra_headers = mk_string_tolower(extra_headers->key.data); + content_encoding_header_location = mk_string_search(extracted_extra_headers, content_encoding, 0); + } + } + + if (content_encoding_header_location == -1) { + uncompressed_data = request->data.data; + uncompressed_size = request->data.len; + } else { + /* Extract content-encoding and everything after up to the next \r\n to find the header value */ + extracted = mk_string_copy_substr(extracted_extra_headers, content_encoding_header_location, strlen(extracted_extra_headers)); + mk_mem_free(extracted_extra_headers); + first_colon = mk_string_search(extracted, ":", 0); + first_newline = mk_string_search(extracted, "\r\n", 0); + + content_encoding_header_value = mk_string_copy_substr(extracted, first_colon+1, first_newline); + mk_mem_free(extracted); + mk_string_trim(&content_encoding_header_value); + content_encoding_compare = strncasecmp(content_encoding_header_value, "gzip", 5); + mk_mem_free(content_encoding_header_value); + if (content_encoding_compare == 0) { + gzip_ret = flb_gzip_uncompress(request->data.data, request->data.len, (void **)&uncompressed_data, &uncompressed_size); + if (gzip_ret == -1) { + flb_error("[http] gzip decompression failed"); + + return -1; + } + } else { + send_response(conn, 400, "error: invalid 'Content-Encoding'\n"); + return -1; + } + } + if (type == -1) { send_response(conn, 400, "error: invalid 'Content-Type'\n"); return -1; } - if (request->data.len <= 0) { + if (uncompressed_size <= 0) { send_response(conn, 400, "error: no payload found\n"); return -1; } if (type == HTTP_CONTENT_JSON) { - parse_payload_json(ctx, tag, request->data.data, request->data.len); + parse_payload_json(ctx, tag, uncompressed_data, uncompressed_size); } else if (type == HTTP_CONTENT_URLENCODED) { parse_payload_urlencoded(ctx, tag, request->data.data, request->data.len); } + + /* only needs to be freed if we ungzipped something */ + if (content_encoding_compare == 0) { + flb_free(uncompressed_data); + } return 0; } @@ -614,23 +680,18 @@ int http_prot_handle(struct flb_http *ctx, struct http_conn *conn, } mk_mem_free(uri); - /* Check if we have a Host header: Hostname ; port */ mk_http_point_header(&request->host, &session->parser, MK_HEADER_HOST); - /* Header: Connection */ mk_http_point_header(&request->connection, &session->parser, MK_HEADER_CONNECTION); - /* HTTP/1.1 needs Host header */ if (!request->host.data && request->protocol == MK_HTTP_PROTOCOL_11) { flb_sds_destroy(tag); return -1; } - /* Should we close the session after this request ? */ mk_http_keepalive_check(session, request, ctx->server); - /* Content Length */ header = &session->parser.headers[MK_HEADER_CONTENT_LENGTH]; if (header->type == MK_HEADER_CONTENT_LENGTH) { @@ -640,16 +701,15 @@ int http_prot_handle(struct flb_http *ctx, struct http_conn *conn, else { request->_content_length.data = NULL; } - if (request->method != MK_METHOD_POST) { flb_sds_destroy(tag); send_response(conn, 400, "error: invalid HTTP method\n"); return -1; } - ret = process_payload(ctx, conn, tag, session, request); flb_sds_destroy(tag); send_response(conn, ctx->successful_response_code, NULL); + return ret; }