Software /
code /
prosody
File
spec/util_cache_spec.lua @ 11665:148075532021
net.server_epoll: Prevent stack overflow of opportunistic writes
net.http.files serving a big enough file on a fast enough connection
with opportunistic_writes enabled could trigger a stack overflow through
repeatedly serving more data that immediately gets sent, draining the
buffer and triggering more data to be sent. This also blocked the server
on a single task until completion or an error.
This change prevents nested opportunistic writes, which should prevent
the stack overflow, at the cost of reduced download speed, but this is
unlikely to be noticeable outside of Gbit networks. Speed at the cost of
blocking other processing is not worth it, especially with the risk of
stack overflow.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Sun, 11 Jul 2021 09:39:21 +0200 |
parent | 11366:618ab9bba1c2 |
child | 12571:c4337ff4f1c4 |
line wrap: on
line source
local cache = require "util.cache"; describe("util.cache", function() describe("#new()", function() it("should work", function() local c = cache.new(5); local function expect_kv(key, value, actual_key, actual_value) assert.are.equal(key, actual_key, "key incorrect"); assert.are.equal(value, actual_value, "value incorrect"); end expect_kv(nil, nil, c:head()); expect_kv(nil, nil, c:tail()); assert.are.equal(c:count(), 0); c:set("one", 1) assert.are.equal(c:count(), 1); expect_kv("one", 1, c:head()); expect_kv("one", 1, c:tail()); c:set("two", 2) expect_kv("two", 2, c:head()); expect_kv("one", 1, c:tail()); c:set("three", 3) expect_kv("three", 3, c:head()); expect_kv("one", 1, c:tail()); c:set("four", 4) c:set("five", 5); assert.are.equal(c:count(), 5); expect_kv("five", 5, c:head()); expect_kv("one", 1, c:tail()); c:set("foo", nil); assert.are.equal(c:count(), 5); expect_kv("five", 5, c:head()); expect_kv("one", 1, c:tail()); assert.are.equal(c:get("one"), 1); expect_kv("five", 5, c:head()); expect_kv("one", 1, c:tail()); assert.are.equal(c:get("two"), 2); assert.are.equal(c:get("three"), 3); assert.are.equal(c:get("four"), 4); assert.are.equal(c:get("five"), 5); assert.are.equal(c:get("foo"), nil); assert.are.equal(c:get("bar"), nil); c:set("six", 6); assert.are.equal(c:count(), 5); expect_kv("six", 6, c:head()); expect_kv("two", 2, c:tail()); assert.are.equal(c:get("one"), nil); assert.are.equal(c:get("two"), 2); assert.are.equal(c:get("three"), 3); assert.are.equal(c:get("four"), 4); assert.are.equal(c:get("five"), 5); assert.are.equal(c:get("six"), 6); c:set("three", nil); assert.are.equal(c:count(), 4); assert.are.equal(c:get("one"), nil); assert.are.equal(c:get("two"), 2); assert.are.equal(c:get("three"), nil); assert.are.equal(c:get("four"), 4); assert.are.equal(c:get("five"), 5); assert.are.equal(c:get("six"), 6); c:set("seven", 7); assert.are.equal(c:count(), 5); assert.are.equal(c:get("one"), nil); assert.are.equal(c:get("two"), 2); assert.are.equal(c:get("three"), nil); assert.are.equal(c:get("four"), 4); assert.are.equal(c:get("five"), 5); assert.are.equal(c:get("six"), 6); assert.are.equal(c:get("seven"), 7); c:set("eight", 8); assert.are.equal(c:count(), 5); assert.are.equal(c:get("one"), nil); assert.are.equal(c:get("two"), nil); assert.are.equal(c:get("three"), nil); assert.are.equal(c:get("four"), 4); assert.are.equal(c:get("five"), 5); assert.are.equal(c:get("six"), 6); assert.are.equal(c:get("seven"), 7); assert.are.equal(c:get("eight"), 8); c:set("four", 4); assert.are.equal(c:count(), 5); assert.are.equal(c:get("one"), nil); assert.are.equal(c:get("two"), nil); assert.are.equal(c:get("three"), nil); assert.are.equal(c:get("four"), 4); assert.are.equal(c:get("five"), 5); assert.are.equal(c:get("six"), 6); assert.are.equal(c:get("seven"), 7); assert.are.equal(c:get("eight"), 8); c:set("nine", 9); assert.are.equal(c:count(), 5); assert.are.equal(c:get("one"), nil); assert.are.equal(c:get("two"), nil); assert.are.equal(c:get("three"), nil); assert.are.equal(c:get("four"), 4); assert.are.equal(c:get("five"), nil); assert.are.equal(c:get("six"), 6); assert.are.equal(c:get("seven"), 7); assert.are.equal(c:get("eight"), 8); assert.are.equal(c:get("nine"), 9); do local keys = { "nine", "four", "eight", "seven", "six" }; local values = { 9, 4, 8, 7, 6 }; local i = 0; for k, v in c:items() do i = i + 1; assert.are.equal(k, keys[i]); assert.are.equal(v, values[i]); end assert.are.equal(i, 5); c:set("four", "2+2"); assert.are.equal(c:count(), 5); assert.are.equal(c:get("one"), nil); assert.are.equal(c:get("two"), nil); assert.are.equal(c:get("three"), nil); assert.are.equal(c:get("four"), "2+2"); assert.are.equal(c:get("five"), nil); assert.are.equal(c:get("six"), 6); assert.are.equal(c:get("seven"), 7); assert.are.equal(c:get("eight"), 8); assert.are.equal(c:get("nine"), 9); end do local keys = { "four", "nine", "eight", "seven", "six" }; local values = { "2+2", 9, 8, 7, 6 }; local i = 0; for k, v in c:items() do i = i + 1; assert.are.equal(k, keys[i]); assert.are.equal(v, values[i]); end assert.are.equal(i, 5); c:set("foo", nil); assert.are.equal(c:count(), 5); assert.are.equal(c:get("one"), nil); assert.are.equal(c:get("two"), nil); assert.are.equal(c:get("three"), nil); assert.are.equal(c:get("four"), "2+2"); assert.are.equal(c:get("five"), nil); assert.are.equal(c:get("six"), 6); assert.are.equal(c:get("seven"), 7); assert.are.equal(c:get("eight"), 8); assert.are.equal(c:get("nine"), 9); end do local keys = { "four", "nine", "eight", "seven", "six" }; local values = { "2+2", 9, 8, 7, 6 }; local i = 0; for k, v in c:items() do i = i + 1; assert.are.equal(k, keys[i]); assert.are.equal(v, values[i]); end assert.are.equal(i, 5); c:set("four", nil); assert.are.equal(c:get("one"), nil); assert.are.equal(c:get("two"), nil); assert.are.equal(c:get("three"), nil); assert.are.equal(c:get("four"), nil); assert.are.equal(c:get("five"), nil); assert.are.equal(c:get("six"), 6); assert.are.equal(c:get("seven"), 7); assert.are.equal(c:get("eight"), 8); assert.are.equal(c:get("nine"), 9); end do local keys = { "nine", "eight", "seven", "six" }; local values = { 9, 8, 7, 6 }; local i = 0; for k, v in c:items() do i = i + 1; assert.are.equal(k, keys[i]); assert.are.equal(v, values[i]); end assert.are.equal(i, 4); end do local evicted_key, evicted_value; local c2 = cache.new(3, function (_key, _value) evicted_key, evicted_value = _key, _value; end); local function set(k, v, should_evict_key, should_evict_value) evicted_key, evicted_value = nil, nil; c2:set(k, v); assert.are.equal(evicted_key, should_evict_key); assert.are.equal(evicted_value, should_evict_value); end set("a", 1) set("a", 1) set("a", 1) set("a", 1) set("a", 1) set("b", 2) set("c", 3) set("b", 2) set("d", 4, "a", 1) set("e", 5, "c", 3) end do local evicted_key, evicted_value; local c3 = cache.new(1, function (_key, _value) evicted_key, evicted_value = _key, _value; if _key == "a" then -- Sanity check for what we're evicting assert.are.equal(_key, "a"); assert.are.equal(_value, 1); -- We're going to block eviction of this key/value, so set to nil... evicted_key, evicted_value = nil, nil; -- Returning false to block eviction return false end end); local function set(k, v, should_evict_key, should_evict_value) evicted_key, evicted_value = nil, nil; local ret = c3:set(k, v); assert.are.equal(evicted_key, should_evict_key); assert.are.equal(evicted_value, should_evict_value); return ret; end set("a", 1) set("a", 1) set("a", 1) set("a", 1) set("a", 1) -- Our on_evict prevents "a" from being evicted, causing this to fail... assert.are.equal(set("b", 2), false, "Failed to prevent eviction, or signal result"); expect_kv("a", 1, c3:head()); expect_kv("a", 1, c3:tail()); -- Check the final state is what we expect assert.are.equal(c3:get("a"), 1); assert.are.equal(c3:get("b"), nil); assert.are.equal(c3:count(), 1); end local c4 = cache.new(3, false); assert.are.equal(c4:set("a", 1), true); assert.are.equal(c4:set("a", 1), true); assert.are.equal(c4:set("a", 1), true); assert.are.equal(c4:set("a", 1), true); assert.are.equal(c4:set("b", 2), true); assert.are.equal(c4:set("c", 3), true); assert.are.equal(c4:set("d", 4), false); assert.are.equal(c4:set("d", 4), false); assert.are.equal(c4:set("d", 4), false); expect_kv("c", 3, c4:head()); expect_kv("a", 1, c4:tail()); local c5 = cache.new(3, function (k, v) --luacheck: ignore 212/v if k == "a" then return nil; elseif k == "b" then return true; end return false; end); assert.are.equal(c5:set("a", 1), true); assert.are.equal(c5:set("a", 1), true); assert.are.equal(c5:set("a", 1), true); assert.are.equal(c5:set("a", 1), true); assert.are.equal(c5:set("b", 2), true); assert.are.equal(c5:set("c", 3), true); assert.are.equal(c5:set("d", 4), true); -- "a" evicted (cb returned nil) assert.are.equal(c5:set("d", 4), true); -- nop assert.are.equal(c5:set("d", 4), true); -- nop assert.are.equal(c5:set("e", 5), true); -- "b" evicted (cb returned true) assert.are.equal(c5:set("f", 6), false); -- "c" won't evict (cb returned false) expect_kv("e", 5, c5:head()); expect_kv("c", 3, c5:tail()); end); (_VERSION=="Lua 5.1" and pending or it)(":table works", function () local t = cache.new(3):table(); assert.is.table(t); t["a"] = "1"; assert.are.equal(t["a"], "1"); t["b"] = "2"; assert.are.equal(t["b"], "2"); t["c"] = "3"; assert.are.equal(t["c"], "3"); t["d"] = "4"; assert.are.equal(t["d"], "4"); assert.are.equal(t["a"], nil); local i = spy.new(function () end); for k, v in pairs(t) do i(k,v) end assert.spy(i).was_called(); assert.spy(i).was_called_with("b", "2"); assert.spy(i).was_called_with("c", "3"); assert.spy(i).was_called_with("d", "4"); end); end); end);