diff --git a/tool/net/help.txt b/tool/net/help.txt index fe836f753..2a2b652dd 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -1419,15 +1419,18 @@ FUNCTIONS listing page. The brand string needs to be a UTF-8 value that's encodable as ISO-8859-1. If the brand is changed to something other than redbean, then the promotional links will be removed - from the listing page too. This function should only be called - from /.init.lua. + from the listing page too. + This function should only be called from /.init.lua. - ProgramCache(seconds:int) + ProgramCache(seconds:int[, directive:string]) Configures Cache-Control and Expires header generation for static asset serving. A negative value will disable the headers. Zero means don't cache. Greater than zero asks public proxies and - browsers to cache for a given number of seconds. This should only - be called from /.init.lua. + browsers to cache for a given number of seconds. The directive + value is added to the Cache-Control header when specified (with + "must-revalidate" provided by default) and can be set to an empty + string to remove the default value. + This function should only be called from /.init.lua. ProgramCertificate(pem:str) Same as the -C flag if called from .init.lua, e.g. @@ -1458,7 +1461,7 @@ FUNCTIONS ProgramTimeout(milliseconds:int|seconds:int) Default timeout is 60000ms. Minimal value of timeout is 10(ms). Negative values (<0) sets the keepalive in seconds. - This should only be called from /.init.lua. + This function should only be called from /.init.lua. ProgramPort(uint16) Hard-codes the port number on which to listen, which can be any @@ -1496,8 +1499,8 @@ FUNCTIONS Configures fallback routing for paths which would otherwise return 404 Not Found. If code is 0 then the path is rewritten internally as an accelerated redirect. If code is 301, 302, 307, or 308 then - a redirect response will be sent to the client. This should only - be called from /.init.lua. + a redirect response will be sent to the client. + This function should only be called from /.init.lua. ProgramSslTicketLifetime(seconds:int) Defaults to 86400 (24 hours). This may be set to ≤0 to disable diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 9f65998cb..468fe532a 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -498,6 +498,7 @@ static const char *histpath; static struct pollfd *polls; static size_t payloadlength; static int64_t cacheseconds; +static const char *cachedirective; static const char *monitortty; static const char *serverheader; static struct Strings stagedirs; @@ -1037,8 +1038,9 @@ static void ProgramTimeout(long ms) { } } -static void ProgramCache(long x) { +static void ProgramCache(long x, const char *s) { cacheseconds = x; + if (s) cachedirective = s; } static void SetDefaults(void) { @@ -1046,7 +1048,7 @@ static void SetDefaults(void) { VERSION >> 010, VERSION >> 000))); __log_level = kLogInfo; maxpayloadsize = 64 * 1024; - ProgramCache(-1); + ProgramCache(-1, "must-revalidate"); ProgramTimeout(60 * 1000); ProgramSslTicketLifetime(24 * 60 * 60); sslfetchverify = true; @@ -2275,14 +2277,15 @@ static char *AppendExpires(char *p, int64_t t) { return AppendCrlf(p); } -static char *AppendCache(char *p, int64_t seconds) { +static char *AppendCache(char *p, int64_t seconds, char *directive) { if (seconds < 0) return p; p = stpcpy(p, "Cache-Control: max-age="); p = FormatUint64(p, seconds); if (!seconds) { p = stpcpy(p, ", no-store"); - } else { - p = stpcpy(p, ", must-revalidate"); + } else if (directive && *directive) { + p = stpcpy(p, ", "); + p = stpcpy(p, directive); } p = AppendCrlf(p); return AppendExpires(p, shared->nowish.tv_sec + seconds); @@ -4442,7 +4445,8 @@ static int LuaProgramPort(lua_State *L) { static int LuaProgramCache(lua_State *L) { OnlyCallFromMainProcess(L, "ProgramCache"); - return LuaProgramInt(L, ProgramCache); + ProgramCache(luaL_checkinteger(L, 1), luaL_optstring(L, 2, NULL)); + return 0; } static int LuaProgramTimeout(lua_State *L) { @@ -6159,7 +6163,7 @@ static char *ServeAsset(struct Asset *a, const char *path, size_t pathlen) { p = AppendHeader(p, "Last-Modified", a->lastmodifiedstr); if (cpm.msg.version >= 11) { if (!cpm.gotcachecontrol) { - p = AppendCache(p, cacheseconds); + p = AppendCache(p, cacheseconds, cachedirective); } if (!IsCompressed(a)) { p = stpcpy(p, "Accept-Ranges: bytes\r\n"); @@ -7279,7 +7283,11 @@ static void GetOpts(int argc, char *argv[]) { CASE('G', ProgramGid(atoi(optarg))); CASE('p', ProgramPort(ParseInt(optarg))); CASE('R', ProgramRedirectArg(0, optarg)); - CASE('c', ProgramCache(ParseInt(optarg))); + case 'c': ; // accept "num" or "num,directive" + char *p; + long ret = strtol(optarg, &p, 0); + ProgramCache(ret, *p ? p+1 : NULL); // skip separator, if any + break; CASE('r', ProgramRedirectArg(307, optarg)); CASE('t', ProgramTimeout(ParseInt(optarg))); CASE('h', PrintUsage(1, EXIT_SUCCESS));