From bc74d79c89eaee9efb6092380c470e8c99b2ecc7 Mon Sep 17 00:00:00 2001 From: terror Date: Tue, 11 Jun 2024 19:29:00 +1200 Subject: [PATCH 1/8] Add UUID v7 generation to Redbean --- tool/net/definitions.lua | 4 + tool/net/help.txt | 5 +- tool/net/lfuncs.c | 30 ++- tool/net/lfuncs.h | 1 + tool/net/redbean.c | 411 ++++++++++++++++++++++++++------------- 5 files changed, 308 insertions(+), 143 deletions(-) diff --git a/tool/net/definitions.lua b/tool/net/definitions.lua index f6fecffda64..2424f9241b9 100644 --- a/tool/net/definitions.lua +++ b/tool/net/definitions.lua @@ -1981,6 +1981,10 @@ function Underlong(str) end --- @return string function UuidV4() end +--- Generate a uuid_v7 +--- @return string +function UuidV7() end + ---@param x integer ---@return integer # position of first bit set. --- Passing `0` will raise an error. Same as the Intel x86 instruction BSF. diff --git a/tool/net/help.txt b/tool/net/help.txt index 9ff1450a4a6..b89b6bffebc 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -1065,6 +1065,9 @@ FUNCTIONS UuidV4() -> str Returns an uuid v4 string. + UuidV7() -> str + Returns an uuid v7 string. + Fetch(url:str[,body:str|{method=value:str,body=value:str,headers=table,...}]) ├─→ status:int, {header:str=value:str,...}, body:str └─→ nil, error:str @@ -1612,7 +1615,7 @@ FUNCTIONS called from `.init.lua`. This function is not available in unsecure mode. - ProgramSslRequired(mandatory:str) + ProgramSslRequired(mandatory:bool) Enables the blocking of HTTP so that all inbound clients and must use the TLS transport layer. This has the same effect as the `-J` flag. Fetch() is still allowed to make outbound HTTP diff --git a/tool/net/lfuncs.c b/tool/net/lfuncs.c index 79f0d1ca0cc..ac43fd10eb9 100644 --- a/tool/net/lfuncs.c +++ b/tool/net/lfuncs.c @@ -52,7 +52,7 @@ #include "libc/sysv/consts/rusage.h" #include "libc/sysv/consts/sock.h" #include "libc/thread/thread.h" -#include "libc/time/time.h" +#include "libc/time.h" #include "libc/x/x.h" #include "net/http/escape.h" #include "net/http/http.h" @@ -475,7 +475,8 @@ int LuaSlurp(lua_State *L) { } if (rc != -1) { got = rc; - if (!got) break; + if (!got) + break; luaL_addlstring(&b, tb, got); } else if (errno == EINTR) { errno = olderr; @@ -617,7 +618,8 @@ dontinline int LuaBase32Impl(lua_State *L, const char *a = luaL_optlstring(L, 2, "", &al); if (!IS2POW(al) || al > 128 || al == 1) return luaL_error(L, "alphabet length is not a power of 2 in range 2..128"); - if (!(p = B32(s, sl, a, al, &sl))) return luaL_error(L, "out of memory"); + if (!(p = B32(s, sl, a, al, &sl))) + return luaL_error(L, "out of memory"); lua_pushlstring(L, p, sl); free(p); return 1; @@ -693,10 +695,12 @@ int LuaGetCryptoHash(lua_State *L) { const void *p = luaL_checklstring(L, 2, &pl); const void *k = luaL_optlstring(L, 3, "", &kl); const mbedtls_md_info_t *digest = mbedtls_md_info_from_string(h); - if (!digest) return luaL_argerror(L, 1, "unknown hash type"); + if (!digest) + return luaL_argerror(L, 1, "unknown hash type"); if (kl == 0) { // no key provided, run generic hash function - if ((digest->f_md)(p, pl, d)) return luaL_error(L, "bad input data"); + if ((digest->f_md)(p, pl, d)) + return luaL_error(L, "bad input data"); } else if (mbedtls_md_hmac(digest, k, kl, p, pl, d)) { return luaL_error(L, "bad input data"); } @@ -854,6 +858,22 @@ int LuaUuidV4(lua_State *L) { return 1; } +int LuaUuidV7(lua_State *L) { + char uuid_str[37] = {0}; + struct timespec now = timespec_real(); + uint64_t time_now = timespec_tonanos(now); + uint64_t random_data = _rand64(); + snprintf(uuid_str, sizeof(uuid_str), "%08x-%04x-%04x-%04x-%012llx", + (uint32_t)(time_now >> 32), //8 + (uint16_t)(time_now >> 16), //4 + (uint16_t)((0x7 << 12) | (time_now >> 4 & 0x0fff)), //4 + (uint16_t)((0b10 << 14 | ((time_now & 0x000f) << 10)) | (random_data & 0x03FF)), //4 + (uint64_t)(random_data >> 4 & 0xFFFFFFFFFFFF) //12 + ); + lua_pushfstring(L, uuid_str); + return 1; +} + static dontinline int LuaHasherImpl(lua_State *L, size_t k, int H(const void *, size_t, uint8_t *)) { size_t n; diff --git a/tool/net/lfuncs.h b/tool/net/lfuncs.h index ab82c4e959e..7bc3fc748ff 100644 --- a/tool/net/lfuncs.h +++ b/tool/net/lfuncs.h @@ -91,6 +91,7 @@ int LuaSlurp(lua_State *); int LuaUncompress(lua_State *); int LuaUnderlong(lua_State *); int LuaUuidV4(lua_State *); +int LuaUuidV7(lua_State *); int LuaVisualizeControlCodes(lua_State *); void LuaPushUrlView(lua_State *, struct UrlView *); diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 144e0dcbd62..30cd7a1f5e9 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -644,11 +644,14 @@ static bool ShouldAvoidGzip(void) { static char *MergePaths(const char *p, size_t n, const char *q, size_t m, size_t *z) { char *r; - if (n && p[n - 1] == '/') --n; - if (m && q[0] == '/') ++q, --m; + if (n && p[n - 1] == '/') + --n; + if (m && q[0] == '/') + ++q, --m; r = xmalloc(n + 1 + m + 1); mempcpy(mempcpy(mempcpy(mempcpy(r, p, n), "/", 1), q, m), "", 1); - if (z) *z = n + 1 + m; + if (z) + *z = n + 1 + m; return r; } @@ -698,7 +701,8 @@ static void AppendCert(mbedtls_x509_crt *cert, mbedtls_pk_context *key) { static void InternCertificate(mbedtls_x509_crt *cert, mbedtls_x509_crt *prev) { int r; size_t i; - if (cert->next) InternCertificate(cert->next, cert); + if (cert->next) + InternCertificate(cert->next, cert); if (prev) { if (mbedtls_x509_crt_check_parent(prev, cert, 1)) { DEBUGF("(ssl) unbundling %`'s from %`'s", @@ -728,18 +732,22 @@ static void InternCertificate(mbedtls_x509_crt *cert, mbedtls_x509_crt *prev) { LogCertificate("loaded certificate", cert); if (!cert->next && !IsSelfSigned(cert) && cert->max_pathlen) { for (i = 0; i < certs.n; ++i) { - if (!certs.p[i].cert) continue; + if (!certs.p[i].cert) + continue; if (mbedtls_pk_can_do(&cert->pk, certs.p[i].cert->sig_pk) && !mbedtls_x509_crt_check_parent(cert, certs.p[i].cert, 1) && !IsSelfSigned(certs.p[i].cert)) { - if (ChainCertificate(cert, certs.p[i].cert)) break; + if (ChainCertificate(cert, certs.p[i].cert)) + break; } } } if (!IsSelfSigned(cert)) { for (i = 0; i < certs.n; ++i) { - if (!certs.p[i].cert) continue; - if (certs.p[i].cert->next) continue; + if (!certs.p[i].cert) + continue; + if (certs.p[i].cert->next) + continue; if (certs.p[i].cert->max_pathlen && mbedtls_pk_can_do(&certs.p[i].cert->pk, cert->sig_pk) && !mbedtls_x509_crt_check_parent(certs.p[i].cert, cert, 1)) { @@ -782,7 +790,8 @@ static void ProgramPrivateKey(const char *p, size_t n) { rc = mbedtls_pk_parse_key(key, waqapi, n + 1, 0, 0); mbedtls_platform_zeroize(waqapi, n); free(waqapi); - if (rc != 0) FATALF("(ssl) error: load key (grep -0x%04x)", -rc); + if (rc != 0) + FATALF("(ssl) error: load key (grep -0x%04x)", -rc); for (i = 0; i < certs.n; ++i) { if (certs.p[i].cert && !certs.p[i].key && !mbedtls_pk_check_pair(&certs.p[i].cert->pk, key)) { @@ -811,7 +820,8 @@ static void ProgramPort(long port) { if (!(0 <= port && port <= 65535)) { FATALF("(cfg) error: bad port: %d", port); } - if (port == 443) listeningonport443 = true; + if (port == 443) + listeningonport443 = true; ports.p = realloc(ports.p, ++ports.n * sizeof(*ports.p)); ports.p[ports.n - 1] = port; } @@ -942,13 +952,15 @@ static void DescribeAddress(char buf[40], uint32_t addr, uint16_t port) { static inline int GetServerAddr(uint32_t *ip, uint16_t *port) { *ip = ntohl(serveraddr->sin_addr.s_addr); - if (port) *port = ntohs(serveraddr->sin_port); + if (port) + *port = ntohs(serveraddr->sin_port); return 0; } static inline int GetClientAddr(uint32_t *ip, uint16_t *port) { *ip = ntohl(clientaddr.sin_addr.s_addr); - if (port) *port = ntohs(clientaddr.sin_port); + if (port) + *port = ntohs(clientaddr.sin_port); return 0; } @@ -1038,7 +1050,8 @@ static void ProgramTimeout(long ms) { static void ProgramCache(long x, const char *s) { cacheseconds = x; - if (s) cachedirective = strdup(s); + if (s) + cachedirective = strdup(s); } static void SetDefaults(void) { @@ -1183,9 +1196,11 @@ static void ChangeUser(void) { } static void Daemonize(void) { - if (fork() > 0) exit(0); + if (fork() > 0) + exit(0); setsid(); - if (fork() > 0) _exit(0); + if (fork() > 0) + _exit(0); umask(0); } @@ -1209,7 +1224,8 @@ static void LuaEvalCode(const char *code) { // handle `-F PATH` arg static void LuaEvalFile(const char *path) { char *f = gc(xslurp(path, 0)); - if (!f) FATALF("(cfg) error: failed to read file %`'s", path); + if (!f) + FATALF("(cfg) error: failed to read file %`'s", path); LuaEvalCode(f); } @@ -1465,8 +1481,10 @@ static ssize_t WritevAll(int fd, struct iovec *iov, int iovlen) { total = 0; do { if (i) { - while (i < iovlen && !iov[i].iov_len) ++i; - if (i == iovlen) break; + while (i < iovlen && !iov[i].iov_len) + ++i; + if (i == iovlen) + break; } if ((rc = writev(fd, iov + i, iovlen - i)) != -1) { wrote = rc; @@ -1501,7 +1519,8 @@ static int TlsFlush(struct TlsBio *bio, const unsigned char *buf, size_t len) { v[1].iov_base = (void *)buf; v[1].iov_len = len; if (WritevAll(bio->fd, v, 2) != -1) { - if (bio->c > 0) bio->c = 0; + if (bio->c > 0) + bio->c = 0; } else if (errno == EINTR) { errno = 0; return MBEDTLS_ERR_NET_CONN_RESET; @@ -1526,7 +1545,8 @@ static int TlsSend(void *ctx, const unsigned char *buf, size_t len) { bio->c += len; return len; } - if ((rc = TlsFlush(bio, buf, len)) < 0) return rc; + if ((rc = TlsFlush(bio, buf, len)) < 0) + return rc; return len; } @@ -1534,11 +1554,13 @@ static int TlsRecvImpl(void *ctx, unsigned char *p, size_t n, uint32_t o) { int r; struct iovec v[2]; struct TlsBio *bio = ctx; - if ((r = TlsFlush(bio, 0, 0)) < 0) return r; + if ((r = TlsFlush(bio, 0, 0)) < 0) + return r; if (bio->a < bio->b) { r = MIN(n, bio->b - bio->a); memcpy(p, bio->t + bio->a, r); - if ((bio->a += r) == bio->b) bio->a = bio->b = 0; + if ((bio->a += r) == bio->b) + bio->a = bio->b = 0; return r; } v[0].iov_base = p; @@ -1559,7 +1581,8 @@ static int TlsRecvImpl(void *ctx, unsigned char *p, size_t n, uint32_t o) { return MBEDTLS_ERR_NET_RECV_FAILED; } } - if (r > n) bio->b = r - n; + if (r > n) + bio->b = r - n; return MIN(n, r); } @@ -1655,11 +1678,15 @@ static void NotifyClose(void) { static void WipeSigningKeys(void) { size_t i; - if (uniprocess) return; + if (uniprocess) + return; for (i = 0; i < certs.n; ++i) { - if (!certs.p[i].key) continue; - if (!certs.p[i].cert) continue; - if (!certs.p[i].cert->ca_istrue) continue; + if (!certs.p[i].key) + continue; + if (!certs.p[i].cert) + continue; + if (!certs.p[i].cert->ca_istrue) + continue; mbedtls_pk_free(certs.p[i].key); Free(&certs.p[i].key); } @@ -1695,7 +1722,8 @@ static void CertsDestroy(void) { } static void WipeServingKeys(void) { - if (uniprocess) return; + if (uniprocess) + return; mbedtls_ssl_ticket_free(&ssltick); mbedtls_ssl_key_cert_free(conf.key_cert), conf.key_cert = 0; CertsDestroy(); @@ -1897,13 +1925,15 @@ static void ConfigureCertificate(mbedtls_x509write_cert *cw, struct Cert *ca, // for (int i = 0; i < ips.n; ++i) { uint32_t ip = ips.p[i]; - if (IsLoopbackIp(ip)) continue; + if (IsLoopbackIp(ip)) + continue; char rname[NI_MAXHOST]; struct sockaddr_in addr4 = {AF_INET, 0, {htonl(ip)}}; if (getnameinfo((struct sockaddr *)&addr4, sizeof(addr4), rname, sizeof(rname), 0, 0, NI_NAMEREQD) == 0) { char *s = gc(strdup(rname)); - if (!name) name = s; + if (!name) + name = s; bool isduplicate = false; for (int j = 0; j < nsan; ++j) { if (san[j].tag == MBEDTLS_X509_SAN_DNS_NAME && @@ -1925,7 +1955,8 @@ static void ConfigureCertificate(mbedtls_x509write_cert *cw, struct Cert *ca, // add san entry to cert for each ip address owned by system for (int i = 0; i < ips.n; ++i) { uint32_t ip = ips.p[i]; - if (IsLoopbackIp(ip)) continue; + if (IsLoopbackIp(ip)) + continue; san = realloc(san, ++nsan * sizeof(*san)); san[nsan - 1].tag = MBEDTLS_X509_SAN_IP_ADDRESS; san[nsan - 1].ip4 = ip; @@ -1974,9 +2005,12 @@ static void ConfigureCertificate(mbedtls_x509write_cert *cw, struct Cert *ca, static struct Cert GetKeySigningKey(void) { size_t i; for (i = 0; i < certs.n; ++i) { - if (!certs.p[i].key) continue; - if (!certs.p[i].cert) continue; - if (!certs.p[i].cert->ca_istrue) continue; + if (!certs.p[i].key) + continue; + if (!certs.p[i].cert) + continue; + if (!certs.p[i].cert->ca_istrue) + continue; if (mbedtls_x509_crt_check_key_usage(certs.p[i].cert, MBEDTLS_X509_KU_KEY_CERT_SIGN)) { continue; @@ -2059,7 +2093,8 @@ static void LoadCertificates(void) { } #ifdef MBEDTLS_ECP_C ecp = GenerateEcpCertificate(ksk.key ? &ksk : 0); - if (!havecert) UseCertificate(&conf, &ecp, "server"); + if (!havecert) + UseCertificate(&conf, &ecp, "server"); if (!haveclientcert && ksk.key) { UseCertificate(&confcli, &ecp, "client"); } @@ -2068,7 +2103,8 @@ static void LoadCertificates(void) { #ifdef MBEDTLS_RSA_C if (!norsagen) { rsa = GenerateRsaCertificate(ksk.key ? &ksk : 0); - if (!havecert) UseCertificate(&conf, &rsa, "server"); + if (!havecert) + UseCertificate(&conf, &rsa, "server"); if (!haveclientcert && ksk.key) { UseCertificate(&confcli, &rsa, "client"); } @@ -2237,11 +2273,13 @@ static bool OpenZip(bool force) { static struct Asset *GetAssetZip(const char *path, size_t pathlen) { uint32_t i, step, hash; - if (pathlen > 1 && path[0] == '/') ++path, --pathlen; + if (pathlen > 1 && path[0] == '/') + ++path, --pathlen; hash = Hash(path, pathlen); for (step = 0;; ++step) { i = (hash + ((step * (step + 1)) >> 1)) & (assets.n - 1); - if (!assets.p[i].hash) return NULL; + if (!assets.p[i].hash) + return NULL; if (hash == assets.p[i].hash && pathlen == ZIP_CFILE_NAMESIZE(zmap + assets.p[i].cf) && memcmp(path, ZIP_CFILE_NAME(zmap + assets.p[i].cf), pathlen) == 0) { @@ -2288,7 +2326,8 @@ static struct Asset *GetAsset(const char *path, size_t pathlen) { } static char *AppendHeader(char *p, const char *k, const char *v) { - if (!v) return p; + if (!v) + return p; return AppendCrlf(stpcpy(stpcpy(stpcpy(p, k), ": "), v)); } @@ -2316,7 +2355,8 @@ static char *AppendExpires(char *p, int64_t t) { } static char *AppendCache(char *p, int64_t seconds, char *directive) { - if (seconds < 0) return p; + if (seconds < 0) + return p; p = stpcpy(p, "Cache-Control: max-age="); p = FormatUint64(p, seconds); if (!seconds) { @@ -2392,7 +2432,8 @@ static void *LoadAsset(struct Asset *a, size_t *out_size) { } if (!a->file) { size = GetZipLfileUncompressedSize(zmap + a->lf); - if (size == SIZE_MAX || !(data = malloc(size + 1))) return NULL; + if (size == SIZE_MAX || !(data = malloc(size + 1))) + return NULL; if (IsCompressed(a)) { if (!Inflate(data, size, ZIP_LFILE_CONTENT(zmap + a->lf), GetZipCfileCompressedSize(zmap + a->cf))) { @@ -2407,7 +2448,8 @@ static void *LoadAsset(struct Asset *a, size_t *out_size) { return NULL; } data[size] = '\0'; - if (out_size) *out_size = size; + if (out_size) + *out_size = size; return data; } else { LockInc(&shared->c.slurps); @@ -2622,7 +2664,8 @@ static ssize_t YieldGenerator(struct iovec v[3]) { int nresults, status; if (cpm.isyielding > 1) { do { - if (!YL || lua_status(YL) != LUA_YIELD) return 0; // done yielding + if (!YL || lua_status(YL) != LUA_YIELD) + return 0; // done yielding cpm.contentlength = 0; status = lua_resume(YL, NULL, 0, &nresults); if (status != LUA_OK && status != LUA_YIELD) { @@ -2631,7 +2674,8 @@ static ssize_t YieldGenerator(struct iovec v[3]) { return -1; } lua_pop(YL, nresults); - if (!cpm.contentlength) UseOutput(); + if (!cpm.contentlength) + UseOutput(); // continue yielding if nothing to return to keep generator running } while (!cpm.contentlength); } @@ -2667,7 +2711,8 @@ static int LuaCallWithYield(lua_State *L) { CHECK_GT(lua_gettop(L), 0); // make sure that coroutine is anchored YL = co; cpm.generator = YieldGenerator; - if (!cpm.isyielding) cpm.isyielding = 1; + if (!cpm.isyielding) + cpm.isyielding = 1; status = LUA_OK; } return status; @@ -2770,7 +2815,8 @@ static ssize_t InflateGenerator(struct iovec v[3]) { dg.s.next_out = dg.b; dg.s.avail_out = dg.z; rc = inflate(&dg.s, Z_NO_FLUSH); - if (rc != Z_OK && rc != Z_STREAM_END) DIEF("(zip) inflate()→%d", rc); + if (rc != Z_OK && rc != Z_STREAM_END) + DIEF("(zip) inflate()→%d", rc); no = dg.z - dg.s.avail_out; if (no) { v[i].iov_base = dg.b; @@ -2877,7 +2923,8 @@ static char *GetAssetPath(uint8_t *zcf, size_t *out_size) { p2[0] = '/'; memcpy(p2 + 1, p1, n1); p2[1 + n1] = '\0'; - if (out_size) *out_size = 1 + n1; + if (out_size) + *out_size = 1 + n1; return p2; } @@ -2928,8 +2975,10 @@ static void LaunchBrowser(const char *path) { port = ntohs(servers.p[0].addr.sin_port); } // assign a loopback address if no server or unknown server address - if (!servers.n || !addr.s_addr) addr.s_addr = htonl(INADDR_LOOPBACK); - if (*path != '/') path = gc(xasprintf("/%s", path)); + if (!servers.n || !addr.s_addr) + addr.s_addr = htonl(INADDR_LOOPBACK); + if (*path != '/') + path = gc(xasprintf("/%s", path)); launch_browser(gc(xasprintf("http://%s:%d%s", inet_ntoa(addr), port, path))); } @@ -3102,11 +3151,13 @@ static const char *MergeNames(const char *a, const char *b) { } static void AppendLong1(const char *a, long x) { - if (x) appendf(&cpm.outbuf, "%s: %ld\r\n", a, x); + if (x) + appendf(&cpm.outbuf, "%s: %ld\r\n", a, x); } static void AppendLong2(const char *a, const char *b, long x) { - if (x) appendf(&cpm.outbuf, "%s.%s: %ld\r\n", a, b, x); + if (x) + appendf(&cpm.outbuf, "%s.%s: %ld\r\n", a, b, x); } static void AppendTimeval(const char *a, struct timeval *tv) { @@ -3282,7 +3333,8 @@ static char *HandleRedirect(struct Redirect *r) { } else { LockInc(&shared->c.redirects); code = r->code; - if (!code) code = 307; + if (!code) + code = 307; DEBUGF("(rsp) %d redirect to %`'s", code, r->location.s); return AppendHeader( SetStatus(code, GetHttpReason(code)), "Location", @@ -3629,8 +3681,10 @@ static void StoreAsset(const char *path, size_t pathlen, const char *data, } INFOF("(srvr) storing asset %`'s", path); disk = gflags = iattrs = 0; - if (isutf8(path, pathlen)) gflags |= kZipGflagUtf8; - if (istext(data, datalen)) iattrs |= kZipIattrText; + if (isutf8(path, pathlen)) + gflags |= kZipGflagUtf8; + if (istext(data, datalen)) + iattrs |= kZipIattrText; crc = crc32_z(0, data, datalen); if (datalen < 100) { method = kZipCompressionNone; @@ -3661,9 +3715,12 @@ static void StoreAsset(const char *path, size_t pathlen, const char *data, OpenZip(false); now = timespec_real(); a = GetAssetZip(path, pathlen); - if (!mode) mode = a ? GetMode(a) : 0644; - if (!(mode & S_IFMT)) mode |= S_IFREG; - if (pathlen > 1 && path[0] == '/') ++path, --pathlen; + if (!mode) + mode = a ? GetMode(a) : 0644; + if (!(mode & S_IFMT)) + mode |= S_IFREG; + if (pathlen > 1 && path[0] == '/') + ++path, --pathlen; dosmode = !(mode & 0200) ? kNtFileAttributeReadonly : 0; ft = (now.tv_sec + MODERNITYSECONDS) * HECTONANOSECONDS; GetDosLocalTime(now.tv_sec, &mtime, &mdate); @@ -3812,12 +3869,14 @@ static void StoreFile(const char *path) { struct stat st; size_t plen, tlen; const char *target = path; - if (startswith(target, "./")) target += 2; + if (startswith(target, "./")) + target += 2; tlen = strlen(target); if (!IsReasonablePath(target, tlen)) FATALF("(cfg) error: can't store %`'s: contains '.' or '..' segments", target); - if (lstat(path, &st) == -1) FATALF("(cfg) error: can't stat %`'s: %m", path); + if (lstat(path, &st) == -1) + FATALF("(cfg) error: can't stat %`'s: %m", path); if (!(p = xslurp(path, &plen))) FATALF("(cfg) error: can't read %`'s: %m", path); StoreAsset(target, tlen, p, plen, st.st_mode & 0777); @@ -3831,10 +3890,13 @@ static void StorePath(const char *dirpath) { if (!isdirectory(dirpath) && !endswith(dirpath, "/")) { return StoreFile(dirpath); } - if (!(d = opendir(dirpath))) FATALF("(cfg) error: can't open %`'s", dirpath); + if (!(d = opendir(dirpath))) + FATALF("(cfg) error: can't open %`'s", dirpath); while ((e = readdir(d))) { - if (strcmp(e->d_name, ".") == 0) continue; - if (strcmp(e->d_name, "..") == 0) continue; + if (strcmp(e->d_name, ".") == 0) + continue; + if (strcmp(e->d_name, "..") == 0) + continue; path = gc(xjoinpaths(dirpath, e->d_name)); if (e->d_type == DT_DIR) { StorePath(path); @@ -3861,7 +3923,8 @@ static int LuaStoreAsset(lua_State *L) { static void ReseedRng(mbedtls_ctr_drbg_context *r, const char *s) { #ifndef UNSECURE - if (unsecure) return; + if (unsecure) + return; CHECK_EQ(0, mbedtls_ctr_drbg_reseed(r, (void *)s, strlen(s))); #endif } @@ -3869,8 +3932,10 @@ static void ReseedRng(mbedtls_ctr_drbg_context *r, const char *s) { static void LogMessage(const char *d, const char *s, size_t n) { size_t n2, n3; char *s2, *s3; - if (!LOGGABLE(kLogInfo)) return; - while (n && (s[n - 1] == '\r' || s[n - 1] == '\n')) --n; + if (!LOGGABLE(kLogInfo)) + return; + while (n && (s[n - 1] == '\r' || s[n - 1] == '\n')) + --n; if ((s2 = DecodeLatin1(s, n, &n2))) { if ((s3 = IndentLines(s2, n2, &n3, 1))) { INFOF("(stat) %s %,ld byte message\r\n%.*s", d, n, n3, s3); @@ -3883,9 +3948,12 @@ static void LogMessage(const char *d, const char *s, size_t n) { static void LogBody(const char *d, const char *s, size_t n) { char *s2, *s3; size_t n2, n3; - if (!n) return; - if (!LOGGABLE(kLogInfo)) return; - while (n && (s[n - 1] == '\r' || s[n - 1] == '\n')) --n; + if (!n) + return; + if (!LOGGABLE(kLogInfo)) + return; + while (n && (s[n - 1] == '\r' || s[n - 1] == '\n')) + --n; if ((s2 = VisualizeControlCodes(s, n, &n2))) { if ((s3 = IndentLines(s2, n2, &n3, 1))) { INFOF("(stat) %s %,ld byte payload\r\n%.*s", d, n, n3, s3); @@ -4107,7 +4175,8 @@ static int LuaGetUser(lua_State *L) { if (url.user.p) { LuaPushUrlView(L, &url.user); } else if ((p = gc(GetBasicAuthorization(&n)))) { - if (!(q = memchr(p, ':', n))) q = p + n; + if (!(q = memchr(p, ':', n))) + q = p + n; lua_pushlstring(L, p, q - p); } else { lua_pushnil(L); @@ -4148,8 +4217,10 @@ static int LuaGetHost(lua_State *L) { static int LuaGetPort(lua_State *L) { int i, x = 0; OnlyCallDuringRequest(L, "GetPort"); - for (i = 0; i < url.port.n; ++i) x = url.port.p[i] - '0' + x * 10; - if (!x) x = ntohs(serveraddr->sin_port); + for (i = 0; i < url.port.n; ++i) + x = url.port.p[i] - '0' + x * 10; + if (!x) + x = ntohs(serveraddr->sin_port); lua_pushinteger(L, x); return 1; } @@ -4219,7 +4290,8 @@ static int LuaSetHeader(lua_State *L) { OnlyCallDuringRequest(L, "SetHeader"); key = luaL_checklstring(L, 1, &keylen); val = luaL_optlstring(L, 2, 0, &vallen); - if (!val) return 0; + if (!val) + return 0; if ((h = GetHttpHeader(key, keylen)) == -1) { if (!IsValidHttpToken(key, keylen)) { luaL_argerror(L, 1, "invalid"); @@ -4283,7 +4355,8 @@ static int LuaGetCookie(lua_State *L) { } else { lua_pushnil(L); } - if (cookie) free(cookie); + if (cookie) + free(cookie); return 1; } @@ -4503,7 +4576,8 @@ static int LuaProgramUniprocess(lua_State *L) { return luaL_argerror(L, 1, "invalid uniprocess mode; boolean expected"); } lua_pushboolean(L, uniprocess); - if (lua_isboolean(L, 1)) uniprocess = lua_toboolean(L, 1); + if (lua_isboolean(L, 1)) + uniprocess = lua_toboolean(L, 1); return 1; } @@ -4525,7 +4599,8 @@ static int LuaProgramMaxWorkers(lua_State *L) { return luaL_argerror(L, 1, "invalid number of workers; integer expected"); } lua_pushinteger(L, maxworkers); - if (lua_isinteger(L, 1)) maxworkers = lua_tointeger(L, 1); + if (lua_isinteger(L, 1)) + maxworkers = lua_tointeger(L, 1); maxworkers = MAX(maxworkers, 1); return 1; } @@ -4803,7 +4878,8 @@ static int LuaIsAssetCompressed(lua_State *L) { static bool Blackhole(uint32_t ip) { char buf[4]; - if (blackhole.fd <= 0) return false; + if (blackhole.fd <= 0) + return false; WRITE32BE(buf, ip); if (sendto(blackhole.fd, &buf, 4, 0, (struct sockaddr *)&blackhole.addr, sizeof(blackhole.addr)) != -1) { @@ -4927,8 +5003,10 @@ static int LuaProgramTokenBucket(lua_State *L) { reject, // ignore, // ban); - if (ignore == -1) ignore = -128; - if (ban == -1) ban = -128; + if (ignore == -1) + ignore = -128; + if (ban == -1) + ban = -128; if (ban >= 0 && (IsLinux() || IsBsd())) { uint32_t testip = 0; blackhole.addr.sun_family = AF_UNIX; @@ -4955,14 +5033,16 @@ static int LuaProgramTokenBucket(lua_State *L) { tokenbucket.replenish = timespec_fromnanos(1 / replenish * 1e9); int pid = fork(); npassert(pid != -1); - if (!pid) Replenisher(); + if (!pid) + Replenisher(); ++shared->workers; return 0; } static const char *GetContentTypeExt(const char *path, size_t n) { const char *r = NULL, *e; - if ((r = FindContentType(path, n))) return r; + if ((r = FindContentType(path, n))) + return r; #ifndef STATIC int top; lua_State *L = GL; @@ -5053,7 +5133,8 @@ static bool LuaRunAsset(const char *path, bool mandatory) { if (status != LUA_OK || LuaCallWithTrace(L, 0, 0, NULL) != LUA_OK) { LogLuaError("lua code", lua_tostring(L, -1)); lua_pop(L, 1); // pop error - if (mandatory) exit(1); + if (mandatory) + exit(1); } } } @@ -5288,6 +5369,7 @@ static const luaL_Reg kLuaFuncs[] = { {"Uncompress", LuaUncompress}, // {"Underlong", LuaUnderlong}, // {"UuidV4", LuaUuidV4}, // + {"UuidV7", LuaUuidV7}, // {"VisualizeControlCodes", LuaVisualizeControlCodes}, // {"Write", LuaWrite}, // {"bin", LuaBin}, // @@ -5416,7 +5498,8 @@ static void LuaPrint(lua_State *L) { n = lua_gettop(L); if (n > 0) { for (i = 1; i <= n; i++) { - if (i > 1) appendw(&b, '\t'); + if (i > 1) + appendw(&b, '\t'); struct EncoderConfig conf = { .maxdepth = 64, .sorted = true, @@ -5450,12 +5533,14 @@ static int LuaInterpreter(lua_State *L) { const char *script; if (optind < __argc) { script = __argv[optind]; - if (!strcmp(script, "-")) script = 0; + if (!strcmp(script, "-")) + script = 0; if ((status = luaL_loadfile(L, script)) == LUA_OK) { lua_getglobal(L, "arg"); n = luaL_len(L, -1); luaL_checkstack(L, n + 3, "too many script args"); - for (i = 1; i <= n; i++) lua_rawgeti(L, -i, i); + for (i = 1; i <= n; i++) + lua_rawgeti(L, -i, i); lua_remove(L, -i); // remove arg table from stack TRACE_BEGIN; status = lua_runchunk(L, n, LUA_MULTRET); @@ -5469,7 +5554,8 @@ static int LuaInterpreter(lua_State *L) { EnableRawMode(); for (;;) { status = lua_loadline(L); - if (status == -1) break; // eof + if (status == -1) + break; // eof if (status == -2) { if (errno == EINTR) { if ((sig = linenoiseGetInterrupt())) { @@ -5579,10 +5665,14 @@ static void LuaOnServerReload(bool reindex) { } static const char *DescribeClose(void) { - if (killed) return "killed"; - if (meltdown) return "meltdown"; - if (terminated) return "terminated"; - if (connectionclose) return "connection closed"; + if (killed) + return "killed"; + if (meltdown) + return "meltdown"; + if (terminated) + return "terminated"; + if (connectionclose) + return "connection closed"; return "destroyed"; } @@ -5859,7 +5949,8 @@ static char *ReadMore(void) { ssize_t rc; LockInc(&shared->c.frags); if ((rc = reader(client, inbuf.p + amtread, inbuf.n - amtread)) != -1) { - if (!(got = rc)) return HandlePayloadDisconnect(); + if (!(got = rc)) + return HandlePayloadDisconnect(); amtread += got; } else if (errno == EINTR) { LockInc(&shared->c.readinterrupts); @@ -5877,10 +5968,12 @@ static char *ReadMore(void) { static char *SynchronizeLength(void) { char *p; if (hdrsize + payloadlength > amtread) { - if (hdrsize + payloadlength > inbuf.n) return HandleHugePayload(); + if (hdrsize + payloadlength > inbuf.n) + return HandleHugePayload(); SendContinueIfNeeded(); while (amtread < hdrsize + payloadlength) { - if ((p = ReadMore())) return p; + if ((p = ReadMore())) + return p; } } cpm.msgsize = hdrsize + payloadlength; @@ -5894,9 +5987,11 @@ static char *SynchronizeChunked(void) { SendContinueIfNeeded(); while (!(transferlength = Unchunk(&u, inbuf.p + hdrsize, amtread - hdrsize, &payloadlength))) { - if ((p = ReadMore())) return p; + if ((p = ReadMore())) + return p; } - if (transferlength == -1) return HandleHugePayload(); + if (transferlength == -1) + return HandleHugePayload(); cpm.msgsize = hdrsize + transferlength; return NULL; } @@ -5987,8 +6082,10 @@ static char *HandleRequest(void) { } else { return HandleVersionNotSupported(); } - if ((p = SynchronizeStream())) return p; - if (logbodies) LogBody("received", inbuf.p + hdrsize, payloadlength); + if ((p = SynchronizeStream())) + return p; + if (logbodies) + LogBody("received", inbuf.p + hdrsize, payloadlength); if (cpm.msg.version < 11 || HeaderEqualCase(kHttpConnection, "close")) { connectionclose = true; } @@ -6025,7 +6122,8 @@ static char *HandleRequest(void) { } FreeLater(url.params.p); #ifndef STATIC - if (hasonhttprequest) return LuaOnHttpRequest(); + if (hasonhttprequest) + return LuaOnHttpRequest(); #endif return Route(url.host.p, url.host.n, url.path.p, url.path.n); } @@ -6041,7 +6139,8 @@ static char *Route(const char *host, size_t hostlen, const char *path, return p; } if (SlicesEqual(path, pathlen, "/", 1)) { - if ((p = ServeIndex("/", 1))) return p; + if ((p = ServeIndex("/", 1))) + return p; return ServeListing(); } else if ((p = RoutePath(path, pathlen))) { return p; @@ -6090,16 +6189,19 @@ static char *RouteHost(const char *host, size_t hostlen, const char *path, hp = hm <= sizeof(b) ? b : FreeLater(xmalloc(hm)); hp[0] = '/'; mempcpy(mempcpy(hp + 1, host, hostlen), path, pathlen); - if ((p = RoutePath(hp, hn))) return p; + if ((p = RoutePath(hp, hn))) + return p; if (!isdigit(host[0])) { if (hostlen > 4 && READ32LE(host) == ('w' | 'w' << 8 | 'w' << 16 | '.' << 24)) { mempcpy(mempcpy(hp + 1, host + 4, hostlen - 4), path, pathlen); - if ((p = RoutePath(hp, hn - 4))) return p; + if ((p = RoutePath(hp, hn - 4))) + return p; } else { mempcpy(mempcpy(mempcpy(hp + 1, "www.", 4), host, hostlen), path, pathlen); - if ((p = RoutePath(hp, hn + 4))) return p; + if ((p = RoutePath(hp, hn + 4))) + return p; } } } @@ -6123,7 +6225,8 @@ static inline bool IsLua(struct Asset *a) { static char *HandleAsset(struct Asset *a, const char *path, size_t pathlen) { char *p; #ifndef STATIC - if (IsLua(a)) return ServeLua(a, path, pathlen); + if (IsLua(a)) + return ServeLua(a, path, pathlen); #endif if (cpm.msg.method == kHttpGet || cpm.msg.method == kHttpHead) { LockInc(&shared->c.staticrequests); @@ -6150,8 +6253,10 @@ static const char *GetContentType(struct Asset *a, const char *path, size_t n) { } static bool IsNotModified(struct Asset *a) { - if (cpm.msg.version < 10) return false; - if (!HasHeader(kHttpIfModifiedSince)) return false; + if (cpm.msg.version < 10) + return false; + if (!HasHeader(kHttpIfModifiedSince)) + return false; return a->lastmodified <= ParseHttpDateTime(HeaderData(kHttpIfModifiedSince), HeaderLength(kHttpIfModifiedSince)); @@ -6217,8 +6322,10 @@ static char *ServeAsset(struct Asset *a, const char *path, size_t pathlen) { static char *SetStatus(unsigned code, const char *reason) { if (cpm.msg.version == 10) { - if (code == 307) code = 302; - if (code == 308) code = 301; + if (code == 307) + code = 302; + if (code == 308) + code = 301; } cpm.statuscode = code; cpm.hascontenttype = false; @@ -6321,7 +6428,8 @@ static bool StreamResponse(char *p) { iov[3].iov_len = 0; iov[4].iov_base = 0; iov[4].iov_len = 0; - if ((rc = cpm.generator(iov + 2)) <= 0) break; + if ((rc = cpm.generator(iov + 2)) <= 0) + break; if (cpm.msg.version >= 11) { s = chunkbuf; s += uint64toarray_radix16(rc, s); @@ -6329,7 +6437,8 @@ static bool StreamResponse(char *p) { iov[1].iov_base = chunkbuf; iov[1].iov_len = s - chunkbuf; } - if (Send(iov, 6) == -1) break; + if (Send(iov, 6) == -1) + break; iov[0].iov_base = 0; iov[0].iov_len = 0; } @@ -6350,8 +6459,9 @@ static bool HandleMessageActual(void) { long reqtime, contime; char *p; struct timespec now; - if ((rc = ParseHttpMessage(&cpm.msg, inbuf.p, amtread)) != -1) { - if (!rc) return false; + if ((rc = ParseHttpMessage(&cpm.msg, inbuf.p, amtread, inbuf.n)) != -1) { + if (!rc) + return false; hdrsize = rc; if (logmessages) { LogMessage("received", inbuf.p, hdrsize); @@ -6373,8 +6483,10 @@ static bool HandleMessageActual(void) { } if (cpm.msg.version >= 10) { p = AppendCrlf(stpcpy(stpcpy(p, "Date: "), shared->currentdate)); - if (!cpm.branded) p = stpcpy(p, serverheader); - if (extrahdrs) p = stpcpy(p, extrahdrs); + if (!cpm.branded) + p = stpcpy(p, serverheader); + if (extrahdrs) + p = stpcpy(p, extrahdrs); if (connectionclose) { p = stpcpy(p, "Connection: close\r\n"); } else if (timeout.tv_sec < 0 && cpm.msg.version >= 11) { @@ -6395,7 +6507,8 @@ static bool HandleMessageActual(void) { now = timespec_real(); reqtime = timespec_tomicros(timespec_sub(now, startrequest)); contime = timespec_tomicros(timespec_sub(now, startconnection)); - if (hasonloglatency) LuaOnLogLatency(reqtime, contime); + if (hasonloglatency) + LuaOnLogLatency(reqtime, contime); if (loglatency || LOGGABLE(kLogDebug)) LOGF(kLogDebug, "(stat) %`'.*s latency r: %,ldµs c: %,ldµs", cpm.msg.uri.b - cpm.msg.uri.a, inbuf.p + cpm.msg.uri.a, reqtime, @@ -6422,8 +6535,10 @@ static void InitRequest(void) { } static bool IsSsl(unsigned char c) { - if (c == 22) return true; - if (!(c & 128)) return false; + if (c == 22) + return true; + if (!(c & 128)) + return false; /* RHEL5 sends SSLv2 hello but supports TLS */ DEBUGF("(ssl) %s SSLv2 hello D:", DescribeClient()); return true; @@ -6440,7 +6555,8 @@ static void HandleMessages(void) { for (;;) { if (!cpm.msg.i && amtread) { startrequest = timespec_real(); - if (HandleMessage()) break; + if (HandleMessage()) + break; } if ((rc = reader(client, inbuf.p + amtread, inbuf.n - amtread)) != -1) { startrequest = timespec_real(); @@ -6483,7 +6599,8 @@ static void HandleMessages(void) { errno = 0; } else if (errno == EAGAIN) { LockInc(&shared->c.readtimeouts); - if (amtread) SendTimeout(); + if (amtread) + SendTimeout(); NotifyClose(); LogClose("read timeout"); return; @@ -6646,7 +6763,8 @@ static void *MemoryMonitor(void *arg) { if (tty != -1) { for (gen = 0, mi = 0, b = 0; !terminatemonitor;) { workers = atomic_load_explicit(&shared->workers, memory_order_relaxed); - if (id) id = MAX(1, MIN(id, workers)); + if (id) + id = MAX(1, MIN(id, workers)); if (!id && workers) { usleep(50000); continue; @@ -6931,11 +7049,16 @@ static int HandleConnection(size_t i) { static void MakeExecutableModifiable(void) { #ifdef __x86_64__ int ft; - if (!(SUPPORT_VECTOR & (_HOSTMETAL | _HOSTWINDOWS | _HOSTXNU))) return; - if (IsWindows()) return; // TODO - if (IsOpenbsd()) return; // TODO - if (IsNetbsd()) return; // TODO - if (endswith(zpath, ".dbg")) return; + if (!(SUPPORT_VECTOR & (_HOSTMETAL | _HOSTWINDOWS | _HOSTXNU))) + return; + if (IsWindows()) + return; // TODO + if (IsOpenbsd()) + return; // TODO + if (IsNetbsd()) + return; // TODO + if (endswith(zpath, ".dbg")) + return; close(zfd); ft = ftrace_enabled(0); if ((zfd = __open_executable()) == -1) { @@ -6999,8 +7122,10 @@ static int HandlePoll(int ms) { if (nfds) { // handle pollid/o events for (pollid = 0; pollid < 1 + servers.n; ++pollid) { - if (!polls[pollid].revents) continue; - if (polls[pollid].fd < 0) continue; + if (!polls[pollid].revents) + continue; + if (polls[pollid].fd < 0) + continue; if (polls[pollid].fd) { // handle listen socket lua_repl_lock(); @@ -7011,12 +7136,14 @@ static int HandlePoll(int ms) { rc = HandleConnection(serverid); ishandlingconnection = false; lua_repl_unlock(); - if (rc == -1) return -1; + if (rc == -1) + return -1; #ifndef STATIC } else { // handle standard input rc = HandleReadline(); - if (rc == -1) return rc; + if (rc == -1) + return rc; #endif } } @@ -7024,7 +7151,8 @@ static int HandlePoll(int ms) { } else if (__ttyconf.replmode) { // handle refresh repl line rc = HandleReadline(); - if (rc < 0) return rc; + if (rc < 0) + return rc; #endif } } else { @@ -7094,7 +7222,8 @@ static void Listen(void) { } port = ntohs(servers.p[n].addr.sin_port); ip = ntohl(servers.p[n].addr.sin_addr.s_addr); - if (ip == INADDR_ANY) ip = INADDR_LOOPBACK; + if (ip == INADDR_ANY) + ip = INADDR_LOOPBACK; INFOF("(srvr) listen http://%hhu.%hhu.%hhu.%hhu:%d", ip >> 24, ip >> 16, ip >> 8, ip, port); if (printport && !ports.p[j]) { @@ -7122,7 +7251,8 @@ static void HandleShutdown(void) { CloseServerFds(); INFOF("(srvr) received %s", strsignal(shutdownsig)); if (shutdownsig != SIGINT && shutdownsig != SIGQUIT) { - if (!killed) terminated = false; + if (!killed) + terminated = false; INFOF("(srvr) killing process group"); KillGroup(); } @@ -7193,7 +7323,8 @@ static void SigInit(void) { static void TlsInit(void) { #ifndef UNSECURE int suite; - if (unsecure) return; + if (unsecure) + return; if (suiteb && !mbedtls_aes_uses_hardware()) { WARNF("(srvr) requested suiteb crypto, but hardware aes not present"); @@ -7227,7 +7358,8 @@ static void TlsInit(void) { mbedtls_ssl_ticket_parse, &ssltick); } - if (sslinitialized) return; + if (sslinitialized) + return; sslinitialized = true; LoadCertificates(); @@ -7257,7 +7389,8 @@ static void TlsInit(void) { static void TlsDestroy(void) { #ifndef UNSECURE - if (unsecure) return; + if (unsecure) + return; mbedtls_ssl_free(&ssl); mbedtls_ssl_free(&sslcli); mbedtls_ctr_drbg_free(&rng); @@ -7352,9 +7485,11 @@ static void GetOpts(int argc, char *argv[]) { } } // if storing asset(s) is requested, don't need to continue - if (storeasset) exit(0); + if (storeasset) + exit(0); // we don't want to drop into a repl after using -e in -i mode - if (interpretermode && got_e_arg) exit(0); + if (interpretermode && got_e_arg) + exit(0); } void RedBean(int argc, char *argv[]) { @@ -7362,7 +7497,8 @@ void RedBean(int argc, char *argv[]) { int fd; // don't complain about --assimilate if it's the only parameter, // as it can only get here if it's already native or assimilated - if (argc == 2 && strcmp(argv[1], "--assimilate") == 0) return; + if (argc == 2 && strcmp(argv[1], "--assimilate") == 0) + return; if (IsLinux()) { // disable weird linux capabilities for (int e = errno, i = 0;; ++i) { @@ -7415,7 +7551,8 @@ void RedBean(int argc, char *argv[]) { shared->workers = 1; } if (daemonize) { - if (!logpath) ProgramLogPath("/dev/null"); + if (!logpath) + ProgramLogPath("/dev/null"); dup2(2, 1); } SigInit(); From 91fc1dff373c6e65ce0db449db4e48f3aeb1b6dd Mon Sep 17 00:00:00 2001 From: terror Date: Tue, 11 Jun 2024 19:37:20 +1200 Subject: [PATCH 2/8] Add uuidv7 test --- test/tool/net/uuidv7_test.lua | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 test/tool/net/uuidv7_test.lua diff --git a/test/tool/net/uuidv7_test.lua b/test/tool/net/uuidv7_test.lua new file mode 100644 index 00000000000..98281f9d980 --- /dev/null +++ b/test/tool/net/uuidv7_test.lua @@ -0,0 +1,25 @@ +-- Copyright 2024 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. +for _ = 1, 1000 do + local uuid = UuidV4() + assert(#uuid == 36) + assert(string.sub(uuid, 9, 9) == "-") + assert(string.sub(uuid, 14, 14) == "-") + assert(string.sub(uuid, 15, 15) == "7") + assert(string.sub(uuid, 19, 19) == "-") + local y = string.sub(uuid, 20, 20) + assert(y == "8" or y == "9" or y == "a" or y == "b") + assert(string.sub(uuid, 24, 24) == "-") +end From 050e318ea9c71d3ac15d15967b800ba5fdd99ab6 Mon Sep 17 00:00:00 2001 From: terror Date: Wed, 12 Jun 2024 18:56:38 +1200 Subject: [PATCH 3/8] Do not use timespec_tonanos --- tool/net/lfuncs.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tool/net/lfuncs.c b/tool/net/lfuncs.c index ac43fd10eb9..a76cf8b1864 100644 --- a/tool/net/lfuncs.c +++ b/tool/net/lfuncs.c @@ -860,15 +860,15 @@ int LuaUuidV4(lua_State *L) { int LuaUuidV7(lua_State *L) { char uuid_str[37] = {0}; - struct timespec now = timespec_real(); - uint64_t time_now = timespec_tonanos(now); + struct timespec ts = timespec_real(); + uint64_t time_now = ts.tv_sec * 1000000000 + ts.tv_nsec; uint64_t random_data = _rand64(); - snprintf(uuid_str, sizeof(uuid_str), "%08x-%04x-%04x-%04x-%012llx", - (uint32_t)(time_now >> 32), //8 - (uint16_t)(time_now >> 16), //4 - (uint16_t)((0x7 << 12) | (time_now >> 4 & 0x0fff)), //4 - (uint16_t)((0b10 << 14 | ((time_now & 0x000f) << 10)) | (random_data & 0x03FF)), //4 - (uint64_t)(random_data >> 4 & 0xFFFFFFFFFFFF) //12 + snprintf(uuid_str, 37, "%08x-%04x-%04x-%04x-%012llx", + (uint32_t)(time_now >> 32), + (uint16_t)(time_now >> 16), + (uint16_t)((0x7 << 12) | (time_now >> 4 & 0x0fff)), + (uint16_t)((0b10 << 14 | ((time_now & 0x000f) << 10)) | (random_data & 0x03FF)), + (uint64_t)(random_data >> 4 & 0xFFFFFFFFFFFF) ); lua_pushfstring(L, uuid_str); return 1; From 15f3d7819b7335347ef5c9b710ad6a7dff0c10ab Mon Sep 17 00:00:00 2001 From: terror Date: Wed, 12 Jun 2024 19:10:20 +1200 Subject: [PATCH 4/8] Correct time.h location --- tool/net/lfuncs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/net/lfuncs.c b/tool/net/lfuncs.c index a76cf8b1864..6de78187d71 100644 --- a/tool/net/lfuncs.c +++ b/tool/net/lfuncs.c @@ -52,7 +52,7 @@ #include "libc/sysv/consts/rusage.h" #include "libc/sysv/consts/sock.h" #include "libc/thread/thread.h" -#include "libc/time.h" +#include "libc/time/time.h" #include "libc/x/x.h" #include "net/http/escape.h" #include "net/http/http.h" From 88faf60c0de93b7b73ff5383abe597ac856e65a0 Mon Sep 17 00:00:00 2001 From: terror Date: Thu, 20 Jun 2024 19:32:24 +1200 Subject: [PATCH 5/8] Explicit implementation of the UUID v7 algorithm --- tool/net/lfuncs.c | 71 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 10 deletions(-) diff --git a/tool/net/lfuncs.c b/tool/net/lfuncs.c index a76cf8b1864..e77c35c4ac9 100644 --- a/tool/net/lfuncs.c +++ b/tool/net/lfuncs.c @@ -859,17 +859,68 @@ int LuaUuidV4(lua_State *L) { } int LuaUuidV7(lua_State *L) { - char uuid_str[37] = {0}; + char bin[16], uuid_str[37]; struct timespec ts = timespec_real(); - uint64_t time_now = ts.tv_sec * 1000000000 + ts.tv_nsec; - uint64_t random_data = _rand64(); - snprintf(uuid_str, 37, "%08x-%04x-%04x-%04x-%012llx", - (uint32_t)(time_now >> 32), - (uint16_t)(time_now >> 16), - (uint16_t)((0x7 << 12) | (time_now >> 4 & 0x0fff)), - (uint16_t)((0b10 << 14 | ((time_now & 0x000f) << 10)) | (random_data & 0x03FF)), - (uint64_t)(random_data >> 4 & 0xFFFFFFFFFFFF) - ); + uint64_t unix_ts_ms = (uint64_t)((ts.tv_sec * 1000) + (ts.tv_nsec / 1000000)); + int fractional_ms = (int)floor((double)((double)(ts.tv_nsec - (ts.tv_nsec / 1000000) * 1000000)/1000000) * 4096) <<4; + uint64_t rand_b = _rand64(); + int rand_a = fractional_ms | (rand_b & 0x000000000000000f); //use the last 4 bits of rand_b + + bin[0] = unix_ts_ms >> 050; + bin[1] = unix_ts_ms >> 040; + bin[2] = unix_ts_ms >> 030; + bin[3] = unix_ts_ms >> 020; + bin[4] = unix_ts_ms >> 010; + bin[5] = unix_ts_ms >> 000; + bin[6] = rand_a >> 010; + bin[7] = rand_a >> 000; + bin[8] = rand_b >> 070; + bin[9] = rand_b >> 060; + bin[10] = rand_b >> 050; + bin[11] = rand_b >> 040; + bin[12] = rand_b >> 030; + bin[13] = rand_b >> 020; + bin[14] = rand_b >> 010; + bin[15] = rand_b >> 000; + + uuid_str[0] = "0123456789abcdef"[(bin[0] & 0xf0) >>4]; + uuid_str[1] = "0123456789abcdef"[(bin[0] & 0x0f)]; + uuid_str[2] = "0123456789abcdef"[(bin[1] & 0xf0) >>4]; + uuid_str[3] = "0123456789abcdef"[(bin[1] & 0x0f)]; + uuid_str[4] = "0123456789abcdef"[(bin[2] & 0xf0) >>4]; + uuid_str[5] = "0123456789abcdef"[(bin[2] & 0x0f)]; + uuid_str[6] = "0123456789abcdef"[(bin[3] & 0xf0) >>4]; + uuid_str[7] = "0123456789abcdef"[(bin[3] & 0x0f)]; + uuid_str[8] = '-'; + uuid_str[9] = "0123456789abcdef"[(bin[4] & 0xf0) >>4]; + uuid_str[10] = "0123456789abcdef"[(bin[4] & 0x0f)]; + uuid_str[11] = "0123456789abcdef"[(bin[5] & 0xf0) >>4]; + uuid_str[12] = "0123456789abcdef"[(bin[5] & 0x0f)]; + uuid_str[13] = '-'; + uuid_str[14] = '7'; + uuid_str[15] = "0123456789abcdef"[(bin[6] & 0xf0) >>4]; + uuid_str[16] = "0123456789abcdef"[(bin[6] & 0x0f)]; + uuid_str[17] = "0123456789abcdef"[(bin[7] & 0xf0) >>4]; + uuid_str[18] = '-'; + uuid_str[19] = "0123456789abcdef"[(0x8 | ((bin[7] & 0x0f) >>2))]; + uuid_str[20] = "0123456789abcdef"[(bin[7] & 0x03) | (bin[8] & 0xf0) >>6]; + uuid_str[21] = "0123456789abcdef"[(bin[8] & 0x0f)]; + uuid_str[22] = "0123456789abcdef"[(bin[9] & 0xf0) >>4]; + uuid_str[23] = '-'; + uuid_str[24] = "0123456789abcdef"[(bin[9] & 0x0f)]; + uuid_str[25] = "0123456789abcdef"[(bin[10] & 0xf0) >>4]; + uuid_str[26] = "0123456789abcdef"[(bin[10] & 0x0f)]; + uuid_str[27] = "0123456789abcdef"[(bin[11] & 0xf0) >>4]; + uuid_str[28] = "0123456789abcdef"[(bin[11] & 0x0f)]; + uuid_str[29] = "0123456789abcdef"[(bin[12] & 0xf0) >>4]; + uuid_str[30] = "0123456789abcdef"[(bin[12] & 0x0f)]; + uuid_str[31] = "0123456789abcdef"[(bin[13] & 0xf0) >>4]; + uuid_str[32] = "0123456789abcdef"[(bin[13] & 0x0f)]; + uuid_str[33] = "0123456789abcdef"[(bin[14] & 0xf0) >>4]; + uuid_str[34] = "0123456789abcdef"[(bin[14] & 0x0f)]; + uuid_str[35] = "0123456789abcdef"[(bin[15] & 0xf0) >>4]; + uuid_str[36] = '\0'; + lua_pushfstring(L, uuid_str); return 1; } From edc28828d4675e318b26e0adadf02ebcc136209b Mon Sep 17 00:00:00 2001 From: Terror Date: Fri, 21 Jun 2024 11:19:26 +1200 Subject: [PATCH 6/8] Fix grammar --- tool/net/help.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tool/net/help.txt b/tool/net/help.txt index b89b6bffebc..f031a83aefd 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -1063,10 +1063,10 @@ FUNCTIONS This function is not available in unsecure mode. UuidV4() -> str - Returns an uuid v4 string. + Returns a uuid v4 string. UuidV7() -> str - Returns an uuid v7 string. + Returns a uuid v7 string. Fetch(url:str[,body:str|{method=value:str,body=value:str,headers=table,...}]) ├─→ status:int, {header:str=value:str,...}, body:str From e3897ffd90220df2047e5f9f8458371ed49408f0 Mon Sep 17 00:00:00 2001 From: Terror Date: Fri, 21 Jun 2024 11:23:53 +1200 Subject: [PATCH 7/8] Add link to rfc --- tool/net/lfuncs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tool/net/lfuncs.c b/tool/net/lfuncs.c index e77c35c4ac9..798099c7db0 100644 --- a/tool/net/lfuncs.c +++ b/tool/net/lfuncs.c @@ -859,6 +859,7 @@ int LuaUuidV4(lua_State *L) { } int LuaUuidV7(lua_State *L) { + //See https://www.rfc-editor.org/rfc/rfc9562.html char bin[16], uuid_str[37]; struct timespec ts = timespec_real(); uint64_t unix_ts_ms = (uint64_t)((ts.tv_sec * 1000) + (ts.tv_nsec / 1000000)); @@ -903,7 +904,7 @@ int LuaUuidV7(lua_State *L) { uuid_str[17] = "0123456789abcdef"[(bin[7] & 0xf0) >>4]; uuid_str[18] = '-'; uuid_str[19] = "0123456789abcdef"[(0x8 | ((bin[7] & 0x0f) >>2))]; - uuid_str[20] = "0123456789abcdef"[(bin[7] & 0x03) | (bin[8] & 0xf0) >>6]; + uuid_str[20] = "0123456789abcdef"[(bin[7] & 0x03) | (bin[8] & 0xf0) >>6]; //See https://www.rfc-editor.org/rfc/rfc9562.html#version_field uuid_str[21] = "0123456789abcdef"[(bin[8] & 0x0f)]; uuid_str[22] = "0123456789abcdef"[(bin[9] & 0xf0) >>4]; uuid_str[23] = '-'; From 2bbf9d9ea07caa910a2cd82bfb9c03e181b0b837 Mon Sep 17 00:00:00 2001 From: Terror Date: Mon, 24 Jun 2024 09:57:53 +1200 Subject: [PATCH 8/8] Update uuidv7_test.lua Oops, forgot to change the 4 for a 7 --- test/tool/net/uuidv7_test.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tool/net/uuidv7_test.lua b/test/tool/net/uuidv7_test.lua index 98281f9d980..063a9da94b5 100644 --- a/test/tool/net/uuidv7_test.lua +++ b/test/tool/net/uuidv7_test.lua @@ -13,7 +13,7 @@ -- TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -- PERFORMANCE OF THIS SOFTWARE. for _ = 1, 1000 do - local uuid = UuidV4() + local uuid = UuidV7() assert(#uuid == 36) assert(string.sub(uuid, 9, 9) == "-") assert(string.sub(uuid, 14, 14) == "-")