From b227424187fd49aa57f46b8f0a7e1dfd174c0cde Mon Sep 17 00:00:00 2001 From: Thibault Charbonnier Date: Fri, 29 May 2015 01:08:07 +0200 Subject: [PATCH] feat(APIAnalytics) config options + polishing - fix: don't mix up different APIs. Using a shared memory zone for having per-API ALFs instead of one ALF having all the APIs entries, which was a mistake. --- kong/kong.lua | 2 +- kong/plugins/apianalytics/handler.lua | 48 +++++++--- kong/plugins/apianalytics/schema.lua | 5 +- kong/plugins/httplog/log.lua | 10 +- kong/plugins/log_serializers/alf.lua | 60 +++++++----- kong/tools/http_client.lua | 9 +- kong/tools/ngx_stub.lua | 1 + kong/vendor/resty_http.lua | 6 +- spec/integration/proxy/resolver_spec.lua | 20 ++-- .../apianalytics/alf_serializer_spec.lua | 95 +++++++++++++++--- .../apianalytics/fixtures/requests.lua | 96 ++++++++++--------- spec/plugins/logging_spec.lua | 8 +- spec/unit/stub_coverage_spec.lua | 1 + 13 files changed, 236 insertions(+), 125 deletions(-) diff --git a/kong/kong.lua b/kong/kong.lua index 71e284a3764..6bcf2cabdab 100644 --- a/kong/kong.lua +++ b/kong/kong.lua @@ -166,7 +166,7 @@ function _M.exec_plugins_certificate() return end --- Calls plugins_access() on every loaded plugin +-- Calls plugins' access() on every loaded plugin function _M.exec_plugins_access() -- Setting a property that will be available for every plugin ngx.ctx.started_at = ngx.req.start_time() diff --git a/kong/plugins/apianalytics/handler.lua b/kong/plugins/apianalytics/handler.lua index 1dc1d453eeb..36f79f0e1cc 100644 --- a/kong/plugins/apianalytics/handler.lua +++ b/kong/plugins/apianalytics/handler.lua @@ -3,14 +3,16 @@ local BasePlugin = require "kong.plugins.base_plugin" local ALFSerializer = require "kong.plugins.log_serializers.alf" local APIANALYTICS_SOCKET = { - host = "localhost", + host = "localhost", -- socket.apianalytics.mashape.com port = 58000, path = "/alf_1.0.0" } -local function send_batch(premature, message) +local function send_batch(premature, conf, alf) + local message = alf:to_json_string(conf.service_token) + local client = http:new() - client:set_timeout(1000) -- 1 sec + client:set_timeout(50000) -- 5 sec local ok, err = client:connect(APIANALYTICS_SOCKET.host, APIANALYTICS_SOCKET.port) if not ok then @@ -21,7 +23,6 @@ local function send_batch(premature, message) local res, err = client:request({ path = APIANALYTICS_SOCKET.path, body = message }) if not res then ngx.log(ngx.ERR, "[apianalytics] failed to send batch: "..err) - return end -- close connection, or put it into the connection pool @@ -29,14 +30,13 @@ local function send_batch(premature, message) local ok, err = client:close() if not ok then ngx.log(ngx.ERR, "[apianalytics] failed to close: "..err) - return end else client:set_keepalive() end if res.status == 200 then - ALFSerializer:flush_entries() + alf:flush_entries() ngx.log(ngx.DEBUG, "[apianalytics] successfully saved the batch") else ngx.log(ngx.ERR, "[apianalytics] socket refused the batch: "..res.body) @@ -56,19 +56,26 @@ end function APIAnalyticsHandler:access(conf) APIAnalyticsHandler.super.access(self) - ngx.req.read_body() + -- Retrieve and keep in memory the bodies for this request ngx.ctx.apianalytics = { - req_body = ngx.req.get_body_data(), + req_body = "", res_body = "" } + + if conf.log_body then + ngx.req.read_body() + ngx.ctx.apianalytics.req_body = ngx.req.get_body_data() + end end function APIAnalyticsHandler:body_filter(conf) APIAnalyticsHandler.super.body_filter(self) - -- concatenate response chunks for ALF's `response.content.text` local chunk, eof = ngx.arg[1], ngx.arg[2] - ngx.ctx.apianalytics.res_body = ngx.ctx.apianalytics.res_body..chunk + -- concatenate response chunks for ALF's `response.content.text` + if conf.log_body then + ngx.ctx.apianalytics.res_body = ngx.ctx.apianalytics.res_body..chunk + end if eof then -- latest chunk ngx.ctx.apianalytics.response_received = ngx.now() @@ -78,13 +85,24 @@ end function APIAnalyticsHandler:log(conf) APIAnalyticsHandler.super.log(self) - local entries_size = ALFSerializer:add_entry(ngx) + local api_id = ngx.ctx.api.id + + -- Shared memory zone for apianalytics ALFs + if not ngx.shared.apianalytics then + ngx.shared.apianalytics = {} + end + + -- Create the ALF if not existing for this API + if not ngx.shared.apianalytics[api_id] then + ngx.shared.apianalytics[api_id] = ALFSerializer:new_alf() + end - if entries_size > 2 then - local message = ALFSerializer:to_json_string("54d2b98ee0d5076065fd6f93") - print("MESSAGE: "..message) + -- Simply adding the entry to the ALF + local n_entries = ngx.shared.apianalytics[api_id]:add_entry(ngx) - local ok, err = ngx.timer.at(0, send_batch, message) + -- Batch size reached, let's send the data + if n_entries >= conf.batch_size then + local ok, err = ngx.timer.at(0, send_batch, conf, ngx.shared.apianalytics[api_id]) if not ok then ngx.log(ngx.ERR, "[apianalytics] failed to create timer: ", err) end diff --git a/kong/plugins/apianalytics/schema.lua b/kong/plugins/apianalytics/schema.lua index 2ad13dcfb9e..c0d1b7c0bf2 100644 --- a/kong/plugins/apianalytics/schema.lua +++ b/kong/plugins/apianalytics/schema.lua @@ -1,4 +1,5 @@ return { - serviceToken = { type = "string", required = true }, - log_body = { type = "boolean", default = true } + service_token = { type = "string", required = true }, + batch_size = { type = "number", default = 100 }, + log_body = { type = "boolean", default = false } } diff --git a/kong/plugins/httplog/log.lua b/kong/plugins/httplog/log.lua index 7d3faf8f2ef..d1031c3ebb1 100644 --- a/kong/plugins/httplog/log.lua +++ b/kong/plugins/httplog/log.lua @@ -9,9 +9,9 @@ local _M = {} -- @param `message` Message to be logged -- @return `payload` http payload local function generate_post_payload(method, parsed_url, message) - local body = cjson.encode(message); + local body = cjson.encode(message) local payload = string.format( - "%s %s HTTP/1.1\r\nHost: %s\r\nConnection: Keep-Alive\r\nContent-Type: application/json\r\nContent-Length: %s\r\n\r\n%s", + "%s %s HTTP/1.1\r\nHost: %s\r\nConnection: Keep-Alive\r\nContent-Type: application/json\r\nContent-Length: %s\r\n\r\n%s", method:upper(), parsed_url.path, parsed_url.host, string.len(body), body) return payload end @@ -65,11 +65,11 @@ local function log(premature, conf, message) end end -function _M.execute(conf) - local ok, err = ngx.timer.at(0, log, conf, ngx.ctx.log_message) +function _M.execute(conf, message) + local ok, err = ngx.timer.at(0, log, conf, message) if not ok then ngx.log(ngx.ERR, "[httplog] failed to create timer: ", err) end end -return _M \ No newline at end of file +return _M diff --git a/kong/plugins/log_serializers/alf.lua b/kong/plugins/log_serializers/alf.lua index c5ba8acf2f2..610a4180bcd 100644 --- a/kong/plugins/log_serializers/alf.lua +++ b/kong/plugins/log_serializers/alf.lua @@ -1,6 +1,18 @@ -- ALF serializer module. -- ALF is the format supported by API Analytics (http://apianalytics.com) -- +-- This module represents _one_ ALF entry, which can have multiple requests entries. +-- # Usage: +-- +-- ## Create the ALF like so: +-- local alf = ALFSerializer:new_alf() +-- +-- ## Add entries: +-- local n_entries = alf:add_entry(ngx) +-- +-- ## Output the ALF with all its entries as JSON: +-- local json_str = alf:to_json_str(service_token) +-- -- - ALF specifications: https://github.com/Mashape/api-log-format -- - Nginx lua module documentation: http://wiki.nginx.org/HttpLuaModule -- - ngx_http_core_module: http://wiki.nginx.org/HttpCoreModule#.24http_HEADER @@ -12,20 +24,24 @@ local EMPTY_ARRAY_PLACEHOLDER = "__empty_array_placeholder__" local alf_mt = {} alf_mt.__index = alf_mt -local ALF = { - version = "1.0.0", - serviceToken = "", -- will be filled by to_json_string() - har = { - log = { - version = "1.2", - creator = { - name = "kong-api-analytics-plugin", - version = "0.1" - }, - entries = {} +function alf_mt:new_alf() + local ALF = { + version = "1.0.0", + serviceToken = "", -- will be filled by to_json_string() + har = { + log = { + version = "1.2", + creator = { + name = "kong-api-analytics-plugin", + version = "0.1" + }, + entries = {} + } } } -} + + return setmetatable(ALF, self) +end -- Transform a key/value lua table into an array of elements with `name`, `value`. -- Since Lua won't recognize {} as an empty array but an empty object, we need to force it @@ -65,6 +81,7 @@ end -- Serialize `ngx` into one ALF entry. -- For performance reasons, it tries to use the NGINX Lua API instead of -- ngx_http_core_module when possible. +-- Public for unit testing. function alf_mt:serialize_entry(ngx) -- Extracted data local req_headers = ngx.req.get_headers() @@ -79,10 +96,10 @@ function alf_mt:serialize_entry(ngx) -- ALF properties -- timers local send_time = round(ngx.ctx.proxy_started_at - started_at) - local wait_time = round(ngx.ctx.proxy_ended_at - ngx.ctx.proxy_started_at) - local receive_time = round(apianalytics_data.response_received - ngx.ctx.proxy_ended_at) + local wait_time = ngx.ctx.proxy_ended_at - ngx.ctx.proxy_started_at + local receive_time = apianalytics_data.response_received - ngx.ctx.proxy_ended_at -- headers and headers size - local req_headers_str, res_headers_str= "", "" + local req_headers_str, res_headers_str = "", "" local req_headers_arr = dic_to_array(req_headers, function(k, v) req_headers_str = req_headers_str..k..v end) local res_headers_arr = dic_to_array(res_headers, function(k, v) res_headers_str = res_headers_str..k..v end) local req_headers_size = string.len(req_headers_str) @@ -90,12 +107,11 @@ function alf_mt:serialize_entry(ngx) -- values extracted from headers 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_bodySize = req_headers["Content-Length"] and req_headers["Content-Length"] or -1 return { startedDateTime = os.date("!%Y-%m-%dT%TZ", started_at), clientIPAddress = ngx.var.remote_addr, - time = send_time + wait_time + receive_time, + time = round(send_time + wait_time + receive_time), request = { method = ngx.req.get_method(), url = ngx.var.scheme.."://"..ngx.var.host..ngx.var.uri, @@ -104,7 +120,7 @@ function alf_mt:serialize_entry(ngx) headers = req_headers_arr, headersSize = req_headers_size, cookies = {EMPTY_ARRAY_PLACEHOLDER}, - bodySize = tonumber(alf_req_bodySize), + bodySize = string.len(req_body), postData = { mimeType = alf_req_mimeType, params = dic_to_array(ngx.req.get_post_args()), @@ -128,9 +144,9 @@ function alf_mt:serialize_entry(ngx) }, cache = {}, timings = { - send = send_time, - wait = wait_time, - receive = receive_time, + send = round(send_time), + wait = round(wait_time), + receive = round(receive_time), blocked = -1, connect = -1, dns = -1, @@ -160,4 +176,4 @@ function alf_mt:flush_entries() self.har.log.entries = {} end -return setmetatable(ALF, alf_mt) +return alf_mt diff --git a/kong/tools/http_client.lua b/kong/tools/http_client.lua index 89228166a95..936279e0d63 100644 --- a/kong/tools/http_client.lua +++ b/kong/tools/http_client.lua @@ -26,10 +26,13 @@ local function http_call(options) options.protocol = "tlsv1" options.mode = "client" options.options = "all" - end - local _, code, headers = http.request(options) - return resp[1], code, headers + local _, code, headers = https.request(options) + return resp[1], code, headers + else + local _, code, headers = http.request(options) + return resp[1], code, headers + end end local function with_body(method) diff --git a/kong/tools/ngx_stub.lua b/kong/tools/ngx_stub.lua index 89ef4893635..bce037c8ec7 100644 --- a/kong/tools/ngx_stub.lua +++ b/kong/tools/ngx_stub.lua @@ -9,6 +9,7 @@ _G.ngx = { exit = function() end, say = function() end, log = function() end, + socket = { tcp = {} }, time = function() return os.time() end, re = { match = reg.match diff --git a/kong/vendor/resty_http.lua b/kong/vendor/resty_http.lua index 86dc5bba137..ee002ade830 100644 --- a/kong/vendor/resty_http.lua +++ b/kong/vendor/resty_http.lua @@ -3,6 +3,8 @@ local type = type local tonumber = tonumber local tostring = tostring local setmetatable = setmetatable +local encode_args = ngx.encode_args +local tcp = ngx.socket.tcp local concat = table.concat local insert = table.insert local upper = string.upper @@ -46,7 +48,7 @@ local function _req_header(conf, opts) -- Normalize query string if type(opts.query) == "table" then - opts.query = ngx.encode_args(opts.query) + opts.query = encode_args(opts.query) end -- Append query string @@ -216,7 +218,7 @@ end -------------------------------------- function new(self) - local sock, err = ngx.socket.tcp() + local sock, err = tcp() if not sock then return nil, err end diff --git a/spec/integration/proxy/resolver_spec.lua b/spec/integration/proxy/resolver_spec.lua index b33bc0e7e76..e4460913a0f 100644 --- a/spec/integration/proxy/resolver_spec.lua +++ b/spec/integration/proxy/resolver_spec.lua @@ -26,8 +26,8 @@ describe("Resolver", function() spec_helper.prepare_db() spec_helper.insert_fixtures { api = { - { name = "tests host resolver 1", public_dns = "mocbkin.com", target_url = "http://mockbin.com" }, - { name = "tests host resolver 2", public_dns = "mocbkin-auth.com", target_url = "http://mockbin.com" } + { name = "tests host resolver 1", public_dns = "mockbin.com", target_url = "http://mockbin.com" }, + { name = "tests host resolver 2", public_dns = "mockbin-auth.com", target_url = "http://mockbin.com" } }, plugin_configuration = { { name = "keyauth", value = {key_names = {"apikey"} }, __api = 2 } @@ -55,7 +55,7 @@ describe("Resolver", function() describe("SSL", function() it("should work when calling SSL port", function() - local response, status = http_client.get(STUB_GET_SSL_URL, nil, { host = "mocbkin.com" }) + local response, status = http_client.get(STUB_GET_SSL_URL, nil, { host = "mockbin.com" }) assert.are.equal(200, status) assert.truthy(response) local parsed_response = cjson.decode(response) @@ -101,24 +101,24 @@ describe("Resolver", function() describe("Existing API", function() it("should proxy when the API is in Kong", function() - local _, status = http_client.get(STUB_GET_URL, nil, { host = "mocbkin.com"}) + local _, status = http_client.get(STUB_GET_URL, nil, { host = "mockbin.com"}) assert.are.equal(200, status) end) it("should proxy when the Host header is not trimmed", function() - local _, status = http_client.get(STUB_GET_URL, nil, { host = " mocbkin.com "}) + local _, status = http_client.get(STUB_GET_URL, nil, { host = " mockbin.com "}) assert.are.equal(200, status) end) it("should return the correct Server and Via headers when the request was proxied", function() - local _, status, headers = http_client.get(STUB_GET_URL, nil, { host = "mocbkin.com"}) + local _, status, headers = http_client.get(STUB_GET_URL, nil, { host = "mockbin.com"}) assert.are.equal(200, status) assert.are.equal("cloudflare-nginx", headers.server) assert.are.equal(constants.NAME.."/"..constants.VERSION, headers.via) end) it("should return the correct Server and no Via header when the request was NOT proxied", function() - local _, status, headers = http_client.get(STUB_GET_URL, nil, { host = "mocbkin-auth.com"}) + local _, status, headers = http_client.get(STUB_GET_URL, nil, { host = "mockbin-auth.com"}) assert.are.equal(403, status) assert.are.equal(constants.NAME.."/"..constants.VERSION, headers.server) assert.falsy(headers.via) @@ -131,7 +131,7 @@ describe("Resolver", function() local tcp = socket.tcp() tcp:connect(host, port) - tcp:send("GET "..parsed_url.path.." HTTP/1.0\r\nHost: mocbkin.com\r\n\r\n"); + tcp:send("GET "..parsed_url.path.." HTTP/1.0\r\nHost: mockbin.com\r\n\r\n"); local response = "" while true do local s, status, partial = tcp:receive() @@ -150,7 +150,7 @@ describe("Resolver", function() local tcp = socket.tcp() tcp:connect(host, port) - tcp:send("GET "..parsed_url.path.." HTTP/1.0\r\nHost: fake.com\r\nHost: mocbkin.com\r\n\r\n"); + tcp:send("GET "..parsed_url.path.." HTTP/1.0\r\nHost: fake.com\r\nHost: mockbin.com\r\n\r\n"); local response = "" while true do local s, status, partial = tcp:receive() @@ -163,7 +163,7 @@ describe("Resolver", function() end) it("should proxy when the request has no Host header but the X-Host-Override header", function() - local _, status = http_client.get(STUB_GET_URL, nil, { ["X-Host-Override"] = "mocbkin.com"}) + local _, status = http_client.get(STUB_GET_URL, nil, { ["X-Host-Override"] = "mockbin.com"}) assert.are.equal(200, status) end) diff --git a/spec/plugins/apianalytics/alf_serializer_spec.lua b/spec/plugins/apianalytics/alf_serializer_spec.lua index dceaa2b3202..035c994dcf6 100644 --- a/spec/plugins/apianalytics/alf_serializer_spec.lua +++ b/spec/plugins/apianalytics/alf_serializer_spec.lua @@ -1,27 +1,94 @@ -local ALFSerializer = require "kong.plugins.log_serializers.alf" local fixtures = require "spec.plugins.apianalytics.fixtures.requests" +local ALFSerializer = require "kong.plugins.log_serializers.alf" + +-- @see http://lua-users.org/wiki/CopyTable +local function deepcopy(orig) + local copy = {} + if type(orig) == "table" then + for orig_key, orig_value in next, orig, nil do + copy[deepcopy(orig_key)] = deepcopy(orig_value) + end + else -- number, string, boolean, etc + copy = orig + end + return copy +end + +local function sameEntry(state, arguments) + local fixture_entry = deepcopy(arguments[1]) + local entry = deepcopy(arguments[2]) + + local delta = 0.000000000000001 + -- Compare timings + for k, fixture_timer in pairs(fixture_entry.timings) do + assert.True(math.abs(entry.timings[k] - fixture_timer) < delta) + end + + -- Compare time property + assert.True(math.abs(entry.time - fixture_entry.time) < delta) + + entry.time = nil + entry.timings = nil + fixture_entry.time = nil + fixture_entry.timings = nil + + assert.same(fixture_entry, entry) + return true +end + +local say = require("say") +say:set("assertion.sameEntry.positive", "Not the same entries") +say:set("assertion.sameEntry.negative", "Not the same entries") +assert:register("assertion", "sameEntry", sameEntry, "assertion.sameEntry.positive", "assertion.sameEntry.negative") describe("ALF serializer", function() + local alf + + describe("#new_alf()", function () + + it("should create a new ALF", function() + alf = ALFSerializer:new_alf() + assert.same({ + version = "1.0.0", + serviceToken = "", + har = { + log = { + version = "1.2", + creator = { name = "kong-api-analytics-plugin", version = "0.1" + }, + entries = {} + } + } + }, alf) + end) + + end) + describe("#serialize_entry()", function() it("should serialize an ngx GET request/response", function() - local entry = ALFSerializer:serialize_entry(fixtures.GET.NGX_STUB) + local entry = alf:serialize_entry(fixtures.GET.NGX_STUB) assert.are.same(fixtures.GET.ENTRY, entry) end) end) - + describe("#add_entry()", function() it("should add the entry to the serializer entries property", function() - ALFSerializer:add_entry(fixtures.GET.NGX_STUB) - assert.are.same(1, table.getn(ALFSerializer.har.log.entries)) - assert.are.same(fixtures.GET.ENTRY, ALFSerializer.har.log.entries[1]) + alf:add_entry(fixtures.GET.NGX_STUB) + assert.are.same(1, table.getn(alf.har.log.entries)) + assert.are.sameEntry(fixtures.GET.ENTRY, alf.har.log.entries[1]) + + alf:add_entry(fixtures.GET.NGX_STUB) + assert.are.same(2, table.getn(alf.har.log.entries)) + assert.are.sameEntry(fixtures.GET.ENTRY, alf.har.log.entries[2]) + end) - ALFSerializer:add_entry(fixtures.GET.NGX_STUB) - assert.are.same(2, table.getn(ALFSerializer.har.log.entries)) - assert.are.same(fixtures.GET.ENTRY, ALFSerializer.har.log.entries[2]) + it("#new_alf() should instanciate a new ALF that has nothing to do with the existing one", function() + local other_alf = ALFSerializer:new_alf() + assert.are_not_same(alf, other_alf) end) end) @@ -29,12 +96,12 @@ describe("ALF serializer", function() describe("#to_json_string()", function() it("should throw an error if no token was given", function() - assert.has_error(function() ALFSerializer:to_json_string() end, + assert.has_error(function() alf:to_json_string() end, "API Analytics serviceToken required") end) it("should return a JSON string", function() - local json_str = ALFSerializer:to_json_string("stub_service_token") + local json_str = alf:to_json_string("stub_service_token") assert.are.same("string", type(json_str)) end) @@ -43,11 +110,9 @@ describe("ALF serializer", function() describe("#flush_entries()", function() it("should remove any existing entry", function() - ALFSerializer:flush_entries() - assert.are.same(0, table.getn(ALFSerializer.har.log.entries)) + alf:flush_entries() + assert.are.same(0, table.getn(alf.har.log.entries)) end) end) - - -- TODO: tests empty queryString (empty array) both JSON + Lua formats end) diff --git a/spec/plugins/apianalytics/fixtures/requests.lua b/spec/plugins/apianalytics/fixtures/requests.lua index 0e8b0d06db1..df512c8ef93 100644 --- a/spec/plugins/apianalytics/fixtures/requests.lua +++ b/spec/plugins/apianalytics/fixtures/requests.lua @@ -1,3 +1,5 @@ +local EMPTY_ARRAY_PLACEHOLDER = "__empty_array_placeholder__" + return { ["GET"] = { ["NGX_STUB"] = { @@ -6,7 +8,7 @@ return { http_version = function() return 1.1 end, get_headers = function() return {["Accept"]="/*/",["Host"]="mockbin.com"} end, get_uri_args = function() return {["hello"]="world",["foo"]="bar"} end, - start_time = function() return 1429723321.026 end + get_post_args = function() return {["hello"]={"world", "earth"}} end }, resp = { get_headers = function() return {["Connection"]="close",["Content-Type"]="application/json",["Content-Length"]="934"} end @@ -17,40 +19,45 @@ return { host = "mockbin.com", uri = "/request", request_length = 123, - bytes_sent = 934, + body_bytes_sent = 934, remote_addr = "127.0.0.1" }, ctx = { - req_body = "request body", - res_body = "response body" + started_at = 1432844571.623, + proxy_started_at = 1432844571.719, + proxy_ended_at = 1432844572.11, + apianalytics = { + req_body = "hello=world&hello=earth", + res_body = "{\"message\":\"response body\"}", + response_received = 1432844572.11 + } } }, ["ENTRY"] = { + cache = {}, clientIPAddress = "127.0.0.1", request = { - bodySize = 0, - content = { - mimeType = "application/octet-stream", - size = 123, - text = "" + bodySize = 23, + cookies = {EMPTY_ARRAY_PLACEHOLDER}, + headers = { + { name = "Accept", value = "/*/"}, + { name = "Host", value = "mockbin.com" } }, - headers = { { - name = "Accept", - value = "/*/" - }, { - name = "Host", - value = "mockbin.com" - } }, - headersSize = 10, + headersSize = 24, httpVersion = "HTTP/1.1", method = "GET", - queryString = { { - name = "foo", - value = "bar" - }, { - name = "hello", - value = "world" - } }, + postData = { + mimeType = "application/octet-stream", + 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 = { @@ -58,34 +65,31 @@ return { content = { mimeType = "application/json", size = 934, - text = "" + text = "{\"message\":\"response body\"}" + }, + cookies = {EMPTY_ARRAY_PLACEHOLDER}, + headers = { + { name = "Content-Length", value = "934" }, + { name = "Content-Type", value = "application/json" }, + { name = "Connection", value = "close" } }, - headers = { { - name = "Content-Length", - value = "934" - }, { - name = "Content-Type", - value = "application/json" - }, { - name = "Connection", - value = "close" - } }, - headersSize = 10, + headersSize = 60, httpVersion = "", + redirectURL = "", status = 200, statusText = "" }, - startedDateTime = "2015-04-22T17:22:01Z", - time = 3, + startedDateTime = "2015-05-28T20:22:51Z", + time = 0.487, timings = { - blocked = 0, - connect = 0, - dns = 0, - receive = 1, - send = 1, - ssl = 0, - wait = 1 + blocked = -1, + connect = -1, + dns = -1, + receive = 0, + send = 0.096, + ssl = -1, + wait = 0.391 } } } -} \ No newline at end of file +} diff --git a/spec/plugins/logging_spec.lua b/spec/plugins/logging_spec.lua index 7a5345be66b..c3374d5e450 100644 --- a/spec/plugins/logging_spec.lua +++ b/spec/plugins/logging_spec.lua @@ -1,10 +1,10 @@ -local spec_helper = require "spec.spec_helpers" -local http_client = require "kong.tools.http_client" -local cjson = require "cjson" -local yaml = require "yaml" local IO = require "kong.tools.io" +local yaml = require "yaml" local uuid = require "uuid" +local cjson = require "cjson" local stringy = require "stringy" +local spec_helper = require "spec.spec_helpers" +local http_client = require "kong.tools.http_client" -- This is important to seed the UUID generator uuid.seed() diff --git a/spec/unit/stub_coverage_spec.lua b/spec/unit/stub_coverage_spec.lua index 7a5efa1fa4e..d10321a785d 100644 --- a/spec/unit/stub_coverage_spec.lua +++ b/spec/unit/stub_coverage_spec.lua @@ -2,6 +2,7 @@ -- since not all files are currently unit tested and the coverage is erroneous. local IO = require "kong.tools.io" +require "kong.tools.ngx_stub" -- Stub DAO for lapis controllers _G.dao = {}