diff --git a/docs/src/en/guides/http-best-practices.md b/docs/src/en/guides/http-best-practices.md index 581b6bca8..14a68108d 100644 --- a/docs/src/en/guides/http-best-practices.md +++ b/docs/src/en/guides/http-best-practices.md @@ -724,6 +724,7 @@ Use `pcall` to catch exceptions, avoiding service crashes from individual reques local silly = require "silly" local http = require "silly.net.http" local logger = require "silly.logger" +local trace = require "silly.trace" local json = require "silly.encoding.json" -- Global error handler @@ -1033,6 +1034,7 @@ Implement a middleware system for cross-cutting concerns (logging, authenticatio local silly = require "silly" local http = require "silly.net.http" local logger = require "silly.logger" +local trace = require "silly.trace" -- Middleware chain local function chain(middlewares, final_handler) @@ -1142,6 +1144,7 @@ Record detailed information for each request for audit and troubleshooting. local silly = require "silly" local http = require "silly.net.http" local logger = require "silly.logger" +local trace = require "silly.trace" local json = require "silly.encoding.json" local function access_log(stream, status, duration, response_size) @@ -1294,6 +1297,7 @@ Use Trace ID to track request flow through the system. local silly = require "silly" local http = require "silly.net.http" local logger = require "silly.logger" +local trace = require "silly.trace" http.listen { addr = ":8080", @@ -1301,10 +1305,10 @@ http.listen { -- Get or generate Trace ID from request header local trace_id = tonumber(stream.header["x-trace-id"]) if trace_id then - silly.traceset(trace_id) + trace.attach(trace_id) else - silly.tracespawn() - trace_id = silly.tracepropagate() -- For returning to client + trace.spawn() + trace_id = trace.propagate() -- For returning to client end logger.info("Request started:", stream.method, stream.path) @@ -1340,7 +1344,7 @@ http.listen { -- Automatically pass current Trace ID when calling external services function call_external_service() - local trace_id = silly.tracepropagate() + local trace_id = trace.propagate() local response = http.get("http://other-service/api", { ["x-trace-id"] = tostring(trace_id), }) @@ -1654,6 +1658,7 @@ local silly = require "silly" local http = require "silly.net.http" local json = require "silly.encoding.json" local logger = require "silly.logger" +local trace = require "silly.trace" local prometheus = require "silly.metrics.prometheus" -- Configuration @@ -1723,9 +1728,9 @@ local function logging_middleware(stream, next) -- Set or create trace ID local trace_id = tonumber(stream.header["x-trace-id"]) if trace_id then - silly.traceset(trace_id) + trace.attach(trace_id) else - silly.tracespawn() + trace.spawn() end logger.info("Request:", stream.method, stream.path, "from", stream.remoteaddr) diff --git a/docs/src/en/guides/logging-monitoring.md b/docs/src/en/guides/logging-monitoring.md index f4d6b1d91..8f6499159 100644 --- a/docs/src/en/guides/logging-monitoring.md +++ b/docs/src/en/guides/logging-monitoring.md @@ -27,7 +27,7 @@ The Silly framework provides built-in support for all three aspects: - `silly.logger`: Hierarchical logging system with log rotation support - `silly.metrics.prometheus`: Prometheus metrics collection and export -- `silly.tracespawn/traceset`: Distributed trace ID generation and propagation +- `trace.spawn/trace.attach`: Distributed trace ID generation and propagation ## Logging System @@ -37,6 +37,7 @@ The Silly framework provides built-in support for all three aspects: ```lua local logger = require "silly.logger" +local trace = require "silly.trace" -- Set log level (only output INFO and above) logger.setlevel(logger.INFO) @@ -89,6 +90,7 @@ Choose appropriate log levels based on different scenarios: ```lua local logger = require "silly.logger" +local trace = require "silly.trace" -- Production environment: use INFO level logger.setlevel(logger.INFO) @@ -110,6 +112,7 @@ Use formatted log functions (`*f` series) to improve log readability: ```lua local logger = require "silly.logger" +local trace = require "silly.trace" -- Use string.format style logger.infof("User [%s] completed %d operations in %d seconds", @@ -129,6 +132,7 @@ For easier log analysis, use structured log format: ```lua local logger = require "silly.logger" +local trace = require "silly.trace" local json = require "silly.encoding.json" -- Define log helper function @@ -194,6 +198,7 @@ In production environments, dynamically adjust log levels via signals to avoid s ```lua local logger = require "silly.logger" +local trace = require "silly.trace" local signal = require "silly.signal" -- Initialize to INFO level @@ -367,6 +372,7 @@ An HTTP service example with complete monitoring: local silly = require "silly" local http = require "silly.net.http" local logger = require "silly.logger" +local trace = require "silly.trace" local prometheus = require "silly.metrics.prometheus" -- Define metrics @@ -500,6 +506,7 @@ Silly provides a distributed trace ID system where each coroutine has an indepen local silly = require "silly" local task = require "silly.task" local logger = require "silly.logger" +local trace = require "silly.trace" task.fork(function() -- Create new trace ID (if current coroutine doesn't have one) @@ -518,6 +525,7 @@ In microservice architecture, trace IDs need to be propagated to downstream serv local silly = require "silly" local http = require "silly.net.http" local logger = require "silly.logger" +local trace = require "silly.trace" local httpc = http.newclient() -- Service A: Initiate HTTP request local function call_service_b() @@ -525,17 +533,22 @@ local function call_service_b() local trace_id = trace.propagate() logger.info("Calling service B") - -- Pass trace ID via HTTP Header - local response = httpc:request { - method = "POST", - url = "http://service-b:8080/api/process", - headers = { + -- Pass trace ID via HTTP header + local stream, err = httpc:request( + "POST", + "http://service-b:8080/api/process", + { ["X-Trace-Id"] = tostring(trace_id), - }, - body = '{"data": "value"}', - } + ["content-type"] = "application/json", + } + ) + if not stream then + return nil, err + end - return response + stream:closewrite('{"data": "value"}') + local body, status = stream:readall() + return {status = status, body = body} end -- Service B: Receive request and use incoming trace ID @@ -543,7 +556,7 @@ local server = http.listen { addr = "0.0.0.0:8080", handler = function(stream) -- Extract and set trace ID - local trace_id = tonumber(stream.headers["x-trace-id"]) + local trace_id = tonumber(stream.header["x-trace-id"]) if trace_id then trace.attach(trace_id) else @@ -564,6 +577,7 @@ When making RPC calls using `silly.net.cluster`, trace IDs are automatically pro ```lua local cluster = require "silly.net.cluster" local logger = require "silly.logger" +local trace = require "silly.trace" -- Create cluster service cluster.serve { @@ -593,6 +607,7 @@ Integrate trace ID into logs to achieve complete request tracking: ```lua local silly = require "silly" local logger = require "silly.logger" +local trace = require "silly.trace" local json = require "silly.encoding.json" -- Structured log helper function @@ -758,6 +773,7 @@ You can also implement simple alert logic within the application: local silly = require "silly" local time = require "silly.time" local logger = require "silly.logger" +local trace = require "silly.trace" local prometheus = require "silly.metrics.prometheus" -- Define alert thresholds @@ -854,6 +870,7 @@ A production-grade HTTP service example with complete logging, monitoring, and t local silly = require "silly" local http = require "silly.net.http" local logger = require "silly.logger" +local trace = require "silly.trace" local signal = require "silly.signal" local time = require "silly.time" local prometheus = require "silly.metrics.prometheus" @@ -971,16 +988,16 @@ local function handle_request(stream) http_requests_in_flight:inc() -- Get or create trace ID - local trace_id = tonumber(stream.headers["x-trace-id"]) + local trace_id = tonumber(stream.header["x-trace-id"]) if trace_id then - silly.traceset(trace_id) + trace.attach(trace_id) else - silly.tracespawn() - trace_id = silly.tracepropagate() -- Get current trace ID for response header + trace.spawn() + trace_id = trace.propagate() -- Get current trace ID for response header end -- Record request size - local req_size = tonumber(stream.headers["content-length"]) or 0 + local req_size = tonumber(stream.header["content-length"]) or 0 http_request_size:observe(req_size) -- Route handling @@ -1044,7 +1061,7 @@ local server = http.listen { handler = function(stream) local ok, err = silly.pcall(handle_request, stream) if not ok then - silly.tracespawn() -- Create new trace ID + trace.spawn() -- Create new trace ID logger.error("Request handling failed:", err) stream:respond(500, {["content-type"] = "application/json"}) diff --git a/docs/src/en/guides/tls-configuration.md b/docs/src/en/guides/tls-configuration.md index df3bd4514..fbd8cbefb 100644 --- a/docs/src/en/guides/tls-configuration.md +++ b/docs/src/en/guides/tls-configuration.md @@ -573,7 +573,7 @@ signal.register("SIGUSR1", function() end -- Hot reload certificates - local success, reload_err = tls.reload(listenfd, { + local success, reload_err = listenfd:reload({ certs = {{cert = cert_pem, key = key_pem}} }) @@ -779,7 +779,7 @@ local function get_connection(host, port) -- Create new connection local ip = dns.lookup(host, dns.A) - conn = tls.connect(ip .. ":" .. port, nil, host, {"http/1.1"}) + conn = tls.connect(ip .. ":" .. port, {hostname = host, alpnprotos = {"http/1.1"}}) if conn then connection_pool[key] = conn diff --git a/docs/src/en/reference/metrics/collector.md b/docs/src/en/reference/metrics/collector.md index b7da57273..6db6ec014 100644 --- a/docs/src/en/reference/metrics/collector.md +++ b/docs/src/en/reference/metrics/collector.md @@ -783,10 +783,10 @@ local server = http.listen { ["content-type"] = "text/plain; version=0.0.4; charset=utf-8", ["content-length"] = #metrics_data, }) - stream:close(metrics_data) + stream:closewrite(metrics_data) else stream:respond(404) - stream:close("Not Found") + stream:closewrite("Not Found") end end } diff --git a/docs/src/en/reference/metrics/counter.md b/docs/src/en/reference/metrics/counter.md index 01229a3d3..0e7015600 100644 --- a/docs/src/en/reference/metrics/counter.md +++ b/docs/src/en/reference/metrics/counter.md @@ -533,10 +533,10 @@ local server = http.listen { ["content-type"] = "text/plain; version=0.0.4; charset=utf-8", ["content-length"] = #metrics_data, }) - stream:close(metrics_data) + stream:closewrite(metrics_data) else stream:respond(404) - stream:close("Not Found") + stream:closewrite("Not Found") end end } diff --git a/docs/src/en/reference/metrics/histogram.md b/docs/src/en/reference/metrics/histogram.md index a7eb311ee..b182c64a8 100644 --- a/docs/src/en/reference/metrics/histogram.md +++ b/docs/src/en/reference/metrics/histogram.md @@ -671,7 +671,7 @@ task.fork(function() ["content-type"] = "application/json", ["content-length"] = #response_body }) - stream:close(response_body) + stream:closewrite(response_body) -- Record metrics local duration = (silly.time.now() - start_time) / 1000 @@ -690,10 +690,10 @@ task.fork(function() ["content-type"] = "text/plain; version=0.0.4; charset=utf-8", ["content-length"] = #metrics }) - stream:close(metrics) + stream:closewrite(metrics) else stream:respond(404, {["content-type"] = "text/plain"}) - stream:close("Not Found") + stream:closewrite("Not Found") end end } diff --git a/docs/src/en/reference/metrics/prometheus.md b/docs/src/en/reference/metrics/prometheus.md index e81728ba1..d36c00a85 100644 --- a/docs/src/en/reference/metrics/prometheus.md +++ b/docs/src/en/reference/metrics/prometheus.md @@ -359,11 +359,11 @@ local server = http.listen { ["content-type"] = "text/plain; version=0.0.4; charset=utf-8", ["content-length"] = #metrics, }) - stream:close(metrics) + stream:closewrite(metrics) else -- Business logic stream:respond(200, {["content-type"] = "text/plain"}) - stream:close("Hello World") + stream:closewrite("Hello World") -- Record metrics local duration = os.clock() - start @@ -428,19 +428,19 @@ local server = http.listen { stream:respond(200, { ["content-type"] = "text/plain; version=0.0.4; charset=utf-8", }) - stream:close(metrics) + stream:closewrite(metrics) requests_in_flight:dec() return end -- Record request size - local req_size = stream.headers["content-length"] or 0 + local req_size = stream.header["content-length"] or 0 request_size:observe(tonumber(req_size)) -- Business logic local response_data = "Response data" stream:respond(200, {["content-type"] = "text/plain"}) - stream:close(response_data) + stream:closewrite(response_data) -- Record response metrics local duration = os.clock() - start @@ -993,10 +993,10 @@ local metrics_server = http.listen { stream:respond(200, { ["content-type"] = "text/plain; version=0.0.4", }) - stream:close(metrics) + stream:closewrite(metrics) else stream:respond(404) - stream:close("Not Found") + stream:closewrite("Not Found") end end } diff --git a/docs/src/en/reference/net/cluster.md b/docs/src/en/reference/net/cluster.md index e09ba27b6..6c80297d4 100644 --- a/docs/src/en/reference/net/cluster.md +++ b/docs/src/en/reference/net/cluster.md @@ -1077,7 +1077,7 @@ end) Cluster automatically propagates trace IDs: ```lua --- Client initiates request, automatically carries current trace ID using silly.tracepropagate() +-- Client initiates request, automatically carries current trace ID using trace.propagate() local resp = cluster.call(peer, "ping", data) -- Server processes, trace ID is automatically set by cluster diff --git a/docs/src/en/reference/net/grpc.md b/docs/src/en/reference/net/grpc.md index 529194956..c634cacd2 100644 --- a/docs/src/en/reference/net/grpc.md +++ b/docs/src/en/reference/net/grpc.md @@ -265,20 +265,16 @@ end) ### grpc.newclient(conf) -Create a gRPC client. +Create a gRPC connection pool. - **Parameters**: - `conf`: `table` - Client configuration table - - `service`: `string` (required) - Service name (corresponds to service name in proto) - - `endpoints`: `string[]` (required) - List of gRPC server addresses, format `"host:port"` - - `proto`: `table` (required) - Proto definition loaded by protoc + - `targets`: `string[]` (required) - List of gRPC targets, format `"host:port"` (or `dns://host:port`) - `tls`: `boolean|nil` (optional) - Whether to use TLS, default false - - `timeout`: `number|nil` (optional) - Request timeout (milliseconds) - **Returns**: - - Success: `client` - Client object + - Success: `conn` - gRPC connection object - Failure: `nil, string` - nil and error message -- **Note**: Client object dynamically generates methods with names corresponding to RPC methods defined in proto -- **Load Balancing**: Uses round-robin strategy with multiple endpoints +- **Load Balancing**: Uses round-robin strategy with multiple targets - **Example**: ```lua validate @@ -306,19 +302,23 @@ task.fork(function() } ]], "hello.proto") - local client, err = grpc.newclient { - service = "Greeter", - endpoints = {"127.0.0.1:50051", "127.0.0.1:50052"}, - proto = p.loaded["hello.proto"], - timeout = 5000, + local client_conn, err = grpc.newclient { + targets = {"127.0.0.1:50051", "127.0.0.1:50052"}, } + if not client_conn then + print("Failed to create client connection:", err) + return + end + + local client + client, err = grpc.newservice(client_conn, p.loaded["hello.proto"], "Greeter") if not client then - print("Failed to create client:", err) + print("Failed to create service client:", err) return end - -- Client object automatically generates SayHello method + -- Service object dynamically provides methods from proto local response, err = client.SayHello({name = "World"}) if response then print("Response:", response.message) @@ -328,7 +328,19 @@ task.fork(function() end) ``` -### client.MethodName(request) +### grpc.newservice(conn, proto, service_name) + +Create a typed service client from a gRPC connection. + +- **Parameters**: + - `conn`: `silly.net.grpc.client.conn` - Connection returned by `grpc.newclient` + - `proto`: `table` - Proto definition loaded by protoc + - `service_name`: `string` - Service name defined in proto +- **Returns**: + - Success: `service` - Service client object + - Failure: `nil, string` - nil and error message + +### service.MethodName(request) Call an RPC method (Unary RPC). @@ -367,11 +379,10 @@ task.fork(function() } ]], "math.proto") - local client = grpc.newclient { - service = "MathService", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["math.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["math.proto"], "MathService")) -- Call Multiply method local result, err = client.Multiply({x = 6, y = 7}) @@ -383,7 +394,7 @@ task.fork(function() end) ``` -### client.StreamMethod() +### service.StreamMethod() Create a streaming RPC connection (Streaming RPC). @@ -416,11 +427,10 @@ task.fork(function() } ]], "stream.proto") - local client = grpc.newclient { - service = "StreamService", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["stream.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["stream.proto"], "StreamService")) -- Create bidirectional stream local stream, err = client.BiStream() @@ -486,11 +496,10 @@ task.fork(function() } ]], "file.proto") - local client = grpc.newclient { - service = "FileService", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["file.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["file.proto"], "FileService")) local stream = client.Upload() @@ -548,11 +557,10 @@ task.fork(function() } ]], "log.proto") - local client = grpc.newclient { - service = "LogService", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["log.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["log.proto"], "LogService")) local stream = client.StreamLogs() @@ -603,11 +611,10 @@ task.fork(function() } ]], "chat.proto") - local client = grpc.newclient { - service = "ChatService", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["chat.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["chat.proto"], "ChatService")) local stream = client.Chat() @@ -680,12 +687,10 @@ task.fork(function() print("Greeter server started on 127.0.0.1:50051") -- Create client - local client = grpc.newclient { - service = "Greeter", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["greeter.proto"], - timeout = 5000, + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["greeter.proto"], "Greeter")) -- Call RPC local resp1 = client.SayHello({name = "Alice"}) @@ -747,12 +752,10 @@ task.fork(function() } -- Client side - local client = grpc.newclient { - service = "Calculator", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["calculator.proto"], - timeout = 5000, + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["calculator.proto"], "Calculator")) -- Normal call local result, err = client.Divide({ @@ -844,14 +847,13 @@ task.fork(function() print("Started two servers on ports 50051 and 50052") -- Client connects to multiple endpoints - local client = grpc.newclient { - service = "Counter", - endpoints = { + local client_conn = grpc.newclient { + targets = { "127.0.0.1:50051", "127.0.0.1:50052" }, - proto = p.loaded["counter.proto"], } + local client = assert(grpc.newservice(client_conn, p.loaded["counter.proto"], "Counter")) -- Requests will be distributed to both servers for i = 1, 4 do @@ -910,15 +912,13 @@ task.fork(function() } -- Client: set short timeout - local client = grpc.newclient { - service = "SlowService", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["slow.proto"], - timeout = 1000, -- 1 second timeout + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["slow.proto"], "SlowService")) -- Fast request (should succeed) - local result1, err1 = client.SlowMethod({delay_ms = 100}) + local result1, err1 = client.SlowMethod({delay_ms = 100}, 1000) if result1 then print("Fast request:", result1.result) else @@ -926,7 +926,7 @@ task.fork(function() end -- Slow request (should timeout) - local result2, err2 = client.SlowMethod({delay_ms = 2000}) + local result2, err2 = client.SlowMethod({delay_ms = 2000}, 1000) if result2 then print("Slow request:", result2.result) else @@ -1043,12 +1043,11 @@ v1HSCliKZXW8cusnBRD2IOyxuIUV/qiMfARylMvlLBccgJR8+olH9f/yF2EFWhoy print("Secure gRPC server started") -- TLS client - local client = grpc.newclient { - service = "SecureService", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["secure.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, tls = true, } + local client = assert(grpc.newservice(client_conn, p.loaded["secure.proto"], "SecureService")) local result = client.ProcessSecret({ secret = "my-sensitive-data" @@ -1111,11 +1110,10 @@ task.fork(function() } -- Client - local client = grpc.newclient { - service = "DataService", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["api.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["api.proto"], "DataService")) -- Concurrent requests for multiple resources local wg = waitgroup.new() @@ -1189,11 +1187,10 @@ task.fork(function() registrar = reg, } - local client = grpc.newclient { - service = "EventService", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["events.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["events.proto"], "EventService")) -- For streaming methods, call returns stream object local stream, err = client.Subscribe() @@ -1286,11 +1283,10 @@ task.fork(function() } -- Client - local client = grpc.newclient { - service = "UserService", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["user.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["user.proto"], "UserService")) local response = client.CreateUser({ user = { @@ -1337,7 +1333,7 @@ local task = require "silly.task" -- Correct: call in coroutine task.fork(function() - local client = grpc.newclient({ + local client_conn = grpc.newclient({ -- ... }) -- normal usage @@ -1390,11 +1386,16 @@ task.fork(function() } ]], "test.proto") - local client, err = grpc.newclient { - service = "Test", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["test.proto"], + local client_conn, err = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + if not client_conn then + print("Failed to create client connection:", err) + return + end + + local client + client, err = grpc.newservice(client_conn, p.loaded["test.proto"], "Test") if not client then print("Failed to create client:", err) @@ -1443,15 +1444,13 @@ task.fork(function() } ]], "test.proto") - local client = grpc.newclient { - service = "Test", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["test.proto"], - timeout = 5000, -- 5 second timeout (recommended) + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["test.proto"], "Test")) -- Returns nil and error message on timeout - local result, err = client.Ping({}) + local result, err = client.Ping({}, 5000) -- 5 second timeout if not result then print("Request timeout or failed:", err) end @@ -1519,16 +1518,15 @@ task.fork(function() ]], "test.proto") -- Requests are distributed to three endpoints in order - local client = grpc.newclient { - service = "Test", - endpoints = { + local client_conn = grpc.newclient { + targets = { "server1.example.com:50051", -- 1st request "server2.example.com:50051", -- 2nd request "server3.example.com:50051", -- 3rd request -- 4th request goes back to server1... }, - proto = p.loaded["test.proto"], } + local client = assert(grpc.newservice(client_conn, p.loaded["test.proto"], "Test")) -- 10 requests will be evenly distributed across 3 servers for i = 1, 10 do @@ -1567,11 +1565,10 @@ task.fork(function() } ]], "test.proto") - local client = grpc.newclient { - service = "Test", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["test.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["test.proto"], "Test")) -- HTTP/2 connection is automatically reused for i = 1, 100 do @@ -1608,11 +1605,10 @@ task.fork(function() } ]], "test.proto") - local client = grpc.newclient { - service = "Test", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["test.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["test.proto"], "Test")) local wg = waitgroup.new() diff --git a/docs/src/en/reference/net/tls.md b/docs/src/en/reference/net/tls.md index c5b38a6c8..cc8611ade 100644 --- a/docs/src/en/reference/net/tls.md +++ b/docs/src/en/reference/net/tls.md @@ -153,7 +153,7 @@ v1HSCliKZXW8cusnBRD2IOyxuIUV/qiMfARylMvlLBccgJR8+olH9f/yF2EFWhoy end } - if not listenfd then + if not listener then print("Failed to start server") return end @@ -162,7 +162,7 @@ v1HSCliKZXW8cusnBRD2IOyxuIUV/qiMfARylMvlLBccgJR8+olH9f/yF2EFWhoy -- Wait for some time to process requests wg:wait() - tls.close(listenfd) + listener:close() end) ``` @@ -189,8 +189,8 @@ task.fork(function() ip .. ":443", -- Server address { bind = nil, -- No local address binding - server = "www.example.com", -- SNI hostname - alpn = {"http/1.1"} -- ALPN protocol + hostname = "www.example.com", -- SNI hostname + alpnprotos = {"http/1.1"} -- ALPN protocol } ) @@ -320,11 +320,11 @@ Start a TLS server listening on the given address. - `cert`: `string` - Certificate content in PEM format - `key`: `string` - Private key content in PEM format - `backlog`: `integer|nil` (optional) - Maximum length of the pending connection queue - - `accept`: `fun(fd: integer, addr: string)` (required) - Connection handler, called for each new connection + - `accept`: `fun(conn: silly.net.tls.conn)` (required) - Connection handler, called for each new connection - `ciphers`: `string|nil` (optional) - Allowed cipher suites in OpenSSL format - `alpnprotos`: `string[]|nil` (optional) - List of supported ALPN protocols, e.g. `{"http/1.1", "h2"}` - **Return value**: - - Success: `integer` - Listener file descriptor + - Success: `silly.net.tls.listener` - Listener object - Failure: `nil, string` - nil and error message - **Example**: @@ -381,6 +381,14 @@ if not conn then end ``` +### listener:close() + +Close the TLS listener. + +- **Return value**: + - Success: `true` + - Failure: `false, string` - false and error message + ### listener:reload([conf]) Hot reload the TLS server's certificate configuration without restarting the service. @@ -554,7 +562,7 @@ print("Remote address:", conn.remoteaddr) 1. **Key Protection**: Private key files should have strict access permissions (e.g. `chmod 600`) 2. **Cipher Suites**: In production environments, it's recommended to configure the `ciphers` parameter to disable insecure encryption algorithms -3. **Certificate Updates**: Use `tls.reload()` to regularly update certificates to avoid certificate expiration +3. **Certificate Updates**: Use `listener:reload()` to regularly update certificates to avoid certificate expiration 4. **ALPN Negotiation**: Use `alpnprotos` to explicitly specify supported protocols, avoiding protocol downgrade attacks ### Common Errors diff --git a/docs/src/en/reference/net/websocket.md b/docs/src/en/reference/net/websocket.md index 235d290d0..5f22dae7a 100644 --- a/docs/src/en/reference/net/websocket.md +++ b/docs/src/en/reference/net/websocket.md @@ -95,7 +95,7 @@ task.fork(function() else -- Handle regular HTTP requests stream:respond(200, {["content-type"] = "text/plain"}) - stream:close("Not a WebSocket request") + stream:closewrite("Not a WebSocket request") end end } @@ -286,7 +286,7 @@ task.fork(function() end else stream:respond(404, {}) - stream:close() + stream:closewrite() end end } diff --git a/docs/src/en/tutorials/database-app.md b/docs/src/en/tutorials/database-app.md index 49958ad9d..8898ce5a7 100644 --- a/docs/src/en/tutorials/database-app.md +++ b/docs/src/en/tutorials/database-app.md @@ -584,14 +584,14 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #body, }) - stream:close(body) + stream:closewrite(body) else local body = json.encode({success = false, error = err}) stream:respond(500, { ["content-type"] = "application/json", ["content-length"] = #body, }) - stream:close(body) + stream:closewrite(body) end return end @@ -606,14 +606,14 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #body, }) - stream:close(body) + stream:closewrite(body) else local body = json.encode({success = false, error = err}) stream:respond(404, { ["content-type"] = "application/json", ["content-length"] = #body, }) - stream:close(body) + stream:closewrite(body) end return end @@ -627,7 +627,7 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) return end @@ -641,7 +641,7 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) return end @@ -655,14 +655,14 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) else local resp = json.encode({success = false, error = err}) stream:respond(500, { ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) end return end @@ -677,7 +677,7 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) return end @@ -691,7 +691,7 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) return end @@ -702,7 +702,7 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) else local resp = json.encode({success = false, error = err}) local status = err == "User not found" and 404 or 500 @@ -710,7 +710,7 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) end return end @@ -725,7 +725,7 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) else local resp = json.encode({success = false, error = err}) local status = err == "User not found" and 404 or 500 @@ -733,7 +733,7 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) end return end @@ -744,7 +744,7 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) end -- Start HTTP server diff --git a/docs/src/en/tutorials/websocket-chat.md b/docs/src/en/tutorials/websocket-chat.md index b9ab0a91a..44f20cbef 100644 --- a/docs/src/en/tutorials/websocket-chat.md +++ b/docs/src/en/tutorials/websocket-chat.md @@ -78,7 +78,7 @@ http.listen { handler = function(stream) if stream.header["upgrade"] ~= "websocket" then stream:respond(404, {}) - stream:close("Not Found") + stream:closewrite("Not Found") return end @@ -156,7 +156,7 @@ http.listen { handler = function(stream) if stream.header["upgrade"] ~= "websocket" then stream:respond(404, {}) - stream:close("Not Found") + stream:closewrite("Not Found") return end @@ -248,7 +248,7 @@ http.listen { handler = function(stream) if stream.header["upgrade"] ~= "websocket" then stream:respond(404, {}) - stream:close("Not Found") + stream:closewrite("Not Found") return end @@ -377,7 +377,7 @@ http.listen { handler = function(stream) if stream.header["upgrade"] ~= "websocket" then stream:respond(404, {}) - stream:close("Not Found") + stream:closewrite("Not Found") return end @@ -715,7 +715,7 @@ http.listen { handler = function(stream) if stream.header["upgrade"] ~= "websocket" then stream:respond(404, {}) - stream:close("Not Found") + stream:closewrite("Not Found") return end diff --git a/docs/src/guides/http-best-practices.md b/docs/src/guides/http-best-practices.md index 3323f6611..5ab268ac5 100644 --- a/docs/src/guides/http-best-practices.md +++ b/docs/src/guides/http-best-practices.md @@ -724,6 +724,7 @@ http.listen { local silly = require "silly" local http = require "silly.net.http" local logger = require "silly.logger" +local trace = require "silly.trace" local json = require "silly.encoding.json" -- 全局错误处理器 @@ -1033,6 +1034,7 @@ http.listen { local silly = require "silly" local http = require "silly.net.http" local logger = require "silly.logger" +local trace = require "silly.trace" -- 中间件链 local function chain(middlewares, final_handler) @@ -1142,6 +1144,7 @@ http.listen { local silly = require "silly" local http = require "silly.net.http" local logger = require "silly.logger" +local trace = require "silly.trace" local json = require "silly.encoding.json" local function access_log(stream, status, duration, response_size) @@ -1294,6 +1297,7 @@ scrape_configs: local silly = require "silly" local http = require "silly.net.http" local logger = require "silly.logger" +local trace = require "silly.trace" http.listen { addr = ":8080", @@ -1301,10 +1305,10 @@ http.listen { -- 从请求头获取或生成 Trace ID local trace_id = tonumber(stream.header["x-trace-id"]) if trace_id then - silly.traceset(trace_id) + trace.attach(trace_id) else - silly.tracespawn() - trace_id = silly.tracepropagate() -- 用于返回给客户端 + trace.spawn() + trace_id = trace.propagate() -- 用于返回给客户端 end logger.info("Request started:", stream.method, stream.path) @@ -1340,7 +1344,7 @@ http.listen { -- 调用外部服务时自动传递当前 Trace ID function call_external_service() - local trace_id = silly.tracepropagate() + local trace_id = trace.propagate() local response = http.get("http://other-service/api", { ["x-trace-id"] = tostring(trace_id), }) @@ -1654,6 +1658,7 @@ local silly = require "silly" local http = require "silly.net.http" local json = require "silly.encoding.json" local logger = require "silly.logger" +local trace = require "silly.trace" local prometheus = require "silly.metrics.prometheus" -- 配置 @@ -1723,9 +1728,9 @@ local function logging_middleware(stream, next) -- 设置或创建 trace ID local trace_id = tonumber(stream.header["x-trace-id"]) if trace_id then - silly.traceset(trace_id) + trace.attach(trace_id) else - silly.tracespawn() + trace.spawn() end logger.info("Request:", stream.method, stream.path, "from", stream.remoteaddr) diff --git a/docs/src/guides/logging-monitoring.md b/docs/src/guides/logging-monitoring.md index 69727e63c..b642fcfd5 100644 --- a/docs/src/guides/logging-monitoring.md +++ b/docs/src/guides/logging-monitoring.md @@ -27,7 +27,7 @@ Silly 框架为这三个方面提供了内置支持: - `silly.logger`:分级日志系统,支持日志轮转 - `silly.metrics.prometheus`:Prometheus 指标收集和导出 -- `silly.tracespawn/traceset`:分布式追踪 ID 生成和传播 +- `trace.spawn/trace.attach`:分布式追踪 ID 生成和传播 ## 日志系统 @@ -37,6 +37,7 @@ Silly 框架为这三个方面提供了内置支持: ```lua local logger = require "silly.logger" +local trace = require "silly.trace" -- 设置日志级别(只输出 INFO 及以上级别) logger.setlevel(logger.INFO) @@ -89,6 +90,7 @@ logger.info("Processing request") ```lua local logger = require "silly.logger" +local trace = require "silly.trace" -- 生产环境:使用 INFO 级别 logger.setlevel(logger.INFO) @@ -110,6 +112,7 @@ end ```lua local logger = require "silly.logger" +local trace = require "silly.trace" -- 使用 string.format 格式 logger.infof("用户 [%s] 在 %d 秒内完成了 %d 次操作", @@ -129,6 +132,7 @@ logger.debugf("%.2f%% 的请求在 %dms 内完成", ```lua local logger = require "silly.logger" +local trace = require "silly.trace" local json = require "silly.encoding.json" -- 定义日志辅助函数 @@ -194,6 +198,7 @@ find /var/log -name "myapp.log.*" -mtime +7 -delete ```lua local logger = require "silly.logger" +local trace = require "silly.trace" local signal = require "silly.signal" -- 初始化为 INFO 级别 @@ -367,6 +372,7 @@ response_size:labels("GET"):observe(1234) local silly = require "silly" local http = require "silly.net.http" local logger = require "silly.logger" +local trace = require "silly.trace" local prometheus = require "silly.metrics.prometheus" -- 定义指标 @@ -500,6 +506,7 @@ Silly 提供了分布式追踪 ID 系统,每个协程都有独立的 trace ID local silly = require "silly" local task = require "silly.task" local logger = require "silly.logger" +local trace = require "silly.trace" task.fork(function() -- 创建新的 trace ID(如果当前协程没有) @@ -518,6 +525,7 @@ end) local silly = require "silly" local http = require "silly.net.http" local logger = require "silly.logger" +local trace = require "silly.trace" -- 服务 A:发起 HTTP 请求 local function call_service_b() -- 生成传播用的 trace ID @@ -525,16 +533,22 @@ local function call_service_b() logger.info("调用服务 B") -- 通过 HTTP Header 传递 trace ID - local response = http.request { - method = "POST", - url = "http://service-b:8080/api/process", - headers = { + local httpc = http.newclient() + local stream, err = httpc:request( + "POST", + "http://service-b:8080/api/process", + { ["X-Trace-Id"] = tostring(trace_id), - }, - body = '{"data": "value"}', - } + ["content-type"] = "application/json", + } + ) + if not stream then + return nil, err + end - return response + stream:closewrite('{"data": "value"}') + local body, status = stream:readall() + return {status = status, body = body} end -- 服务 B:接收请求并使用传入的 trace ID @@ -542,7 +556,7 @@ local server = http.listen { addr = "0.0.0.0:8080", handler = function(stream) -- 提取并设置 trace ID - local trace_id = tonumber(stream.headers["x-trace-id"]) + local trace_id = tonumber(stream.header["x-trace-id"]) if trace_id then trace.attach(trace_id) else @@ -563,6 +577,7 @@ local server = http.listen { ```lua local cluster = require "silly.net.cluster" local logger = require "silly.logger" +local trace = require "silly.trace" -- 创建 cluster 服务 cluster.serve { @@ -592,6 +607,7 @@ local result = cluster.call(peer, "get_user", {user_id = 123}) ```lua local silly = require "silly" local logger = require "silly.logger" +local trace = require "silly.trace" local json = require "silly.encoding.json" -- 结构化日志辅助函数 @@ -757,6 +773,7 @@ receivers: local silly = require "silly" local time = require "silly.time" local logger = require "silly.logger" +local trace = require "silly.trace" local prometheus = require "silly.metrics.prometheus" -- 定义告警阈值 @@ -852,6 +869,7 @@ end) local silly = require "silly" local http = require "silly.net.http" local logger = require "silly.logger" +local trace = require "silly.trace" local signal = require "silly.signal" local time = require "silly.time" local prometheus = require "silly.metrics.prometheus" @@ -969,16 +987,16 @@ local function handle_request(stream) http_requests_in_flight:inc() -- 获取或创建 trace ID - local trace_id = tonumber(stream.headers["x-trace-id"]) + local trace_id = tonumber(stream.header["x-trace-id"]) if trace_id then - silly.traceset(trace_id) + trace.attach(trace_id) else - silly.tracespawn() - trace_id = silly.tracepropagate() -- 获取当前 trace ID 用于响应头 + trace.spawn() + trace_id = trace.propagate() -- 获取当前 trace ID 用于响应头 end -- 记录请求大小 - local req_size = tonumber(stream.headers["content-length"]) or 0 + local req_size = tonumber(stream.header["content-length"]) or 0 http_request_size:observe(req_size) -- 路由处理 @@ -1042,7 +1060,7 @@ local server = http.listen { handler = function(stream) local ok, err = silly.pcall(handle_request, stream) if not ok then - silly.tracespawn() -- 创建新的 trace ID + trace.spawn() -- 创建新的 trace ID logger.error("请求处理失败:", err) stream:respond(500, {["content-type"] = "application/json"}) diff --git a/docs/src/guides/tls-configuration.md b/docs/src/guides/tls-configuration.md index 3f782a6ce..8e817ef80 100644 --- a/docs/src/guides/tls-configuration.md +++ b/docs/src/guides/tls-configuration.md @@ -573,7 +573,7 @@ signal.register("SIGUSR1", function() end -- 热重载证书 - local success, reload_err = tls.reload(listenfd, { + local success, reload_err = listenfd:reload({ certs = {{cert = cert_pem, key = key_pem}} }) @@ -779,7 +779,7 @@ local function get_connection(host, port) -- 创建新连接 local ip = dns.lookup(host, dns.A) - conn = tls.connect(ip .. ":" .. port, nil, host, {"http/1.1"}) + conn = tls.connect(ip .. ":" .. port, {hostname = host, alpnprotos = {"http/1.1"}}) if conn then connection_pool[key] = conn diff --git a/docs/src/reference/metrics/collector.md b/docs/src/reference/metrics/collector.md index 241f56b9f..1cacafc4f 100644 --- a/docs/src/reference/metrics/collector.md +++ b/docs/src/reference/metrics/collector.md @@ -783,10 +783,10 @@ local server = http.listen { ["content-type"] = "text/plain; version=0.0.4; charset=utf-8", ["content-length"] = #metrics_data, }) - stream:close(metrics_data) + stream:closewrite(metrics_data) else stream:respond(404) - stream:close("Not Found") + stream:closewrite("Not Found") end end } diff --git a/docs/src/reference/metrics/counter.md b/docs/src/reference/metrics/counter.md index 2ab62e6bf..5ef50721a 100644 --- a/docs/src/reference/metrics/counter.md +++ b/docs/src/reference/metrics/counter.md @@ -533,10 +533,10 @@ local server = http.listen { ["content-type"] = "text/plain; version=0.0.4; charset=utf-8", ["content-length"] = #metrics_data, }) - stream:close(metrics_data) + stream:closewrite(metrics_data) else stream:respond(404) - stream:close("Not Found") + stream:closewrite("Not Found") end end } diff --git a/docs/src/reference/metrics/histogram.md b/docs/src/reference/metrics/histogram.md index c8e4e0820..65e9075da 100644 --- a/docs/src/reference/metrics/histogram.md +++ b/docs/src/reference/metrics/histogram.md @@ -672,7 +672,7 @@ task.fork(function() ["content-type"] = "application/json", ["content-length"] = #response_body }) - stream:close(response_body) + stream:closewrite(response_body) -- 记录指标 local duration = (silly.time.now() - start_time) / 1000 @@ -691,10 +691,10 @@ task.fork(function() ["content-type"] = "text/plain; version=0.0.4; charset=utf-8", ["content-length"] = #metrics }) - stream:close(metrics) + stream:closewrite(metrics) else stream:respond(404, {["content-type"] = "text/plain"}) - stream:close("Not Found") + stream:closewrite("Not Found") end end } diff --git a/docs/src/reference/metrics/prometheus.md b/docs/src/reference/metrics/prometheus.md index d1033fc37..d374c7cbe 100644 --- a/docs/src/reference/metrics/prometheus.md +++ b/docs/src/reference/metrics/prometheus.md @@ -359,11 +359,11 @@ local server = http.listen { ["content-type"] = "text/plain; version=0.0.4; charset=utf-8", ["content-length"] = #metrics, }) - stream:close(metrics) + stream:closewrite(metrics) else -- 业务逻辑 stream:respond(200, {["content-type"] = "text/plain"}) - stream:close("Hello World") + stream:closewrite("Hello World") -- 记录指标 local duration = os.clock() - start @@ -428,19 +428,19 @@ local server = http.listen { stream:respond(200, { ["content-type"] = "text/plain; version=0.0.4; charset=utf-8", }) - stream:close(metrics) + stream:closewrite(metrics) requests_in_flight:dec() return end -- 记录请求大小 - local req_size = stream.headers["content-length"] or 0 + local req_size = stream.header["content-length"] or 0 request_size:observe(tonumber(req_size)) -- 业务逻辑 local response_data = "Response data" stream:respond(200, {["content-type"] = "text/plain"}) - stream:close(response_data) + stream:closewrite(response_data) -- 记录响应指标 local duration = os.clock() - start @@ -993,10 +993,10 @@ local metrics_server = http.listen { stream:respond(200, { ["content-type"] = "text/plain; version=0.0.4", }) - stream:close(metrics) + stream:closewrite(metrics) else stream:respond(404) - stream:close("Not Found") + stream:closewrite("Not Found") end end } diff --git a/docs/src/reference/net/cluster.md b/docs/src/reference/net/cluster.md index e72a60fb1..9d143acab 100644 --- a/docs/src/reference/net/cluster.md +++ b/docs/src/reference/net/cluster.md @@ -1077,7 +1077,7 @@ end) cluster 自动传播 trace ID: ```lua --- 客户端发起请求时,自动使用 silly.tracepropagate() 携带当前 trace ID +-- 客户端发起请求时,自动使用 trace.propagate() 携带当前 trace ID local resp = cluster.call(peer, "ping", data) -- 服务器端处理时,trace ID 已由 cluster 自动设置 diff --git a/docs/src/reference/net/grpc.md b/docs/src/reference/net/grpc.md index 49e8c6440..dc9248b54 100644 --- a/docs/src/reference/net/grpc.md +++ b/docs/src/reference/net/grpc.md @@ -265,20 +265,16 @@ end) ### grpc.newclient(conf) -创建 gRPC 客户端。 +创建 gRPC 连接池。 - **参数**: - `conf`: `table` - 客户端配置表 - - `service`: `string` (必需) - 服务名称(对应 proto 中的 service 名) - - `endpoints`: `string[]` (必需) - gRPC 服务器地址列表,格式 `"host:port"` - - `proto`: `table` (必需) - protoc 加载的 proto 定义 + - `targets`: `string[]` (必需) - gRPC 目标地址列表,格式 `"host:port"`(或 `dns://host:port`) - `tls`: `boolean|nil` (可选) - 是否使用 TLS,默认 false - - `timeout`: `number|nil` (可选) - 请求超时时间(毫秒) - **返回值**: - - 成功: `client` - 客户端对象 + - 成功: `conn` - gRPC 连接对象 - 失败: `nil, string` - nil 和错误信息 -- **注意**: 客户端对象会动态生成方法,方法名对应 proto 中定义的 RPC 方法 -- **负载均衡**: 多个 endpoint 时使用轮询策略 +- **负载均衡**: 多个 target 时使用轮询策略 - **示例**: ```lua validate @@ -306,19 +302,23 @@ task.fork(function() } ]], "hello.proto") - local client, err = grpc.newclient { - service = "Greeter", - endpoints = {"127.0.0.1:50051", "127.0.0.1:50052"}, - proto = p.loaded["hello.proto"], - timeout = 5000, + local client_conn, err = grpc.newclient { + targets = {"127.0.0.1:50051", "127.0.0.1:50052"}, } + if not client_conn then + print("Failed to create client connection:", err) + return + end + + local client + client, err = grpc.newservice(client_conn, p.loaded["hello.proto"], "Greeter") if not client then - print("Failed to create client:", err) + print("Failed to create service client:", err) return end - -- 客户端对象会自动生成 SayHello 方法 + -- service 对象会按 proto 动态提供方法 local response, err = client.SayHello({name = "World"}) if response then print("Response:", response.message) @@ -328,7 +328,19 @@ task.fork(function() end) ``` -### client.MethodName(request) +### grpc.newservice(conn, proto, service_name) + +基于 gRPC 连接创建类型化的 service 客户端。 + +- **参数**: + - `conn`: `silly.net.grpc.client.conn` - `grpc.newclient` 返回的连接对象 + - `proto`: `table` - protoc 加载的 proto 定义 + - `service_name`: `string` - proto 中定义的服务名 +- **返回值**: + - 成功: `service` - service 客户端对象 + - 失败: `nil, string` - nil 和错误信息 + +### service.MethodName(request) 调用 RPC 方法(Unary RPC)。 @@ -367,11 +379,10 @@ task.fork(function() } ]], "math.proto") - local client = grpc.newclient { - service = "MathService", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["math.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["math.proto"], "MathService")) -- 调用 Multiply 方法 local result, err = client.Multiply({x = 6, y = 7}) @@ -383,7 +394,7 @@ task.fork(function() end) ``` -### client.StreamMethod() +### service.StreamMethod() 创建流式 RPC 连接(Streaming RPC)。 @@ -416,11 +427,10 @@ task.fork(function() } ]], "stream.proto") - local client = grpc.newclient { - service = "StreamService", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["stream.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["stream.proto"], "StreamService")) -- 创建双向流 local stream, err = client.BiStream() @@ -486,11 +496,10 @@ task.fork(function() } ]], "file.proto") - local client = grpc.newclient { - service = "FileService", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["file.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["file.proto"], "FileService")) local stream = client.Upload() @@ -548,11 +557,10 @@ task.fork(function() } ]], "log.proto") - local client = grpc.newclient { - service = "LogService", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["log.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["log.proto"], "LogService")) local stream = client.StreamLogs() @@ -603,11 +611,10 @@ task.fork(function() } ]], "chat.proto") - local client = grpc.newclient { - service = "ChatService", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["chat.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["chat.proto"], "ChatService")) local stream = client.Chat() @@ -680,12 +687,10 @@ task.fork(function() print("Greeter server started on 127.0.0.1:50051") -- 创建客户端 - local client = grpc.newclient { - service = "Greeter", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["greeter.proto"], - timeout = 5000, + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["greeter.proto"], "Greeter")) -- 调用 RPC local resp1 = client.SayHello({name = "Alice"}) @@ -747,12 +752,10 @@ task.fork(function() } -- 客户端 - local client = grpc.newclient { - service = "Calculator", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["calculator.proto"], - timeout = 5000, + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["calculator.proto"], "Calculator")) -- 正常调用 local result, err = client.Divide({ @@ -844,14 +847,13 @@ task.fork(function() print("Started two servers on ports 50051 and 50052") -- 客户端连接多个端点 - local client = grpc.newclient { - service = "Counter", - endpoints = { + local client_conn = grpc.newclient { + targets = { "127.0.0.1:50051", "127.0.0.1:50052" }, - proto = p.loaded["counter.proto"], } + local client = assert(grpc.newservice(client_conn, p.loaded["counter.proto"], "Counter")) -- 请求会轮询到两个服务器 for i = 1, 4 do @@ -910,15 +912,13 @@ task.fork(function() } -- 客户端:设置短超时 - local client = grpc.newclient { - service = "SlowService", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["slow.proto"], - timeout = 1000, -- 1秒超时 + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["slow.proto"], "SlowService")) -- 快速请求(应该成功) - local result1, err1 = client.SlowMethod({delay_ms = 100}) + local result1, err1 = client.SlowMethod({delay_ms = 100}, 1000) if result1 then print("Fast request:", result1.result) else @@ -926,7 +926,7 @@ task.fork(function() end -- 慢速请求(应该超时) - local result2, err2 = client.SlowMethod({delay_ms = 2000}) + local result2, err2 = client.SlowMethod({delay_ms = 2000}, 1000) if result2 then print("Slow request:", result2.result) else @@ -1043,12 +1043,11 @@ v1HSCliKZXW8cusnBRD2IOyxuIUV/qiMfARylMvlLBccgJR8+olH9f/yF2EFWhoy print("Secure gRPC server started") -- TLS 客户端 - local client = grpc.newclient { - service = "SecureService", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["secure.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, tls = true, } + local client = assert(grpc.newservice(client_conn, p.loaded["secure.proto"], "SecureService")) local result = client.ProcessSecret({ secret = "my-sensitive-data" @@ -1111,11 +1110,10 @@ task.fork(function() } -- 客户端 - local client = grpc.newclient { - service = "DataService", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["api.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["api.proto"], "DataService")) -- 并发请求多个资源 local wg = waitgroup.new() @@ -1189,11 +1187,10 @@ task.fork(function() registrar = reg, } - local client = grpc.newclient { - service = "EventService", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["events.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["events.proto"], "EventService")) -- 对于流式方法,调用返回流对象 local stream, err = client.Subscribe() @@ -1286,11 +1283,10 @@ task.fork(function() } -- 客户端 - local client = grpc.newclient { - service = "UserService", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["user.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["user.proto"], "UserService")) local response = client.CreateUser({ user = { @@ -1337,7 +1333,7 @@ local task = require "silly.task" -- 正确:在协程中调用 task.fork(function() - local client = grpc.newclient({ + local client_conn = grpc.newclient({ -- ... }) -- 正常使用 @@ -1390,11 +1386,16 @@ task.fork(function() } ]], "test.proto") - local client, err = grpc.newclient { - service = "Test", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["test.proto"], + local client_conn, err = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + if not client_conn then + print("Failed to create client connection:", err) + return + end + + local client + client, err = grpc.newservice(client_conn, p.loaded["test.proto"], "Test") if not client then print("Failed to create client:", err) @@ -1443,15 +1444,13 @@ task.fork(function() } ]], "test.proto") - local client = grpc.newclient { - service = "Test", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["test.proto"], - timeout = 5000, -- 5秒超时(推荐设置) + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["test.proto"], "Test")) -- 超时后返回 nil 和错误信息 - local result, err = client.Ping({}) + local result, err = client.Ping({}, 5000) -- 5秒超时 if not result then print("Request timeout or failed:", err) end @@ -1519,16 +1518,15 @@ task.fork(function() ]], "test.proto") -- 请求会按顺序分发到三个端点 - local client = grpc.newclient { - service = "Test", - endpoints = { + local client_conn = grpc.newclient { + targets = { "server1.example.com:50051", -- 第1个请求 "server2.example.com:50051", -- 第2个请求 "server3.example.com:50051", -- 第3个请求 -- 第4个请求回到 server1... }, - proto = p.loaded["test.proto"], } + local client = assert(grpc.newservice(client_conn, p.loaded["test.proto"], "Test")) -- 10 个请求会均匀分布到 3 个服务器 for i = 1, 10 do @@ -1567,11 +1565,10 @@ task.fork(function() } ]], "test.proto") - local client = grpc.newclient { - service = "Test", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["test.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["test.proto"], "Test")) -- HTTP/2 连接会被自动复用 for i = 1, 100 do @@ -1608,11 +1605,10 @@ task.fork(function() } ]], "test.proto") - local client = grpc.newclient { - service = "Test", - endpoints = {"127.0.0.1:50051"}, - proto = p.loaded["test.proto"], + local client_conn = grpc.newclient { + targets = {"127.0.0.1:50051"}, } + local client = assert(grpc.newservice(client_conn, p.loaded["test.proto"], "Test")) local wg = waitgroup.new() diff --git a/docs/src/reference/net/tcp.md b/docs/src/reference/net/tcp.md index 9e14acd5c..88e6f484f 100644 --- a/docs/src/reference/net/tcp.md +++ b/docs/src/reference/net/tcp.md @@ -245,7 +245,7 @@ end) ### conn:unreadbytes() ::: warning 名称变更 -此方法替代了旧的 `tcp.recvsize(fd)`。获取当前接收缓冲区中未读取的数据量。 +此方法替代了旧的 `tcp.recvsize(conn)`。获取当前接收缓冲区中未读取的数据量。 ::: 获取接收缓冲区中当前可用但尚未读取的数据量。 @@ -288,7 +288,7 @@ conn:limit(nil) ### conn:unsentbytes() ::: warning 名称变更 -此方法替代了旧的 `tcp.sendsize(fd)`。获取发送缓冲区中等待发送的数据量。 +此方法替代了旧的 `tcp.sendsize(conn)`。获取发送缓冲区中等待发送的数据量。 ::: 获取当前发送缓冲区(已排队但尚未传输)中保存的数据量。 @@ -381,9 +381,9 @@ task.fork(function() -- 启动服务器 local listenfd = tcp.listen { addr = "127.0.0.1:9988", - accept = function(fd, addr) + accept = function(conn) wg:fork(function() - print("Client connected:", addr) + print("Client connected:", conn.remoteaddr) -- 持续回显数据,直到连接关闭 while true do @@ -407,8 +407,8 @@ task.fork(function() wg:fork(function() time.sleep(100) -- 等待服务器启动 - local fd, err = tcp.connect("127.0.0.1:9988") - if not fd then + local conn, err = tcp.connect("127.0.0.1:9988") + if not conn then print("Connect failed:", err) return end @@ -422,7 +422,7 @@ task.fork(function() end) wg:wait() - tcp.close(listenfd) + listenfd:close() end) ``` @@ -436,8 +436,8 @@ local task = require "silly.task" local tcp = require "silly.net.tcp" task.fork(function() - local fd, err = tcp.connect("example.com:80") - if not fd then + local conn, err = tcp.connect("example.com:80") + if not conn then print("Connect failed:", err) return end @@ -505,7 +505,9 @@ task.fork(function() local wg = waitgroup.new() -- 服务器:接收二进制消息 - local listenfd = tcp.listen("127.0.0.1:9989", function(fd, addr) + local listenfd = tcp.listen { + addr = "127.0.0.1:9989", + accept = function(conn) wg:fork(function() while true do -- 读取4字节长度头 @@ -532,14 +534,15 @@ task.fork(function() conn:close() end) - end) + end + } -- 客户端:发送二进制消息 wg:fork(function() time.sleep(100) - local fd = tcp.connect("127.0.0.1:9989") - if not fd then + local conn = tcp.connect("127.0.0.1:9989") + if not conn then return end @@ -561,7 +564,7 @@ task.fork(function() end) wg:wait() - tcp.close(listenfd) + listenfd:close() end) ``` @@ -580,7 +583,9 @@ task.fork(function() local wg = waitgroup.new() -- 服务器:快速发送大量数据 - local listenfd = tcp.listen("127.0.0.1:9990", function(fd, addr) + local listenfd = tcp.listen { + addr = "127.0.0.1:9990", + accept = function(conn) wg:fork(function() -- 发送10MB数据 local chunk = string.rep("A", 1024 * 1024) @@ -590,14 +595,15 @@ task.fork(function() end conn:close() end) - end) + end + } -- 客户端:限制接收缓冲区,慢速消费 wg:fork(function() time.sleep(100) - local fd = tcp.connect("127.0.0.1:9990") - if not fd then + local conn = tcp.connect("127.0.0.1:9990") + if not conn then return end @@ -613,7 +619,7 @@ task.fork(function() end total = total + #data - print("Received:", total, "bytes, buffered:", tcp.recvsize(fd)) + print("Received:", total, "bytes, buffered:", tcp.recvsize(conn)) -- 模拟慢速处理 time.sleep(100) @@ -624,7 +630,7 @@ task.fork(function() end) wg:wait() - tcp.close(listenfd) + listenfd:close() end) ``` @@ -648,24 +654,24 @@ local pool = { function pool:acquire() -- 优先使用空闲连接 if #self.idle > 0 then - local fd = table.remove(self.idle) + local conn = table.remove(self.idle) if conn:isalive() then - return fd + return conn end conn:close() end -- 创建新连接 - local fd, err = tcp.connect(self.host) - if not fd then + local conn, err = tcp.connect(self.host) + if not conn then return nil, err end - return fd + return conn end -- 归还连接 -function pool:release(fd) +function pool:release(conn) if not conn:isalive() then conn:close() return @@ -673,7 +679,7 @@ function pool:release(fd) -- 如果池未满,放回池中 if #self.idle < self.max_size then - table.insert(self.idle, fd) + table.insert(self.idle, conn) else conn:close() end @@ -684,20 +690,20 @@ local task = require "silly.task" task.fork(function() -- 发起多个请求,复用连接 for i = 1, 5 do - local fd, err = pool:acquire() - if not fd then + local conn, err = pool:acquire() + if not conn then print("Failed to acquire connection:", err) return end - print("Request", i, "using fd:", fd) + print("Request", i, "using conn:", conn) conn:write( "GET / HTTP/1.1\r\n\r\n") -- 读取响应(简化) time.sleep(100) -- 归还连接 - pool:release(fd) + pool:release(conn) end end) ``` @@ -715,12 +721,12 @@ local tcp = require "silly.net.tcp" -- 正确:在协程中调用 task.fork(function() - local fd = tcp.connect("127.0.0.1:8080") + local conn = tcp.connect("127.0.0.1:8080") -- ... end) -- 错误:不能在主线程中直接调用 --- local fd = tcp.connect("127.0.0.1:8080") -- 这会失败! +-- local conn = tcp.connect("127.0.0.1:8080") -- 这会失败! ``` ### 2. 及时关闭连接 @@ -733,8 +739,8 @@ local task = require "silly.task" local tcp = require "silly.net.tcp" task.fork(function() - local fd = tcp.connect("127.0.0.1:8080") - if not fd then + local conn = tcp.connect("127.0.0.1:8080") + if not conn then return end @@ -761,8 +767,8 @@ local task = require "silly.task" local tcp = require "silly.net.tcp" task.fork(function() - local fd, err = tcp.connect("127.0.0.1:8080") - if not fd then + local conn, err = tcp.connect("127.0.0.1:8080") + if not conn then print("Connect failed:", err) return end @@ -798,7 +804,7 @@ local tcp = require "silly.net.tcp" local time = require "silly.time" -- 如果发送缓冲区过大,等待一段时间 -if tcp.sendsize(fd) > 10 * 1024 * 1024 then +if tcp.sendsize(conn) > 10 * 1024 * 1024 then time.sleep(100) end ``` @@ -820,18 +826,21 @@ local waitgroup = require "silly.sync.waitgroup" task.fork(function() local wg = waitgroup.new() - local listenfd = tcp.listen("127.0.0.1:8080", function(fd, addr) + local listenfd = tcp.listen { + addr = "127.0.0.1:8080", + accept = function(conn) wg:fork(function() -- 处理连接 conn:close() end) - end) + end + } -- 等待所有连接处理完成 wg:wait() -- 现在可以安全关闭监听器 - tcp.close(listenfd) + listenfd:close() end) ``` @@ -878,8 +887,8 @@ local tcp = require "silly.net.tcp" local task = require "silly.task" task.fork(function() - local fd = tcp.connect("127.0.0.1:8080") - if not fd then return end + local conn = tcp.connect("127.0.0.1:8080") + if not conn then return end -- 推荐:按行读取 local line = conn:read("\n") diff --git a/docs/src/reference/net/tls.md b/docs/src/reference/net/tls.md index c37d990c6..78e0f21d3 100644 --- a/docs/src/reference/net/tls.md +++ b/docs/src/reference/net/tls.md @@ -153,7 +153,7 @@ v1HSCliKZXW8cusnBRD2IOyxuIUV/qiMfARylMvlLBccgJR8+olH9f/yF2EFWhoy end } - if not listenfd then + if not listener then print("启动服务器失败") return end @@ -162,7 +162,7 @@ v1HSCliKZXW8cusnBRD2IOyxuIUV/qiMfARylMvlLBccgJR8+olH9f/yF2EFWhoy -- 等待一段时间以处理请求 wg:wait() - tls.close(listenfd) + listener:close() end) ``` @@ -189,8 +189,8 @@ task.fork(function() ip .. ":443", -- 服务器地址 { bind = nil, -- 不绑定本地地址 - server = "www.example.com", -- SNI hostname - alpn = {"http/1.1"} -- ALPN 协议 + hostname = "www.example.com", -- SNI hostname + alpnprotos = {"http/1.1"} -- ALPN 协议 } ) @@ -320,11 +320,11 @@ end) - `cert`: `string` - PEM 格式的证书内容 - `key`: `string` - PEM 格式的私钥内容 - `backlog`: `integer|nil` (可选) - 等待连接队列的最大长度 - - `accept`: `fun(fd: integer, addr: string)` (必需) - 连接处理器,为每个新连接调用 + - `accept`: `fun(conn: silly.net.tls.conn)` (必需) - 连接处理器,为每个新连接调用 - `ciphers`: `string|nil` (可选) - 允许的加密套件,使用 OpenSSL 格式 - `alpnprotos`: `string[]|nil` (可选) - 支持的 ALPN 协议列表,例如 `{"http/1.1", "h2"}` - **返回值**: - - 成功: `integer` - 监听器文件描述符 + - 成功: `silly.net.tls.listener` - 监听器对象 - 失败: `nil, string` - nil 和错误信息 - **示例**: @@ -381,6 +381,14 @@ if not conn then end ``` +### listener:close() + +关闭 TLS 监听器。 + +- **返回值**: + - 成功: `true` + - 失败: `false, string` - false 和错误信息 + ### listener:reload([conf]) 热重载 TLS 服务器的证书配置,无需重启服务。 @@ -554,7 +562,7 @@ print("Remote address:", conn.remoteaddr) 1. **密钥保护**: 私钥文件应设置严格的访问权限(如 `chmod 600`) 2. **加密套件**: 生产环境建议配置 `ciphers` 参数,禁用不安全的加密算法 -3. **证书更新**: 使用 `tls.reload()` 定期更新证书,避免证书过期 +3. **证书更新**: 使用 `listener:reload()` 定期更新证书,避免证书过期 4. **ALPN 协商**: 使用 `alpnprotos` 明确支持的协议,避免协议降级攻击 ### 常见错误 diff --git a/docs/src/reference/net/websocket.md b/docs/src/reference/net/websocket.md index 50006dbcd..cdc3f71a7 100644 --- a/docs/src/reference/net/websocket.md +++ b/docs/src/reference/net/websocket.md @@ -95,7 +95,7 @@ task.fork(function() else -- 处理普通 HTTP 请求 stream:respond(200, {["content-type"] = "text/plain"}) - stream:close("Not a WebSocket request") + stream:closewrite("Not a WebSocket request") end end } @@ -286,7 +286,7 @@ task.fork(function() end else stream:respond(404, {}) - stream:close() + stream:closewrite() end end } diff --git a/docs/src/tutorials/database-app.md b/docs/src/tutorials/database-app.md index c565a40a9..12db85372 100644 --- a/docs/src/tutorials/database-app.md +++ b/docs/src/tutorials/database-app.md @@ -584,14 +584,14 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #body, }) - stream:close(body) + stream:closewrite(body) else local body = json.encode({success = false, error = err}) stream:respond(500, { ["content-type"] = "application/json", ["content-length"] = #body, }) - stream:close(body) + stream:closewrite(body) end return end @@ -606,14 +606,14 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #body, }) - stream:close(body) + stream:closewrite(body) else local body = json.encode({success = false, error = err}) stream:respond(404, { ["content-type"] = "application/json", ["content-length"] = #body, }) - stream:close(body) + stream:closewrite(body) end return end @@ -627,7 +627,7 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) return end @@ -641,7 +641,7 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) return end @@ -655,14 +655,14 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) else local resp = json.encode({success = false, error = err}) stream:respond(500, { ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) end return end @@ -677,7 +677,7 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) return end @@ -691,7 +691,7 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) return end @@ -702,7 +702,7 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) else local resp = json.encode({success = false, error = err}) local status = err == "用户不存在" and 404 or 500 @@ -710,7 +710,7 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) end return end @@ -725,7 +725,7 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) else local resp = json.encode({success = false, error = err}) local status = err == "用户不存在" and 404 or 500 @@ -733,7 +733,7 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) end return end @@ -744,7 +744,7 @@ local function handle_request(stream) ["content-type"] = "application/json", ["content-length"] = #resp, }) - stream:close(resp) + stream:closewrite(resp) end -- 启动 HTTP 服务器 diff --git a/docs/src/tutorials/websocket-chat.md b/docs/src/tutorials/websocket-chat.md index a0eba1a37..e195bb6e8 100644 --- a/docs/src/tutorials/websocket-chat.md +++ b/docs/src/tutorials/websocket-chat.md @@ -78,7 +78,7 @@ http.listen { handler = function(stream) if stream.header["upgrade"] ~= "websocket" then stream:respond(404, {}) - stream:close("Not Found") + stream:closewrite("Not Found") return end @@ -156,7 +156,7 @@ http.listen { handler = function(stream) if stream.header["upgrade"] ~= "websocket" then stream:respond(404, {}) - stream:close("Not Found") + stream:closewrite("Not Found") return end @@ -248,7 +248,7 @@ http.listen { handler = function(stream) if stream.header["upgrade"] ~= "websocket" then stream:respond(404, {}) - stream:close("Not Found") + stream:closewrite("Not Found") return end @@ -377,7 +377,7 @@ http.listen { handler = function(stream) if stream.header["upgrade"] ~= "websocket" then stream:respond(404, {}) - stream:close("Not Found") + stream:closewrite("Not Found") return end @@ -715,7 +715,7 @@ http.listen { handler = function(stream) if stream.header["upgrade"] ~= "websocket" then stream:respond(404, {}) - stream:close("Not Found") + stream:closewrite("Not Found") return end