From af10c59eaf0220aadd52b763d9ab4e78aeed78ab Mon Sep 17 00:00:00 2001 From: Cloud Wu Date: Mon, 3 Nov 2014 15:18:00 +0800 Subject: [PATCH 01/12] release v0.8.1 --- HISTORY.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index 64c30744..f15ef86c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,12 @@ +v0.8.1 (2014-11-3) +----------- +* Send to an invalid remote service will raise an error +* Bugifx: socket open address string +* Remove sha1 from mysqlaux +* merge lua and sproto bugfix , use crypt lib instead +* Fix a memory leak in socket +* minor bugfix in http module + v0.8.0 (2014-10-27) ----------- * Add mysql client driver From 5fad5d63ae939f1ac3639c0eb011c358162a884f Mon Sep 17 00:00:00 2001 From: Cloud Wu Date: Tue, 4 Nov 2014 18:12:25 +0800 Subject: [PATCH 02/12] socket close fd when auth failed --- lualib/snax/loginserver.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lualib/snax/loginserver.lua b/lualib/snax/loginserver.lua index c099ddb8..3eafa674 100644 --- a/lualib/snax/loginserver.lua +++ b/lualib/snax/loginserver.lua @@ -87,16 +87,17 @@ local function launch_slave(auth_handler) return ok, server, uid, secret end - local function ret_pack(ok, err, ...) + local function ret_pack(fd, ok, err, ...) if ok then skynet.ret(skynet.pack(err, ...)) elseif err ~= socket_error then + socket.close(fd) error(err) end end - skynet.dispatch("lua", function(_,_,...) - ret_pack(pcall(auth, ...)) + skynet.dispatch("lua", function(_,_,fd,...) + ret_pack(fd,pcall(auth,fd,...)) end) end From 765749f6080c144ec3bf14c202b7b67c7219c986 Mon Sep 17 00:00:00 2001 From: Cloud Wu Date: Tue, 4 Nov 2014 18:25:15 +0800 Subject: [PATCH 03/12] accept may not start fd, so it can't close at last --- lualib/snax/loginserver.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lualib/snax/loginserver.lua b/lualib/snax/loginserver.lua index 3eafa674..2605a028 100644 --- a/lualib/snax/loginserver.lua +++ b/lualib/snax/loginserver.lua @@ -87,17 +87,16 @@ local function launch_slave(auth_handler) return ok, server, uid, secret end - local function ret_pack(fd, ok, err, ...) + local function ret_pack(ok, err, ...) if ok then skynet.ret(skynet.pack(err, ...)) - elseif err ~= socket_error then - socket.close(fd) + else error(err) end end - skynet.dispatch("lua", function(_,_,fd,...) - ret_pack(fd,pcall(auth,fd,...)) + skynet.dispatch("lua", function(_,_,...) + ret_pack(pcall(auth, ...)) end) end @@ -164,6 +163,7 @@ local function launch_master(conf) if err ~= socket_error then skynet.error(string.format("invalid client (fd = %d) error = %s", fd, err)) end + socket.start(fd) end socket.close(fd) end) From 929ce385a3b9e2295c4a4c4d2900c08af3df891c Mon Sep 17 00:00:00 2001 From: Cloud Wu Date: Mon, 10 Nov 2014 10:53:29 +0800 Subject: [PATCH 04/12] fprintf need cr --- skynet-src/socket_server.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skynet-src/socket_server.c b/skynet-src/socket_server.c index 11ba4b0b..98248134 100644 --- a/skynet-src/socket_server.c +++ b/skynet-src/socket_server.c @@ -528,7 +528,7 @@ send_socket(struct socket_server *ss, struct request_send * request, struct sock n = 0; break; default: - fprintf(stderr, "socket-server: write to %d (fd=%d) error.",id,s->fd); + fprintf(stderr, "socket-server: write to %d (fd=%d) error :%s.\n",id,s->fd,strerror(errno)); force_close(ss,s,result); return SOCKET_CLOSE; } @@ -660,7 +660,7 @@ block_readpipe(int pipefd, void *buffer, int sz) { if (n<0) { if (errno == EINTR) continue; - fprintf(stderr, "socket-server : read pipe error %s.",strerror(errno)); + fprintf(stderr, "socket-server : read pipe error %s.\n",strerror(errno)); return; } // must atomic read from a pipe From c87dea3d9b6ba089ede598d1dfe87c60afd2f51c Mon Sep 17 00:00:00 2001 From: Cloud Wu Date: Mon, 10 Nov 2014 20:58:10 +0800 Subject: [PATCH 05/12] fix issue #200 --- lualib-src/lua-seri.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib-src/lua-seri.c b/lualib-src/lua-seri.c index fe4bfa46..c41456be 100644 --- a/lualib-src/lua-seri.c +++ b/lualib-src/lua-seri.c @@ -179,7 +179,7 @@ wb_string(struct write_block *wb, const char *str, int len) { wb_push(wb, str, len); } } else { - int n; + uint8_t n; if (len < 0x10000) { n = COMBINE_TYPE(TYPE_LONG_STRING, 2); wb_push(wb, &n, 1); From e06a9e3701a25da87e74537ae01624c0d65fd936 Mon Sep 17 00:00:00 2001 From: Cloud Wu Date: Tue, 11 Nov 2014 11:33:57 +0800 Subject: [PATCH 06/12] dispatch read before write, and try to dispath both --- skynet-src/socket_server.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/skynet-src/socket_server.c b/skynet-src/socket_server.c index 98248134..6ed9efad 100644 --- a/skynet-src/socket_server.c +++ b/skynet-src/socket_server.c @@ -904,15 +904,20 @@ socket_server_poll(struct socket_server *ss, struct socket_message * result, int fprintf(stderr, "socket-server: invalid socket\n"); break; default: - if (e->write) { - int type = send_buffer(ss, s, result); + if (e->read) { + int type = forward_message(ss, s, result); + if (e->write) { + // Try to dispatch write message next step if write flag set. + e->read = false; + --ss->event_index; + } if (type == -1) break; clear_closed_event(ss, result, type); return type; } - if (e->read) { - int type = forward_message(ss, s, result); + if (e->write) { + int type = send_buffer(ss, s, result); if (type == -1) break; clear_closed_event(ss, result, type); From fa78623b1cec717e3e7a6aed047fbd620148c787 Mon Sep 17 00:00:00 2001 From: Cloud Wu Date: Tue, 11 Nov 2014 13:54:43 +0800 Subject: [PATCH 07/12] support user defined send object --- skynet-src/socket_server.c | 86 ++++++++++++++++++++++++++++---------- skynet-src/socket_server.h | 9 ++++ 2 files changed, 73 insertions(+), 22 deletions(-) diff --git a/skynet-src/socket_server.c b/skynet-src/socket_server.c index 6ed9efad..da167658 100644 --- a/skynet-src/socket_server.c +++ b/skynet-src/socket_server.c @@ -40,8 +40,9 @@ struct write_buffer { struct write_buffer * next; char *ptr; - int sz; void *buffer; + int sz; + bool userobject; }; struct wb_list { @@ -68,6 +69,7 @@ struct socket_server { int alloc_id; int event_n; int event_index; + struct socket_object_interface soi; struct event ev[MAX_EVENT]; struct socket slot[MAX_SOCKET]; char buffer[MAX_INFO]; @@ -137,9 +139,40 @@ union sockaddr_all { struct sockaddr_in6 v6; }; +struct send_object { + void * buffer; + int sz; + void (*free_func)(void *); +}; + #define MALLOC skynet_malloc #define FREE skynet_free +static inline bool +send_object_init(struct socket_server *ss, struct send_object *so, void *object, int sz) { + if (sz < 0) { + so->buffer = ss->soi.buffer(object); + so->sz = ss->soi.size(object); + so->free_func = ss->soi.free; + return true; + } else { + so->buffer = object; + so->sz = sz; + so->free_func = FREE; + return false; + } +} + +static inline void +write_buffer_free(struct socket_server *ss, struct write_buffer *wb) { + if (wb->userobject) { + ss->soi.free(wb->buffer); + } else { + FREE(wb->buffer); + } + FREE(wb); +} + static void socket_keepalive(int fd) { int keepalive = 1; @@ -213,6 +246,7 @@ socket_server_create() { ss->alloc_id = 0; ss->event_n = 0; ss->event_index = 0; + memset(&ss->soi, 0, sizeof(ss->soi)); FD_ZERO(&ss->rfds); assert(ss->recvctrl_fd < FD_SETSIZE); @@ -220,13 +254,12 @@ socket_server_create() { } static void -free_wb_list(struct wb_list *list) { +free_wb_list(struct socket_server *ss, struct wb_list *list) { struct write_buffer *wb = list->head; while (wb) { struct write_buffer *tmp = wb; wb = wb->next; - FREE(tmp->buffer); - FREE(tmp); + write_buffer_free(ss, tmp); } list->head = NULL; list->tail = NULL; @@ -242,8 +275,8 @@ force_close(struct socket_server *ss, struct socket *s, struct socket_message *r return; } assert(s->type != SOCKET_TYPE_RESERVE); - free_wb_list(&s->high); - free_wb_list(&s->low); + free_wb_list(ss,&s->high); + free_wb_list(ss,&s->low); if (s->type != SOCKET_TYPE_PACCEPT && s->type != SOCKET_TYPE_PLISTEN) { sp_del(ss->event_fd, s->fd); } @@ -395,8 +428,7 @@ send_list(struct socket_server *ss, struct socket *s, struct wb_list *list, stru break; } list->head = tmp->next; - FREE(tmp->buffer); - FREE(tmp); + write_buffer_free(ss,tmp); } list->tail = NULL; @@ -469,10 +501,12 @@ send_buffer(struct socket_server *ss, struct socket *s, struct socket_message *r } static int -append_sendbuffer_(struct wb_list *s, struct request_send * request, int n) { +append_sendbuffer_(struct socket_server *ss, struct wb_list *s, struct request_send * request, int n) { struct write_buffer * buf = MALLOC(sizeof(*buf)); - buf->ptr = request->buffer+n; - buf->sz = request->sz - n; + struct send_object so; + buf->userobject = send_object_init(ss, &so, request->buffer, request->sz); + buf->ptr = so.buffer+n; + buf->sz = so.sz - n; buf->buffer = request->buffer; buf->next = NULL; if (s->head == NULL) { @@ -487,13 +521,13 @@ append_sendbuffer_(struct wb_list *s, struct request_send * request, int n) { } static inline void -append_sendbuffer(struct socket *s, struct request_send * request, int n) { - s->wb_size += append_sendbuffer_(&s->high, request, n); +append_sendbuffer(struct socket_server *ss, struct socket *s, struct request_send * request, int n) { + s->wb_size += append_sendbuffer_(ss, &s->high, request, n); } static inline void -append_sendbuffer_low(struct socket *s, struct request_send * request) { - s->wb_size += append_sendbuffer_(&s->low, request, 0); +append_sendbuffer_low(struct socket_server *ss,struct socket *s, struct request_send * request) { + s->wb_size += append_sendbuffer_(ss, &s->low, request, 0); } static inline int @@ -512,15 +546,17 @@ static int send_socket(struct socket_server *ss, struct request_send * request, struct socket_message *result, int priority) { int id = request->id; struct socket * s = &ss->slot[HASH_ID(id)]; + struct send_object so; + send_object_init(ss, &so, request->buffer, request->sz); if (s->type == SOCKET_TYPE_INVALID || s->id != id || s->type == SOCKET_TYPE_HALFCLOSE || s->type == SOCKET_TYPE_PACCEPT) { - FREE(request->buffer); + so.free_func(request->buffer); return -1; } assert(s->type != SOCKET_TYPE_PLISTEN && s->type != SOCKET_TYPE_LISTEN); if (send_buffer_empty(s) && s->type == SOCKET_TYPE_CONNECTED) { - int n = write(s->fd, request->buffer, request->sz); + int n = write(s->fd, so.buffer, so.sz); if (n<0) { switch(errno) { case EINTR: @@ -533,17 +569,17 @@ send_socket(struct socket_server *ss, struct request_send * request, struct sock return SOCKET_CLOSE; } } - if (n == request->sz) { - FREE(request->buffer); + if (n == so.sz) { + so.free_func(request->buffer); return -1; } - append_sendbuffer(s, request, n); // add to high priority list, even priority == PRIORITY_LOW + append_sendbuffer(ss, s, request, n); // add to high priority list, even priority == PRIORITY_LOW sp_write(ss->event_fd, s->fd, s, true); } else { if (priority == PRIORITY_LOW) { - append_sendbuffer_low(s, request); + append_sendbuffer_low(ss, s, request); } else { - append_sendbuffer(s, request, 0); + append_sendbuffer(ss, s, request, 0); } } return -1; @@ -1092,3 +1128,9 @@ socket_server_nodelay(struct socket_server *ss, int id) { request.u.setopt.value = 1; send_request(ss, &request, 'T', sizeof(request.u.setopt)); } + +void +socket_server_userobject(struct socket_server *ss, struct socket_object_interface *soi) { + ss->soi = *soi; +} + diff --git a/skynet-src/socket_server.h b/skynet-src/socket_server.h index 66648719..a4dcf71e 100644 --- a/skynet-src/socket_server.h +++ b/skynet-src/socket_server.h @@ -38,4 +38,13 @@ int socket_server_bind(struct socket_server *, uintptr_t opaque, int fd); void socket_server_nodelay(struct socket_server *, int id); +struct socket_object_interface { + void * (*buffer)(void *); + int (*size)(void *); + void (*free)(void *); +}; + +// if you send package sz == -1, use soi. +void socket_server_userobject(struct socket_server *, struct socket_object_interface *soi); + #endif From 8e4a1751550da9dad68c588a741afe6b973e36af Mon Sep 17 00:00:00 2001 From: Cloud Wu Date: Thu, 13 Nov 2014 11:19:58 +0800 Subject: [PATCH 08/12] use fork to dispatch more socket message --- lualib-src/lua-netpack.c | 41 ++++++++++++-------------------------- lualib/snax/gateserver.lua | 24 ++++++++++++++++------ 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/lualib-src/lua-netpack.c b/lualib-src/lua-netpack.c index e424f90c..f42e21ca 100644 --- a/lualib-src/lua-netpack.c +++ b/lualib-src/lua-netpack.c @@ -240,19 +240,12 @@ filter_data_(lua_State *L, int fd, uint8_t * buffer, int size) { buffer += need; size -= need; if (size == 0) { - if (q == NULL || q->head == q->tail ) { - lua_pushvalue(L, lua_upvalueindex(TYPE_DATA)); - lua_pushinteger(L, fd); - lua_pushlightuserdata(L, uc->pack.buffer); - lua_pushinteger(L, uc->pack.size); - skynet_free(uc); - return 5; - } - else{ - push_data(L, fd, uc->pack.buffer, uc->pack.size, 0); - skynet_free(uc); - return 1; - } + lua_pushvalue(L, lua_upvalueindex(TYPE_DATA)); + lua_pushinteger(L, fd); + lua_pushlightuserdata(L, uc->pack.buffer); + lua_pushinteger(L, uc->pack.size); + skynet_free(uc); + return 5; } // more data push_data(L, fd, uc->pack.buffer, uc->pack.size, 0); @@ -281,21 +274,13 @@ filter_data_(lua_State *L, int fd, uint8_t * buffer, int size) { } if (size == pack_size) { // just one package - if ( q == NULL || q->head == q->tail) { - lua_pushvalue(L, lua_upvalueindex(TYPE_DATA)); - lua_pushinteger(L, fd); - void * result = skynet_malloc(pack_size); - memcpy(result, buffer, size); - lua_pushlightuserdata(L, result); - lua_pushinteger(L, size); - return 5; - } - else{ - push_data(L, fd, buffer, pack_size, 1); - buffer += pack_size; - size -= pack_size; - return 1; - } + lua_pushvalue(L, lua_upvalueindex(TYPE_DATA)); + lua_pushinteger(L, fd); + void * result = skynet_malloc(pack_size); + memcpy(result, buffer, size); + lua_pushlightuserdata(L, result); + lua_pushinteger(L, size); + return 5; } // more data push_data(L, fd, buffer, pack_size, 1); diff --git a/lualib/snax/gateserver.lua b/lualib/snax/gateserver.lua index 6cce5266..e68efc12 100644 --- a/lualib/snax/gateserver.lua +++ b/lualib/snax/gateserver.lua @@ -53,20 +53,32 @@ function gateserver.start(handler) local MSG = {} - function MSG.data(fd, msg, sz) + local function dispatch_msg(fd, msg, sz) if connection[fd] then handler.message(fd, msg, sz) + else + skynet.error(string.format("Drop message from fd (%d) : %s", fd, netpack.tostring(msg,sz))) end end - function MSG.more() - for fd, msg, sz in netpack.pop, queue do - if connection[fd] then - handler.message(fd, msg, sz) + MSG.data = dispatch_msg + + local function dispatch_queue() + local fd, msg, sz = netpack.pop(queue) + if fd then + -- may dispatch even the handler.message blocked + -- If the handler.message never block, the queue should be empty, so only fork once and then exit. + skynet.fork(dispatch_queue) + dispatch_msg(fd, msg, sz) + + for fd, msg, sz in netpack.pop, queue do + dispatch_msg(fd, msg, sz) end end end + MSG.more = dispatch_queue + function MSG.open(fd, msg) if client_number >= maxclient then socketdriver.close(fd) @@ -128,4 +140,4 @@ function gateserver.start(handler) end) end -return gateserver \ No newline at end of file +return gateserver From b9e06dc0656e7be9e039201bc2b9efa175e53ac4 Mon Sep 17 00:00:00 2001 From: Cloud Wu Date: Fri, 14 Nov 2014 15:11:05 +0800 Subject: [PATCH 09/12] add snax.self and snax.exit --- lualib/snax.lua | 8 ++++++++ test/pingserver.lua | 5 +++++ test/testping.lua | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lualib/snax.lua b/lualib/snax.lua index f810e392..6a01460f 100644 --- a/lualib/snax.lua +++ b/lualib/snax.lua @@ -132,6 +132,14 @@ function snax.kill(obj, ...) skynet_call(obj.handle, "snax", t.system.exit, ...) end +function snax.self() + return snax.bind(skynet.self(), SERVICE_NAME) +end + +function snax.exit(...) + snax.kill(snax.self(), ...) +end + local function test_result(ok, ...) if ok then return ... diff --git a/test/pingserver.lua b/test/pingserver.lua index 2ddefd7f..5c44a21a 100644 --- a/test/pingserver.lua +++ b/test/pingserver.lua @@ -1,5 +1,6 @@ local skynet = require "skynet" local queue = require "skynet.queue" +local snax = require "snax" local i = 0 local hello = "hello" @@ -32,6 +33,10 @@ function accept.hello() end) end +function accept.exit(...) + snax.exit(...) +end + function response.error() error "throw an error" end diff --git a/test/testping.lua b/test/testping.lua index c9f05e63..fb0514e8 100644 --- a/test/testping.lua +++ b/test/testping.lua @@ -31,6 +31,6 @@ end print(string.format("%s\tcount:%d time:%f", name, v.count, v.time)) end - print(snax.kill(ps,"exit")) + print(ps.post.exit("exit")) -- == snax.kill(ps, "exit") skynet.exit() end) From 6df72e58420a5688f80f12bef359bf2606473506 Mon Sep 17 00:00:00 2001 From: Cloud Wu Date: Fri, 14 Nov 2014 16:13:48 +0800 Subject: [PATCH 10/12] remove snax queue mode --- service/snaxd.lua | 69 ++------------------------------------------- test/pingserver.lua | 3 -- 2 files changed, 3 insertions(+), 69 deletions(-) diff --git a/service/snaxd.lua b/service/snaxd.lua index 0ff9baa0..8be269e9 100644 --- a/service/snaxd.lua +++ b/service/snaxd.lua @@ -12,10 +12,6 @@ package.path = snax_path .. "?.lua;" .. package.path SERVICE_NAME = snax_name SERVICE_PATH = snax_path -local mode -local thread_id -local message_queue = {} -local init = false local profile_table = {} local function update_stat(name, ti) @@ -38,57 +34,6 @@ local function dispatch(f, ...) return skynet.pack(f(...)) end -local function message_dispatch() - while true do - if #message_queue==0 then - thread_id = coroutine.running() - skynet.wait() - else - local msg = table.remove(message_queue,1) - local method = msg.method - local f = method[4] - if f then - if method[2] == "accept" then - -- no return - profile.start() - local ok, data = xpcall(f, traceback, table.unpack(msg)) - local ti = profile.stop() - update_stat(method[3], ti) - if not ok then - print(string.format("Error on [:%x] to [:%x] %s", msg.source, skynet.self(), tostring(data))) - end - else - profile.start() - local ok, data, size = xpcall(dispatch, traceback, f, table.unpack(msg)) - local ti = profile.stop() - update_stat(method[3], ti) - if ok then - -- skynet.PTYPE_RESPONSE == 1 - c.send(msg.source, 1, msg.session, data, size) - if method[2] == "system" then - init = false - skynet.exit() - break - end - else - -- Can't throw error, so print it directly - print(string.format("Error on [:%x] to [:%x] %s", msg.source, skynet.self(), tostring(data))) - c.send(msg.source, skynet.PTYPE_ERROR, msg.session, "") - end - end - end - end - end -end - -local function queue( session, source, method, ...) - table.insert(message_queue, {session = session, source = source, method = method, ... }) - if thread_id then - skynet.wakeup(thread_id) - thread_id = nil - end -end - local function return_f(f, ...) return skynet.ret(skynet.pack(f(...))) end @@ -109,6 +54,7 @@ end skynet.start(function() skynet.dispatch("snax", function ( session , source , id, ...) + local init = false local method = func[id] if method[2] == "system" then @@ -119,17 +65,12 @@ skynet.start(function() elseif command == "init" then assert(not init, "Already init") local initfunc = method[4] or function() end - mode = initfunc(...) - if mode == "queue" then - skynet.fork(message_dispatch) - end + initfunc(...) skynet.ret() skynet.info_func(function() return profile_table end) init = true - elseif mode == "queue" then - queue( session, source, method , ...) else assert(init, "Never init") assert(command == "exit") @@ -141,11 +82,7 @@ skynet.start(function() end else assert(init, "Init first") - if mode == "queue" then - queue(session, source, method , ...) - else - timing(method, ...) - end + timing(method, ...) end end) end) diff --git a/test/pingserver.lua b/test/pingserver.lua index 5c44a21a..65a25405 100644 --- a/test/pingserver.lua +++ b/test/pingserver.lua @@ -45,9 +45,6 @@ function init( ... ) print ("ping server start:", ...) -- init queue lock = queue() - --- You can return "queue" for queue service mode --- return "queue" end function exit(...) From 6ee014dd99323fedebe2e8e511265920f40550ac Mon Sep 17 00:00:00 2001 From: Cloud Wu Date: Sat, 15 Nov 2014 21:28:03 +0800 Subject: [PATCH 11/12] bugfix: init should be out --- service/snaxd.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/snaxd.lua b/service/snaxd.lua index 8be269e9..02e3738c 100644 --- a/service/snaxd.lua +++ b/service/snaxd.lua @@ -53,8 +53,8 @@ local function timing( method, ... ) end skynet.start(function() + local init = false skynet.dispatch("snax", function ( session , source , id, ...) - local init = false local method = func[id] if method[2] == "system" then From 3be0f507221617f68b27b81e1ef6cc0dafc062df Mon Sep 17 00:00:00 2001 From: Cloud Wu Date: Sat, 15 Nov 2014 21:33:25 +0800 Subject: [PATCH 12/12] use snax.newservice would be better --- test/testping.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/testping.lua b/test/testping.lua index fb0514e8..c40392cf 100644 --- a/test/testping.lua +++ b/test/testping.lua @@ -2,7 +2,7 @@ local skynet = require "skynet" local snax = require "snax" skynet.start(function() - local ps = snax.uniqueservice ("pingserver", "hello world") + local ps = snax.newservice ("pingserver", "hello world") print(ps.req.ping("foobar")) print(ps.post.hello()) print(pcall(ps.req.error))