Skip to content

Commit

Permalink
add Co\Socket::getOption/setOption
Browse files Browse the repository at this point in the history
  • Loading branch information
matyhtf committed Mar 20, 2019
1 parent 928e407 commit 9d13c29
Show file tree
Hide file tree
Showing 13 changed files with 2,296 additions and 8 deletions.
4 changes: 4 additions & 0 deletions config.m4
Expand Up @@ -492,6 +492,10 @@ if test "$PHP_SWOOLE" != "no"; then
swoole_websocket_server.cc"

swoole_source_file="$swoole_source_file \
thirdparty/sockets/multicast.cc \
thirdparty/sockets/sendrecvmsg.cc \
thirdparty/sockets/conversions.cc \
thirdparty/sockets/sockaddr_conv.cc \
thirdparty/swoole_http_parser.c \
thirdparty/multipart_parser.c"

Expand Down
8 changes: 4 additions & 4 deletions include/socket.h
Expand Up @@ -167,7 +167,7 @@ class Socket
}

/* set connect read write timeout */
inline void set_timeout(double timeout, enum swTimeout_type type = SW_TIMEOUT_ALL)
inline void set_timeout(double timeout, int type = SW_TIMEOUT_ALL)
{
if (timeout == 0)
{
Expand All @@ -187,7 +187,7 @@ class Socket
}
}

inline void set_timeout(struct timeval *timeout, enum swTimeout_type type = SW_TIMEOUT_ALL)
inline void set_timeout(struct timeval *timeout, int type = SW_TIMEOUT_ALL)
{
set_timeout((double) timeout->tv_sec + ((double) timeout->tv_usec / 1000 / 1000), type);
}
Expand All @@ -198,11 +198,11 @@ class Socket
{
return connect_timeout;
}
if (type & SW_TIMEOUT_READ)
else if (type & SW_TIMEOUT_READ)
{
return read_timeout;
}
if (type & SW_TIMEOUT_WRITE)
else if (type & SW_TIMEOUT_WRITE)
{
return write_timeout;
}
Expand Down
2 changes: 1 addition & 1 deletion swoole_client_coro.cc
Expand Up @@ -644,7 +644,7 @@ static PHP_METHOD(swoole_client_coro, __construct)
{
zend_long type = 0;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|ls", &type) == FAILURE)
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &type) == FAILURE)
{
swoole_php_fatal_error(E_ERROR, "socket type param is required.");
RETURN_FALSE;
Expand Down
252 changes: 249 additions & 3 deletions swoole_socket_coro.cc
Expand Up @@ -18,9 +18,9 @@

#include "php_swoole_cxx.h"

#ifdef SW_COROUTINE
#include "swoole_coroutine.h"
#include "socket.h"
#include "thirdparty/sockets/php_sockets_cxx.h"

#include <string>

using namespace swoole;
Expand Down Expand Up @@ -53,6 +53,8 @@ static PHP_METHOD(swoole_socket_coro, recvAll);
static PHP_METHOD(swoole_socket_coro, sendAll);
static PHP_METHOD(swoole_socket_coro, recvfrom);
static PHP_METHOD(swoole_socket_coro, sendto);
static PHP_METHOD(swoole_socket_coro, getOption);
static PHP_METHOD(swoole_socket_coro, setOption);
static PHP_METHOD(swoole_socket_coro, shutdown);
static PHP_METHOD(swoole_socket_coro, close);
static PHP_METHOD(swoole_socket_coro, getsockname);
Expand Down Expand Up @@ -101,6 +103,17 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_recvfrom, 0, 0, 1)
ZEND_ARG_INFO(0, timeout)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_getOption, 0, 0, 2)
ZEND_ARG_INFO(0, level)
ZEND_ARG_INFO(0, opt_name)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_setOption, 0, 0, 3)
ZEND_ARG_INFO(0, level)
ZEND_ARG_INFO(0, opt_name)
ZEND_ARG_INFO(0, opt_value)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_socket_coro_sendto, 0, 0, 3)
ZEND_ARG_INFO(0, addr)
ZEND_ARG_INFO(0, port)
Expand All @@ -127,6 +140,8 @@ static const zend_function_entry swoole_socket_coro_methods[] =
PHP_ME(swoole_socket_coro, sendAll, arginfo_swoole_socket_coro_send, ZEND_ACC_PUBLIC)
PHP_ME(swoole_socket_coro, recvfrom, arginfo_swoole_socket_coro_recvfrom, ZEND_ACC_PUBLIC)
PHP_ME(swoole_socket_coro, sendto, arginfo_swoole_socket_coro_sendto, ZEND_ACC_PUBLIC)
PHP_ME(swoole_socket_coro, getOption, arginfo_swoole_socket_coro_getOption, ZEND_ACC_PUBLIC)
PHP_ME(swoole_socket_coro, setOption, arginfo_swoole_socket_coro_setOption, ZEND_ACC_PUBLIC)
PHP_ME(swoole_socket_coro, shutdown, arginfo_swoole_socket_coro_shutdown, ZEND_ACC_PUBLIC)
PHP_ME(swoole_socket_coro, close, arginfo_swoole_void, ZEND_ACC_PUBLIC)
PHP_ME(swoole_socket_coro, getpeername, arginfo_swoole_void, ZEND_ACC_PUBLIC)
Expand Down Expand Up @@ -661,6 +676,238 @@ static PHP_METHOD(swoole_socket_coro, getpeername)
}
}

