Tested in windows. Still needs more testing, but progress has been made.

This commit is contained in:
Diego Nehab 2004-01-17 00:17:46 +00:00
parent 89f3ecf782
commit 076451c753
11 changed files with 620 additions and 296 deletions

11
TODO
View File

@ -1,8 +1,13 @@
change send/recv to avoid using select
check for interrupt compliance
add connect with timeout
add gethostname and use it in HTTP, SMTP etc, and add manual entry. add gethostname and use it in HTTP, SMTP etc, and add manual entry.
add local connect, and manual entry add local connect, and manual entry
add shutdown, and manual entry
add shutdown manual entry
only allocate in case of success only allocate in case of success
only call select if io fails... only call select if io fails...
@ -65,6 +70,8 @@ Ajeitar o protocolo da luaopen_socket()... sei l
- testar os options! - testar os options!
- adicionar exemplos de expansão: pipe, local, named pipe - adicionar exemplos de expansão: pipe, local, named pipe
* add shutdown
* change send/recv to avoid using select
* O location do "redirect" pode ser relativo ao servidor atual (não pode, * O location do "redirect" pode ser relativo ao servidor atual (não pode,
mas os servidores fazem merda...) mas os servidores fazem merda...)
* Ajeitar para Lua 5.0 * Ajeitar para Lua 5.0

23
luasocket.sln Normal file
View File

@ -0,0 +1,23 @@
Microsoft Visual Studio Solution File, Format Version 8.00
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "luasocket", "luasocket.vcproj", "{4FAAB633-F0E7-4D12-B680-D150A0DD7268}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfiguration) = preSolution
Debug = Debug
Release = Release
EndGlobalSection
GlobalSection(ProjectConfiguration) = postSolution
{4FAAB633-F0E7-4D12-B680-D150A0DD7268}.Debug.ActiveCfg = Debug|Win32
{4FAAB633-F0E7-4D12-B680-D150A0DD7268}.Debug.Build.0 = Debug|Win32
{4FAAB633-F0E7-4D12-B680-D150A0DD7268}.Release.ActiveCfg = Release|Win32
{4FAAB633-F0E7-4D12-B680-D150A0DD7268}.Release.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionItems) = postSolution
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EndGlobalSection
GlobalSection(ExtensibilityAddIns) = postSolution
EndGlobalSection
EndGlobal

216
luasocket.vcproj Normal file
View File

@ -0,0 +1,216 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.10"
Name="luasocket"
ProjectGUID="{4FAAB633-F0E7-4D12-B680-D150A0DD7268}"
Keyword="Win32Proj">
<Platforms>
<Platform
Name="Win32"/>
</Platforms>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="."
IntermediateDirectory="."
ConfigurationType="1"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="net/include"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE, LUASOCKET_DEBUG"
MinimalRebuild="TRUE"
BasicRuntimeChecks="3"
RuntimeLibrary="5"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="4"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="lua.lib lualib.lib ws2_32.lib"
OutputFile="$(OutDir)/luasocket.exe"
LinkIncremental="2"
AdditionalLibraryDirectories="net/lib"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile="$(OutDir)/luasocket.pdb"
SubSystem="1"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="."
IntermediateDirectory="."
ConfigurationType="1"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="net/include"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE, LUASOCKET_DEBUG"
RuntimeLibrary="4"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="3"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="lua.lib lualib.lib ws2_32.lib"
OutputFile="$(OutDir)/luasocket.exe"
LinkIncremental="1"
AdditionalLibraryDirectories="net/lib"
GenerateDebugInformation="TRUE"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
<File
RelativePath=".\auxiliar.c">
</File>
<File
RelativePath=".\buffer.c">
</File>
<File
RelativePath=".\code.c">
</File>
<File
RelativePath=".\error.c">
</File>
<File
RelativePath=".\inet.c">
</File>
<File
RelativePath=".\io.c">
</File>
<File
RelativePath=".\lua.c">
</File>
<File
RelativePath=".\luasocket.c">
</File>
<File
RelativePath=".\select.c">
</File>
<File
RelativePath=".\tcp.c">
</File>
<File
RelativePath=".\timeout.c">
</File>
<File
RelativePath=".\udp.c">
</File>
<File
RelativePath=".\wsocket.c">
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
<File
RelativePath=".\auxiliar.h">
</File>
<File
RelativePath=".\buffer.h">
</File>
<File
RelativePath=".\code.h">
</File>
<File
RelativePath=".\error.h">
</File>
<File
RelativePath=".\inet.h">
</File>
<File
RelativePath=".\io.h">
</File>
<File
RelativePath=".\luasocket.h">
</File>
<File
RelativePath=".\select.h">
</File>
<File
RelativePath=".\socket.h">
</File>
<File
RelativePath=".\stdafx.h">
</File>
<File
RelativePath=".\tcp.h">
</File>
<File
RelativePath=".\timeout.h">
</File>
<File
RelativePath=".\udp.h">
</File>
<File
RelativePath=".\wsocket.h">
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -19,6 +19,10 @@
#include <lua.h> #include <lua.h>
#include "socket.h" #include "socket.h"
#ifdef WIN32
#define INET_ATON
#endif
void inet_open(lua_State *L); void inet_open(lua_State *L);
const char *inet_tryconnect(p_sock ps, const char *address, const char *inet_tryconnect(p_sock ps, const char *address,
unsigned short port); unsigned short port);

