mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
Add SetCookie method to redbean Lua (#265)
This commit is contained in:
parent
969174e155
commit
31dd714081
5 changed files with 178 additions and 1 deletions
|
@ -198,6 +198,7 @@ char *FormatHttpDateTime(char[hasatleast 30], struct tm *);
|
||||||
bool ParseHttpRange(const char *, size_t, long, long *, long *);
|
bool ParseHttpRange(const char *, size_t, long, long *, long *);
|
||||||
int64_t ParseHttpDateTime(const char *, size_t);
|
int64_t ParseHttpDateTime(const char *, size_t);
|
||||||
bool IsValidHttpToken(const char *, size_t);
|
bool IsValidHttpToken(const char *, size_t);
|
||||||
|
bool IsValidCookieValue(const char *, size_t);
|
||||||
bool IsAcceptablePath(const char *, size_t);
|
bool IsAcceptablePath(const char *, size_t);
|
||||||
bool IsAcceptableHost(const char *, size_t);
|
bool IsAcceptableHost(const char *, size_t);
|
||||||
bool IsAcceptablePort(const char *, size_t);
|
bool IsAcceptablePort(const char *, size_t);
|
||||||
|
|
43
net/http/isvalidcookievalue.c
Normal file
43
net/http/isvalidcookievalue.c
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
#include "net/http/http.h"
|
||||||
|
|
||||||
|
static inline bool IsCookieOctet(unsigned char i) {
|
||||||
|
return i > 0x20 && i < 0x7F && i != ',' && i != ';' && i != '\\' &&
|
||||||
|
i != ' ' && i != '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if string is a valid cookie value
|
||||||
|
* (any ASCII excluding control char, whitespace,
|
||||||
|
* double quotes, comma, semicolon, and backslash).
|
||||||
|
*
|
||||||
|
* @param n if -1 implies strlen
|
||||||
|
*/
|
||||||
|
bool IsValidCookieValue(const char *s, size_t n) {
|
||||||
|
size_t i;
|
||||||
|
if (n == -1) n = s ? strlen(s) : 0;
|
||||||
|
for (i = 0; i < n; ++i) {
|
||||||
|
if (!IsCookieOctet(s[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -18,6 +18,7 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/bits/bits.h"
|
#include "libc/bits/bits.h"
|
||||||
#include "libc/time/time.h"
|
#include "libc/time/time.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
#include "net/http/http.h"
|
#include "net/http/http.h"
|
||||||
|
|
||||||
static unsigned ParseMonth(const char *p) {
|
static unsigned ParseMonth(const char *p) {
|
||||||
|
@ -36,10 +37,12 @@ static unsigned ParseMonth(const char *p) {
|
||||||
* Sun, 04 Oct 2020 19:50:10 GMT
|
* Sun, 04 Oct 2020 19:50:10 GMT
|
||||||
*
|
*
|
||||||
* @return seconds from unix epoch
|
* @return seconds from unix epoch
|
||||||
|
* @param n if -1 implies strlen
|
||||||
* @see FormatHttpDateTime()
|
* @see FormatHttpDateTime()
|
||||||
*/
|
*/
|
||||||
int64_t ParseHttpDateTime(const char *p, size_t n) {
|
int64_t ParseHttpDateTime(const char *p, size_t n) {
|
||||||
unsigned weekday, year, month, day, hour, minute, second, yday, leap;
|
unsigned weekday, year, month, day, hour, minute, second, yday, leap;
|
||||||
|
if (n == -1) n = p ? strlen(p) : 0;
|
||||||
if (n != 29) return 0;
|
if (n != 29) return 0;
|
||||||
day = (p[5] - '0') * 10 + (p[6] - '0') - 1;
|
day = (p[5] - '0') * 10 + (p[6] - '0') - 1;
|
||||||
month = ParseMonth(p + 8);
|
month = ParseMonth(p + 8);
|
||||||
|
|
|
@ -435,6 +435,30 @@ FUNCTIONS
|
||||||
Date, which are abstracted by the transport layer. In such cases,
|
Date, which are abstracted by the transport layer. In such cases,
|
||||||
consider calling ServeAsset.
|
consider calling ServeAsset.
|
||||||
|
|
||||||
|
SetCookie(name:str,value:str[,options:table])
|
||||||
|
Appends Set-Cookie HTTP header to the response header buffer.
|
||||||
|
Several Set-Cookie headers can be added to the same response.
|
||||||
|
__Host- and __Secure- prefixes are supported and may set or
|
||||||
|
overwrite some of the options (for example, specifying __Host-
|
||||||
|
prefix sets the Secure option to true, sets the path to "/", and
|
||||||
|
removes the Domain option). The following options can be used (their
|
||||||
|
lowercase equivalents are supported as well):
|
||||||
|
- Expires: sets the maximum lifetime of the cookie as an HTTP-date
|
||||||
|
timestamp. Can be specified as a Date in the RFC1123 (string)
|
||||||
|
format or as a UNIX timestamp (number of seconds).
|
||||||
|
- MaxAge: sets number of seconds until the cookie expires. A zero
|
||||||
|
or negative number will expire the cookie immediately. If both
|
||||||
|
Expires and MaxAge are set, MaxAge has precedence.
|
||||||
|
- Domain: sets the host to which the cookie will be sent.
|
||||||
|
- Path: sets the path that must be present in the request URL, or
|
||||||
|
the client will not send the Cookie header.
|
||||||
|
- Secure: (bool) requests the cookie to be only send to the
|
||||||
|
server when a request is made with the https: scheme.
|
||||||
|
- HttpOnly: (bool) forbids JavaScript from accessing the cookie.
|
||||||
|
- SameSite: (Strict, Lax, or None) controls whether a cookie is
|
||||||
|
sent with cross-origin requests, providing some protection
|
||||||
|
against cross-site request forgery attacks.
|
||||||
|
|
||||||
GetParam(name:str) → value:str
|
GetParam(name:str) → value:str
|
||||||
Returns first value associated with name. name is handled in a
|
Returns first value associated with name. name is handled in a
|
||||||
case-sensitive manner. This function checks Request-URL parameters
|
case-sensitive manner. This function checks Request-URL parameters
|
||||||
|
@ -540,7 +564,7 @@ FUNCTIONS
|
||||||
EscapeUser(str) → str
|
EscapeUser(str) → str
|
||||||
Escapes URL username. See kescapeauthority.c.
|
Escapes URL username. See kescapeauthority.c.
|
||||||
|
|
||||||
Fetch(url:str[,body:str|{method=value:str,body=value:str}])
|
Fetch(url:str[,body:str|{method=value:str,body=value:str,...}])
|
||||||
→ status:int,{header:str=value:str,...},body:str
|
→ status:int,{header:str=value:str,...},body:str
|
||||||
Sends an HTTP/HTTPS request to the specified URL. If only the URL is
|
Sends an HTTP/HTTPS request to the specified URL. If only the URL is
|
||||||
provided, then a GET request is sent. If both URL and body parameters
|
provided, then a GET request is sent. If both URL and body parameters
|
||||||
|
|
|
@ -4163,6 +4163,111 @@ static int LuaSetHeader(lua_State *L) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int LuaSetCookie(lua_State *L) {
|
||||||
|
const char *key, *val;
|
||||||
|
size_t keylen, vallen;
|
||||||
|
char *expires, *samesite = "";
|
||||||
|
char *buf = 0;
|
||||||
|
bool ishostpref, issecurepref;
|
||||||
|
const char *hostpref = "__Host-";
|
||||||
|
const char *securepref = "__Secure-";
|
||||||
|
|
||||||
|
OnlyCallDuringRequest("SetCookie");
|
||||||
|
key = luaL_checklstring(L, 1, &keylen);
|
||||||
|
val = luaL_checklstring(L, 2, &vallen);
|
||||||
|
|
||||||
|
if (!IsValidHttpToken(key, keylen)) {
|
||||||
|
luaL_argerror(L, 1, "invalid");
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
if (!IsValidCookieValue(val, vallen)) {
|
||||||
|
luaL_argerror(L, 2, "invalid");
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
ishostpref = keylen > strlen(hostpref)
|
||||||
|
&& SlicesEqual(key, strlen(hostpref), hostpref, strlen(hostpref));
|
||||||
|
issecurepref = keylen > strlen(securepref)
|
||||||
|
&& SlicesEqual(key, strlen(securepref), securepref, strlen(securepref));
|
||||||
|
if ((ishostpref || issecurepref) && !usessl) {
|
||||||
|
luaL_argerror(L, 1, "__Host- and __Secure- prefixes require SSL");
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
appends(&buf, key);
|
||||||
|
appends(&buf, "=");
|
||||||
|
appends(&buf, val);
|
||||||
|
|
||||||
|
if (lua_istable(L, 3)) {
|
||||||
|
if (lua_getfield(L, 3, "expires") != LUA_TNIL
|
||||||
|
|| lua_getfield(L, 3, "Expires") != LUA_TNIL) {
|
||||||
|
if (lua_isnumber(L, -1)) {
|
||||||
|
expires = FormatUnixHttpDateTime(
|
||||||
|
FreeLater(xmalloc(30)), lua_tonumber(L, -1));
|
||||||
|
} else {
|
||||||
|
expires = lua_tostring(L, -1);
|
||||||
|
if (!ParseHttpDateTime(expires, -1)) {
|
||||||
|
luaL_argerror(L, 3, "invalid data format in Expires");
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
appends(&buf, "; Expires=");
|
||||||
|
appends(&buf, expires);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((lua_getfield(L, 3, "maxage") == LUA_TNUMBER
|
||||||
|
|| lua_getfield(L, 3, "MaxAge") == LUA_TNUMBER)
|
||||||
|
&& lua_isinteger(L, -1)) {
|
||||||
|
appends(&buf, "; Max-Age=");
|
||||||
|
appends(&buf, lua_tostring(L, -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lua_getfield(L, 3, "samesite") == LUA_TSTRING
|
||||||
|
|| lua_getfield(L, 3, "SameSite") == LUA_TSTRING) {
|
||||||
|
samesite = lua_tostring(L, -1); // also used in the Secure check
|
||||||
|
appends(&buf, "; SameSite=");
|
||||||
|
appends(&buf, samesite);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Secure attribute is required for __Host and __Secure prefixes
|
||||||
|
// as well as for the SameSite=None
|
||||||
|
if (ishostpref || issecurepref || !strcmp(samesite, "None")
|
||||||
|
|| ((lua_getfield(L, 3, "secure") == LUA_TBOOLEAN
|
||||||
|
|| lua_getfield(L, 3, "Secure") == LUA_TBOOLEAN)
|
||||||
|
&& lua_toboolean(L, -1))) {
|
||||||
|
appends(&buf, "; Secure");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ishostpref
|
||||||
|
&& (lua_getfield(L, 3, "domain") == LUA_TSTRING
|
||||||
|
|| lua_getfield(L, 3, "Domain") == LUA_TSTRING)) {
|
||||||
|
appends(&buf, "; Domain=");
|
||||||
|
appends(&buf, lua_tostring(L, -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ishostpref
|
||||||
|
|| lua_getfield(L, 3, "path") == LUA_TSTRING
|
||||||
|
|| lua_getfield(L, 3, "Path") == LUA_TSTRING) {
|
||||||
|
appends(&buf, "; Path=");
|
||||||
|
appends(&buf, ishostpref ? "/" : lua_tostring(L, -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((lua_getfield(L, 3, "httponly") == LUA_TBOOLEAN
|
||||||
|
|| lua_getfield(L, 3, "HttpOnly") == LUA_TBOOLEAN)
|
||||||
|
&& lua_toboolean(L, -1)) {
|
||||||
|
appends(&buf, "; HttpOnly");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DEBUGF("(srvr) Set-Cookie: %s", buf);
|
||||||
|
|
||||||
|
// empty the stack and push header key/value
|
||||||
|
lua_settop(L, 0);
|
||||||
|
lua_pushliteral(L, "Set-Cookie");
|
||||||
|
lua_pushstring(L, buf);
|
||||||
|
free(buf);
|
||||||
|
return LuaSetHeader(L);
|
||||||
|
}
|
||||||
|
|
||||||
static int LuaHasParam(lua_State *L) {
|
static int LuaHasParam(lua_State *L) {
|
||||||
size_t i, n;
|
size_t i, n;
|
||||||
const char *s;
|
const char *s;
|
||||||
|
@ -5260,6 +5365,7 @@ static const luaL_Reg kLuaFuncs[] = {
|
||||||
{"ServeListing", LuaServeListing}, //
|
{"ServeListing", LuaServeListing}, //
|
||||||
{"ServeRedirect", LuaServeRedirect}, //
|
{"ServeRedirect", LuaServeRedirect}, //
|
||||||
{"ServeStatusz", LuaServeStatusz}, //
|
{"ServeStatusz", LuaServeStatusz}, //
|
||||||
|
{"SetCookie", LuaSetCookie}, //
|
||||||
{"SetHeader", LuaSetHeader}, //
|
{"SetHeader", LuaSetHeader}, //
|
||||||
{"SetLogLevel", LuaSetLogLevel}, //
|
{"SetLogLevel", LuaSetLogLevel}, //
|
||||||
{"SetStatus", LuaSetStatus}, //
|
{"SetStatus", LuaSetStatus}, //
|
||||||
|
|
Loading…
Reference in a new issue