static PHP_METHOD(swoole_socket_coro, getOption)
{
struct linger linger_val;
struct timeval tv;
socklen_t optlen;
int other_val;
zend_long level, optname;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &level, &optname) == FAILURE)
{
return;
}

swoole_get_socket_coro(sock, getThis());

if (level == IPPROTO_IP)
{
switch (optname)
{
case IP_MULTICAST_IF:
{
struct in_addr if_addr;
unsigned int if_index;
optlen = sizeof(if_addr);
if (getsockopt(sock->socket->get_fd(), level, optname, (char*) &if_addr, &optlen) != 0)
{
swoole_php_sys_error(E_WARNING, "getsockopt(%d, %ld, %ld)", sock->socket->get_fd(), level, optname);
RETURN_FALSE;
}
if (php_add4_to_if_index(&if_addr, sock->socket, &if_index) == SUCCESS)
{
RETURN_LONG((zend_long ) if_index);
}
else
{
RETURN_FALSE;
}
}
}
}
else if (level == IPPROTO_IPV6)
{
int ret = php_do_getsockopt_ipv6_rfc3542(sock->socket, level, optname, return_value);
if (ret == SUCCESS)
{
return;
}
else if (ret == FAILURE)
{
RETURN_FALSE;
} /* else continue */
}

/* sol_socket options and general case */
switch (optname)
{
case SO_LINGER:
optlen = sizeof(linger_val);

if (getsockopt(sock->socket->get_fd(), level, optname, (char*) &linger_val, &optlen) != 0)
{
swoole_php_sys_error(E_WARNING, "getsockopt(%d, %ld, %ld)", sock->socket->get_fd(), level, optname);
RETURN_FALSE;
}

array_init(return_value);
add_assoc_long(return_value, "l_onoff", linger_val.l_onoff);
add_assoc_long(return_value, "l_linger", linger_val.l_linger);
break;

case SO_RCVTIMEO:
case SO_SNDTIMEO:
optlen = sizeof(tv);

if (getsockopt(sock->socket->get_fd(), level, optname, (char*) &tv, &optlen) != 0)
{
swoole_php_sys_error(E_WARNING, "getsockopt(%d, %ld, %ld)", sock->socket->get_fd(), level, optname);
RETURN_FALSE;
}

array_init(return_value);

add_assoc_long(return_value, "sec", tv.tv_sec);
add_assoc_long(return_value, "usec", tv.tv_usec);
break;

default:
optlen = sizeof(other_val);

if (getsockopt(sock->socket->get_fd(), level, optname, (char*) &other_val, &optlen) != 0)
{
swoole_php_sys_error(E_WARNING, "getsockopt(%d, %ld, %ld)", sock->socket->get_fd(), level, optname);
RETURN_FALSE;
}
if (optlen == 1)
{
other_val = *((unsigned char *) &other_val);
}

RETURN_LONG(other_val);
break;
}
}

static PHP_METHOD(swoole_socket_coro, setOption)
{
zval *arg4;
struct linger lv;
int ov, optlen, retval;
struct timeval tv;
zend_long level, optname;
char *opt_ptr;
HashTable *opt_ht;
zval *l_onoff, *l_linger;
zval *sec, *usec;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "llz", &level, &optname, &arg4) == FAILURE)
{
return;
}

swoole_get_socket_coro(sock, getThis());

#define HANDLE_SUBCALL(res) \
do { \
if (res == 1) { goto default_case; } \
else if (res == SUCCESS) { RETURN_TRUE; } \
else { RETURN_FALSE; } \
} while (0)

if (level == IPPROTO_IP)
{
int res = php_do_setsockopt_ip_mcast(sock->socket, level, optname, arg4);
HANDLE_SUBCALL(res);
}
else if (level == IPPROTO_IPV6)
{
int res = php_do_setsockopt_ipv6_mcast(sock->socket, level, optname, arg4);
if (res == 1)
{
res = php_do_setsockopt_ipv6_rfc3542(sock->socket, level, optname, arg4);
}
HANDLE_SUBCALL(res);
}