View File

@ -18,7 +18,9 @@
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Library's namespace * Library's namespace
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
#ifndef LUASOCKET_LIBNAME
#define LUASOCKET_LIBNAME "socket" #define LUASOCKET_LIBNAME "socket"
#endif
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* This macro prefixes all exported API functions * This macro prefixes all exported API functions

View File

@ -36,6 +36,7 @@ static int meth_dirty(lua_State *L);
static int opt_tcp_nodelay(lua_State *L); static int opt_tcp_nodelay(lua_State *L);
static int opt_keepalive(lua_State *L); static int opt_keepalive(lua_State *L);
static int opt_linger(lua_State *L); static int opt_linger(lua_State *L);
static int opt_reuseaddr(lua_State *L);
/* tcp object methods */ /* tcp object methods */
static luaL_reg tcp[] = { static luaL_reg tcp[] = {
@ -61,6 +62,7 @@ static luaL_reg tcp[] = {
/* socket option handlers */ /* socket option handlers */
static luaL_reg opt[] = { static luaL_reg opt[] = {
{"keepalive", opt_keepalive}, {"keepalive", opt_keepalive},
{"reuseaddr", opt_reuseaddr},
{"tcp-nodelay", opt_tcp_nodelay}, {"tcp-nodelay", opt_tcp_nodelay},
{"linger", opt_linger}, {"linger", opt_linger},
{NULL, NULL} {NULL, NULL}
@ -123,7 +125,7 @@ static int meth_setoption(lua_State *L)
static int opt_boolean(lua_State *L, int level, int name) static int opt_boolean(lua_State *L, int level, int name)
{ {
p_tcp tcp = (p_tcp) aux_checkgroup(L, "tcp{client,server}", 1); p_tcp tcp = (p_tcp) aux_checkgroup(L, "tcp{any}", 1);
int val = aux_checkboolean(L, 2); int val = aux_checkboolean(L, 2);
if (setsockopt(tcp->sock, level, name, (char *) &val, sizeof(val)) < 0) { if (setsockopt(tcp->sock, level, name, (char *) &val, sizeof(val)) < 0) {
lua_pushnil(L); lua_pushnil(L);
@ -134,16 +136,16 @@ static int opt_boolean(lua_State *L, int level, int name)
return 1; return 1;
} }
/* enables reuse of local address */
static int opt_reuseaddr(lua_State *L)
{
return opt_boolean(L, SOL_SOCKET, SO_REUSEADDR);
}
/* disables the Naggle algorithm */ /* disables the Naggle algorithm */
static int opt_tcp_nodelay(lua_State *L) static int opt_tcp_nodelay(lua_State *L)
{ {
struct protoent *pe = getprotobyname("TCP"); return opt_boolean(L, IPPROTO_TCP, TCP_NODELAY);
if (!pe) {
lua_pushnil(L);
lua_pushstring(L, "getprotobyname");
return 2;
}
return opt_boolean(L, pe->p_proto, TCP_NODELAY);
} }
static int opt_keepalive(lua_State *L) static int opt_keepalive(lua_State *L)

View File

@ -16,6 +16,7 @@
#ifdef WIN32 #ifdef WIN32
#include <windows.h> #include <windows.h>
#else #else
#include <time.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/times.h> #include <sys/times.h>
#include <unistd.h> #include <unistd.h>

View File

@ -35,6 +35,11 @@ static int meth_fd(lua_State *L);
static int meth_dirty(lua_State *L); static int meth_dirty(lua_State *L);
static int opt_dontroute(lua_State *L); static int opt_dontroute(lua_State *L);
static int opt_broadcast(lua_State *L); static int opt_broadcast(lua_State *L);
static int opt_reuseaddr(lua_State *L);
static int opt_ip_multicast_ttl(lua_State *L);
static int opt_ip_multicast_loop(lua_State *L);
static int opt_ip_add_membership(lua_State *L);
static int opt_ip_drop_membersip(lua_State *L);
/* udp object methods */ /* udp object methods */
static luaL_reg udp[] = { static luaL_reg udp[] = {
@ -57,8 +62,13 @@ static luaL_reg udp[] = {
/* socket options */ /* socket options */
static luaL_reg opt[] = { static luaL_reg opt[] = {
{"dontroute", opt_dontroute}, {"dontroute", opt_dontroute},
{"broadcast", opt_broadcast}, {"broadcast", opt_broadcast},
{"reuseaddr", opt_reuseaddr},
{"ip-multicast-ttl", opt_ip_multicast_ttl},
{"ip-multicast-loop", opt_ip_multicast_loop},
{"ip-add-membership", opt_ip_add_membership},
{"ip-drop-membership", opt_ip_drop_membersip},
{NULL, NULL} {NULL, NULL}
}; };
@ -244,11 +254,72 @@ static int opt_dontroute(lua_State *L)
return opt_boolean(L, SOL_SOCKET, SO_DONTROUTE); return opt_boolean(L, SOL_SOCKET, SO_DONTROUTE);
} }
static int opt_reuseaddr(lua_State *L)
{
return opt_boolean(L, SOL_SOCKET, SO_REUSEADDR);
}
static int opt_broadcast(lua_State *L) static int opt_broadcast(lua_State *L)
{ {
return opt_boolean(L, SOL_SOCKET, SO_BROADCAST); return opt_boolean(L, SOL_SOCKET, SO_BROADCAST);
} }
static int opt_ip_multicast_loop(lua_State *L)
{
return opt_boolean(L, IPPROTO_IP, IP_MULTICAST_LOOP);
}
static int opt_ip_multicast_ttl(lua_State *L)
{
p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1);
int val = (int) luaL_checknumber(L, 2);
if (setsockopt(udp->sock, IPPROTO_IP, IP_MULTICAST_TTL,
(char *) &val, sizeof(val)) < 0) {
lua_pushnil(L);
lua_pushstring(L, "setsockopt failed");
return 2;
}
lua_pushnumber(L, 1);
return 1;
}
static int opt_membership(lua_State *L, int level, int name)
{
p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1);
struct ip_mreq val;
if (!lua_istable(L, 2))
luaL_typerror(L, 2, lua_typename(L, LUA_TTABLE));
lua_pushstring(L, "multiaddr");
lua_gettable(L, 2);
if (!lua_isstring(L, -1)) luaL_argerror(L, 2, "invalid 'group' field");
if (!inet_aton(lua_tostring(L, -1), &val.imr_multiaddr))
luaL_argerror(L, 3, "invalid 'multiaddr' ip address");
lua_pushstring(L, "interface");
lua_gettable(L, 2);
if (!lua_isstring(L, -1)) luaL_argerror(L, 2, "invalid 'interface' field");
val.imr_interface.s_addr = htonl(INADDR_ANY);
if (strcmp(lua_tostring(L, -1), "*") &&
!inet_aton(lua_tostring(L, -1), &val.imr_interface))
luaL_argerror(L, 3, "invalid 'interface' ip address");
if (setsockopt(udp->sock, level, name, (char *) &val, sizeof(val)) < 0) {
lua_pushnil(L);
lua_pushstring(L, "setsockopt failed");
return 2;
}
lua_pushnumber(L, 1);
return 1;
}
static int opt_ip_add_membership(lua_State *L)
{
return opt_membership(L, IPPROTO_IP, IP_ADD_MEMBERSHIP);
}
static int opt_ip_drop_membersip(lua_State *L)
{
return opt_membership(L, IPPROTO_IP, IP_DROP_MEMBERSHIP);
}
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Just call tm methods * Just call tm methods
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/

View File

@ -145,7 +145,7 @@ int sock_send(p_sock ps, const char *data, size_t count, size_t *sent,
else return IO_TIMEOUT; else return IO_TIMEOUT;
/* here we know the connection has been closed */ /* here we know the connection has been closed */
} else return IO_CLOSED; } else return IO_CLOSED;
/* here we sent successfully sent something */ /* here we successfully sent something */
} else { } else {
*sent = put; *sent = put;
return IO_DONE; return IO_DONE;
@ -159,34 +159,36 @@ int sock_sendto(p_sock ps, const char *data, size_t count, size_t *sent,
SA *addr, socklen_t addr_len, int timeout) SA *addr, socklen_t addr_len, int timeout)
{ {
t_sock sock = *ps; t_sock sock = *ps;
struct timeval tv; ssize_t put;
fd_set fds;
ssize_t put = 0;
int err;
int ret; int ret;
/* avoid making system calls on closed sockets */
if (sock == SOCK_INVALID) return IO_CLOSED; if (sock == SOCK_INVALID) return IO_CLOSED;
tv.tv_sec = timeout / 1000; /* make sure we repeat in case the call was interrupted */
tv.tv_usec = (timeout % 1000) * 1000; do put = sendto(sock, data, count, 0, addr, addr_len);
FD_ZERO(&fds); while (put <= 0 && errno == EINTR);
FD_SET(sock, &fds); /* deal with failure */
ret = select(sock+1, NULL, &fds, NULL, timeout >= 0 ? &tv : NULL); if (put <= 0) {
if (ret > 0) { /* in any case, nothing has been sent */
put = sendto(sock, data, count, 0, addr, addr_len);
if (put <= 0) {
err = IO_CLOSED;
#ifdef __CYGWIN__
/* this is for CYGWIN, which is like Unix but has Win32 bugs */
if (sent < 0 && errno == EWOULDBLOCK) err = IO_DONE;
#endif
*sent = 0;
} else {
*sent = put;
err = IO_DONE;
}
return err;
} else {
*sent = 0; *sent = 0;
return IO_TIMEOUT; /* run select to avoid busy wait */
if (errno != EPIPE) {
struct timeval tv;
fd_set fds;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
FD_ZERO(&fds);
FD_SET(sock, &fds);
ret = select(sock+1, NULL, &fds, NULL, timeout >= 0 ? &tv : NULL);
/* tell the caller to call us again because there is more data */
if (ret > 0) return IO_DONE;
/* tell the caller there was no data before timeout */
else return IO_TIMEOUT;
/* here we know the connection has been closed */
} else return IO_CLOSED;
/* here we successfully sent something */
} else {
*sent = put;
return IO_DONE;
} }
} }
@ -232,28 +234,26 @@ int sock_recvfrom(p_sock ps, char *data, size_t count, size_t *got,
SA *addr, socklen_t *addr_len, int timeout) SA *addr, socklen_t *addr_len, int timeout)
{ {
t_sock sock = *ps; t_sock sock = *ps;
struct timeval tv; ssize_t taken;
fd_set fds;
int ret;
if (sock == SOCK_INVALID) return IO_CLOSED; if (sock == SOCK_INVALID) return IO_CLOSED;
ssize_t taken = 0; do taken = recvfrom(sock, data, count, 0, addr, addr_len);
tv.tv_sec = timeout / 1000; while (taken <= 0 && errno == EINTR);
tv.tv_usec = (timeout % 1000) * 1000; if (taken <= 0) {
FD_ZERO(&fds); struct timeval tv;
FD_SET(sock, &fds); fd_set fds;
ret = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL); int ret;
if (ret > 0) {
taken = recvfrom(sock, data, count, 0, addr, addr_len);
if (taken <= 0) {
*got = 0;
return IO_CLOSED;
} else {
*got = taken;
return IO_DONE;
}
} else {
*got = 0; *got = 0;
return IO_TIMEOUT; if (taken == 0) return IO_CLOSED;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
FD_ZERO(&fds);
FD_SET(sock, &fds);
ret = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL);
if (ret > 0) return IO_DONE;
else return IO_TIMEOUT;
} else {
*got = taken;
return IO_DONE;
} }
} }

View File

@ -100,7 +100,7 @@ int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len,
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(sock, &fds); FD_SET(sock, &fds);
*pa = SOCK_INVALID; *pa = SOCK_INVALID;
if (select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL) <= 0) if (select(0, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL) <= 0)
return IO_TIMEOUT; return IO_TIMEOUT;
if (!addr) addr = &dummy_addr; if (!addr) addr = &dummy_addr;
if (!addr_len) addr_len = &dummy_len; if (!addr_len) addr_len = &dummy_len;
@ -116,34 +116,35 @@ int sock_send(p_sock ps, const char *data, size_t count, size_t *sent,
int timeout) int timeout)
{ {
t_sock sock = *ps; t_sock sock = *ps;
struct timeval tv; ssize_t put;
fd_set fds;
ssize_t put = 0;
if (sock == SOCK_INVALID) return IO_CLOSED;
int err;
int ret; int ret;
tv.tv_sec = timeout / 1000; /* avoid making system calls on closed sockets */
tv.tv_usec = (timeout % 1000) * 1000; if (sock == SOCK_INVALID) return IO_CLOSED;
FD_ZERO(&fds); /* try to send something */
FD_SET(sock, &fds); put = send(sock, data, (int) count, 0);
ret = select(sock+1, NULL, &fds, NULL, timeout >= 0 ? &tv : NULL); /* deal with failure */
if (ret > 0) { if (put <= 0) {
put = send(sock, data, count, 0); /* in any case, nothing has been sent */
if (put <= 0) {
/* a bug in WinSock forces us to do a busy wait until we manage
** to write, because select returns immediately even though it
** should have blocked us until we could write... */
if (WSAGetLastError() == WSAEWOULDBLOCK) err = IO_DONE;
else err = IO_CLOSED;
*sent = 0;
} else {
*sent = put;
err = IO_DONE;
}
return err;
} else {
*sent = 0; *sent = 0;
return IO_TIMEOUT; /* run select to avoid busy wait */
if (WSAGetLastError() == WSAEWOULDBLOCK) {
struct timeval tv;
fd_set fds;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
FD_ZERO(&fds);
FD_SET(sock, &fds);
ret = select(0, NULL, &fds, NULL, timeout >= 0 ? &tv : NULL);
/* tell the caller to call us again because there is more data */
if (ret > 0) return IO_DONE;
/* tell the caller there was no data before timeout */
else return IO_TIMEOUT;
/* here we know the connection has been closed */
} else return IO_CLOSED;
/* here we successfully sent something */
} else {
*sent = put;
return IO_DONE;
} }
} }
@ -154,34 +155,35 @@ int sock_sendto(p_sock ps, const char *data, size_t count, size_t *sent,
SA *addr, socklen_t addr_len, int timeout) SA *addr, socklen_t addr_len, int timeout)
{ {
t_sock sock = *ps; t_sock sock = *ps;
struct timeval tv; ssize_t put;
fd_set fds;
ssize_t put = 0;
int err;
int ret; int ret;
/* avoid making system calls on closed sockets */
if (sock == SOCK_INVALID) return IO_CLOSED; if (sock == SOCK_INVALID) return IO_CLOSED;
tv.tv_sec = timeout / 1000; /* try to send something */
tv.tv_usec = (timeout % 1000) * 1000; put = sendto(sock, data, (int) count, 0, addr, addr_len);
FD_ZERO(&fds); /* deal with failure */
FD_SET(sock, &fds); if (put <= 0) {
ret = select(sock+1, NULL, &fds, NULL, timeout >= 0 ? &tv : NULL); /* in any case, nothing has been sent */
if (ret > 0) {
put = sendto(sock, data, count, 0, addr, addr_len);
if (put <= 0) {
/* a bug in WinSock forces us to do a busy wait until we manage
** to write, because select returns immediately even though it
** should have blocked us until we could write... */
if (WSAGetLastError() == WSAEWOULDBLOCK) err = IO_DONE;
else err = IO_CLOSED;
*sent = 0;
} else {
*sent = put;
err = IO_DONE;
}
return err;
} else {
*sent = 0; *sent = 0;
return IO_TIMEOUT; /* run select to avoid busy wait */
if (WSAGetLastError() == WSAEWOULDBLOCK) {
struct timeval tv;
fd_set fds;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
FD_ZERO(&fds);
FD_SET(sock, &fds);
ret = select(0, NULL, &fds, NULL, timeout >= 0 ? &tv : NULL);
/* tell the caller to call us again because there is more data */
if (ret > 0) return IO_DONE;
/* tell the caller there was no data before timeout */
else return IO_TIMEOUT;
/* here we know the connection has been closed */
} else return IO_CLOSED;
/* here we successfully sent something */
} else {
*sent = put;
return IO_DONE;
} }
} }
@ -191,28 +193,25 @@ int sock_sendto(p_sock ps, const char *data, size_t count, size_t *sent,
int sock_recv(p_sock ps, char *data, size_t count, size_t *got, int timeout) int sock_recv(p_sock ps, char *data, size_t count, size_t *got, int timeout)
{ {
t_sock sock = *ps; t_sock sock = *ps;
struct timeval tv; ssize_t taken;
fd_set fds;
int ret;
ssize_t taken = 0;
if (sock == SOCK_INVALID) return IO_CLOSED; if (sock == SOCK_INVALID) return IO_CLOSED;
tv.tv_sec = timeout / 1000; taken = recv(sock, data, (int) count, 0);
tv.tv_usec = (timeout % 1000) * 1000; if (taken <= 0) {
FD_ZERO(&fds); struct timeval tv;
FD_SET(sock, &fds); fd_set fds;
ret = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL); int ret;
if (ret > 0) {
taken = recv(sock, data, count, 0);
if (taken <= 0) {
*got = 0;
return IO_CLOSED;
} else {
*got = taken;
return IO_DONE;
}
} else {
*got = 0; *got = 0;
return IO_TIMEOUT; if (taken == 0) return IO_CLOSED;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
FD_ZERO(&fds);
FD_SET(sock, &fds);
ret = select(0, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL);
if (ret > 0) return IO_DONE;
else return IO_TIMEOUT;
} else {
*got = taken;
return IO_DONE;
} }
} }
@ -223,28 +222,25 @@ int sock_recvfrom(p_sock ps, char *data, size_t count, size_t *got,
SA *addr, socklen_t *addr_len, int timeout) SA *addr, socklen_t *addr_len, int timeout)
{ {
t_sock sock = *ps; t_sock sock = *ps;
struct timeval tv; ssize_t taken;
fd_set fds;
int ret;
ssize_t taken = 0;
if (sock == SOCK_INVALID) return IO_CLOSED; if (sock == SOCK_INVALID) return IO_CLOSED;
tv.tv_sec = timeout / 1000; taken = recvfrom(sock, data, (int) count, 0, addr, addr_len);
tv.tv_usec = (timeout % 1000) * 1000; if (taken <= 0) {
FD_ZERO(&fds); struct timeval tv;
FD_SET(sock, &fds); fd_set fds;
ret = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL); int ret;
if (ret > 0) {
taken = recvfrom(sock, data, count, 0, addr, addr_len);
if (taken <= 0) {
*got = 0;
return IO_CLOSED;
} else {
*got = taken;
return IO_DONE;
}
} else {
*got = 0; *got = 0;
return IO_TIMEOUT; if (taken == 0) return IO_CLOSED;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
FD_ZERO(&fds);
FD_SET(sock, &fds);
ret = select(0, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL);
if (ret > 0) return IO_DONE;
else return IO_TIMEOUT;
} else {
*got = taken;
return IO_DONE;
} }
} }

