diff --git a/kong/plugins/log-serializers/alf.lua b/kong/plugins/log-serializers/alf.lua index f38baaab385c..079264bb587c 100644 --- a/kong/plugins/log-serializers/alf.lua +++ b/kong/plugins/log-serializers/alf.lua @@ -16,6 +16,15 @@ -- - ngx_http_core_module: http://wiki.nginx.org/HttpCoreModule#.24http_HEADER local stringy = require "stringy" +local table_insert = table.insert +local tostring = tostring +local pairs = pairs +local ipairs = ipairs +local type = type +local tonumber = tonumber +local string_len = string.len +local os_date = os.date + local EMPTY_ARRAY_PLACEHOLDER = "__empty_array_placeholder__" @@ -38,7 +47,7 @@ local function dic_to_array(hash, fn) for _, val in ipairs(v) do k = tostring(k) val = tostring(val) - table.insert(arr, {name = k, value = val}) + table_insert(arr, {name = k, value = val}) fn(k, val) end end @@ -50,6 +59,26 @@ local function dic_to_array(hash, fn) end end +--- Get a header from nginx's headers +-- Make sure that is multiple headers of a same name are present, +-- we only want the last one. Also include a default value if +-- no header is present. +-- @param `headers` ngx's request or response headers table. +-- @param `name` Name of the desired header to retrieve. +-- @param `default` String returned in case no header is found. +-- @return `header` The header value (a string) or the default, or nil. +local function get_header(headers, name, default) + local val = headers[name] + if val ~= nil then + if type(val) == "table" then + val = val[#val] + end + return val + end + + return default +end + local _M = {} -- Serialize `ngx` into one ALF entry. @@ -108,15 +137,15 @@ function _M.serialize_entry(ngx) local alf_req_headers_arr = dic_to_array(req_headers, function(k, v) req_headers_str = req_headers_str..k..v end) local alf_res_headers_arr = dic_to_array(res_headers, function(k, v) res_headers_str = res_headers_str..k..v end) - local alf_req_headers_size = string.len(req_headers_str) - local alf_res_headers_size = string.len(res_headers_str) + local alf_req_headers_size = string_len(req_headers_str) + local alf_res_headers_size = string_len(res_headers_str) -- mimeType, defaulting to "application/octet-stream" - local alf_req_mimeType = req_headers["Content-Type"] and req_headers["Content-Type"] or "application/octet-stream" - local alf_res_mimeType = res_headers["Content-Type"] and res_headers["Content-Type"] or "application/octet-stream" + local alf_req_mimeType = get_header(req_headers, "Content-Type", "application/octet-stream") + local alf_res_mimeType = get_header(res_headers, "Content-Type", "application/octet-stream") return { - startedDateTime = os.date("!%Y-%m-%dT%TZ", alf_started_at), + startedDateTime = os_date("!%Y-%m-%dT%TZ", alf_started_at), time = alf_time, request = { method = ngx.req.get_method(), @@ -126,7 +155,7 @@ function _M.serialize_entry(ngx) headers = alf_req_headers_arr, headersSize = alf_req_headers_size, cookies = {EMPTY_ARRAY_PLACEHOLDER}, - bodySize = string.len(alf_req_body), + bodySize = string_len(alf_req_body), postData = { mimeType = alf_req_mimeType, params = dic_to_array(alf_req_post_args), diff --git a/spec/plugins/mashape-analytics/alf_serializer_spec.lua b/spec/plugins/mashape-analytics/alf_serializer_spec.lua index f3eff8354e44..4dab502b5bc9 100644 --- a/spec/plugins/mashape-analytics/alf_serializer_spec.lua +++ b/spec/plugins/mashape-analytics/alf_serializer_spec.lua @@ -66,6 +66,11 @@ describe("ALF serializer", function() assert.are.sameEntry(fixtures.MULTIPLE_UPSTREAMS.ENTRY, entry) assert.equal(60468, entry.timings.wait) end) + + it("should return the last header if two are present for mimeType", function() + local entry = ALFSerializer.serialize_entry(fixtures.MULTIPLE_HEADERS.NGX_STUB) + assert.are.sameEntry(fixtures.MULTIPLE_HEADERS.ENTRY, entry) + end) end) describe("#new_alf()", function () diff --git a/spec/plugins/mashape-analytics/fixtures/requests.lua b/spec/plugins/mashape-analytics/fixtures/requests.lua index b0f05cf86000..9592fa0f2fa0 100644 --- a/spec/plugins/mashape-analytics/fixtures/requests.lua +++ b/spec/plugins/mashape-analytics/fixtures/requests.lua @@ -183,5 +183,99 @@ return { wait = 60468 } } + }, + ["MULTIPLE_HEADERS"] = { + ["NGX_STUB"] = { + req = { + start_time = function() return 1432844571.623 end, + get_method = function() return "GET" end, + http_version = function() return 1.1 end, + get_headers = function() return {["Accept"] = "/*/", ["Host"] = "mockbin.com", ["Content-Type"] = {"application/json", "application/www-form-urlencoded"}} end, + get_uri_args = function() return {["hello"] = "world", ["foo"] = "bar"} end + }, + resp = { + get_headers = function() return {["Connection"] = "close", ["Content-Type"] = {"application/json", "application/www-form-urlencoded"}, ["Content-Length"] = "934"} end + }, + status = 200, + var = { + scheme = "http", + host = "mockbin.com", + request_uri = "/request", + request_length = 123, + body_bytes_sent = 934, + remote_addr = "127.0.0.1", + upstream_response_time = "0.391" + }, + ctx = { + proxy_started_at = 1432844571719, + proxy_ended_at = 143284457211, + analytics = { + req_body = "hello=world&hello=earth", + res_body = "{\"message\":\"response body\"}", + req_post_args = {["hello"] = {"world", "earth"}}, + response_received = 143284457211 + } + } + }, + ["ENTRY"] = { + cache = {}, + request = { + bodySize = 23, + cookies = {EMPTY_ARRAY_PLACEHOLDER}, + headers = { + {name = "Accept", value = "/*/"}, + {name = "Host", value = "mockbin.com"}, + {name = "Content-Type", value = "application/json"}, + {name = "Content-Type", value = "application/www-form-urlencoded"} + }, + headersSize = 95, + httpVersion = "HTTP/1.1", + method = "GET", + postData = { + mimeType = "application/www-form-urlencoded", + params = { + {name = "hello", value = "world"}, + {name = "hello", value = "earth"} + }, + text = "hello=world&hello=earth" + }, + queryString = { + {name = "foo", value = "bar"}, + {name = "hello", value = "world"} + }, + url = "http://mockbin.com/request" + }, + response = { + bodySize = 934, + content = { + mimeType = "application/www-form-urlencoded", + size = 934, + text = "{\"message\":\"response body\"}" + }, + cookies = {EMPTY_ARRAY_PLACEHOLDER}, + headers = { + {name = "Content-Length", value = "934"}, + {name = "Content-Type", value = "application/json"}, + {name = "Content-Type", value = "application/www-form-urlencoded"}, + {name = "Connection", value = "close"} + }, + headersSize = 103, + httpVersion = "", + redirectURL = "", + status = 200, + statusText = "" + }, + startedDateTime = "2015-05-28T20:22:51Z", + time = 487, + timings = { + blocked = -1, + connect = -1, + dns = -1, + receive = 0, + send = 96, + ssl = -1, + wait = 391 + } + } } }