Skip to content

Commit

Permalink
Adds openresty testing
Browse files Browse the repository at this point in the history
  • Loading branch information
davidmerfield committed Oct 30, 2023
1 parent 8428c5d commit 224f128
Show file tree
Hide file tree
Showing 12 changed files with 503 additions and 86 deletions.
29 changes: 0 additions & 29 deletions config/openresty/conf/add.lua

This file was deleted.

4 changes: 2 additions & 2 deletions config/openresty/conf/auto-ssl-init.conf
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
# approximately 100 separate domains. Note that this should not cause an
# error if there are too many domains, just that nginx will have to look
# up the certificate in the database which is slower.
# 100mb = 10,000 domains
lua_shared_dict auto_ssl 100m;

# The "auto_ssl" shared dict is used to temporarily store various settings
# like the secret used by the hook server on port 8999. Do not change or
# omit it.
# like the secret used by the hook server on port 8999. Do not change or omit.
lua_shared_dict auto_ssl_settings 64k;

# A DNS resolver must be defined for OCSP stapling to function.
Expand Down
2 changes: 2 additions & 0 deletions config/openresty/conf/auto-ssl.conf
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";

# Hopefully improves performance
# one megabyte can store about 4000 sessions
# 10m = 40,000 sessions
ssl_session_cache shared:SSL:10m;

# Dynamic handler for issuing or returning certs for SNI domains.
Expand Down
261 changes: 261 additions & 0 deletions config/openresty/conf/cacher/cacher.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
local cacher = {
_VERSION = "cacher.lua 1.0.0",
_DESCRIPTION = "Purgable cache for OpenResty",
_URL = "",
_LICENSE = ""
}

local md5 = require "md5"

local function cacher_add (self, ngx)
local shared_dictionary = self.shared_dictionary

local host = ngx.var.host
local request_uri = ngx.var.request_uri
local server_port = ngx.var.server_port
local scheme = ngx.var.scheme

local cache_key = scheme .. "://" .. host .. ":" .. server_port .. request_uri
local cache_key_md5 = md5.sumhexa(cache_key)

-- the cache file path is in the following format:
-- $x/$y/$cache_key_md5
-- where x is the last character of the cache_key_md5
-- and y are the two characters before that
local cache_file_path = cache_key_md5:sub(-1) .. "/" .. cache_key_md5:sub(-3,-2) .. "/" .. cache_key_md5
local already_stored = shared_dictionary:get(cache_file_path)

if (already_stored == nil) then
ngx.log(ngx.NOTICE,host .. " " .. cache_file_path.. " adding to dictionary")
shared_dictionary:rpush(host, cache_file_path)
shared_dictionary:set(cache_file_path, true)
else
ngx.log(ngx.NOTICE,host .. " " .. cache_file_path.. " already stored" )
end
end


-- this file will read the contents of the cache directory and store them in
-- the lua shared dict shared_dictionary so we can purge them later

-- I'm not sure why we need to deduplicate the keys?--
local function deduplicate_key_list_by_host (ngx, host, shared_dict)

ngx.log(ngx.NOTICE, "deduplicate_key_list_by_host: " .. host)

local deduplicated_key_list = {}

-- we lpop from the list until we get nil
local key = shared_dict:lpop(host)

while key do

if (deduplicated_key_list[key] == nil) then
ngx.log(ngx.NOTICE, "unique key: " .. key)
table.insert(deduplicated_key_list, key)
deduplicated_key_list[key] = true
else
ngx.log(ngx.NOTICE, "duplicate key: " .. key)
end

key = shared_dict:lpop(host)
end

-- reinsert the keys into the list
for _, key in ipairs(deduplicated_key_list) do
ngx.log(ngx.NOTICE, "reinserting key: " .. key)
shared_dict:rpush(host, key)
end
end

local function extractHostFromCacheFile (ngx, cache_file_path)
-- emit a warning with the cache_file_path
ngx.log(ngx.NOTICE, "extracting host from cache_file_path: " .. cache_file_path)

-- we need to read the first line of the cache file to get the host
-- the first line is in the format:
-- KEY: http://example.com/xyz/abc
local file = io.open(cache_file_path, "r")
local first_line = file:read()

-- keep reading the file until we get a line that starts with KEY
while (first_line ~= nil and string.match(first_line, "^KEY:") == nil) do
first_line = file:read()
end

if (first_line == nil) then
ngx.log(ngx.NOTICE, "uri is nil")
return nil
end

local uri = string.match(first_line, "KEY: (.*)")

if (uri == nil) then
ngx.log(ngx.NOTICE, "uri is nil")
return nil
end

-- the protocol is included in the uri so we need to remove it
local uri_without_protocol = string.match(uri, "://(.*)")

-- the host is the first part of the uri, up to question mark or slash if present
local host = string.match(uri_without_protocol, "([^/?]+)")

if (host == nil) then
ngx.log(ngx.NOTICE, "host is nil")
return nil
end

file:close()

ngx.log(ngx.NOTICE, "found host from cache_file_path: " .. host)

return host
end

-- list all the items in the cache directory
local function readdirectory(directory)
local i, t, popen = 0, {}, io.popen
local pfile = popen('find "'..directory..'" -maxdepth 1')
for line in pfile:lines() do
i = i + 1
-- the line starts with the directory so we need to remove that
local filename = string.match(line, directory .. "/(.*)")
-- skip the first line which is the directory itself
if (i > 1 and filename ~= nil) then
table.insert(t, filename)
end
end
pfile:close()
return t
end


