From 64dd0b0c568b2149afa7a5f2c0d37ed60ba1972c Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Fri, 1 Aug 2025 12:22:22 -0500 Subject: [PATCH] various string and http improvements --- .gitignore | 4 ++++ README.md | 2 +- build.sh | 4 ++++ go.mod | 4 ++-- go.sum | 24 ++++++++----------- modules/http/http.go | 47 ++++++++++++++++++++++---------------- modules/http/pool.go | 15 +++++++++++- modules/sqlite/sqlite.lua | 3 +-- modules/string+/string.lua | 24 ++++++++++++++----- 9 files changed, 81 insertions(+), 46 deletions(-) create mode 100755 build.sh diff --git a/.gitignore b/.gitignore index 30f2c2a..c141462 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,10 @@ luajit/.git # Go workspace file go.work +# Claude workspace files +.claude +CLAUDE.md + # Test directories and files /*.lua test_fs_dir diff --git a/README.md b/README.md index fb640a8..e1fc918 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Moonshark ```bash -go build -trimpath -ldflags="-s -w" -o moonshark . +go build -trimpath -ldflags="-s -w" -o build/moonshark . ``` diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..4437bcd --- /dev/null +++ b/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +mkdir -p build +go build -trimpath -ldflags="-s -w" -o build/moonshark . diff --git a/go.mod b/go.mod index f6d6390..936223b 100644 --- a/go.mod +++ b/go.mod @@ -6,22 +6,22 @@ require git.sharkk.net/Sky/LuaJIT-to-Go v0.5.6 require ( github.com/go-sql-driver/mysql v1.9.3 + github.com/goccy/go-json v0.10.5 github.com/google/uuid v1.6.0 github.com/jackc/pgx/v5 v5.7.5 + golang.org/x/crypto v0.40.0 zombiezen.com/go/sqlite v1.4.2 ) require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/goccy/go-json v0.10.5 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - golang.org/x/crypto v0.40.0 // indirect golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 // indirect golang.org/x/sync v0.16.0 // indirect golang.org/x/sys v0.34.0 // indirect diff --git a/go.sum b/go.sum index d1a563a..55b4a62 100644 --- a/go.sum +++ b/go.sum @@ -48,12 +48,10 @@ github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZ github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 h1:R9PFI6EUdfVKgwKjZef7QIwGcBKu86OEFpJ9nUEP2l4= golang.org/x/exp v0.0.0-20250718183923-645b1fa84792/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= -golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= -golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= +golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -61,22 +59,22 @@ golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= -golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= -golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= +golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s= -modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= +modernc.org/cc/v4 v4.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM= +modernc.org/cc/v4 v4.26.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU= modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE= -modernc.org/fileutil v1.3.1 h1:8vq5fe7jdtEvoCf3Zf9Nm0Q05sH6kGx0Op2CPx1wTC8= -modernc.org/fileutil v1.3.1/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc= +modernc.org/fileutil v1.3.8 h1:qtzNm7ED75pd1C7WgAGcK4edm4fvhtBsEiI/0NQ54YM= +modernc.org/fileutil v1.3.8/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc= modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= -modernc.org/libc v1.65.7 h1:Ia9Z4yzZtWNtUIuiPuQ7Qf7kxYrxP1/jeHZzG8bFu00= -modernc.org/libc v1.65.7/go.mod h1:011EQibzzio/VX3ygj1qGFt5kMjP0lHb0qCW5/D/pQU= +modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= +modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ= modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= @@ -87,8 +85,6 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= -modernc.org/sqlite v1.37.1 h1:EgHJK/FPoqC+q2YBXg7fUmES37pCHFc97sI7zSayBEs= -modernc.org/sqlite v1.37.1/go.mod h1:XwdRtsE1MpiBcL54+MbKcaDvcuej+IYSMfLN6gSKV8g= modernc.org/sqlite v1.38.0 h1:+4OrfPQ8pxHKuWG4md1JpR/EYAh3Md7TdejuuzE7EUI= modernc.org/sqlite v1.38.0/go.mod h1:1Bj+yES4SVvBZ4cBOpVZ6QgesMCKpJZDq0nxYzOpmNE= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= diff --git a/modules/http/http.go b/modules/http/http.go index 05bb33f..41c13a8 100644 --- a/modules/http/http.go +++ b/modules/http/http.go @@ -140,22 +140,7 @@ func http_listen(s *luajit.State) int { } func http_close_server(s *luajit.State) int { - globalMu.Lock() - defer globalMu.Unlock() - - if globalWorkerPool != nil { - globalWorkerPool.Close() - globalWorkerPool = nil - } - - if globalServer != nil { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - globalServer.ShutdownWithContext(ctx) - globalServer = nil - } - - serverRunning = false + StopAllServers() s.PushBoolean(true) return 1 } @@ -335,17 +320,39 @@ func StopAllServers() { globalMu.Lock() defer globalMu.Unlock() + // Start shutting down both in parallel + var wg sync.WaitGroup + + // Close worker pool in parallel if globalWorkerPool != nil { - globalWorkerPool.Close() + wg.Add(1) + pool := globalWorkerPool globalWorkerPool = nil + go func() { + defer wg.Done() + pool.Close() + }() } + // Shutdown server with 100ms timeout if globalServer != nil { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - globalServer.ShutdownWithContext(ctx) + wg.Add(1) + server := globalServer globalServer = nil + go func() { + defer wg.Done() + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) + defer cancel() + if err := server.ShutdownWithContext(ctx); err != nil { + // Force close if graceful shutdown times out + server.CloseOnShutdown = true + _ = server.Shutdown() + } + }() } serverRunning = false + + // Wait for both to complete + wg.Wait() } diff --git a/modules/http/pool.go b/modules/http/pool.go index 159f1fb..8cbe9da 100644 --- a/modules/http/pool.go +++ b/modules/http/pool.go @@ -218,10 +218,23 @@ func (p *WorkerPool) Close() { } p.closed = true + // Collect all workers first + workers := make([]*Worker, 0, len(p.workers)) close(p.workers) for worker := range p.workers { - worker.Close() + workers = append(workers, worker) } + + // Close all workers in parallel + var wg sync.WaitGroup + for _, worker := range workers { + wg.Add(1) + go func(w *Worker) { + defer wg.Done() + w.Close() + }(worker) + } + wg.Wait() } func (w *Worker) Close() { diff --git a/modules/sqlite/sqlite.lua b/modules/sqlite/sqlite.lua index 3b8f5f4..154867f 100644 --- a/modules/sqlite/sqlite.lua +++ b/modules/sqlite/sqlite.lua @@ -381,13 +381,12 @@ function sqlite.open(database_path) database_path = database_path or ":memory:" if database_path ~= ":memory:" then - database_path = string.trim(database_path) if string.is_blank(database_path) then database_path = ":memory:" end end - local conn_id = moonshark.sql_connect("sqlite", database_path) + local conn_id = moonshark.sql_connect("sqlite", database_path:trim()) if conn_id then local conn = {_id = conn_id} setmetatable(conn, Connection) diff --git a/modules/string+/string.lua b/modules/string+/string.lua index 6f6a0b5..0667757 100644 --- a/modules/string+/string.lua +++ b/modules/string+/string.lua @@ -779,14 +779,26 @@ function string.render(template_str, env) return table.concat(output_buffer) end +-- Helper to retrieve dot-notation table data +local function get_nested_value(env, path) + local current = env + for part in path:gmatch("[^.]+") do + if type(current) ~= "table" then + return nil + end + current = current[part] + end + return current +end + -- Named placeholder processing function string.parse(template_str, env) local pos, output = 1, {} env = env or {} while pos <= #template_str do - local unescaped_start, unescaped_end, unescaped_name = template_str:find("{{{%s*([%w_]+)%s*}}}", pos) - local escaped_start, escaped_end, escaped_name = template_str:find("{{%s*([%w_]+)%s*}}", pos) + local unescaped_start, unescaped_end, unescaped_name = template_str:find("{{%s*([%w_.]+)%s*}}", pos) + local escaped_start, escaped_end, escaped_name = template_str:find("{{{%s*([%w_.]+)%s*}}}", pos) local next_pos, placeholder_end, name, escaped if unescaped_start and (not escaped_start or unescaped_start <= escaped_start) then @@ -806,10 +818,10 @@ function string.parse(template_str, env) table.insert(output, text) end - local value = env[name] + local value = get_nested_value(env, name) local str = tostring(value or "") if escaped then - str:html_special_chars() + str = str:html_special_chars() end table.insert(output, str) @@ -825,8 +837,8 @@ function string.iparse(template_str, values) values = values or {} while pos <= #template_str do - local unescaped_start, unescaped_end = template_str:find("{{{}}}", pos, true) - local escaped_start, escaped_end = template_str:find("{{}}", pos, true) + local unescaped_start, unescaped_end = template_str:find("{{}}", pos, true) + local escaped_start, escaped_end = template_str:find("{{{}}}", pos, true) local next_pos, placeholder_end, escaped if unescaped_start and (not escaped_start or unescaped_start <= escaped_start) then