switch (optname) {
case SO_LINGER: {
const char l_onoff_key[] = "l_onoff";
const char l_linger_key[] = "l_linger";

convert_to_array_ex(arg4);
opt_ht = Z_ARRVAL_P(arg4);

if ((l_onoff = zend_hash_str_find(opt_ht, l_onoff_key, sizeof(l_onoff_key) - 1)) == NULL) {
php_error_docref(NULL, E_WARNING, "no key \"%s\" passed in optval", l_onoff_key);
RETURN_FALSE;
}
if ((l_linger = zend_hash_str_find(opt_ht, l_linger_key, sizeof(l_linger_key) - 1)) == NULL) {
php_error_docref(NULL, E_WARNING, "no key \"%s\" passed in optval", l_linger_key);
RETURN_FALSE;
}

convert_to_long_ex(l_onoff);
convert_to_long_ex(l_linger);

lv.l_onoff = (unsigned short)Z_LVAL_P(l_onoff);
lv.l_linger = (unsigned short)Z_LVAL_P(l_linger);

optlen = sizeof(lv);
opt_ptr = (char*) &lv;
break;
}

case SO_RCVTIMEO:
case SO_SNDTIMEO: {
const char sec_key[] = "sec";
const char usec_key[] = "usec";

convert_to_array_ex(arg4);
opt_ht = Z_ARRVAL_P(arg4);

if ((sec = zend_hash_str_find(opt_ht, sec_key, sizeof(sec_key) - 1)) == NULL) {
php_error_docref(NULL, E_WARNING, "no key \"%s\" passed in optval", sec_key);
RETURN_FALSE;
}
if ((usec = zend_hash_str_find(opt_ht, usec_key, sizeof(usec_key) - 1)) == NULL) {
php_error_docref(NULL, E_WARNING, "no key \"%s\" passed in optval", usec_key);
RETURN_FALSE;
}

convert_to_long_ex(sec);
convert_to_long_ex(usec);
tv.tv_sec = Z_LVAL_P(sec);
tv.tv_usec = Z_LVAL_P(usec);
sock->socket->set_timeout(&tv,
optname == SO_RCVTIMEO ? SW_TIMEOUT_READ : SW_TIMEOUT_CONNECT | SW_TIMEOUT_WRITE);
RETURN_TRUE;
break;
}
#ifdef SO_BINDTODEVICE
case SO_BINDTODEVICE: {
if (Z_TYPE_P(arg4) == IS_STRING) {
opt_ptr = Z_STRVAL_P(arg4);
optlen = Z_STRLEN_P(arg4);
} else {
opt_ptr = "";
optlen = 0;
}
break;
}
#endif

default:
default_case:
convert_to_long_ex(arg4);
ov = Z_LVAL_P(arg4);

optlen = sizeof(ov);
opt_ptr = (char*) &ov;
break;
}

retval = setsockopt(sock->socket->get_fd(), level, optname, opt_ptr, optlen);
if (retval != 0)
{
swoole_php_sys_error(E_WARNING, "setsockopt(%d) failed.", sock->socket->get_fd());
RETURN_FALSE
}

RETURN_TRUE;
}

#ifdef SWOOLE_SOCKETS_SUPPORT
static PHP_METHOD(swoole_socket_coro, getSocket)
{
Expand All @@ -682,4 +929,3 @@ static PHP_METHOD(swoole_socket_coro, getSocket)
Z_TRY_ADDREF_P(sock->resource);
}
#endif
#endif
44 changes: 44 additions & 0 deletions tests/swoole_socket_coro/setopt/multicast.phpt
@@ -0,0 +1,44 @@
--TEST--
swoole_socket_coro: multicast
--SKIPIF--
--FILE--
<?php
$socket = new Co\Socket(AF_INET, SOCK_DGRAM, SOL_UDP);
$socket->bind('0.0.0.0', 9905);

$ret = $socket->setOption(IPPROTO_IP, MCAST_JOIN_GROUP, array(
'group' => '224.10.20.30',
'interface' => 0
));

if ($ret === false)
{
throw new RuntimeException('Unable to join multicast group');
}

go(function () use ($socket) {
$n = 10;
while($n--) {
$addr = [];
$data = $socket->recvfrom($addr);
assert(strlen($data) > 10);
assert(!empty($addr['port']));
assert(!empty($addr['address']));
}
});

go(function () use ($socket) {
$client = new Co\Client(SWOOLE_SOCK_UDP);
$client->connect('224.10.20.30', 9905);
$n = 10;
while($n--) {
$client->send("hello world [$n]\n");
co::sleep(.03);
}
});

swoole_event_wait();

?>
--EXPECTF--

0 comments on commit 9d13c29

Please sign in to comment.