local function cacher_rehydrate (self, ngx)

local cache_directory = self.cache_directory

-- local purged_files = purge_host(ngx.var.arg_host)
local shared_dictionary = self.shared_dictionary

-- first we list all the top level directories in the cache directory
local top_level_directories = readdirectory(cache_directory)

-- store a list of hosts
local hosts = {}

-- then for each directory we list all the files in that directory
for _, top_level_directory in ipairs(top_level_directories) do

local top_level_directory_path = cache_directory .. "/" .. top_level_directory
local second_level_directories = readdirectory(top_level_directory_path)

ngx.log(ngx.NOTICE, "reading top level directory: " .. top_level_directory_path)

for _, second_level_directory in ipairs(second_level_directories) do
local second_level_directory_path = top_level_directory_path .. "/" .. second_level_directory
local files = readdirectory(second_level_directory_path)

for _, file in ipairs(files) do
local cache_file_path = top_level_directory .. "/" .. second_level_directory .. "/" .. file
local host = extractHostFromCacheFile(ngx, second_level_directory_path .. "/" .. file)

if (host ~= nil) then
-- add the host to the list of hosts
if (hosts[host] == nil) then
ngx.log(ngx.NOTICE, "found host: " .. host)
table.insert(hosts, host)
hosts[host] = true
end

ngx.log(ngx.NOTICE, "found cache file path: " .. cache_file_path)
shared_dictionary:set(cache_file_path, true)
shared_dictionary:rpush(host, cache_file_path)
end


end
end
end

for _, host in ipairs(hosts) do
deduplicate_key_list_by_host(ngx, host, shared_dictionary)
end

end


local function file_exists(filepath)
local f = io.open(filepath, "r")
if f~=nil then io.close(f) return true else return false end
end

local function remove_file(filepath)
if (file_exists(filepath)) then
os.remove(filepath)
end
end



local function cacher_purge (self, ngx)

local cache_directory = self.cache_directory

-- local purged_files = purge_host(ngx.var.arg_host)
local shared_dictionary = self.shared_dictionary

local message = "purging..."

-- extract a list of hosts from ngx.var.args which looks something like: "host=127.0.0.1&host=example.com"
local hosts = {}

-- prevent an error if the args are nil
if (ngx.var.args == nil) then
ngx.say("please pass a host to purge")
ngx.exit(ngx.OK)
end

for host in string.gmatch(ngx.var.args, "host=([^&]+)") do
table.insert(hosts, host)
end

for _, host in ipairs(hosts) do
local cached_filename = shared_dictionary:lpop(host)
local number_of_cache_keys = tostring(shared_dictionary:llen(host))

message = message .. "\n host: " .. host .. " number_of_cache_keys: " .. number_of_cache_keys

while cached_filename do
shared_dictionary:delete(cached_filename)
local cache_file_path = cache_directory .. "/" .. cached_filename

remove_file(cache_file_path)

-- message = message .. "\n purged: " .. cache_file_path
cached_filename = shared_dictionary:lpop(host)
end

end

ngx.say(message)
ngx.exit(ngx.OK)
end

--- Create a new cacher instance.

function cacher.new()

local function cacher_set(self, key, value)
self[key] = value
end

return {
rehydrate = cacher_rehydrate,
purge = cacher_purge,
set = cacher_set,
add = cacher_add
}
end

return cacher
File renamed without changes.
59 changes: 59 additions & 0 deletions config/openresty/conf/cacher/tests/basic.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
user {{user}} {{group}};

events {
worker_connections 1000;
multi_accept on;
}

http {

log_format access_log_format '[$time_local] ID=$request_id STATUS=$status REQ_TIME=$request_time REQ_LEN=$request_length RES_LEN=$bytes_sent $scheme://$host$request_uri CACHE_STATUS=$sent_http_cache_status';

error_log {{{data_directory}}}/error.log info;
access_log {{{data_directory}}}/access.log access_log_format;

proxy_cache_path {{{cache_directory}}} levels=1:2 keys_zone=PROXY_CACHE:10m inactive=1y max_size=10g use_temp_path=off;

lua_shared_dict cacher_dictionary 50m;
lua_package_path "{{{lua_directory}}}/?.lua;;";

init_by_lua_block {
cacher = (require "cacher").new()
cacher:set("cache_directory", "{{{cache_directory}}}")
cacher:set("shared_dictionary", ngx.shared.cacher_dictionary)
cacher:rehydrate(ngx)
}

server {
listen {{openresty.port}} default_server;

location = /purge {
content_by_lua_block {
cacher:purge(ngx)
}
}

location = /health {
return 200 'Hello, World!';
}

location / {
open_file_cache off;

proxy_cache PROXY_CACHE;
proxy_cache_valid 200 301 302 304 1y;
proxy_cache_key $scheme://$host:$server_port$request_uri;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;

add_header 'Cache-Status' $upstream_cache_status always;

proxy_pass http://127.0.0.1:{{node.port}};
proxy_http_version 1.1;

# This registers a cached response in our dictionary of cached responses
log_by_lua_block {
cacher:add(ngx)
}
}
}
}
Loading

0 comments on commit 224f128

Please sign in to comment.