feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
// Please do not edit this file. It has been generated automatically
|
|
|
|
|
// from `src/module/web.wren` using `util/wren_to_c_string.py`
|
|
|
|
|
|
|
|
|
|
static const char* webModuleSource =
|
|
|
|
|
"// retoor <retoor@molodetz.nl>\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"import \"net\" for Socket, Server\n"
|
|
|
|
|
"import \"tls\" for TlsSocket\n"
|
|
|
|
|
"import \"dns\" for Dns\n"
|
|
|
|
|
"import \"websocket\" for WebSocket, WebSocketServer, WebSocketMessage\n"
|
|
|
|
|
"import \"json\" for Json\n"
|
|
|
|
|
"import \"html\" for Html\n"
|
|
|
|
|
"import \"uuid\" for Uuid\n"
|
|
|
|
|
"import \"datetime\" for DateTime\n"
|
|
|
|
|
"import \"io\" for File\n"
|
feat: add merge_all_wren utility, strutil demo, and 7 new API manual pages
Add WrenFileReader class in apps/merge_all_wren.wren for recursive .wren file discovery and content printing. Introduce example/strutil_demo.wren demonstrating case conversion, hex encoding, SHA256 hashing, string repetition, padding, HTML/JSON escaping, and URL encoding/decoding. Create manual/api pages for argparse, dataset, html, markdown, uuid, wdantic, and web modules. Update manual/api/index.html sidebar and module count from 21 to 28, adding links and cards for the new modules.
2026-01-25 02:47:47 +01:00
|
|
|
"import \"strutil\" for Str\n"
|
feat: add buildmanual target, faker demo, subprocess examples, and contributing sidebar to manual
Add Makefile buildmanual target for manual generation and include faker_demo.wren, subprocess_async_demo.wren, subprocess_concurrent_demo.wren, and subprocess_fiber_demo.wren example scripts. Update manual sidebar across api pages to link new web-server tutorial and contributing section with module architecture, pure-wren/c-backed modules, foreign classes, async patterns, testing, and documentation pages.
2026-01-25 19:02:02 +01:00
|
|
|
"import \"crypto\" for Crypto, Hash\n"
|
|
|
|
|
"import \"base64\" for Base64\n"
|
|
|
|
|
"import \"bytes\" for Bytes\n"
|
feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
"\n"
|
|
|
|
|
"class Request {\n"
|
|
|
|
|
" construct new_(method, path, query, headers, body, params, socket) {\n"
|
|
|
|
|
" _method = method\n"
|
|
|
|
|
" _path = path\n"
|
|
|
|
|
" _query = query\n"
|
|
|
|
|
" _headers = headers\n"
|
|
|
|
|
" _body = body\n"
|
|
|
|
|
" _params = params\n"
|
|
|
|
|
" _socket = socket\n"
|
|
|
|
|
" _parsedJson = null\n"
|
|
|
|
|
" _parsedForm = null\n"
|
|
|
|
|
" _cookies = null\n"
|
|
|
|
|
" _session = null\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" method { _method }\n"
|
|
|
|
|
" path { _path }\n"
|
|
|
|
|
" query { _query }\n"
|
|
|
|
|
" headers { _headers }\n"
|
|
|
|
|
" body { _body }\n"
|
|
|
|
|
" params { _params }\n"
|
|
|
|
|
" socket { _socket }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" header(name) {\n"
|
|
|
|
|
" var lower = toLower_(name)\n"
|
|
|
|
|
" for (key in _headers.keys) {\n"
|
|
|
|
|
" if (toLower_(key) == lower) return _headers[key]\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" return null\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" json {\n"
|
|
|
|
|
" if (_parsedJson == null && _body.count > 0) {\n"
|
|
|
|
|
" _parsedJson = Json.parse(_body)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" return _parsedJson\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" form {\n"
|
|
|
|
|
" if (_parsedForm == null && _body.count > 0) {\n"
|
2026-01-26 10:21:03 +01:00
|
|
|
" var contentType = header(\"Content-Type\") || \"\"\n"
|
|
|
|
|
" if (contentType.contains(\"multipart/form-data\")) {\n"
|
|
|
|
|
" _parsedForm = parseMultipart_(contentType)\n"
|
|
|
|
|
" } else {\n"
|
|
|
|
|
" _parsedForm = Html.decodeParams(_body)\n"
|
|
|
|
|
" }\n"
|
feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
" }\n"
|
|
|
|
|
" return _parsedForm\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
2026-01-26 10:21:03 +01:00
|
|
|
" parseMultipart_(contentType) {\n"
|
|
|
|
|
" var result = {}\n"
|
|
|
|
|
" var boundary = extractBoundary_(contentType)\n"
|
|
|
|
|
" if (boundary == null) return result\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var parts = _body.split(\"--\" + boundary)\n"
|
|
|
|
|
" for (part in parts) {\n"
|
|
|
|
|
" var trimmed = part.trim()\n"
|
|
|
|
|
" if (trimmed.count == 0 || trimmed == \"--\") continue\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var headerEnd = trimmed.indexOf(\"\\r\\n\\r\\n\")\n"
|
|
|
|
|
" var delimLen = 4\n"
|
|
|
|
|
" if (headerEnd < 0) {\n"
|
|
|
|
|
" headerEnd = trimmed.indexOf(\"\\n\\n\")\n"
|
|
|
|
|
" delimLen = 2\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" if (headerEnd < 0) continue\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var headers = trimmed[0...headerEnd]\n"
|
|
|
|
|
" var content = trimmed[headerEnd + delimLen..-1]\n"
|
|
|
|
|
" if (content.endsWith(\"\\r\\n\")) {\n"
|
|
|
|
|
" content = content[0...-2]\n"
|
|
|
|
|
" } else if (content.endsWith(\"\\n\")) {\n"
|
|
|
|
|
" content = content[0...-1]\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var disposition = extractHeader_(headers, \"Content-Disposition\")\n"
|
|
|
|
|
" if (disposition == null) continue\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var name = extractDispositionParam_(disposition, \"name\")\n"
|
|
|
|
|
" if (name == null) continue\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var filename = extractDispositionParam_(disposition, \"filename\")\n"
|
|
|
|
|
" if (filename != null) {\n"
|
|
|
|
|
" result[name] = {\n"
|
|
|
|
|
" \"filename\": filename,\n"
|
|
|
|
|
" \"content\": content,\n"
|
|
|
|
|
" \"content_type\": extractHeader_(headers, \"Content-Type\") || \"application/octet-stream\"\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" } else {\n"
|
|
|
|
|
" result[name] = content\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" return result\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" extractBoundary_(contentType) {\n"
|
|
|
|
|
" var idx = contentType.indexOf(\"boundary=\")\n"
|
|
|
|
|
" if (idx < 0) return null\n"
|
|
|
|
|
" var boundary = contentType[idx + 9..-1]\n"
|
|
|
|
|
" if (boundary.startsWith(\"\\\"\")) {\n"
|
|
|
|
|
" boundary = boundary[1..-1]\n"
|
|
|
|
|
" var endQuote = boundary.indexOf(\"\\\"\")\n"
|
|
|
|
|
" if (endQuote > 0) boundary = boundary[0...endQuote]\n"
|
|
|
|
|
" } else {\n"
|
|
|
|
|
" var semi = boundary.indexOf(\";\")\n"
|
|
|
|
|
" if (semi > 0) boundary = boundary[0...semi]\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" return boundary.trim()\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" extractHeader_(headers, name) {\n"
|
|
|
|
|
" var lower = toLower_(name)\n"
|
|
|
|
|
" for (line in headers.split(\"\\n\")) {\n"
|
|
|
|
|
" var colonIdx = line.indexOf(\":\")\n"
|
|
|
|
|
" if (colonIdx > 0) {\n"
|
|
|
|
|
" var headerName = toLower_(line[0...colonIdx].trim())\n"
|
|
|
|
|
" if (headerName == lower) {\n"
|
|
|
|
|
" return line[colonIdx + 1..-1].trim()\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" return null\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" extractDispositionParam_(disposition, param) {\n"
|
|
|
|
|
" var search = param + \"=\\\"\"\n"
|
|
|
|
|
" var idx = disposition.indexOf(search)\n"
|
|
|
|
|
" if (idx < 0) {\n"
|
|
|
|
|
" search = param + \"=\"\n"
|
|
|
|
|
" idx = disposition.indexOf(search)\n"
|
|
|
|
|
" if (idx < 0) return null\n"
|
|
|
|
|
" var start = idx + search.count\n"
|
|
|
|
|
" var end = start\n"
|
|
|
|
|
" while (end < disposition.count && disposition[end] != \";\" && disposition[end] != \" \") {\n"
|
|
|
|
|
" end = end + 1\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" return disposition[start...end]\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" var start = idx + search.count\n"
|
|
|
|
|
" var end = disposition.indexOf(\"\\\"\", start)\n"
|
|
|
|
|
" if (end < 0) return null\n"
|
|
|
|
|
" return disposition[start...end]\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
" cookies {\n"
|
|
|
|
|
" if (_cookies == null) {\n"
|
|
|
|
|
" _cookies = {}\n"
|
|
|
|
|
" var cookieHeader = header(\"Cookie\")\n"
|
|
|
|
|
" if (cookieHeader != null) {\n"
|
|
|
|
|
" var pairs = cookieHeader.split(\"; \")\n"
|
|
|
|
|
" for (pair in pairs) {\n"
|
|
|
|
|
" var eq = pair.indexOf(\"=\")\n"
|
|
|
|
|
" if (eq > 0) {\n"
|
|
|
|
|
" var name = pair[0...eq].trim()\n"
|
|
|
|
|
" var value = pair[eq + 1..-1].trim()\n"
|
|
|
|
|
" _cookies[name] = value\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" return _cookies\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" session { _session }\n"
|
|
|
|
|
" session=(s) { _session = s }\n"
|
|
|
|
|
"\n"
|
feat: add merge_all_wren utility, strutil demo, and 7 new API manual pages
Add WrenFileReader class in apps/merge_all_wren.wren for recursive .wren file discovery and content printing. Introduce example/strutil_demo.wren demonstrating case conversion, hex encoding, SHA256 hashing, string repetition, padding, HTML/JSON escaping, and URL encoding/decoding. Create manual/api pages for argparse, dataset, html, markdown, uuid, wdantic, and web modules. Update manual/api/index.html sidebar and module count from 21 to 28, adding links and cards for the new modules.
2026-01-25 02:47:47 +01:00
|
|
|
" toLower_(str) { Str.toLower(str) }\n"
|
feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
"}\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"class Response {\n"
|
|
|
|
|
" construct new() {\n"
|
|
|
|
|
" _status = 200\n"
|
|
|
|
|
" _statusText = \"OK\"\n"
|
|
|
|
|
" _headers = {}\n"
|
|
|
|
|
" _body = \"\"\n"
|
|
|
|
|
" _cookies = []\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" status { _status }\n"
|
|
|
|
|
" status=(s) {\n"
|
|
|
|
|
" _status = s\n"
|
|
|
|
|
" _statusText = statusText_(s)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" headers { _headers }\n"
|
|
|
|
|
" body { _body }\n"
|
|
|
|
|
" body=(b) { _body = b }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" header(name, value) {\n"
|
|
|
|
|
" _headers[name] = value\n"
|
|
|
|
|
" return this\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" cookie(name, value) { cookie(name, value, {}) }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" cookie(name, value, options) {\n"
|
|
|
|
|
" var cookie = name + \"=\" + value\n"
|
|
|
|
|
" if (options.containsKey(\"path\")) cookie = cookie + \"; Path=\" + options[\"path\"]\n"
|
|
|
|
|
" if (options.containsKey(\"maxAge\")) cookie = cookie + \"; Max-Age=\" + options[\"maxAge\"].toString\n"
|
|
|
|
|
" if (options.containsKey(\"httpOnly\") && options[\"httpOnly\"]) cookie = cookie + \"; HttpOnly\"\n"
|
|
|
|
|
" if (options.containsKey(\"secure\") && options[\"secure\"]) cookie = cookie + \"; Secure\"\n"
|
|
|
|
|
" _cookies.add(cookie)\n"
|
|
|
|
|
" return this\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" static text(content) {\n"
|
|
|
|
|
" var r = Response.new()\n"
|
|
|
|
|
" r.header(\"Content-Type\", \"text/plain; charset=utf-8\")\n"
|
|
|
|
|
" r.body = content\n"
|
|
|
|
|
" return r\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" static html(content) {\n"
|
|
|
|
|
" var r = Response.new()\n"
|
|
|
|
|
" r.header(\"Content-Type\", \"text/html; charset=utf-8\")\n"
|
|
|
|
|
" r.body = content\n"
|
|
|
|
|
" return r\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" static json(data) {\n"
|
|
|
|
|
" var r = Response.new()\n"
|
|
|
|
|
" r.header(\"Content-Type\", \"application/json; charset=utf-8\")\n"
|
|
|
|
|
" r.body = Json.stringify(data)\n"
|
|
|
|
|
" return r\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" static redirect(url) { redirect(url, 302) }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" static redirect(url, status) {\n"
|
|
|
|
|
" var r = Response.new()\n"
|
|
|
|
|
" r.status = status\n"
|
|
|
|
|
" r.header(\"Location\", url)\n"
|
|
|
|
|
" return r\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" static file(path) { file(path, null) }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" static file(path, contentType) {\n"
|
|
|
|
|
" if (!File.exists(path)) {\n"
|
|
|
|
|
" var r = Response.new()\n"
|
|
|
|
|
" r.status = 404\n"
|
|
|
|
|
" r.body = \"Not Found\"\n"
|
|
|
|
|
" return r\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" var r = Response.new()\n"
|
|
|
|
|
" var ct = contentType\n"
|
|
|
|
|
" if (ct == null) ct = guessContentType_(path)\n"
|
|
|
|
|
" r.header(\"Content-Type\", ct)\n"
|
|
|
|
|
" r.body = File.read(path)\n"
|
|
|
|
|
" return r\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" static guessContentType_(path) {\n"
|
|
|
|
|
" if (path.endsWith(\".html\") || path.endsWith(\".htm\")) return \"text/html; charset=utf-8\"\n"
|
|
|
|
|
" if (path.endsWith(\".css\")) return \"text/css; charset=utf-8\"\n"
|
|
|
|
|
" if (path.endsWith(\".js\")) return \"application/javascript; charset=utf-8\"\n"
|
|
|
|
|
" if (path.endsWith(\".json\")) return \"application/json; charset=utf-8\"\n"
|
|
|
|
|
" if (path.endsWith(\".png\")) return \"image/png\"\n"
|
|
|
|
|
" if (path.endsWith(\".jpg\") || path.endsWith(\".jpeg\")) return \"image/jpeg\"\n"
|
|
|
|
|
" if (path.endsWith(\".gif\")) return \"image/gif\"\n"
|
|
|
|
|
" if (path.endsWith(\".svg\")) return \"image/svg+xml\"\n"
|
|
|
|
|
" if (path.endsWith(\".ico\")) return \"image/x-icon\"\n"
|
|
|
|
|
" if (path.endsWith(\".txt\")) return \"text/plain; charset=utf-8\"\n"
|
|
|
|
|
" if (path.endsWith(\".xml\")) return \"application/xml; charset=utf-8\"\n"
|
|
|
|
|
" if (path.endsWith(\".pdf\")) return \"application/pdf\"\n"
|
|
|
|
|
" if (path.endsWith(\".woff\")) return \"font/woff\"\n"
|
|
|
|
|
" if (path.endsWith(\".woff2\")) return \"font/woff2\"\n"
|
|
|
|
|
" if (path.endsWith(\".ttf\")) return \"font/ttf\"\n"
|
|
|
|
|
" return \"application/octet-stream\"\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" statusText_(code) {\n"
|
|
|
|
|
" if (code == 200) return \"OK\"\n"
|
|
|
|
|
" if (code == 201) return \"Created\"\n"
|
|
|
|
|
" if (code == 204) return \"No Content\"\n"
|
|
|
|
|
" if (code == 301) return \"Moved Permanently\"\n"
|
|
|
|
|
" if (code == 302) return \"Found\"\n"
|
|
|
|
|
" if (code == 304) return \"Not Modified\"\n"
|
|
|
|
|
" if (code == 400) return \"Bad Request\"\n"
|
|
|
|
|
" if (code == 401) return \"Unauthorized\"\n"
|
|
|
|
|
" if (code == 403) return \"Forbidden\"\n"
|
|
|
|
|
" if (code == 404) return \"Not Found\"\n"
|
|
|
|
|
" if (code == 405) return \"Method Not Allowed\"\n"
|
|
|
|
|
" if (code == 500) return \"Internal Server Error\"\n"
|
|
|
|
|
" return \"Unknown\"\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" build() {\n"
|
|
|
|
|
" var response = \"HTTP/1.1 %(_status) %(_statusText)\\r\\n\"\n"
|
|
|
|
|
" _headers[\"Content-Length\"] = _body.bytes.count.toString\n"
|
|
|
|
|
" for (name in _headers.keys) {\n"
|
|
|
|
|
" response = response + name + \": \" + _headers[name] + \"\\r\\n\"\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" for (cookie in _cookies) {\n"
|
|
|
|
|
" response = response + \"Set-Cookie: \" + cookie + \"\\r\\n\"\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" response = response + \"\\r\\n\" + _body\n"
|
|
|
|
|
" return response\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"}\n"
|
|
|
|
|
"\n"
|
feat: add buildmanual target, faker demo, subprocess examples, and contributing sidebar to manual
Add Makefile buildmanual target for manual generation and include faker_demo.wren, subprocess_async_demo.wren, subprocess_concurrent_demo.wren, and subprocess_fiber_demo.wren example scripts. Update manual sidebar across api pages to link new web-server tutorial and contributing section with module architecture, pure-wren/c-backed modules, foreign classes, async patterns, testing, and documentation pages.
2026-01-25 19:02:02 +01:00
|
|
|
"class WebSocketResponse {\n"
|
|
|
|
|
" construct new() {\n"
|
|
|
|
|
" _socket = null\n"
|
|
|
|
|
" _isPrepared = false\n"
|
|
|
|
|
" _isOpen = false\n"
|
|
|
|
|
" _readBuffer = \"\"\n"
|
|
|
|
|
" _fragmentBuffer = []\n"
|
|
|
|
|
" _fragmentOpcode = null\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" isPrepared { _isPrepared }\n"
|
|
|
|
|
" isOpen { _isOpen }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" prepare(request) {\n"
|
|
|
|
|
" if (_isPrepared) Fiber.abort(\"WebSocket already prepared.\")\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var key = request.header(\"Sec-WebSocket-Key\")\n"
|
|
|
|
|
" if (key == null) Fiber.abort(\"Missing Sec-WebSocket-Key header.\")\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" _socket = request.socket\n"
|
|
|
|
|
" var acceptKey = computeAcceptKey_(key)\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var response = \"HTTP/1.1 101 Switching Protocols\\r\\n\"\n"
|
|
|
|
|
" response = response + \"Upgrade: websocket\\r\\n\"\n"
|
|
|
|
|
" response = response + \"Connection: Upgrade\\r\\n\"\n"
|
|
|
|
|
" response = response + \"Sec-WebSocket-Accept: %(acceptKey)\\r\\n\"\n"
|
|
|
|
|
" response = response + \"\\r\\n\"\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" _socket.write(response)\n"
|
|
|
|
|
" _isPrepared = true\n"
|
|
|
|
|
" _isOpen = true\n"
|
|
|
|
|
" return this\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" send(text) { sendText(text) }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" sendText(text) {\n"
|
|
|
|
|
" if (!_isOpen) Fiber.abort(\"WebSocket is not open.\")\n"
|
|
|
|
|
" if (!(text is String)) Fiber.abort(\"Data must be a string.\")\n"
|
|
|
|
|
" var payload = stringToBytes_(text)\n"
|
|
|
|
|
" sendFrame_(1, payload)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" sendBinary(bytes) {\n"
|
|
|
|
|
" if (!_isOpen) Fiber.abort(\"WebSocket is not open.\")\n"
|
|
|
|
|
" if (!(bytes is List)) Fiber.abort(\"Data must be a list of bytes.\")\n"
|
|
|
|
|
" sendFrame_(2, bytes)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" ping() { ping([]) }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" ping(data) {\n"
|
|
|
|
|
" if (!_isOpen) Fiber.abort(\"WebSocket is not open.\")\n"
|
|
|
|
|
" var payload = data\n"
|
|
|
|
|
" if (data is String) payload = stringToBytes_(data)\n"
|
|
|
|
|
" if (payload.count > 125) Fiber.abort(\"Ping payload too large (max 125 bytes).\")\n"
|
|
|
|
|
" sendFrame_(9, payload)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" pong(data) {\n"
|
|
|
|
|
" if (!_isOpen) Fiber.abort(\"WebSocket is not open.\")\n"
|
|
|
|
|
" var payload = data\n"
|
|
|
|
|
" if (data is String) payload = stringToBytes_(data)\n"
|
|
|
|
|
" if (payload.count > 125) Fiber.abort(\"Pong payload too large (max 125 bytes).\")\n"
|
|
|
|
|
" sendFrame_(10, payload)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" close() { close(1000, \"\") }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" close(code, reason) {\n"
|
|
|
|
|
" if (!_isOpen) return\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var payload = []\n"
|
|
|
|
|
" payload.add((code >> 8) & 0xFF)\n"
|
|
|
|
|
" payload.add(code & 0xFF)\n"
|
|
|
|
|
" for (b in stringToBytes_(reason)) {\n"
|
|
|
|
|
" payload.add(b)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" sendFrame_(8, payload)\n"
|
|
|
|
|
" _isOpen = false\n"
|
|
|
|
|
" _socket.close()\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" receive() {\n"
|
|
|
|
|
" if (!_isOpen) return null\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" while (true) {\n"
|
|
|
|
|
" var frame = readFrame_()\n"
|
|
|
|
|
" if (frame == null) {\n"
|
|
|
|
|
" _isOpen = false\n"
|
|
|
|
|
" return null\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var opcode = frame.opcode\n"
|
|
|
|
|
" var payload = frame.payload\n"
|
|
|
|
|
" var fin = frame.fin\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" if (opcode == 8) {\n"
|
|
|
|
|
" _isOpen = false\n"
|
|
|
|
|
" return frame\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" if (opcode == 9) {\n"
|
|
|
|
|
" sendFrame_(10, payload)\n"
|
|
|
|
|
" continue\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" if (opcode == 10) {\n"
|
|
|
|
|
" continue\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" if (opcode == 0) {\n"
|
|
|
|
|
" for (b in payload) {\n"
|
|
|
|
|
" _fragmentBuffer.add(b)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" if (fin) {\n"
|
|
|
|
|
" var completePayload = _fragmentBuffer\n"
|
|
|
|
|
" var completeOpcode = _fragmentOpcode\n"
|
|
|
|
|
" _fragmentBuffer = []\n"
|
|
|
|
|
" _fragmentOpcode = null\n"
|
|
|
|
|
" return WebSocketMessage.new_(completeOpcode, completePayload, true)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" continue\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" if (opcode == 1 || opcode == 2) {\n"
|
|
|
|
|
" if (fin) {\n"
|
|
|
|
|
" return frame\n"
|
|
|
|
|
" } else {\n"
|
|
|
|
|
" _fragmentOpcode = opcode\n"
|
|
|
|
|
" _fragmentBuffer = []\n"
|
|
|
|
|
" for (b in payload) {\n"
|
|
|
|
|
" _fragmentBuffer.add(b)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" continue\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" Fiber.abort(\"Unknown opcode: %(opcode)\")\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" iterate(callback) {\n"
|
|
|
|
|
" while (_isOpen) {\n"
|
|
|
|
|
" var msg = receive()\n"
|
|
|
|
|
" if (msg == null || msg.isClose) break\n"
|
|
|
|
|
" callback.call(msg)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" sendJson(data) {\n"
|
|
|
|
|
" if (!_isOpen) Fiber.abort(\"WebSocket is not open.\")\n"
|
|
|
|
|
" var text = Json.stringify(data)\n"
|
|
|
|
|
" sendText(text)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" receiveJson() {\n"
|
|
|
|
|
" var msg = receive()\n"
|
|
|
|
|
" if (msg == null) return null\n"
|
|
|
|
|
" if (msg.isClose) return msg\n"
|
|
|
|
|
" if (!msg.isText) Fiber.abort(\"Expected text frame for JSON, got binary.\")\n"
|
|
|
|
|
" return Json.parse(msg.text)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" receiveText() {\n"
|
|
|
|
|
" var msg = receive()\n"
|
|
|
|
|
" if (msg == null) return null\n"
|
|
|
|
|
" if (msg.isClose) return null\n"
|
|
|
|
|
" if (!msg.isText) return null\n"
|
|
|
|
|
" return msg.text\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" receiveBinary() {\n"
|
|
|
|
|
" var msg = receive()\n"
|
|
|
|
|
" if (msg == null) return null\n"
|
|
|
|
|
" if (msg.isClose) return null\n"
|
|
|
|
|
" if (!msg.isBinary) return null\n"
|
|
|
|
|
" return msg.bytes\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" sendFrame_(opcode, payload) {\n"
|
|
|
|
|
" var frame = encodeFrame_(opcode, payload)\n"
|
|
|
|
|
" _socket.write(Bytes.fromList(frame))\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" encodeFrame_(opcode, payload) {\n"
|
|
|
|
|
" var frame = []\n"
|
|
|
|
|
" frame.add(0x80 | opcode)\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var len = payload.count\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" if (len < 126) {\n"
|
|
|
|
|
" frame.add(len)\n"
|
|
|
|
|
" } else if (len < 65536) {\n"
|
|
|
|
|
" frame.add(126)\n"
|
|
|
|
|
" frame.add((len >> 8) & 0xFF)\n"
|
|
|
|
|
" frame.add(len & 0xFF)\n"
|
|
|
|
|
" } else {\n"
|
|
|
|
|
" frame.add(127)\n"
|
|
|
|
|
" for (i in 0...4) frame.add(0)\n"
|
|
|
|
|
" frame.add((len >> 24) & 0xFF)\n"
|
|
|
|
|
" frame.add((len >> 16) & 0xFF)\n"
|
|
|
|
|
" frame.add((len >> 8) & 0xFF)\n"
|
|
|
|
|
" frame.add(len & 0xFF)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" for (b in payload) frame.add(b)\n"
|
|
|
|
|
" return frame\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" readFrame_() {\n"
|
|
|
|
|
" var header = readBytes_(2)\n"
|
|
|
|
|
" if (header == null || Bytes.length(header) < 2) return null\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var headerList = Bytes.toList(header)\n"
|
|
|
|
|
" var fin = (headerList[0] & 0x80) != 0\n"
|
|
|
|
|
" var opcode = headerList[0] & 0x0F\n"
|
|
|
|
|
" var masked = (headerList[1] & 0x80) != 0\n"
|
|
|
|
|
" var len = headerList[1] & 0x7F\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" if (len == 126) {\n"
|
|
|
|
|
" var ext = readBytes_(2)\n"
|
|
|
|
|
" if (ext == null || Bytes.length(ext) < 2) return null\n"
|
|
|
|
|
" var extList = Bytes.toList(ext)\n"
|
|
|
|
|
" len = (extList[0] << 8) | extList[1]\n"
|
|
|
|
|
" } else if (len == 127) {\n"
|
|
|
|
|
" var ext = readBytes_(8)\n"
|
|
|
|
|
" if (ext == null || Bytes.length(ext) < 8) return null\n"
|
|
|
|
|
" var extList = Bytes.toList(ext)\n"
|
|
|
|
|
" len = 0\n"
|
|
|
|
|
" for (i in 4...8) {\n"
|
|
|
|
|
" len = (len << 8) | extList[i]\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var mask = null\n"
|
|
|
|
|
" if (masked) {\n"
|
|
|
|
|
" mask = readBytes_(4)\n"
|
|
|
|
|
" if (mask == null || Bytes.length(mask) < 4) return null\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var payload = \"\"\n"
|
|
|
|
|
" if (len > 0) {\n"
|
|
|
|
|
" payload = readBytes_(len)\n"
|
|
|
|
|
" if (payload == null) return null\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" if (masked && mask != null) {\n"
|
|
|
|
|
" payload = Bytes.xorMask(payload, mask)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" return WebSocketMessage.new_(opcode, Bytes.toList(payload), fin)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" readBytes_(count) {\n"
|
|
|
|
|
" while (Bytes.length(_readBuffer) < count) {\n"
|
|
|
|
|
" var chunk = _socket.read()\n"
|
|
|
|
|
" if (chunk == null || chunk.bytes.count == 0) {\n"
|
|
|
|
|
" if (Bytes.length(_readBuffer) == 0) return null\n"
|
|
|
|
|
" var result = _readBuffer\n"
|
|
|
|
|
" _readBuffer = \"\"\n"
|
|
|
|
|
" return result\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" _readBuffer = Bytes.concat(_readBuffer, chunk)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" var result = Bytes.slice(_readBuffer, 0, count)\n"
|
|
|
|
|
" _readBuffer = Bytes.slice(_readBuffer, count, Bytes.length(_readBuffer))\n"
|
|
|
|
|
" return result\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" computeAcceptKey_(key) {\n"
|
|
|
|
|
" var magic = \"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\"\n"
|
|
|
|
|
" var combined = key + magic\n"
|
|
|
|
|
" var sha1 = Hash.sha1(combined)\n"
|
|
|
|
|
" var sha1Str = bytesToString_(sha1)\n"
|
|
|
|
|
" return Base64.encode(sha1Str)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" bytesToString_(bytes) {\n"
|
|
|
|
|
" var parts = []\n"
|
|
|
|
|
" for (b in bytes) {\n"
|
|
|
|
|
" parts.add(String.fromByte(b))\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" return parts.join(\"\")\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" stringToBytes_(str) {\n"
|
|
|
|
|
" var bytes = []\n"
|
|
|
|
|
" for (b in str.bytes) {\n"
|
|
|
|
|
" bytes.add(b)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" return bytes\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"}\n"
|
|
|
|
|
"\n"
|
feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
"class Session {\n"
|
|
|
|
|
" construct new_(id, data) {\n"
|
|
|
|
|
" _id = id\n"
|
|
|
|
|
" _data = data\n"
|
|
|
|
|
" _modified = false\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" id { _id }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" [key] { _data.containsKey(key) ? _data[key] : null }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" [key]=(value) {\n"
|
|
|
|
|
" _data[key] = value\n"
|
|
|
|
|
" _modified = true\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" remove(key) {\n"
|
|
|
|
|
" if (_data.containsKey(key)) {\n"
|
|
|
|
|
" _data.remove(key)\n"
|
|
|
|
|
" _modified = true\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" data { _data }\n"
|
|
|
|
|
" isModified { _modified }\n"
|
|
|
|
|
"}\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"class SessionStore {\n"
|
|
|
|
|
" construct new() {\n"
|
|
|
|
|
" _sessions = {}\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" get(id) {\n"
|
|
|
|
|
" if (_sessions.containsKey(id)) {\n"
|
|
|
|
|
" return _sessions[id]\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" return null\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" create() {\n"
|
|
|
|
|
" var id = Uuid.v4()\n"
|
|
|
|
|
" _sessions[id] = {}\n"
|
|
|
|
|
" return Session.new_(id, _sessions[id])\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" save(session) {\n"
|
|
|
|
|
" _sessions[session.id] = session.data\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" destroy(id) {\n"
|
|
|
|
|
" if (_sessions.containsKey(id)) {\n"
|
|
|
|
|
" _sessions.remove(id)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"}\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"class Router {\n"
|
|
|
|
|
" construct new() {\n"
|
|
|
|
|
" _routes = []\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" addRoute(method, path, handler) {\n"
|
|
|
|
|
" _routes.add({\"method\": method, \"path\": path, \"handler\": handler, \"pattern\": compilePattern_(path)})\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" get(path, handler) { addRoute(\"GET\", path, handler) }\n"
|
|
|
|
|
" post(path, handler) { addRoute(\"POST\", path, handler) }\n"
|
|
|
|
|
" put(path, handler) { addRoute(\"PUT\", path, handler) }\n"
|
|
|
|
|
" delete(path, handler) { addRoute(\"DELETE\", path, handler) }\n"
|
|
|
|
|
" patch(path, handler) { addRoute(\"PATCH\", path, handler) }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" match(method, path) {\n"
|
|
|
|
|
" for (route in _routes) {\n"
|
|
|
|
|
" if (route[\"method\"] != method && route[\"method\"] != \"*\") continue\n"
|
|
|
|
|
" var match = matchPattern_(route[\"pattern\"], path)\n"
|
|
|
|
|
" if (match != null) {\n"
|
|
|
|
|
" return {\"handler\": route[\"handler\"], \"params\": match}\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" return null\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" compilePattern_(path) {\n"
|
|
|
|
|
" var parts = path.split(\"/\")\n"
|
|
|
|
|
" var pattern = []\n"
|
|
|
|
|
" for (part in parts) {\n"
|
|
|
|
|
" if (part.count == 0) continue\n"
|
|
|
|
|
" if (part.startsWith(\":\")) {\n"
|
|
|
|
|
" pattern.add({\"type\": \"param\", \"name\": part[1..-1]})\n"
|
|
|
|
|
" } else if (part == \"*\") {\n"
|
|
|
|
|
" pattern.add({\"type\": \"wildcard\"})\n"
|
|
|
|
|
" } else {\n"
|
|
|
|
|
" pattern.add({\"type\": \"literal\", \"value\": part})\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" return pattern\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" matchPattern_(pattern, path) {\n"
|
|
|
|
|
" var parts = path.split(\"/\").where { |p| p.count > 0 }.toList\n"
|
|
|
|
|
" if (parts.count != pattern.count) {\n"
|
|
|
|
|
" var hasWildcard = pattern.any { |p| p[\"type\"] == \"wildcard\" }\n"
|
|
|
|
|
" if (!hasWildcard) return null\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var params = {}\n"
|
|
|
|
|
" var pi = 0\n"
|
|
|
|
|
" for (i in 0...pattern.count) {\n"
|
|
|
|
|
" var p = pattern[i]\n"
|
|
|
|
|
" if (p[\"type\"] == \"wildcard\") {\n"
|
|
|
|
|
" break\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" if (pi >= parts.count) return null\n"
|
|
|
|
|
" if (p[\"type\"] == \"literal\") {\n"
|
|
|
|
|
" if (parts[pi] != p[\"value\"]) return null\n"
|
|
|
|
|
" } else if (p[\"type\"] == \"param\") {\n"
|
|
|
|
|
" params[p[\"name\"]] = parts[pi]\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" pi = pi + 1\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" return params\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"}\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"class View {\n"
|
|
|
|
|
" get(request) { methodNotAllowed_() }\n"
|
|
|
|
|
" post(request) { methodNotAllowed_() }\n"
|
|
|
|
|
" put(request) { methodNotAllowed_() }\n"
|
|
|
|
|
" delete(request) { methodNotAllowed_() }\n"
|
|
|
|
|
" patch(request) { methodNotAllowed_() }\n"
|
feat: add buildmanual target, faker demo, subprocess examples, and contributing sidebar to manual
Add Makefile buildmanual target for manual generation and include faker_demo.wren, subprocess_async_demo.wren, subprocess_concurrent_demo.wren, and subprocess_fiber_demo.wren example scripts. Update manual sidebar across api pages to link new web-server tutorial and contributing section with module architecture, pure-wren/c-backed modules, foreign classes, async patterns, testing, and documentation pages.
2026-01-25 19:02:02 +01:00
|
|
|
" websocket(request) { methodNotAllowed_() }\n"
|
feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
"\n"
|
|
|
|
|
" methodNotAllowed_() {\n"
|
|
|
|
|
" var r = Response.new()\n"
|
|
|
|
|
" r.status = 405\n"
|
|
|
|
|
" r.body = \"Method Not Allowed\"\n"
|
|
|
|
|
" return r\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
feat: add buildmanual target, faker demo, subprocess examples, and contributing sidebar to manual
Add Makefile buildmanual target for manual generation and include faker_demo.wren, subprocess_async_demo.wren, subprocess_concurrent_demo.wren, and subprocess_fiber_demo.wren example scripts. Update manual sidebar across api pages to link new web-server tutorial and contributing section with module architecture, pure-wren/c-backed modules, foreign classes, async patterns, testing, and documentation pages.
2026-01-25 19:02:02 +01:00
|
|
|
" isWebSocketRequest_(request) {\n"
|
|
|
|
|
" var upgrade = request.header(\"Upgrade\")\n"
|
|
|
|
|
" var connection = request.header(\"Connection\")\n"
|
|
|
|
|
" if (upgrade == null || connection == null) return false\n"
|
|
|
|
|
" return Str.toLower(upgrade) == \"websocket\" && Str.toLower(connection).contains(\"upgrade\")\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
" dispatch(request) {\n"
|
feat: add buildmanual target, faker demo, subprocess examples, and contributing sidebar to manual
Add Makefile buildmanual target for manual generation and include faker_demo.wren, subprocess_async_demo.wren, subprocess_concurrent_demo.wren, and subprocess_fiber_demo.wren example scripts. Update manual sidebar across api pages to link new web-server tutorial and contributing section with module architecture, pure-wren/c-backed modules, foreign classes, async patterns, testing, and documentation pages.
2026-01-25 19:02:02 +01:00
|
|
|
" if (isWebSocketRequest_(request)) return websocket(request)\n"
|
feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
" if (request.method == \"GET\") return get(request)\n"
|
|
|
|
|
" if (request.method == \"POST\") return post(request)\n"
|
|
|
|
|
" if (request.method == \"PUT\") return put(request)\n"
|
|
|
|
|
" if (request.method == \"DELETE\") return delete(request)\n"
|
|
|
|
|
" if (request.method == \"PATCH\") return patch(request)\n"
|
|
|
|
|
" return methodNotAllowed_()\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"}\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"class Application {\n"
|
|
|
|
|
" construct new() {\n"
|
|
|
|
|
" _router = Router.new()\n"
|
|
|
|
|
" _staticPrefixes = []\n"
|
|
|
|
|
" _sessionStore = SessionStore.new()\n"
|
|
|
|
|
" _sessionCookieName = \"session_id\"\n"
|
|
|
|
|
" _middleware = []\n"
|
|
|
|
|
" _views = {}\n"
|
|
|
|
|
" _wsHandlers = {}\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" router { _router }\n"
|
|
|
|
|
" sessionStore { _sessionStore }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" addRoute(method, path, handler) {\n"
|
|
|
|
|
" _router.addRoute(method, path, handler)\n"
|
|
|
|
|
" return this\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" get(path, handler) { addRoute(\"GET\", path, handler) }\n"
|
|
|
|
|
" post(path, handler) { addRoute(\"POST\", path, handler) }\n"
|
|
|
|
|
" put(path, handler) { addRoute(\"PUT\", path, handler) }\n"
|
|
|
|
|
" delete(path, handler) { addRoute(\"DELETE\", path, handler) }\n"
|
|
|
|
|
" patch(path, handler) { addRoute(\"PATCH\", path, handler) }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" addView(path, viewClass) {\n"
|
|
|
|
|
" _views[path] = viewClass\n"
|
|
|
|
|
" _router.addRoute(\"*\", path, Fn.new { |req| handleView_(viewClass, req) })\n"
|
|
|
|
|
" return this\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" handleView_(viewClass, request) {\n"
|
|
|
|
|
" var view = viewClass.new()\n"
|
|
|
|
|
" return view.dispatch(request)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" static_(prefix, directory) {\n"
|
|
|
|
|
" _staticPrefixes.add({\"prefix\": prefix, \"directory\": directory})\n"
|
|
|
|
|
" return this\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" websocket(path, handler) {\n"
|
|
|
|
|
" _wsHandlers[path] = handler\n"
|
|
|
|
|
" return this\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" use(middleware) {\n"
|
|
|
|
|
" _middleware.add(middleware)\n"
|
|
|
|
|
" return this\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" run(host, port) {\n"
|
|
|
|
|
" var server = Server.bind(host, port)\n"
|
|
|
|
|
" System.print(\"Server running on http://%(host):%(port)\")\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" while (true) {\n"
|
|
|
|
|
" var socket = server.accept()\n"
|
|
|
|
|
" if (socket == null) continue\n"
|
|
|
|
|
" handleConnection_(socket)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" handleConnection_(socket) {\n"
|
2026-01-26 10:21:03 +01:00
|
|
|
" var chunks = []\n"
|
|
|
|
|
" var totalBytes = 0\n"
|
feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
" var contentLength = 0\n"
|
|
|
|
|
" var headersComplete = false\n"
|
2026-01-26 10:21:03 +01:00
|
|
|
" var headerBytes = 0\n"
|
|
|
|
|
" var requestData = null\n"
|
feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
"\n"
|
|
|
|
|
" while (true) {\n"
|
|
|
|
|
" var chunk = socket.read()\n"
|
2026-01-25 10:50:20 +01:00
|
|
|
" if (chunk == null || chunk.bytes.count == 0) {\n"
|
feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
" socket.close()\n"
|
|
|
|
|
" return\n"
|
|
|
|
|
" }\n"
|
2026-01-26 10:21:03 +01:00
|
|
|
" chunks.add(chunk)\n"
|
|
|
|
|
" totalBytes = totalBytes + chunk.bytes.count\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" if (!headersComplete) {\n"
|
|
|
|
|
" requestData = chunks.join(\"\")\n"
|
|
|
|
|
" if (requestData.contains(\"\\r\\n\\r\\n\")) {\n"
|
|
|
|
|
" headersComplete = true\n"
|
|
|
|
|
" var headerEnd = requestData.indexOf(\"\\r\\n\\r\\n\")\n"
|
|
|
|
|
" headerBytes = headerEnd + 4\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var headerPart = requestData[0...headerEnd]\n"
|
|
|
|
|
" var headers = parseHeaders_(headerPart)\n"
|
|
|
|
|
" if (headers.containsKey(\"Content-Length\")) {\n"
|
|
|
|
|
" contentLength = Num.fromString(headers[\"Content-Length\"])\n"
|
|
|
|
|
" } else {\n"
|
|
|
|
|
" break\n"
|
|
|
|
|
" }\n"
|
feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
"\n"
|
2026-01-26 10:21:03 +01:00
|
|
|
" if (totalBytes - headerBytes >= contentLength) break\n"
|
feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
" }\n"
|
2026-01-26 10:21:03 +01:00
|
|
|
" } else {\n"
|
|
|
|
|
" if (totalBytes - headerBytes >= contentLength) break\n"
|
feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
" }\n"
|
2026-01-26 10:21:03 +01:00
|
|
|
" }\n"
|
feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
"\n"
|
2026-01-26 10:21:03 +01:00
|
|
|
" if (requestData == null || chunks.count > 1) {\n"
|
|
|
|
|
" requestData = chunks.join(\"\")\n"
|
feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var headerEnd = requestData.indexOf(\"\\r\\n\\r\\n\")\n"
|
|
|
|
|
" var headerPart = requestData[0...headerEnd]\n"
|
2026-01-26 10:21:03 +01:00
|
|
|
" var body = requestData[headerEnd + 4..-1]\n"
|
feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
" if (contentLength > 0 && body.bytes.count > contentLength) {\n"
|
|
|
|
|
" body = body[0...contentLength]\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var lines = headerPart.split(\"\\r\\n\")\n"
|
|
|
|
|
" if (lines.count == 0) {\n"
|
|
|
|
|
" socket.close()\n"
|
|
|
|
|
" return\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var requestLine = lines[0].split(\" \")\n"
|
|
|
|
|
" if (requestLine.count < 2) {\n"
|
|
|
|
|
" socket.close()\n"
|
|
|
|
|
" return\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var method = requestLine[0]\n"
|
|
|
|
|
" var fullPath = requestLine[1]\n"
|
|
|
|
|
" var path = fullPath\n"
|
|
|
|
|
" var query = {}\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var queryStart = fullPath.indexOf(\"?\")\n"
|
|
|
|
|
" if (queryStart >= 0) {\n"
|
|
|
|
|
" path = fullPath[0...queryStart]\n"
|
|
|
|
|
" query = Html.decodeParams(fullPath[queryStart + 1..-1])\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var headers = {}\n"
|
|
|
|
|
" for (i in 1...lines.count) {\n"
|
|
|
|
|
" var colonPos = lines[i].indexOf(\":\")\n"
|
|
|
|
|
" if (colonPos > 0) {\n"
|
|
|
|
|
" var name = lines[i][0...colonPos].trim()\n"
|
|
|
|
|
" var value = lines[i][colonPos + 1..-1].trim()\n"
|
|
|
|
|
" headers[name] = value\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var request = Request.new_(method, path, query, headers, body, {}, socket)\n"
|
|
|
|
|
" loadSession_(request)\n"
|
|
|
|
|
"\n"
|
feat: add buildmanual target, faker demo, subprocess examples, and contributing sidebar to manual
Add Makefile buildmanual target for manual generation and include faker_demo.wren, subprocess_async_demo.wren, subprocess_concurrent_demo.wren, and subprocess_fiber_demo.wren example scripts. Update manual sidebar across api pages to link new web-server tutorial and contributing section with module architecture, pure-wren/c-backed modules, foreign classes, async patterns, testing, and documentation pages.
2026-01-25 19:02:02 +01:00
|
|
|
" if (isWebSocketRequest_(headers)) {\n"
|
|
|
|
|
" if (_wsHandlers.containsKey(path)) {\n"
|
|
|
|
|
" var handler = _wsHandlers[path]\n"
|
|
|
|
|
" var result = handler.call(request)\n"
|
|
|
|
|
" return\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
" var response = null\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" for (sp in _staticPrefixes) {\n"
|
|
|
|
|
" if (path.startsWith(sp[\"prefix\"])) {\n"
|
|
|
|
|
" var filePath = sp[\"directory\"] + path[sp[\"prefix\"].count..-1]\n"
|
|
|
|
|
" response = Response.file(filePath)\n"
|
|
|
|
|
" break\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" if (response == null) {\n"
|
|
|
|
|
" var route = _router.match(method, path)\n"
|
|
|
|
|
" if (route != null) {\n"
|
|
|
|
|
" request = Request.new_(method, path, query, headers, body, route[\"params\"], socket)\n"
|
|
|
|
|
" loadSession_(request)\n"
|
|
|
|
|
" for (mw in _middleware) {\n"
|
|
|
|
|
" var result = mw.call(request)\n"
|
|
|
|
|
" if (result != null) {\n"
|
|
|
|
|
" response = result\n"
|
|
|
|
|
" break\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" if (response == null) {\n"
|
|
|
|
|
" response = route[\"handler\"].call(request)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" } else {\n"
|
|
|
|
|
" response = Response.new()\n"
|
|
|
|
|
" response.status = 404\n"
|
|
|
|
|
" response.body = \"Not Found\"\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
feat: add buildmanual target, faker demo, subprocess examples, and contributing sidebar to manual
Add Makefile buildmanual target for manual generation and include faker_demo.wren, subprocess_async_demo.wren, subprocess_concurrent_demo.wren, and subprocess_fiber_demo.wren example scripts. Update manual sidebar across api pages to link new web-server tutorial and contributing section with module architecture, pure-wren/c-backed modules, foreign classes, async patterns, testing, and documentation pages.
2026-01-25 19:02:02 +01:00
|
|
|
" if (response is WebSocketResponse) {\n"
|
|
|
|
|
" return\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
" saveSession_(request, response)\n"
|
|
|
|
|
" socket.write(response.build())\n"
|
|
|
|
|
" socket.close()\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
feat: add buildmanual target, faker demo, subprocess examples, and contributing sidebar to manual
Add Makefile buildmanual target for manual generation and include faker_demo.wren, subprocess_async_demo.wren, subprocess_concurrent_demo.wren, and subprocess_fiber_demo.wren example scripts. Update manual sidebar across api pages to link new web-server tutorial and contributing section with module architecture, pure-wren/c-backed modules, foreign classes, async patterns, testing, and documentation pages.
2026-01-25 19:02:02 +01:00
|
|
|
" isWebSocketRequest_(headers) {\n"
|
|
|
|
|
" var upgrade = null\n"
|
|
|
|
|
" var connection = null\n"
|
|
|
|
|
" for (key in headers.keys) {\n"
|
|
|
|
|
" var lower = Str.toLower(key)\n"
|
|
|
|
|
" if (lower == \"upgrade\") upgrade = headers[key]\n"
|
|
|
|
|
" if (lower == \"connection\") connection = headers[key]\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" if (upgrade == null || connection == null) return false\n"
|
|
|
|
|
" return Str.toLower(upgrade) == \"websocket\" && Str.toLower(connection).contains(\"upgrade\")\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
" loadSession_(request) {\n"
|
|
|
|
|
" var sessionId = request.cookies[_sessionCookieName]\n"
|
|
|
|
|
" if (sessionId != null) {\n"
|
|
|
|
|
" var data = _sessionStore.get(sessionId)\n"
|
|
|
|
|
" if (data != null) {\n"
|
|
|
|
|
" request.session = Session.new_(sessionId, data)\n"
|
|
|
|
|
" return\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" request.session = _sessionStore.create()\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" saveSession_(request, response) {\n"
|
|
|
|
|
" if (request.session != null) {\n"
|
|
|
|
|
" _sessionStore.save(request.session)\n"
|
|
|
|
|
" response.cookie(_sessionCookieName, request.session.id, {\"path\": \"/\", \"httpOnly\": true})\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" parseHeaders_(headerPart) {\n"
|
|
|
|
|
" var headers = {}\n"
|
|
|
|
|
" var lines = headerPart.split(\"\\r\\n\")\n"
|
|
|
|
|
" for (i in 1...lines.count) {\n"
|
|
|
|
|
" var colonPos = lines[i].indexOf(\":\")\n"
|
|
|
|
|
" if (colonPos > 0) {\n"
|
|
|
|
|
" var name = lines[i][0...colonPos].trim()\n"
|
|
|
|
|
" var value = lines[i][colonPos + 1..-1].trim()\n"
|
|
|
|
|
" headers[name] = value\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" return headers\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"}\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"class Client {\n"
|
|
|
|
|
" static get(url) { get(url, {}) }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" static get(url, options) {\n"
|
|
|
|
|
" return request_(\"GET\", url, options)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" static post(url) { post(url, {}) }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" static post(url, options) {\n"
|
|
|
|
|
" return request_(\"POST\", url, options)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" static put(url, options) {\n"
|
|
|
|
|
" return request_(\"PUT\", url, options)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" static delete(url, options) {\n"
|
|
|
|
|
" return request_(\"DELETE\", url, options)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" static request_(method, url, options) {\n"
|
|
|
|
|
" var parsed = parseUrl_(url)\n"
|
|
|
|
|
" var host = parsed[\"host\"]\n"
|
|
|
|
|
" var port = parsed[\"port\"]\n"
|
|
|
|
|
" var path = parsed[\"path\"]\n"
|
|
|
|
|
" var isSecure = parsed[\"scheme\"] == \"https\"\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var addresses = Dns.lookup(host, 4)\n"
|
|
|
|
|
" if (addresses.count == 0) {\n"
|
|
|
|
|
" Fiber.abort(\"Could not resolve host: \" + host)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var socket\n"
|
|
|
|
|
" if (isSecure) {\n"
|
|
|
|
|
" socket = TlsSocket.connect(addresses[0], port, host)\n"
|
|
|
|
|
" } else {\n"
|
|
|
|
|
" socket = Socket.connect(addresses[0], port)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var body = options.containsKey(\"body\") ? options[\"body\"] : \"\"\n"
|
|
|
|
|
" if (options.containsKey(\"json\")) {\n"
|
|
|
|
|
" body = Json.stringify(options[\"json\"])\n"
|
|
|
|
|
" if (!options.containsKey(\"headers\")) options[\"headers\"] = {}\n"
|
|
|
|
|
" options[\"headers\"][\"Content-Type\"] = \"application/json\"\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var request = method + \" \" + path + \" HTTP/1.1\\r\\n\"\n"
|
|
|
|
|
" request = request + \"Host: \" + host + \"\\r\\n\"\n"
|
|
|
|
|
" request = request + \"Connection: close\\r\\n\"\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" if (options.containsKey(\"headers\")) {\n"
|
|
|
|
|
" for (entry in options[\"headers\"]) {\n"
|
|
|
|
|
" request = request + entry.key + \": \" + entry.value + \"\\r\\n\"\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" if (body.count > 0) {\n"
|
|
|
|
|
" request = request + \"Content-Length: \" + body.bytes.count.toString + \"\\r\\n\"\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" request = request + \"\\r\\n\" + body\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" socket.write(request)\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var response = \"\"\n"
|
|
|
|
|
" while (true) {\n"
|
|
|
|
|
" var chunk = socket.read()\n"
|
2026-01-25 10:50:20 +01:00
|
|
|
" if (chunk == null || chunk.bytes.count == 0) break\n"
|
feat: add example scripts for argparse, bytes, dataset, html, markdown, uuid, wdantic, and web chat modules
Add comprehensive demo scripts showcasing the usage of multiple Wren modules including argument parsing, byte manipulation, in-memory dataset operations, HTML encoding/slugification, markdown-to-HTML conversion, UUID generation/validation, schema validation with wdantic, and a full web chat application with REST endpoints.
2026-01-24 23:40:22 +01:00
|
|
|
" response = response + chunk\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" socket.close()\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" return parseResponse_(response)\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" static parseUrl_(url) {\n"
|
|
|
|
|
" var result = {\"scheme\": \"http\", \"host\": \"\", \"port\": 80, \"path\": \"/\"}\n"
|
|
|
|
|
" var rest = url\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var schemeEnd = rest.indexOf(\"://\")\n"
|
|
|
|
|
" if (schemeEnd >= 0) {\n"
|
|
|
|
|
" result[\"scheme\"] = rest[0...schemeEnd]\n"
|
|
|
|
|
" rest = rest[schemeEnd + 3..-1]\n"
|
|
|
|
|
" if (result[\"scheme\"] == \"https\") result[\"port\"] = 443\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var pathStart = rest.indexOf(\"/\")\n"
|
|
|
|
|
" var hostPart = pathStart >= 0 ? rest[0...pathStart] : rest\n"
|
|
|
|
|
" result[\"path\"] = pathStart >= 0 ? rest[pathStart..-1] : \"/\"\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var portStart = hostPart.indexOf(\":\")\n"
|
|
|
|
|
" if (portStart >= 0) {\n"
|
|
|
|
|
" result[\"host\"] = hostPart[0...portStart]\n"
|
|
|
|
|
" result[\"port\"] = Num.fromString(hostPart[portStart + 1..-1])\n"
|
|
|
|
|
" } else {\n"
|
|
|
|
|
" result[\"host\"] = hostPart\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" return result\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" static parseResponse_(response) {\n"
|
|
|
|
|
" var headerEnd = response.indexOf(\"\\r\\n\\r\\n\")\n"
|
|
|
|
|
" if (headerEnd < 0) return {\"status\": 0, \"headers\": {}, \"body\": response}\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var headerPart = response[0...headerEnd]\n"
|
|
|
|
|
" var body = response[headerEnd + 4..-1]\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var lines = headerPart.split(\"\\r\\n\")\n"
|
|
|
|
|
" var statusLine = lines[0].split(\" \")\n"
|
|
|
|
|
" var status = statusLine.count > 1 ? Num.fromString(statusLine[1]) : 0\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" var headers = {}\n"
|
|
|
|
|
" for (i in 1...lines.count) {\n"
|
|
|
|
|
" var colonPos = lines[i].indexOf(\":\")\n"
|
|
|
|
|
" if (colonPos > 0) {\n"
|
|
|
|
|
" var name = lines[i][0...colonPos].trim()\n"
|
|
|
|
|
" var value = lines[i][colonPos + 1..-1].trim()\n"
|
|
|
|
|
" headers[name] = value\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" return {\"status\": status, \"headers\": headers, \"body\": body}\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"}\n";
|