View File

@ -99,8 +99,6 @@ else pass("connected!") end
control:setoption("tcp-nodelay", true) control:setoption("tcp-nodelay", true)
------------------------------------------------------------------------ ------------------------------------------------------------------------
test("method registration")
function test_methods(sock, methods) function test_methods(sock, methods)
for _, v in methods do for _, v in methods do
if type(sock[v]) ~= "function" then if type(sock[v]) ~= "function" then
@ -110,38 +108,7 @@ function test_methods(sock, methods)
pass(sock.class .. " methods are ok") pass(sock.class .. " methods are ok")
end end
test_methods(socket.tcp(), {
"connect",
"send",
"receive",
"bind",
"accept",
"setpeername",
"setsockname",
"getpeername",
"getsockname",
"setoption",
"settimeout",
"close",
})
test_methods(socket.udp(), {
"getpeername",
"getsockname",
"setsockname",
"setpeername",
"send",
"sendto",
"receive",
"receivefrom",
"setoption",
"settimeout",
"close",
})
------------------------------------------------------------------------ ------------------------------------------------------------------------
test("mixed patterns")
function test_mixed(len) function test_mixed(len)
reconnect() reconnect()
local inter = math.ceil(len/4) local inter = math.ceil(len/4)
@ -163,21 +130,7 @@ remote "data:send(str); data:close()"
else fail("patterns don't match") end else fail("patterns don't match") end
end end
test_mixed(1)
test_mixed(17)
test_mixed(200)
test_mixed(4091)
test_mixed(80199)
test_mixed(4091)
test_mixed(200)
test_mixed(17)
test_mixed(1)
------------------------------------------------------------------------ ------------------------------------------------------------------------
test("character line")
reconnect()
function test_asciiline(len) function test_asciiline(len)
local str, str10, back, err local str, str10, back, err
str = string.rep("x", math.mod(len, 10)) str = string.rep("x", math.mod(len, 10))
@ -194,22 +147,7 @@ remote "data:send(str, '\\n')"
else fail("lines don't match") end else fail("lines don't match") end
end end
test_asciiline(1)
test_asciiline(17)
test_asciiline(200)
test_asciiline(4091)
test_asciiline(80199)
test_asciiline(800000)
test_asciiline(80199)
test_asciiline(4091)
test_asciiline(200)
test_asciiline(17)
test_asciiline(1)
------------------------------------------------------------------------ ------------------------------------------------------------------------
test("binary line")
reconnect()
function test_rawline(len) function test_rawline(len)
local str, str10, back, err local str, str10, back, err
str = string.rep(string.char(47), math.mod(len, 10)) str = string.rep(string.char(47), math.mod(len, 10))
@ -227,22 +165,7 @@ remote "data:send(str, '\\n')"
else fail("lines don't match") end else fail("lines don't match") end
end end
test_rawline(1)
test_rawline(17)
test_rawline(200)
test_rawline(4091)
test_rawline(80199)
test_rawline(800000)
test_rawline(80199)
test_rawline(4091)
test_rawline(200)
test_rawline(17)
test_rawline(1)
------------------------------------------------------------------------ ------------------------------------------------------------------------
test("raw transfer")
reconnect()
function test_raw(len) function test_raw(len)
local half = math.floor(len/2) local half = math.floor(len/2)
local s1, s2, back, err local s1, s2, back, err
@ -261,38 +184,7 @@ remote "data:send(str)"
else fail("blocks don't match") end else fail("blocks don't match") end
end end
test_raw(1)
test_raw(17)
test_raw(200)
test_raw(4091)
test_raw(80199)
test_raw(800000)
test_raw(80199)
test_raw(4091)
test_raw(200)
test_raw(17)
test_raw(1)
------------------------------------------------------------------------ ------------------------------------------------------------------------
test("non-blocking transfer")
reconnect()
-- the value is not important, we only want
-- to test non-blockin I/O anyways
data:settimeout(200)
test_raw(1)
test_raw(17)
test_raw(200)
test_raw(4091)
test_raw(80199)
test_raw(800000)
test_raw(80199)
test_raw(4091)
test_raw(200)
test_raw(17)
test_raw(1)
------------------------------------------------------------------------
test("total timeout on receive")
function test_totaltimeoutreceive(len, tm, sl) function test_totaltimeoutreceive(len, tm, sl)
local str, err, total local str, err, total
reconnect() reconnect()
@ -311,13 +203,8 @@ function test_totaltimeoutreceive(len, tm, sl)
check_timeout(tm, sl, elapsed, err, "receive", "total", check_timeout(tm, sl, elapsed, err, "receive", "total",
string.len(str) == 2*len) string.len(str) == 2*len)
end end
test_totaltimeoutreceive(800091, 1, 3)
test_totaltimeoutreceive(800091, 2, 3)
test_totaltimeoutreceive(800091, 3, 2)
test_totaltimeoutreceive(800091, 3, 1)
------------------------------------------------------------------------ ------------------------------------------------------------------------
test("total timeout on send")
function test_totaltimeoutsend(len, tm, sl) function test_totaltimeoutsend(len, tm, sl)
local str, err, total local str, err, total
reconnect() reconnect()
@ -336,13 +223,8 @@ function test_totaltimeoutsend(len, tm, sl)
check_timeout(tm, sl, elapsed, err, "send", "total", check_timeout(tm, sl, elapsed, err, "send", "total",
total == 2*len) total == 2*len)
end end
test_totaltimeoutsend(800091, 1, 3)
test_totaltimeoutsend(800091, 2, 3)
test_totaltimeoutsend(800091, 3, 2)
test_totaltimeoutsend(800091, 3, 1)
------------------------------------------------------------------------ ------------------------------------------------------------------------
test("blocking timeout on receive")
function test_blockingtimeoutreceive(len, tm, sl) function test_blockingtimeoutreceive(len, tm, sl)
local str, err, total local str, err, total
reconnect() reconnect()
@ -361,13 +243,8 @@ function test_blockingtimeoutreceive(len, tm, sl)
check_timeout(tm, sl, elapsed, err, "receive", "blocking", check_timeout(tm, sl, elapsed, err, "receive", "blocking",
string.len(str) == 2*len) string.len(str) == 2*len)
end end
test_blockingtimeoutreceive(800091, 1, 3)
test_blockingtimeoutreceive(800091, 2, 3)
test_blockingtimeoutreceive(800091, 3, 2)
test_blockingtimeoutreceive(800091, 3, 1)
------------------------------------------------------------------------ ------------------------------------------------------------------------
test("blocking timeout on send")
function test_blockingtimeoutsend(len, tm, sl) function test_blockingtimeoutsend(len, tm, sl)
local str, err, total local str, err, total
reconnect() reconnect()
@ -386,15 +263,8 @@ function test_blockingtimeoutsend(len, tm, sl)
check_timeout(tm, sl, elapsed, err, "send", "blocking", check_timeout(tm, sl, elapsed, err, "send", "blocking",
total == 2*len) total == 2*len)
end end
test_blockingtimeoutsend(800091, 1, 3)
test_blockingtimeoutsend(800091, 2, 3)
test_blockingtimeoutsend(800091, 3, 2)
test_blockingtimeoutsend(800091, 3, 1)
------------------------------------------------------------------------ ------------------------------------------------------------------------
test("bugs")
io.write("empty host connect: ")
function empty_connect() function empty_connect()
if data then data:close() data = nil end if data then data:close() data = nil end
remote [[ remote [[
@ -408,27 +278,25 @@ function empty_connect()
else fail("should not have connected!") end else fail("should not have connected!") end
end end
empty_connect() ------------------------------------------------------------------------
function isclosed(c)
return c:fd() == -1 or c:fd() == (2^32-1)
end
-- io.write("active close: ")
function active_close() function active_close()
reconnect() reconnect()
if socket._isclosed(data) then fail("should not be closed") end if isclosed(data) then fail("should not be closed") end
data:close() data:close()
if not socket._isclosed(data) then fail("should be closed") end if not isclosed(data) then fail("should be closed") end
data = nil data = nil
local udp = socket.udp() local udp = socket.udp()
if socket._isclosed(udp) then fail("should not be closed") end if isclosed(udp) then fail("should not be closed") end
udp:close() udp:close()
if not socket._isclosed(udp) then fail("should be closed") end if not isclosed(udp) then fail("should be closed") end
pass("ok") pass("ok")
end end
-- active_close()
------------------------------------------------------------------------ ------------------------------------------------------------------------
test("closed connection detection")
function test_closed() function test_closed()
local back, err local back, err
local str = 'little string' local str = 'little string'
@ -453,18 +321,15 @@ function test_closed()
]] ]]
total, err = data:send(string.rep("ugauga", 100000)) total, err = data:send(string.rep("ugauga", 100000))
if not err then if not err then
pass("failed: output buffer is at least %d bytes long!", total) pass("failed: output buffer is at least %d bytes long!", total)
elseif err ~= "closed" then elseif err ~= "closed" then
fail("got '"..err.."' instead of 'closed'.") fail("got '"..err.."' instead of 'closed'.")
else else
pass("graceful 'closed' received after %d bytes were sent", total) pass("graceful 'closed' received after %d bytes were sent", total)
end end
end end
test_closed()
------------------------------------------------------------------------ ------------------------------------------------------------------------
test("select function")
function test_selectbugs() function test_selectbugs()
local r, s, e = socket.select(nil, nil, 0.1) local r, s, e = socket.select(nil, nil, 0.1)
assert(type(r) == "table" and type(s) == "table" and e == "timeout") assert(type(r) == "table" and type(s) == "table" and e == "timeout")
@ -481,7 +346,144 @@ function test_selectbugs()
pass("invalid input: ok") pass("invalid input: ok")
end end
test("method registration")
test_methods(socket.tcp(), {
"connect",
"send",
"receive",
"bind",
"accept",
"setpeername",
"setsockname",
"getpeername",
"getsockname",
"setoption",
"settimeout",
"close",
})
test_methods(socket.udp(), {
"getpeername",
"getsockname",
"setsockname",
"setpeername",
"send",
"sendto",
"receive",
"receivefrom",
"setoption",
"settimeout",
"close",
})
test("mixed patterns")
reconnect()
test_mixed(1)
test_mixed(17)
test_mixed(200)
test_mixed(4091)
test_mixed(801990)
test_mixed(4091)
test_mixed(200)
test_mixed(17)
test_mixed(1)
test("character line")
reconnect()
test_asciiline(1)
test_asciiline(17)
test_asciiline(200)
test_asciiline(4091)
test_asciiline(80199)
test_asciiline(8000000)
test_asciiline(80199)
test_asciiline(4091)
test_asciiline(200)
test_asciiline(17)
test_asciiline(1)
test("binary line")
reconnect()
test_rawline(1)
test_rawline(17)
test_rawline(200)
test_rawline(4091)
test_rawline(80199)
test_rawline(8000000)
test_rawline(80199)
test_rawline(4091)
test_rawline(200)
test_rawline(17)
test_rawline(1)
test("raw transfer")
reconnect()
test_raw(1)
test_raw(17)
test_raw(200)
test_raw(4091)
test_raw(80199)
test_raw(8000000)
test_raw(80199)
test_raw(4091)
test_raw(200)
test_raw(17)
test_raw(1)
test("non-blocking transfer")
reconnect()
-- the value is not important, we only want
-- to test non-blockin I/O anyways
data:settimeout(200)
test_raw(1)
test_raw(17)
test_raw(200)
test_raw(4091)
test_raw(80199)
test_raw(8000000)
test_raw(80199)
test_raw(4091)
test_raw(200)
test_raw(17)
test_raw(1)
test("select function")
test_selectbugs() test_selectbugs()
test("empty host connect: ")
empty_connect()
test("active close: ")
active_close()
test("closed connection detection: ")
test_closed()
a = [[
test("total timeout on send")
test_totaltimeoutsend(800091, 1, 3)
test_totaltimeoutsend(800091, 2, 3)
test_totaltimeoutsend(800091, 3, 2)
test_totaltimeoutsend(800091, 3, 1)
test("total timeout on receive")
test_totaltimeoutreceive(800091, 1, 3)
test_totaltimeoutreceive(800091, 2, 3)
test_totaltimeoutreceive(800091, 3, 2)
test_totaltimeoutreceive(800091, 3, 1)
test("blocking timeout on send")
test_blockingtimeoutsend(800091, 1, 3)
test_blockingtimeoutsend(800091, 2, 3)
test_blockingtimeoutsend(800091, 3, 2)
test_blockingtimeoutsend(800091, 3, 1)
test("blocking timeout on receive")
test_blockingtimeoutreceive(800091, 1, 3)
test_blockingtimeoutreceive(800091, 2, 3)
test_blockingtimeoutreceive(800091, 3, 2)
test_blockingtimeoutreceive(800091, 3, 1)
]]
socket.done()
test(string.format("done in %.2fs", socket.time() - start)) test(string.format("done in %.2fs", socket.time() - start))