skynet.redirect support string address

This commit is contained in:
Cloud Wu
2014-08-11 15:29:48 +08:00
14 changed files with 376 additions and 235 deletions

View File

@@ -6,6 +6,20 @@ Dev version
* skynet.newservice throw error when lanuch faild * skynet.newservice throw error when lanuch faild
* Don't check imported function in snax.hotfix * Don't check imported function in snax.hotfix
* snax service add change SERVICE_PATH and add it to package.path * snax service add change SERVICE_PATH and add it to package.path
* skynet.redirect support string address
v0.5.2 (2014-8-11)
-----------
* Bugfix : httpd request
* Bugifx : http chunked mode
* Add : httpc
* timer support more than 497 days
v0.5.1 (2014-8-4)
-----------
* Bugfix : http module
* Bugfix : multicast local channel delete
* Bugfix : socket.read(fd)
v0.5.0 (2014-7-28) v0.5.0 (2014-7-28)
----------- -----------

View File

@@ -39,6 +39,11 @@ skynet.start(function()
table.insert(tmp, string.format("query: %s= %s", k,v)) table.insert(tmp, string.format("query: %s= %s", k,v))
end end
end end
table.insert(tmp, "-----header----")
for k,v in pairs(header) do
table.insert(tmp, string.format("%s = %s",k,v))
end
table.insert(tmp, "-----body----\n" .. body)
response(id, code, table.concat(tmp,"\n")) response(id, code, table.concat(tmp,"\n"))
end end
else else

View File

@@ -115,46 +115,6 @@ _genid(lua_State *L) {
return 1; return 1;
} }
// copy from _send
static int
_sendname(lua_State *L, struct skynet_context * context, const char * dest) {
int type = luaL_checkinteger(L, 2);
int session = 0;
if (lua_isnil(L,3)) {
type |= PTYPE_TAG_ALLOCSESSION;
} else {
session = luaL_checkinteger(L,3);
}
int mtype = lua_type(L,4);
switch (mtype) {
case LUA_TSTRING: {
size_t len = 0;
void * msg = (void *)lua_tolstring(L,4,&len);
session = skynet_sendname(context, dest, type, session , msg, len);
break;
}
case LUA_TNIL :
session = skynet_sendname(context, dest, type, session , NULL, 0);
break;
case LUA_TLIGHTUSERDATA: {
luaL_checktype(L, 4, LUA_TLIGHTUSERDATA);
void * msg = lua_touserdata(L,4);
int size = luaL_checkinteger(L,5);
session = skynet_sendname(context, dest, type | PTYPE_TAG_DONTCOPY, session, msg, size);
break;
}
default:
luaL_error(L, "skynet.send invalid param %s", lua_typename(L,lua_type(L,4)));
}
if (session < 0) {
return 0;
}
lua_pushinteger(L,session);
return 1;
}
/* /*
unsigned address unsigned address
string address string address
@@ -167,28 +127,10 @@ _sendname(lua_State *L, struct skynet_context * context, const char * dest) {
static int static int
_send(lua_State *L) { _send(lua_State *L) {
struct skynet_context * context = lua_touserdata(L, lua_upvalueindex(1)); struct skynet_context * context = lua_touserdata(L, lua_upvalueindex(1));
int addr_type = lua_type(L,1); uint32_t dest = lua_tounsigned(L, 1);
uint32_t dest = 0; const char * dest_string = NULL;
switch(addr_type) { if (dest == 0) {
case LUA_TNUMBER: dest_string = lua_tostring(L, 1);
dest = lua_tounsigned(L,1);
break;
case LUA_TSTRING: {
const char * addrname = lua_tostring(L,1);
if (addrname[0] == '.' || addrname[0] == ':') {
dest = skynet_queryname(context, addrname);
if (dest == 0) {
luaL_error(L, "Invalid name %s", addrname);
}
} else if ('0' <= addrname[0] && addrname[0] <= '9') {
luaL_error(L, "Invalid name %s: must not start with a digit", addrname);
} else {
return _sendname(L, context, addrname);
}
break;
}
default:
return luaL_error(L, "address must be number or string, got %s",lua_typename(L,addr_type));
} }
int type = luaL_checkinteger(L, 2); int type = luaL_checkinteger(L, 2);
@@ -207,13 +149,21 @@ _send(lua_State *L) {
if (len == 0) { if (len == 0) {
msg = NULL; msg = NULL;
} }
session = skynet_send(context, 0, dest, type, session , msg, len); if (dest_string) {
session = skynet_sendname(context, 0, dest_string, type, session , msg, len);
} else {
session = skynet_send(context, 0, dest, type, session , msg, len);
}
break; break;
} }
case LUA_TLIGHTUSERDATA: { case LUA_TLIGHTUSERDATA: {
void * msg = lua_touserdata(L,4); void * msg = lua_touserdata(L,4);
int size = luaL_checkinteger(L,5); int size = luaL_checkinteger(L,5);
session = skynet_send(context, 0, dest, type | PTYPE_TAG_DONTCOPY, session, msg, size); if (dest_string) {
session = skynet_sendname(context, 0, dest_string, type | PTYPE_TAG_DONTCOPY, session, msg, size);
} else {
session = skynet_send(context, 0, dest, type | PTYPE_TAG_DONTCOPY, session, msg, size);
}
break; break;
} }
default: default:
@@ -231,7 +181,11 @@ _send(lua_State *L) {
static int static int
_redirect(lua_State *L) { _redirect(lua_State *L) {
struct skynet_context * context = lua_touserdata(L, lua_upvalueindex(1)); struct skynet_context * context = lua_touserdata(L, lua_upvalueindex(1));
uint32_t dest = luaL_checkunsigned(L,1); uint32_t dest = lua_tounsigned(L,1);
const char * dest_string = NULL;
if (dest == 0) {
dest_string = lua_tostring(L,1);
}
uint32_t source = luaL_checkunsigned(L,2); uint32_t source = luaL_checkunsigned(L,2);
int type = luaL_checkinteger(L,3); int type = luaL_checkinteger(L,3);
int session = luaL_checkinteger(L,4); int session = luaL_checkinteger(L,4);
@@ -244,13 +198,21 @@ _redirect(lua_State *L) {
if (len == 0) { if (len == 0) {
msg = NULL; msg = NULL;
} }
session = skynet_send(context, source, dest, type, session , msg, len); if (dest_string) {
session = skynet_sendname(context, source, dest_string, type, session , msg, len);
} else {
session = skynet_send(context, source, dest, type, session , msg, len);
}
break; break;
} }
case LUA_TLIGHTUSERDATA: { case LUA_TLIGHTUSERDATA: {
void * msg = lua_touserdata(L,5); void * msg = lua_touserdata(L,5);
int size = luaL_checkinteger(L,6); int size = luaL_checkinteger(L,6);
session = skynet_send(context, source, dest, type | PTYPE_TAG_DONTCOPY, session, msg, size); if (dest_string) {
session = skynet_sendname(context, source, dest_string, type | PTYPE_TAG_DONTCOPY, session, msg, size);
} else {
session = skynet_send(context, source, dest, type | PTYPE_TAG_DONTCOPY, session, msg, size);
}
break; break;
} }
default: default:
@@ -320,7 +282,7 @@ ltrash(lua_State *L) {
} }
int int
luaopen_skynet_c(lua_State *L) { luaopen_skynet_core(lua_State *L) {
luaL_checkversion(L); luaL_checkversion(L);
luaL_Reg l[] = { luaL_Reg l[] = {

114
lualib/http/httpc.lua Normal file
View File

@@ -0,0 +1,114 @@
local socket = require "http.sockethelper"
local url = require "http.url"
local internal = require "http.internal"
local string = string
local table = table
local httpc = {}
local function request(fd, method, host, url, recvheader, header, content)
local read = socket.readfunc(fd)
local write = socket.writefunc(fd)
local header_content = ""
if header then
for k,v in pairs(header) do
header_content = string.format("%s%s:%s\r\n", header_content, k, v)
end
end
if content then
local data = string.format("%s %s HTTP/1.1\r\nhost:%s\r\ncontent-length:%d\r\n%s\r\n%s", method, url, host, #content, header_content, content)
write(data)
else
local request_header = string.format("%s %s HTTP/1.1\r\nhost:%s\r\ncontent-length:0\r\n%s\r\n", method, url, host, header_content)
write(request_header)
end
local tmpline = {}
local body = internal.recvheader(read, tmpline, "")
if not body then
error(socket.socket_error)
end
local statusline = tmpline[1]
local code, info = statusline:match "HTTP/[%d%.]+%s+([%d]+)%s+(.*)$"
code = assert(tonumber(code))
local header = internal.parseheader(tmpline,2,recvheader or {})
if not header then
error("Invalid HTTP response header")
end
local length = header["content-length"]
if length then
length = tonumber(length)
end
local mode = header["transfer-encoding"]
if mode then
if mode ~= "identity" and mode ~= "chunked" then
error ("Unsupport transfer-encoding")
end
end
if mode == "chunked" then
body, header = internal.recvchunkedbody(read, nil, header, body)
if not body then
error("Invalid response body")
end
else
-- identity mode
if length then
if #body >= length then
body = body:sub(1,length)
else
local padding = read(length - #body)
body = body .. padding
end
else
body = nil
end
end
return code, body
end
function httpc.request(method, host, url, recvheader, header, content)
local hostname, port = host:match"([^:]+):?(%d*)$"
if port == "" then
port = 80
else
port = tonumber(port)
end
local fd = socket.connect(hostname, port)
local ok , statuscode, body = pcall(request, fd,method, host, url, recvheader, header, content)
if ok then
return statuscode, body
else
socket.close(fd)
error(statuscode)
end
end
function httpc.get(...)
return httpc.request("GET", ...)
end
local function escape(s)
return (string.gsub(s, "([^A-Za-z0-9_])", function(c)
return string.format("%%%02X", string.byte(c))
end))
end
function httpc.post(host, url, form, recvheader)
local header = {
["content-type"] = "application/x-www-form-urlencoded"
}
local body = {}
for k,v in pairs(form) do
table.insert(body, string.format("%s=%s",escape(k),escape(v)))
end
return httpc.request("POST", host, url, recvheader, header, table.concat(body , "&"))
end
return httpc

View File

@@ -1,3 +1,5 @@
local internal = require "http.internal"
local table = table local table = table
local httpd = {} local httpd = {}
@@ -45,140 +47,9 @@ local http_status_msg = {
[505] = "HTTP Version not supported", [505] = "HTTP Version not supported",
} }
local function recvheader(readbytes, limit, lines, header)
local result
local e = header:find("\r\n\r\n", 1, true)
if e then
result = header:sub(e+4)
else
while true do
local bytes = readbytes()
header = header .. bytes
if #header > limit then
return
end
e = header:find("\r\n\r\n", -#bytes-3, true)
if e then
result = header:sub(e+4)
break
end
end
end
local idx = 1
for v in header:gmatch("(.-)\r\n") do
if v == "" then
break
end
lines[idx] = v
idx = idx + 1
end
for i = idx, #lines do
lines[i] = nil
end
return result
end
local function parseheader(lines, from, header)
local name, value
for i=from,#lines do
local line = lines[i]
if line:byte(1) == 9 then -- tab, append last line
if name == nil then
return
end
header[name] = header[name] .. line:sub(2)
else
name, value = line:match "^(.-):%s*(.*)"
if name == nil or value == nil then
return
end
name = name:lower()
if header[name] then
header[name] = header[name] .. ", " .. value
else
header[name] = value
end
end
end
return header
end
local function chunksize(readbytes, body)
while true do
if #body > 128 then
return
end
body = body .. readbytes()
local f,e = body:find("\r\n",1,true)
if f then
return tonumber(body:sub(1,f-1),16), body:sub(e+1)
end
end
end
local function readcrln(readbytes, body)
if #body > 2 then
if body:sub(1,2) ~= "\r\n" then
return
end
return body:sub(3)
else
body = body .. readbytes(2-#body)
if body ~= "\r\n" then
return
end
return ""
end
end
local tmpline = {}
local function recvchunkedbody(readbytes, bodylimit, header, body)
local result = ""
local size = 0
while true do
local sz
sz , body = chunksize(readbytes, body)
if not sz then
return
end
if sz == 0 then
break
end
size = size + sz
if bodylimit and size > bodylimit then
return
end
if #body >= sz then
result = result .. body:sub(1,sz)
body = body:sub(sz+1)
else
result = result .. body .. readbytes(sz - #body)
body = ""
end
body = readcrln(readbytes, body)
if not body then
return
end
end
body = readcrln(readbytes, body)
if not body then
return
end
body = recvheader(readbytes, 8192, tmpline, body)
if not body then
return
end
header = parseheader(tmpline,1,header)
return result, header
end
local function readall(readbytes, bodylimit) local function readall(readbytes, bodylimit)
local body = recvheader(readbytes, 8192, tmpline, "") local tmpline = {}
local body = internal.recvheader(readbytes, tmpline, "")
if not body then if not body then
return 413 -- Request Entity Too Large return 413 -- Request Entity Too Large
end end
@@ -189,7 +60,7 @@ local function readall(readbytes, bodylimit)
if httpver < 1.0 or httpver > 1.1 then if httpver < 1.0 or httpver > 1.1 then
return 505 -- HTTP Version not supported return 505 -- HTTP Version not supported
end end
local header = parseheader(tmpline,2,{}) local header = internal.parseheader(tmpline,2,{})
if not header then if not header then
return 400 -- Bad request return 400 -- Bad request
end end
@@ -199,13 +70,13 @@ local function readall(readbytes, bodylimit)
end end
local mode = header["transfer-encoding"] local mode = header["transfer-encoding"]
if mode then if mode then
if mode ~= "identity" or mode ~= "chunked" then if mode ~= "identity" and mode ~= "chunked" then
return 501 -- Not Implemented return 501 -- Not Implemented
end end
end end
if mode == "chunked" then if mode == "chunked" then
body, header = recvchunkedbody(readbytes, bodylimit, header, body) body, header = internal.recvchunkedbody(readbytes, bodylimit, header, body)
if not body then if not body then
return 413 return 413
end end

135
lualib/http/internal.lua Normal file
View File

@@ -0,0 +1,135 @@
local M = {}
local LIMIT = 8192
local function chunksize(readbytes, body)
while true do
if #body > 128 then
return
end
body = body .. readbytes()
local f,e = body:find("\r\n",1,true)
if f then
return tonumber(body:sub(1,f-1),16), body:sub(e+1)
end
end
end
local function readcrln(readbytes, body)
if #body >= 2 then
if body:sub(1,2) ~= "\r\n" then
return
end
return body:sub(3)
else
body = body .. readbytes(2-#body)
if body ~= "\r\n" then
return
end
return ""
end
end
function M.recvheader(readbytes, lines, header)
if #header >= 2 then
if header:find "^\r\n" then
return header:sub(3)
end
end
local result
local e = header:find("\r\n\r\n", 1, true)
if e then
result = header:sub(e+4)
else
while true do
local bytes = readbytes()
header = header .. bytes
if #header > LIMIT then
return
end
e = header:find("\r\n\r\n", -#bytes-3, true)
if e then
result = header:sub(e+4)
break
end
if header:find "^\r\n" then
return header:sub(3)
end
end
end
for v in header:gmatch("(.-)\r\n") do
if v == "" then
break
end
table.insert(lines, v)
end
return result
end
function M.parseheader(lines, from, header)
local name, value
for i=from,#lines do
local line = lines[i]
if line:byte(1) == 9 then -- tab, append last line
if name == nil then
return
end
header[name] = header[name] .. line:sub(2)
else
name, value = line:match "^(.-):%s*(.*)"
if name == nil or value == nil then
return
end
name = name:lower()
if header[name] then
header[name] = header[name] .. ", " .. value
else
header[name] = value
end
end
end
return header
end
function M.recvchunkedbody(readbytes, bodylimit, header, body)
local result = ""
local size = 0
while true do
local sz
sz , body = chunksize(readbytes, body)
if not sz then
return
end
if sz == 0 then
break
end
size = size + sz
if bodylimit and size > bodylimit then
return
end
if #body >= sz then
result = result .. body:sub(1,sz)
body = body:sub(sz+1)
else
result = result .. body .. readbytes(sz - #body)
body = ""
end
body = readcrln(readbytes, body)
if not body then
return
end
end
local tmpline = {}
body = M.recvheader(readbytes, tmpline, body)
if not body then
return
end
header = M.parseheader(tmpline,1,header)
return result, header
end
return M

View File

@@ -28,4 +28,16 @@ function sockethelper.writefunc(fd)
end end
end end
function sockethelper.connect(host, port)
local fd = socket.open(host, port)
if fd then
return fd
end
error(socket_error)
end
function sockethelper.close(fd)
socket.close(fd)
end
return sockethelper return sockethelper

View File

@@ -1,4 +1,4 @@
local c = require "skynet.c" local c = require "skynet.core"
local tostring = tostring local tostring = tostring
local tonumber = tonumber local tonumber = tonumber
local coroutine = coroutine local coroutine = coroutine

View File

@@ -55,7 +55,7 @@ traceback (lua_State *L) {
static void static void
_report_launcher_error(struct skynet_context *ctx) { _report_launcher_error(struct skynet_context *ctx) {
// sizeof "ERROR" == 5 // sizeof "ERROR" == 5
skynet_sendname(ctx, ".launcher", PTYPE_TEXT, 0, "ERROR", 5); skynet_sendname(ctx, 0, ".launcher", PTYPE_TEXT, 0, "ERROR", 5);
} }
static const char * static const char *

View File

@@ -30,7 +30,7 @@ void skynet_error(struct skynet_context * context, const char *msg, ...);
const char * skynet_command(struct skynet_context * context, const char * cmd , const char * parm); const char * skynet_command(struct skynet_context * context, const char * cmd , const char * parm);
uint32_t skynet_queryname(struct skynet_context * context, const char * name); uint32_t skynet_queryname(struct skynet_context * context, const char * name);
int skynet_send(struct skynet_context * context, uint32_t source, uint32_t destination , int type, int session, void * msg, size_t sz); int skynet_send(struct skynet_context * context, uint32_t source, uint32_t destination , int type, int session, void * msg, size_t sz);
int skynet_sendname(struct skynet_context * context, const char * destination , int type, int session, void * msg, size_t sz); int skynet_sendname(struct skynet_context * context, uint32_t source, const char * destination , int type, int session, void * msg, size_t sz);
int skynet_isremote(struct skynet_context *, uint32_t handle, int * harbor); int skynet_isremote(struct skynet_context *, uint32_t handle, int * harbor);

View File

@@ -89,7 +89,6 @@ static const char * load_config = "\
local code = assert(f:read \'*a\')\ local code = assert(f:read \'*a\')\
local function getenv(name) return assert(os.getenv(name), name) end\ local function getenv(name) return assert(os.getenv(name), name) end\
code = string.gsub(code, \'%$([%w_%d]+)\', getenv)\ code = string.gsub(code, \'%$([%w_%d]+)\', getenv)\
print(code)\
f:close()\ f:close()\
local result = {}\ local result = {}\
assert(load(code,\'=(load)\',\'t\',result))()\ assert(load(code,\'=(load)\',\'t\',result))()\

View File

@@ -614,8 +614,10 @@ skynet_send(struct skynet_context * context, uint32_t source, uint32_t destinati
} }
int int
skynet_sendname(struct skynet_context * context, const char * addr , int type, int session, void * data, size_t sz) { skynet_sendname(struct skynet_context * context, uint32_t source, const char * addr , int type, int session, void * data, size_t sz) {
uint32_t source = context->handle; if (source == 0) {
source = context->handle;
}
uint32_t des = 0; uint32_t des = 0;
if (addr[0] == ':') { if (addr[0] == ':') {
des = strtoul(addr+1, NULL, 16); des = strtoul(addr+1, NULL, 16);

View File

@@ -9,6 +9,7 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h>
#if defined(__APPLE__) #if defined(__APPLE__)
#include <sys/time.h> #include <sys/time.h>
@@ -33,7 +34,7 @@ struct timer_event {
struct timer_node { struct timer_node {
struct timer_node *next; struct timer_node *next;
int expire; uint32_t expire;
}; };
struct link_list { struct link_list {
@@ -43,9 +44,9 @@ struct link_list {
struct timer { struct timer {
struct link_list near[TIME_NEAR]; struct link_list near[TIME_NEAR];
struct link_list t[4][TIME_LEVEL-1]; struct link_list t[4][TIME_LEVEL];
int lock; int lock;
int time; uint32_t time;
uint32_t current; uint32_t current;
uint32_t starttime; uint32_t starttime;
uint64_t current_point; uint64_t current_point;
@@ -72,21 +73,22 @@ link(struct link_list *list,struct timer_node *node) {
static void static void
add_node(struct timer *T,struct timer_node *node) { add_node(struct timer *T,struct timer_node *node) {
int time=node->expire; uint32_t time=node->expire;
int current_time=T->time; uint32_t current_time=T->time;
if ((time|TIME_NEAR_MASK)==(current_time|TIME_NEAR_MASK)) { if ((time|TIME_NEAR_MASK)==(current_time|TIME_NEAR_MASK)) {
link(&T->near[time&TIME_NEAR_MASK],node); link(&T->near[time&TIME_NEAR_MASK],node);
} else { } else {
int i; int i;
int mask=TIME_NEAR << TIME_LEVEL_SHIFT; uint32_t mask=TIME_NEAR << TIME_LEVEL_SHIFT;
for (i=0;i<3;i++) { for (i=0;i<3;i++) {
if ((time|(mask-1))==(current_time|(mask-1))) { if ((time|(mask-1))==(current_time|(mask-1))) {
break; break;
} }
mask <<= TIME_LEVEL_SHIFT; mask <<= TIME_LEVEL_SHIFT;
} }
link(&T->t[i][((time>>(TIME_NEAR_SHIFT + i*TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK)-1],node);
link(&T->t[i][((time>>(TIME_NEAR_SHIFT + i*TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK)],node);
} }
} }
@@ -103,29 +105,38 @@ timer_add(struct timer *T,void *arg,size_t sz,int time) {
UNLOCK(T); UNLOCK(T);
} }
static void
move_list(struct timer *T, int level, int idx) {
struct timer_node *current = link_clear(&T->t[level][idx]);
while (current) {
struct timer_node *temp=current->next;
add_node(T,current);
current=temp;
}
}
static void static void
timer_shift(struct timer *T) { timer_shift(struct timer *T) {
LOCK(T); LOCK(T);
int mask = TIME_NEAR; int mask = TIME_NEAR;
int time = (++T->time) >> TIME_NEAR_SHIFT; uint32_t ct = ++T->time;
int i=0; if (ct == 0) {
move_list(T, 3, 0);
while ((T->time & (mask-1))==0) { } else {
int idx=time & TIME_LEVEL_MASK; uint32_t time = ct >> TIME_NEAR_SHIFT;
if (idx!=0) { int i=0;
--idx;
struct timer_node *current = link_clear(&T->t[i][idx]); while ((ct & (mask-1))==0) {
while (current) { int idx=time & TIME_LEVEL_MASK;
struct timer_node *temp=current->next; if (idx!=0) {
add_node(T,current); move_list(T, i, idx);
current=temp; break;
} }
break; mask <<= TIME_LEVEL_SHIFT;
time >>= TIME_LEVEL_SHIFT;
++i;
} }
mask <<= TIME_LEVEL_SHIFT; }
time >>= TIME_LEVEL_SHIFT;
++i;
}
UNLOCK(T); UNLOCK(T);
} }
@@ -187,7 +198,7 @@ timer_create_timer() {
} }
for (i=0;i<4;i++) { for (i=0;i<4;i++) {
for (j=0;j<TIME_LEVEL-1;j++) { for (j=0;j<TIME_LEVEL;j++) {
link_clear(&r->t[i][j]); link_clear(&r->t[i][j]);
} }
} }

16
test/testhttp.lua Normal file
View File

@@ -0,0 +1,16 @@
local skynet = require "skynet"
local httpc = require "http.httpc"
skynet.start(function()
print("GET baidu.com")
local header = {}
local status, body = httpc.get("baidu.com", "/", header)
print("[header] =====>")
for k,v in pairs(header) do
print(k,v)
end
print("[body] =====>", status)
print(body)
skynet.exit()
end)