# HG changeset patch # User jbe # Date 1428437833 -7200 # Node ID de3982f17d05c971dca42f2a2f3bdcf5fe3be6be # Parent de0e69673953a9780cde02752c84c098fb36e795 Removed timeout option from socket:close(); Simulate shutdown for local sockets (Unix Domain Sockets) diff -r de0e69673953 -r de3982f17d05 moonbridge.c --- a/moonbridge.c Tue Apr 07 04:08:41 2015 +0200 +++ b/moonbridge.c Tue Apr 07 22:17:13 2015 +0200 @@ -842,7 +842,7 @@ } if (controlmsg == MOONBR_COMMAND_TERMINATE) break; listener = moonbr_child_receive_pointer(MOONBR_FD_CONTROL); - if (fd) moonbr_io_pushhandle(L, fd, 1, controlmsg == MOONBR_SOCKETTYPE_NETWORK); + if (fd) moonbr_io_pushhandle(L, fd, controlmsg == MOONBR_SOCKETTYPE_NETWORK); lua_rawgetp(L, LUA_REGISTRYINDEX, moonbr_luakey_connect_func(pool)); if (!fd) { lua_newtable(L); diff -r de0e69673953 -r de3982f17d05 moonbridge_io.c --- a/moonbridge_io.c Tue Apr 07 04:08:41 2015 +0200 +++ b/moonbridge_io.c Tue Apr 07 22:17:13 2015 +0200 @@ -25,9 +25,9 @@ typedef struct { int fd; - int issocket; - int canshutdown; - int shutwr; + int useshutdown; + int finished; + int closed; int nonblocking; int readerr; int readbufcnt; @@ -65,7 +65,7 @@ static void moonbr_io_handle_set_linger(lua_State *L, moonbr_io_handle_t *handle, int timeout) { struct linger lingerval = { 0, }; - if (!handle->issocket) return; + if (!handle->useshutdown) return; if (timeout >= 0) { lingerval.l_onoff = 1; lingerval.l_linger = timeout; @@ -95,7 +95,8 @@ terminator = terminatorstr[0]; } lua_settop(L, 1); /* return handle on drain, terminator string may be garbage collected */ - if (handle->fd < 0) luaL_error(L, "Attempt to read from a closed I/O handle"); + if (handle->closed) luaL_error(L, "Attempt to read from a closed I/O handle"); + if (handle->fd < 0) goto moonbr_io_read_impl_eof; /* fake EOF to simulate shutdown */ if (handle->readerr) { lua_pushnil(L); lua_pushliteral(L, "Previous read error"); @@ -147,6 +148,7 @@ handle->readbufcnt = 0; if (!drain) { if (!luabufcnt && result == 0) { + moonbr_io_read_impl_eof: lua_pushboolean(L, 0); lua_pushliteral(L, "End of file"); return 2; @@ -182,8 +184,8 @@ size_t written; ssize_t result; handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); - if (handle->fd < 0) luaL_error(L, "Attempt to write to a closed I/O handle"); - if (handle->shutwr) luaL_error(L, "Attempt to write to a finished I/O handle"); + if (handle->closed) luaL_error(L, "Attempt to write to a closed I/O handle"); + if (handle->finished) luaL_error(L, "Attempt to write to a finished I/O handle"); if (handle->writeerr) { lua_pushnil(L); lua_pushliteral(L, "Previous write error"); @@ -295,34 +297,44 @@ static int moonbr_io_finish(lua_State *L) { moonbr_io_handle_t *handle; handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); - if (handle->fd < 0) luaL_error(L, "Attempt to finish a closed I/O handle"); - if (handle->shutwr) luaL_error(L, "Attempt to finish a finished I/O handle"); - handle->shutwr = 1; - if (handle->canshutdown) { - if (handle->writeleft) { - lua_pushcfunction(L, moonbr_io_flush); - lua_pushvalue(L, 1); - lua_call(L, 1, 2); - if (!lua_toboolean(L, -2)) return 2; + if (handle->closed) luaL_error(L, "Attempt to finish a closed I/O handle"); + if (handle->finished) luaL_error(L, "Attempt to finish a finished I/O handle"); + if (handle->writeleft) { + lua_pushcfunction(L, moonbr_io_flush); + lua_pushvalue(L, 1); + lua_call(L, 1, 2); + if (!lua_toboolean(L, -2)) { + handle->finished = 1; + return 2; } + } + handle->finished = 1; + if (handle->useshutdown) { if (shutdown(handle->fd, SHUT_WR)) { moonbr_io_errmsg(); lua_pushnil(L); lua_pushstring(L, errmsg); return 2; } + } else { + if (close(handle->fd)) { + moonbr_io_errmsg(); + handle->fd = -1; + lua_pushnil(L); + lua_pushstring(L, errmsg); + return 2; + } + handle->fd = -1; /* fake EOF on read */ } lua_pushboolean(L, 1); return 1; } -static int moonbr_io_close(lua_State *L) { +static int moonbr_io_close_impl(lua_State *L, int reset) { moonbr_io_handle_t *handle; - int timeout; handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); - timeout = luaL_optinteger(L, 2, -1); - if (handle->fd < 0) luaL_error(L, "Attempt to close a closed I/O handle"); - if (timeout != 0) { + if (handle->closed) luaL_error(L, "Attempt to close a closed I/O handle"); + if (!reset) { if (handle->writeleft) { lua_pushcfunction(L, moonbr_io_flush); lua_pushvalue(L, 1); @@ -333,14 +345,16 @@ return 2; } } - moonbr_io_handle_set_linger(L, handle, timeout); + moonbr_io_handle_set_linger(L, handle, -1); } - if (close(handle->fd)) { - moonbr_io_errmsg(); - handle->fd = -1; - lua_pushnil(L); - lua_pushstring(L, errmsg); - return 2; + if (handle->fd >= 0) { + if (close(handle->fd)) { + moonbr_io_errmsg(); + handle->fd = -1; + lua_pushnil(L); + lua_pushstring(L, errmsg); + return 2; + } } handle->fd = -1; lua_pushboolean(L, 1); @@ -348,14 +362,12 @@ } +static int moonbr_io_close(lua_State *L) { + return moonbr_io_close_impl(L, 0); +} + static int moonbr_io_reset(lua_State *L) { - luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); - lua_settop(L, 1); - lua_pushcfunction(L, moonbr_io_close); - lua_insert(L, 1); - lua_pushinteger(L, 0); - lua_call(L, 2, LUA_MULTRET); - return lua_gettop(L); + return moonbr_io_close_impl(L, 1); } static int moonbr_io_gc(lua_State *L) { @@ -381,13 +393,13 @@ } } -void moonbr_io_pushhandle(lua_State *L, int fd, int issocket, int canshutdown) { +void moonbr_io_pushhandle(lua_State *L, int fd, int useshutdown) { moonbr_io_handle_t *handle; handle = lua_newuserdata(L, sizeof(moonbr_io_handle_t)); handle->fd = fd; - handle->issocket = issocket; - handle->canshutdown = canshutdown; - handle->shutwr = 0; + handle->useshutdown = useshutdown; + handle->finished = 0; + handle->closed = 0; handle->nonblocking = -1; handle->readerr = 0; handle->readbufcnt = 0; diff -r de0e69673953 -r de3982f17d05 moonbridge_io.h --- a/moonbridge_io.h Tue Apr 07 04:08:41 2015 +0200 +++ b/moonbridge_io.h Tue Apr 07 22:17:13 2015 +0200 @@ -1,5 +1,5 @@ -void moonbr_io_pushhandle(lua_State *L, int fd, int issocket, int canshutdown); +void moonbr_io_pushhandle(lua_State *L, int fd, int useshutdown); void moonbr_io_closehandle(lua_State *L, int idx, int timeout); int luaopen_moonbridge_io(lua_State *L); diff -r de0e69673953 -r de3982f17d05 reference.txt --- a/reference.txt Tue Apr 07 04:08:41 2015 +0200 +++ b/reference.txt Tue Apr 07 22:17:13 2015 +0200 @@ -52,23 +52,19 @@ a single socket object as argument, which is described below: -### socket:close(timeout) +### socket:close() Closes the socket connection (input and output stream) by flushing all data and -sending a TCP FIN packet. If the timeout value is non-nil but zero, a TCP RST -is sent instead. If a positive timeout value is given and if the remote peer -doesn't respond with a TCP FIN within the given time, a TCP RST is sent in -addition to the previously sent TCP FIN packet. If the timeout value is nil or -negative, the call returns immediately and the operating system will wait for -the peer's TCP FIN packet. +sending a TCP FIN packet. Returns true on success, or nil plus error message in case of an I/O error. Using this method on sockets that have already been closed (or reset) will throw an error. Warning: Pending data on the input stream may cause connection aborts (TCP RST) -depending on the particular operating system used. All pending input data -should have been read (or drained) before calling socket:close(). +when network connections are used. All pending input data should have been read +(or drained) before calling socket:close(). Use socket:finish() to send a +TCP FIN packet to the peer before waiting for EOF from the peer. ### socket:drain(maxlen, terminator) @@ -91,6 +87,17 @@ second result value) are returned. +### socket:finish() + +Sends a TCP FIN packet to indicate EOF on write stream. Subsequent reads are +still possible. When there is no more input data to be read, the connection +should finally be closed with socket:close(). + +In case of local sockets (Unix Domain Sockets), socket:finish() simply closes +the underlying socket and emulates EOF on subsequent reads. Also in this case, +the connection should be finally closed with socket:close(). + + ### socket:flush(...) Same as socket:write(...) but additionally flushes the socket (i.e. all pending