diff --git a/php_uv.c b/php_uv.c index 40159e6..e407d6d 100644 --- a/php_uv.c +++ b/php_uv.c @@ -27,6 +27,12 @@ typedef struct { uv_buf_t buf; } write_req_t; +typedef struct { + uv_udp_send_t req; + uv_buf_t buf; +} send_req_t; + + #define PHP_UV_INIT_ZVALS(uv) \ { \ uv->address = NULL; \ @@ -37,7 +43,9 @@ typedef struct { uv->timer_cb = NULL; \ uv->idle_cb = NULL; \ uv->connect_cb = NULL; \ - uv->getaddr_cb = NULL; \ + uv->getaddr_cb = NULL; \ + uv->udp_recv_cb = NULL; \ + uv->udp_send_cb = NULL; \ } /* static variables */ @@ -170,6 +178,16 @@ void static destruct_uv(zend_rsrc_list_entry *rsrc TSRMLS_DC) zval_ptr_dtor(&obj->connect_cb); obj->connect_cb = NULL; } + if (obj->udp_recv_cb) { + //fprintf(stderr, "listencb: %d\n", Z_REFCOUNT_P(obj->listen_cb)); + zval_ptr_dtor(&obj->udp_recv_cb); + obj->udp_recv_cb = NULL; + } + if (obj->udp_send_cb) { + //fprintf(stderr, "listencb: %d\n", Z_REFCOUNT_P(obj->listen_cb)); + zval_ptr_dtor(&obj->udp_send_cb); + obj->udp_send_cb = NULL; + } if (obj->resource_id) { base_id = obj->resource_id; @@ -267,6 +285,39 @@ static void php_uv_write_cb(uv_write_t* req, int status) efree(wr); } +static void php_uv_udp_send_cb(uv_udp_send_t* req, int status) +{ + TSRMLS_FETCH(); + send_req_t* wr; + zval *retval_ptr, *stat, *client= NULL; + zval **params[2]; + + wr = (send_req_t*) req; + php_uv_t *uv = (php_uv_t*)req->data; + + MAKE_STD_ZVAL(stat); + ZVAL_LONG(stat, status); + + MAKE_STD_ZVAL(client); + ZVAL_RESOURCE(client, uv->resource_id); + zend_list_addref(uv->resource_id); + + params[0] = &stat; + params[1] = &client; + + php_uv_do_callback(&retval_ptr, uv->udp_send_cb, params, 2 TSRMLS_CC); + + zval_ptr_dtor(&retval_ptr); + zval_ptr_dtor(&stat); + zval_ptr_dtor(&client); + + if (wr->buf.base) { + efree(wr->buf.base); + } + efree(wr); +} + + static void php_uv_listen_cb(uv_stream_t* server, int status) { TSRMLS_FETCH(); @@ -351,6 +402,60 @@ static void php_uv_read_cb(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) } } + +static void php_uv_udp_recv_cb(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) +{ + TSRMLS_FETCH(); + zval *retval_ptr = NULL; + zval **params[2]; + zval *buffer; + + if (nread < 0) { + /* does this should be in user-land ? */ + uv_shutdown_t* req; + + /* Error or EOF */ + assert(uv_last_error(uv_default_loop()).code == UV_EOF); + + if (buf.base) { + efree(buf.base); + } + + req = (uv_shutdown_t*) emalloc(sizeof *req); + uv_shutdown(req, handle, php_uv_shutdown_cb); + return; + } + + if (nread == 0) { + /* Everything OK, but nothing read. */ + efree(buf.base); + return; + } + + php_uv_t *uv = (php_uv_t*)handle->data; + + MAKE_STD_ZVAL(buffer); + ZVAL_STRINGL(buffer,buf.base,nread, 1); + + zval *rsc; + MAKE_STD_ZVAL(rsc); + ZVAL_RESOURCE(rsc, uv->resource_id); + zend_list_addref(uv->resource_id); + + params[0] = &buffer; + params[1] = &rsc; + + php_uv_do_callback(&retval_ptr, uv->udp_recv_cb, params, 2 TSRMLS_CC); + + zval_ptr_dtor(&buffer); + zval_ptr_dtor(&rsc); + zval_ptr_dtor(&retval_ptr); + + if (buf.base) { + efree(buf.base); + } +} + static uv_buf_t php_uv_read_alloc(uv_handle_t* handle, size_t suggested_size) { return uv_buf_init(emalloc(suggested_size), suggested_size); @@ -557,6 +662,47 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_ares_gethostbyname, 0, 0, 2) ZEND_ARG_INFO(0, port) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_uv_udp_init, 0, 0, 1) + ZEND_ARG_INFO(0, loop) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_uv_udp_bind, 0, 0, 3) + ZEND_ARG_INFO(0, resource) + ZEND_ARG_INFO(0, address) + ZEND_ARG_INFO(0, port) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_uv_udp_recv_start, 0, 0, 2) + ZEND_ARG_INFO(0, server) + ZEND_ARG_INFO(0, callback) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_uv_udp_recv_stop, 0, 0, 1) + ZEND_ARG_INFO(0, server) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_uv_udp_set_multicast_loop, 0, 0, 2) + ZEND_ARG_INFO(0, server) + ZEND_ARG_INFO(0, enabled) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_uv_udp_set_multicast_ttl, 0, 0, 2) + ZEND_ARG_INFO(0, server) + ZEND_ARG_INFO(0, ttl) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_uv_udp_set_broadcast, 0, 0, 2) + ZEND_ARG_INFO(0, server) + ZEND_ARG_INFO(0, enabled) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_uv_udp_send, 0, 0, 4) + ZEND_ARG_INFO(0, server) + ZEND_ARG_INFO(0, buffer) + ZEND_ARG_INFO(0, address) + ZEND_ARG_INFO(0, callback) +ZEND_END_ARG_INFO() /* PHP Functions */ @@ -794,7 +940,11 @@ PHP_FUNCTION(uv_close) Z_ADDREF_P(callback); uv->close_cb = callback; } - uv_close((uv_stream_t*)&uv->uv.tcp, php_uv_close_cb); + if (uv->type == IS_UV_TCP) { + uv_close((uv_stream_t*)&uv->uv.tcp, php_uv_close_cb); + } else if(uv->type == IS_UV_UDP) { + uv_close((uv_stream_t*)&uv->uv.udp, php_uv_close_cb); + } } /* }}} */ @@ -1035,6 +1185,7 @@ PHP_FUNCTION(uv_tcp_init) return; } + uv->type = IS_UV_TCP; r = uv_tcp_init(uv_default_loop(), &uv->uv.tcp); if (r) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "uv_tcp_init failed"); @@ -1084,6 +1235,207 @@ PHP_FUNCTION(uv_default_loop) } /* }}} */ + +/* {{{ */ +PHP_FUNCTION(uv_udp_init) +{ + int r; + /* TODO */ + zval *loop; + php_uv_t *uv; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "|z",&loop) == FAILURE) { + return; + } + + uv = (php_uv_t *)emalloc(sizeof(php_uv_t)); + if (!uv) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "uv_udp_init emalloc failed"); + return; + } + + uv->type = IS_UV_UDP; + r = uv_udp_init(uv_default_loop(), &uv->uv.udp); + if (r) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "uv_udp_init failed"); + return; + } + + uv->uv.udp.data = uv; + PHP_UV_INIT_ZVALS(uv) + + ZEND_REGISTER_RESOURCE(return_value, uv, uv_resource_handle); + uv->resource_id = Z_LVAL_P(return_value); +} +/* }}} */ + +/* {{{ */ +PHP_FUNCTION(uv_udp_bind) +{ + zval *resource; + char *address; + int address_len; + long port = 0; + long flags = 0; + struct sockaddr_in addr; + php_uv_t *uv; + int r; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "zsl|l",&resource, &address, &address_len, &port, &flags) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(uv, php_uv_t *, &resource, -1, PHP_UV_RESOURCE_NAME, uv_resource_handle); + addr = uv_ip4_addr(address, port); + + r = uv_udp_bind((uv_tcp_t*)&uv->uv.udp, addr, flags); + if (r) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "uv_udp_bind failed"); + } +} +/* }}} */ + +/* {{{ */ +PHP_FUNCTION(uv_udp_recv_start) +{ + zval *client, *callback; + php_uv_t *uv; + int r; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "rz",&client, &callback) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(uv, php_uv_t *, &client, -1, PHP_UV_RESOURCE_NAME, uv_resource_handle); + Z_ADDREF_P(callback); + zend_list_addref(uv->resource_id); + + uv->udp_recv_cb = callback; + uv->uv.udp.data = uv; + + r = uv_udp_recv_start((uv_stream_t*)&uv->uv.udp, php_uv_read_alloc, php_uv_udp_recv_cb); + if (r) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "read failed"); + } +} +/* }}} */ + +/* {{{ */ +PHP_FUNCTION(uv_udp_recv_stop) +{ + zval *client; + php_uv_t *uv; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "r", &client) == FAILURE) { + return; + } + ZEND_FETCH_RESOURCE(uv, php_uv_t *, &client, -1, PHP_UV_RESOURCE_NAME, uv_resource_handle); + zend_list_delete(uv->resource_id); + + uv_udp_recv_stop((uv_timer_t*)&uv->uv.udp); +} +/* }}} */ + +/* {{{ */ +PHP_FUNCTION(uv_udp_set_multicast_loop) +{ + zval *client; + php_uv_t *uv; + long enabled = 0; + int r; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "rl",&client, &enabled) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(uv, php_uv_t *, &client, -1, PHP_UV_RESOURCE_NAME, uv_resource_handle); + + r = uv_udp_set_multicast_loop((uv_stream_t*)&uv->uv.udp, enabled); + if (r) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "uv_udp_set_muticast_loop failed"); + } +} +/* }}} */ + +/* {{{ */ +PHP_FUNCTION(uv_udp_set_multicast_ttl) +{ + zval *client; + php_uv_t *uv; + long ttl = 0; /* 1 through 255 */ + int r; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "rl",&client, &ttl) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(uv, php_uv_t *, &client, -1, PHP_UV_RESOURCE_NAME, uv_resource_handle); + + r = uv_udp_set_multicast_ttl((uv_stream_t*)&uv->uv.udp, ttl); + if (r) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "uv_udp_set_muticast_ttl failed"); + } +} +/* }}} */ + +/* {{{ */ +PHP_FUNCTION(uv_udp_set_broadcast) +{ + zval *client; + php_uv_t *uv; + long enabled = 0; + int r; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "rl",&client, &enabled) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(uv, php_uv_t *, &client, -1, PHP_UV_RESOURCE_NAME, uv_resource_handle); + + r = uv_udp_set_broadcast((uv_stream_t*)&uv->uv.udp, enabled); + if (r) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "uv_udp_set_muticast_loop failed"); + } +} +/* }}} */ + +/* {{{ */ +PHP_FUNCTION(uv_udp_send) +{ + zval *z_cli,*z_addr, *callback; + char *data; + int data_len = 0; + php_uv_t *client; + send_req_t *w; + php_uv_sockaddr_t *addr; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "zszz",&z_cli, &data, &data_len, &z_addr, &callback) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(client, php_uv_t *, &z_cli, -1, PHP_UV_RESOURCE_NAME, uv_resource_handle); + ZEND_FETCH_RESOURCE(addr, php_uv_sockaddr_t *, &z_addr, -1, PHP_UV_SOCKADDR_RESOURCE_NAME, uv_sockaddr_handle); + + zend_list_addref(client->resource_id); + Z_ADDREF_P(callback); + client->udp_send_cb = callback; + + w = emalloc(sizeof(send_req_t)); + w->req.data = client; + w->buf = uv_buf_init(estrndup(data,data_len), data_len); + uv_udp_send(&w->req, &client->uv.udp, &w->buf, 1, addr->addr.ipv4, php_uv_udp_send_cb); +} +/* }}} */ + + static zend_function_entry uv_functions[] = { /* general */ PHP_FE(uv_ref, arginfo_uv_ref) @@ -1111,6 +1463,14 @@ static zend_function_entry uv_functions[] = { PHP_FE(uv_listen, arginfo_uv_listen) PHP_FE(uv_accept, arginfo_uv_accept) PHP_FE(uv_tcp_connect, arginfo_uv_tcp_connect) + /* udp */ + PHP_FE(uv_udp_init, arginfo_uv_udp_init) + PHP_FE(uv_udp_bind, arginfo_uv_udp_bind) + PHP_FE(uv_udp_set_multicast_loop, arginfo_uv_udp_set_multicast_loop) + PHP_FE(uv_udp_set_multicast_ttl, arginfo_uv_udp_set_multicast_ttl) + PHP_FE(uv_udp_send, arginfo_uv_udp_send) + PHP_FE(uv_udp_recv_start, arginfo_uv_udp_recv_start) + PHP_FE(uv_udp_recv_stop, arginfo_uv_udp_recv_stop) /* for debug */ PHP_FE(uv_loop_refcount, arginfo_uv_loop_refcount) /* c-ares */ diff --git a/php_uv.h b/php_uv.h index 9c54973..d41719d 100644 --- a/php_uv.h +++ b/php_uv.h @@ -23,9 +23,24 @@ extern zend_module_entry uv_module_entry; extern zend_class_entry *uv_class_entry; +enum php_uv_resource_type{ + IS_UV_TCP = 0, + IS_UV_UDP = 1, + IS_UV_PIPE = 2, + IS_UV_IDLE = 3, + IS_UV_TIMER = 4, + IS_UV_ASYNC = 5, + IS_UV_LOOP = 6, + IS_UV_HANDLE = 7, + IS_UV_STREAM = 8, + IS_UV_ADDRINFO = 9, + IS_UV_MAX = 10 +}; + typedef struct { int in_free; int resource_id; + int type; union { uv_tcp_t tcp; uv_udp_t udp; @@ -47,6 +62,8 @@ typedef struct { zval *idle_cb; zval *connect_cb; zval *getaddr_cb; + zval *udp_recv_cb; + zval *udp_send_cb; } php_uv_t; typedef struct {