/* +----------------------------------------------------------------------+ | Swoole | +----------------------------------------------------------------------+ | This source file is subject to version 2.0 of the Apache license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.apache.org/licenses/LICENSE-2.0.html | | If you did not receive a copy of the Apache2.0 license and are unable| | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "php_swoole.h" #include "php_streams.h" #include "php_network.h" #include "ext/standard/file.h" #ifdef SW_COROUTINE #include "swoole_coroutine.h" #include "ext/standard/basic_functions.h" #include #endif typedef struct { #if PHP_MAJOR_VERSION >= 7 zval _callback; zval _filename; #endif zval *callback; zval *filename; int fd; off_t offset; uint16_t type; uint8_t once; char *content; uint32_t length; } file_request; typedef struct { int fd; off_t offset; } file_handle; typedef struct { #if PHP_MAJOR_VERSION >= 7 zval _callback; zval _domain; #endif zval *callback; zval *domain; #ifdef SW_COROUTINE php_context *context; //add for coro uint8_t useless; //1 代表没有用 swTimer_node *timer; #endif } dns_request; typedef struct { swString *zaddress; int64_t update_time; } dns_cache; typedef struct { zval *callback; #ifdef SW_COROUTINE php_context *context; #endif pid_t pid; int fd; swString *buffer; } process_stream; static void php_swoole_aio_onComplete(swAio_event *event); static void php_swoole_dns_callback(char *domain, swDNSResolver_result *result, void *data); #ifdef SW_COROUTINE static void php_swoole_dns_callback_coro(char *domain, swDNSResolver_result *result, void *data); static void php_swoole_dns_timeout_coro(swTimer *timer, swTimer_node *tnode); #endif static void php_swoole_file_request_free(void *data); static swHashMap *php_swoole_open_files; static swHashMap *php_swoole_aio_request; #ifdef SW_COROUTINE static swHashMap *request_cache_map = NULL; //以domin为区分 #endif #ifdef SW_COROUTINE static sw_inline int64_t swTimer_get_now_msec() { struct timeval now; if (swTimer_now(&now) < 0) { return SW_ERR; } int64_t msec1 = (now.tv_sec) * 1000; int64_t msec2 = (now.tv_usec) / 1000; return msec1 + msec2; } #endif static void php_swoole_file_request_free(void *data) { file_request *file_req = data; if (file_req->callback) { sw_zval_ptr_dtor(&file_req->callback); } efree(file_req->content); sw_zval_ptr_dtor(&file_req->filename); efree(file_req); } void swoole_async_init(int module_number TSRMLS_DC) { bzero(&SwooleAIO, sizeof(SwooleAIO)); REGISTER_LONG_CONSTANT("SWOOLE_AIO_BASE", 0, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SWOOLE_AIO_LINUX", 0, CONST_CS | CONST_PERSISTENT); php_swoole_open_files = swHashMap_new(SW_HASHMAP_INIT_BUCKET_N, NULL); if (php_swoole_open_files == NULL) { swoole_php_fatal_error(E_ERROR, "create hashmap[1] failed."); } php_swoole_aio_request = swHashMap_new(SW_HASHMAP_INIT_BUCKET_N, php_swoole_file_request_free); if (php_swoole_aio_request == NULL) { swoole_php_fatal_error(E_ERROR, "create hashmap[2] failed."); } } void php_swoole_check_aio() { if (SwooleAIO.init == 0) { php_swoole_check_reactor(); swAio_init(); } if (!SwooleAIO.callback) { SwooleAIO.callback = php_swoole_aio_onComplete; } } static void php_swoole_dns_callback(char *domain, swDNSResolver_result *result, void *data) { SWOOLE_GET_TSRMLS; dns_request *req = data; zval *retval = NULL; zval *zaddress; zval **args[2]; char *address; SW_MAKE_STD_ZVAL(zaddress); if (result->num > 0) { if (SwooleG.dns_lookup_random) { address = result->hosts[rand() % result->num].address; } else { address = result->hosts[0].address; } SW_ZVAL_STRING(zaddress, address, 1); } else { SW_ZVAL_STRING(zaddress, "", 1); } args[0] = &req->domain; args[1] = &zaddress; zval *zcallback = req->callback; if (sw_call_user_function_ex(EG(function_table), NULL, zcallback, &retval, 2, args, 0, NULL TSRMLS_CC) == FAILURE) { swoole_php_fatal_error(E_WARNING, "swoole_asyns_dns_lookup handler error."); return; } if (EG(exception)) { zend_exception_error(EG(exception), E_ERROR TSRMLS_CC); } sw_zval_ptr_dtor(&req->callback); sw_zval_ptr_dtor(&req->domain); efree(req); if (retval) { sw_zval_ptr_dtor(&retval); } sw_zval_ptr_dtor(&zaddress); } //用于coro 回调 #ifdef SW_COROUTINE static void php_swoole_dns_callback_coro(char *domain, swDNSResolver_result *result, void *data) { SWOOLE_GET_TSRMLS; dns_request *req = data; zval *retval = NULL; zval *zaddress; char *address; SW_MAKE_STD_ZVAL(zaddress); if (result->num > 0) { if (SwooleG.dns_lookup_random) { address = result->hosts[rand() % result->num].address; } else { address = result->hosts[0].address; } SW_ZVAL_STRING(zaddress, address, 1); } else { SW_ZVAL_STRING(zaddress, "", 1); } //update cache dns_cache *cache = swHashMap_find(request_cache_map, Z_STRVAL_P(req->domain), Z_STRLEN_P(req->domain)); if (cache == NULL ) { cache = emalloc(sizeof(dns_cache)); swHashMap_add(request_cache_map, Z_STRVAL_P(req->domain), Z_STRLEN_P(req->domain), cache); cache->zaddress = swString_new(20); } swString_write_ptr(cache->zaddress, 0, Z_STRVAL_P(zaddress), Z_STRLEN_P(zaddress)); cache->update_time = (int64_t) swTimer_get_now_msec + (int64_t) (SwooleG.dns_cache_refresh_time * 1000); //timeout if (req->timer) { swTimer_del(&SwooleG.timer, req->timer); req->timer = NULL; } if (req->useless) { efree(req); return; } int ret = coro_resume(req->context, zaddress, &retval); if (ret > 0) { goto free_zdata; } if (retval != NULL) { sw_zval_ptr_dtor(&retval); } //说明已经yield走了 free_zdata: // free 上下文 sw_zval_ptr_dtor(&zaddress); efree(req->context); efree(req); } //用于timeout static void php_swoole_dns_timeout_coro(swTimer *timer, swTimer_node *tnode) { zval *retval = NULL; zval *zaddress; php_context *cxt = (php_context *) tnode->data; #if PHP_MAJOR_VERSION < 7 dns_request *req =(dns_request *) cxt->coro_params; #else dns_request *req = (dns_request *) cxt->coro_params.value.ptr; #endif SW_MAKE_STD_ZVAL(zaddress); dns_cache *cache = swHashMap_find(request_cache_map, Z_STRVAL_P(req->domain), Z_STRLEN_P(req->domain)); if (cache != NULL && cache->update_time > (int64_t) swTimer_get_now_msec) { SW_ZVAL_STRINGL(zaddress, (*cache->zaddress).str, (*cache->zaddress).length, 1); } else { SW_ZVAL_STRING(zaddress, "", 1); } int ret = coro_resume(req->context, zaddress, &retval); if (ret > 0) { goto free_zdata; } if (retval != NULL) { sw_zval_ptr_dtor(&retval); } free_zdata: sw_zval_ptr_dtor(&zaddress); efree(req->context); req->useless = 1; } #endif static void php_swoole_aio_onComplete(swAio_event *event) { int isEOF = SW_FALSE; int64_t ret; zval *retval = NULL, *zcallback = NULL, *zwriten = NULL; zval *zcontent = NULL; zval **args[2]; file_request *file_req = NULL; dns_request *dns_req = NULL; #if PHP_MAJOR_VERSION < 7 TSRMLS_FETCH_FROM_CTX(sw_thread_ctx ? sw_thread_ctx : NULL); #else zval _zcontent; zval _zwriten; bzero(&_zcontent, sizeof(zval)); bzero(&_zwriten, sizeof(zval)); #endif if (event->type == SW_AIO_GETHOSTBYNAME) { dns_req = (dns_request *) event->req; if (dns_req->callback == NULL) { swoole_php_error(E_WARNING, "swoole_async: onAsyncComplete callback not found[0]"); return; } zcallback = dns_req->callback; } else { file_req = swHashMap_find_int(php_swoole_aio_request, event->task_id); if (!file_req) { swoole_php_fatal_error(E_WARNING, "swoole_async: onAsyncComplete callback not found[1]"); return; } if (file_req->callback == NULL && file_req->type == SW_AIO_READ) { swoole_php_fatal_error(E_WARNING, "swoole_async: onAsyncComplete callback not found[2]"); return; } zcallback = file_req->callback; } ret = event->ret; if (ret < 0) { SwooleG.error = event->error; swoole_php_error(E_WARNING, "Aio Error: %s[%d]", strerror(event->error), event->error); } else if (file_req != NULL) { if (ret == 0) { bzero(event->buf, event->nbytes); isEOF = SW_TRUE; } else if (file_req->once == 1 && ret < file_req->length) { swoole_php_fatal_error(E_WARNING, "ret_length[%d] < req->length[%d].", (int ) ret, file_req->length); } else if (event->type == SW_AIO_READ) { file_req->offset += event->ret; } } if (event->type == SW_AIO_READ) { args[0] = &file_req->filename; args[1] = &zcontent; #if PHP_MAJOR_VERSION < 7 SW_MAKE_STD_ZVAL(zcontent); #else zcontent = &_zcontent; #endif if (ret < 0) { SW_ZVAL_STRING(zcontent, "", 1); } else { SW_ZVAL_STRINGL(zcontent, event->buf, ret, 1); } } else if (event->type == SW_AIO_WRITE) { #if PHP_MAJOR_VERSION < 7 SW_MAKE_STD_ZVAL(zwriten); #else zwriten = &_zwriten; #endif args[0] = &file_req->filename; args[1] = &zwriten; ZVAL_LONG(zwriten, ret); } else if(event->type == SW_AIO_GETHOSTBYNAME) { args[0] = &dns_req->domain; #if PHP_MAJOR_VERSION < 7 SW_MAKE_STD_ZVAL(zcontent); #else zcontent = &_zcontent; #endif if (ret < 0) { SW_ZVAL_STRING(zcontent, "", 1); } else { SW_ZVAL_STRING(zcontent, event->buf, 1); } args[1] = &zcontent; } else { swoole_php_fatal_error(E_WARNING, "swoole_async: onAsyncComplete unknown event type[%d].", event->type); return; } if (zcallback) { if (sw_call_user_function_ex(EG(function_table), NULL, zcallback, &retval, 2, args, 0, NULL TSRMLS_CC) == FAILURE) { swoole_php_fatal_error(E_WARNING, "swoole_async: onAsyncComplete handler error"); return; } if (EG(exception)) { zend_exception_error(EG(exception), E_ERROR TSRMLS_CC); } } //file io if (file_req) { if (file_req->once == 1) { close_file: close(event->fd); swHashMap_del_int(php_swoole_aio_request, event->task_id); } else if(file_req->type == SW_AIO_WRITE) { if (retval != NULL && !ZVAL_IS_NULL(retval) && !Z_BVAL_P(retval)) { swHashMap_del(php_swoole_open_files, Z_STRVAL_P(file_req->filename), Z_STRLEN_P(file_req->filename)); goto close_file; } else { swHashMap_del_int(php_swoole_aio_request, event->task_id); } } else { if ((retval != NULL && !ZVAL_IS_NULL(retval) && !Z_BVAL_P(retval)) || isEOF) { goto close_file; } //Less than expected, at the end of the file else if (event->ret < event->nbytes) { event->ret = 0; php_swoole_aio_onComplete(event); } //continue to read else { int ret = SwooleAIO.read(event->fd, event->buf, event->nbytes, file_req->offset); if (ret < 0) { swoole_php_fatal_error(E_WARNING, "swoole_async: continue to read failed. Error: %s[%d]", strerror(event->error), event->error); goto close_file; } else { swHashMap_move_int(php_swoole_aio_request, event->task_id, ret); } } } } else if (dns_req) { sw_zval_ptr_dtor(&dns_req->callback); sw_zval_ptr_dtor(&dns_req->domain); efree(dns_req); efree(event->buf); } if (zcontent) { sw_zval_ptr_dtor(&zcontent); } if (zwriten) { sw_zval_ptr_dtor(&zwriten); } if (retval) { sw_zval_ptr_dtor(&retval); } } PHP_FUNCTION(swoole_async_read) { zval *callback; zval *filename; long buf_size = SW_AIO_DEFAULT_CHUNK_SIZE; long offset = 0; int open_flag = O_RDONLY; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|ll", &filename, &callback, &buf_size, &offset) == FAILURE) { return; } if (offset < 0) { swoole_php_fatal_error(E_WARNING, "offset must be greater than 0."); RETURN_FALSE; } if (buf_size > SW_AIO_MAX_CHUNK_SIZE) { buf_size = SW_AIO_MAX_CHUNK_SIZE; } convert_to_string(filename); int fd = open(Z_STRVAL_P(filename), open_flag, 0644); if (fd < 0) { swoole_php_sys_error(E_WARNING, "open(%s, O_RDONLY) failed.", Z_STRVAL_P(filename)); RETURN_FALSE; } struct stat file_stat; if (fstat(fd, &file_stat) < 0) { swoole_php_sys_error(E_WARNING, "fstat(%s) failed.", Z_STRVAL_P(filename)); close(fd); RETURN_FALSE; } if (offset >= file_stat.st_size) { swoole_php_fatal_error(E_WARNING, "offset must be less than file_size[=%ld].", file_stat.st_size); close(fd); RETURN_FALSE; } void *fcnt = emalloc(buf_size); if (fcnt == NULL) { swoole_php_sys_error(E_WARNING, "malloc failed."); close(fd); RETURN_FALSE; } file_request *req = emalloc(sizeof(file_request)); req->fd = fd; req->filename = filename; sw_zval_add_ref(&filename); sw_copy_to_stack(req->filename, req->_filename); if (callback && !ZVAL_IS_NULL(callback)) { req->callback = callback; sw_zval_add_ref(&callback); sw_copy_to_stack(req->callback, req->_callback); } req->content = fcnt; req->once = 0; req->type = SW_AIO_READ; req->length = buf_size; req->offset = offset; php_swoole_check_aio(); int ret = SwooleAIO.read(fd, fcnt, buf_size, offset); if (ret == SW_ERR) { RETURN_FALSE; } else { swHashMap_add_int(php_swoole_aio_request, ret, req); RETURN_TRUE; } } PHP_FUNCTION(swoole_async_write) { zval *callback = NULL; zval *filename; char *fcnt; zend_size_t fcnt_len = 0; off_t offset = -1; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs|lz", &filename, &fcnt, &fcnt_len, &offset, &callback) == FAILURE) { return; } if (fcnt_len <= 0) { RETURN_FALSE; } if (callback && !ZVAL_IS_NULL(callback)) { char *func_name = NULL; if (!sw_zend_is_callable(callback, 0, &func_name TSRMLS_CC)) { swoole_php_fatal_error(E_WARNING, "Function '%s' is not callable", func_name); efree(func_name); RETURN_FALSE; } efree(func_name); } convert_to_string(filename); long fd = (long) swHashMap_find(php_swoole_open_files, Z_STRVAL_P(filename), Z_STRLEN_P(filename)); if (fd == 0) { int open_flag = O_WRONLY | O_CREAT; if (offset < 0) { open_flag |= O_APPEND; } fd = open(Z_STRVAL_P(filename), open_flag, 0644); if (fd < 0) { swoole_php_fatal_error(E_WARNING, "open(%s, %d) failed. Error: %s[%d]", Z_STRVAL_P(filename), open_flag, strerror(errno), errno); RETURN_FALSE; } swHashMap_add(php_swoole_open_files, Z_STRVAL_P(filename), Z_STRLEN_P(filename), (void*) fd); } if (offset < 0) { offset = 0; } file_request *req = emalloc(sizeof(file_request)); char *wt_cnt = emalloc(fcnt_len); req->fd = fd; req->content = wt_cnt; req->once = 0; req->type = SW_AIO_WRITE; req->length = fcnt_len; req->offset = offset; req->filename = filename; sw_zval_add_ref(&filename); sw_copy_to_stack(req->filename, req->_filename); if (callback && !ZVAL_IS_NULL(callback)) { req->callback = callback; sw_zval_add_ref(&callback); sw_copy_to_stack(req->callback, req->_callback); } else { req->callback = NULL; } memcpy(wt_cnt, fcnt, fcnt_len); php_swoole_check_aio(); int ret = SwooleAIO.write(fd, wt_cnt, fcnt_len, offset); if (ret == SW_ERR) { RETURN_FALSE; } else { swHashMap_add_int(php_swoole_aio_request, ret, req); RETURN_TRUE; } } PHP_FUNCTION(swoole_async_readfile) { zval *callback; zval *filename; int open_flag = O_RDONLY; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &filename, &callback) == FAILURE) { return; } convert_to_string(filename); int fd = open(Z_STRVAL_P(filename), open_flag, 0644); if (fd < 0) { swoole_php_fatal_error(E_WARNING, "open file[%s] failed. Error: %s[%d]", Z_STRVAL_P(filename), strerror(errno), errno); RETURN_FALSE; } struct stat file_stat; if (fstat(fd, &file_stat) < 0) { swoole_php_fatal_error(E_WARNING, "fstat failed. Error: %s[%d]", strerror(errno), errno); close(fd); RETURN_FALSE; } if (file_stat.st_size <= 0) { swoole_php_fatal_error(E_WARNING, "file is empty."); close(fd); RETURN_FALSE; } if (file_stat.st_size > SW_AIO_MAX_FILESIZE) { swoole_php_fatal_error(E_WARNING, "file_size[size=%ld|max_size=%d] is too big. Please use swoole_async_read.", (long int) file_stat.st_size, SW_AIO_MAX_FILESIZE); close(fd); RETURN_FALSE; } size_t length = file_stat.st_size; file_request *req = emalloc(sizeof(file_request)); req->fd = fd; req->filename = filename; sw_zval_add_ref(&filename); sw_copy_to_stack(req->filename, req->_filename); if (callback && !ZVAL_IS_NULL(callback)) { req->callback = callback; sw_zval_add_ref(&callback); sw_copy_to_stack(req->callback, req->_callback); } req->content = emalloc(length); req->once = 1; req->type = SW_AIO_READ; req->length = length; req->offset = 0; php_swoole_check_aio(); int ret = SwooleAIO.read(fd, req->content, length, 0); if (ret == SW_ERR) { RETURN_FALSE; } else { swHashMap_add_int(php_swoole_aio_request, ret, req); RETURN_TRUE; } } PHP_FUNCTION(swoole_async_writefile) { zval *callback = NULL; zval *filename; char *fcnt; zend_size_t fcnt_len; long flags = 0; if (zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC, "zs|zl", &filename, &fcnt, &fcnt_len, &callback, &flags) == FAILURE) { return; } int open_flag = O_CREAT | O_WRONLY; if (flags & PHP_FILE_APPEND) { open_flag |= O_APPEND; } else { open_flag |= O_TRUNC; } if (fcnt_len <= 0) { RETURN_FALSE; } if (fcnt_len > SW_AIO_MAX_FILESIZE) { swoole_php_fatal_error(E_WARNING, "file_size[size=%d|max_size=%d] is too big. Please use swoole_async_write.", fcnt_len, SW_AIO_MAX_FILESIZE); RETURN_FALSE; } if (callback && !ZVAL_IS_NULL(callback)) { char *func_name = NULL; if (!sw_zend_is_callable(callback, 0, &func_name TSRMLS_CC)) { swoole_php_fatal_error(E_WARNING, "Function '%s' is not callable", func_name); efree(func_name); RETURN_FALSE; } efree(func_name); } convert_to_string(filename); int fd = open(Z_STRVAL_P(filename), open_flag, 0644); if (fd < 0) { swoole_php_fatal_error(E_WARNING, "open file failed. Error: %s[%d]", strerror(errno), errno); RETURN_FALSE; } size_t memory_size = fcnt_len; char *wt_cnt = emalloc(memory_size); file_request *req = emalloc(sizeof(file_request)); req->filename = filename; sw_zval_add_ref(&filename); sw_copy_to_stack(req->filename, req->_filename); if (callback && !ZVAL_IS_NULL(callback)) { req->callback = callback; sw_zval_add_ref(&callback); sw_copy_to_stack(req->callback, req->_callback); } else { req->callback = NULL; } req->fd = fd; req->type = SW_AIO_WRITE; req->content = wt_cnt; req->once = 1; req->length = fcnt_len; req->offset = 0; memcpy(wt_cnt, fcnt, fcnt_len); php_swoole_check_aio(); int ret = SwooleAIO.write(fd, wt_cnt, memory_size, 0); if (ret == SW_ERR) { RETURN_FALSE; } else { swHashMap_add_int(php_swoole_aio_request, ret, req); RETURN_TRUE; } } PHP_FUNCTION(swoole_async_set) { if (SwooleG.main_reactor != NULL) { swoole_php_fatal_error(E_ERROR, "eventLoop has already been created. unable to change settings."); RETURN_FALSE; } zval *zset = NULL; HashTable *vht; zval *v; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zset) == FAILURE) { return; } php_swoole_array_separate(zset); vht = Z_ARRVAL_P(zset); if (php_swoole_array_get_value(vht, "thread_num", v)) { convert_to_long(v); SwooleAIO.thread_num = (uint8_t) Z_LVAL_P(v); } if (php_swoole_array_get_value(vht, "enable_signalfd", v)) { convert_to_boolean(v); SwooleG.enable_signalfd = Z_BVAL_P(v); } if (php_swoole_array_get_value(vht, "dns_cache_refresh_time", v)) { convert_to_double(v); SwooleG.dns_cache_refresh_time = Z_DVAL_P(v); } if (php_swoole_array_get_value(vht, "socket_buffer_size", v)) { convert_to_long(v); SwooleG.socket_buffer_size = Z_LVAL_P(v); if (SwooleG.socket_buffer_size <= 0 || SwooleG.socket_buffer_size > SW_MAX_INT) { SwooleG.socket_buffer_size = SW_MAX_INT; } } if (php_swoole_array_get_value(vht, "log_level", v)) { convert_to_long(v); SwooleG.log_level = Z_LVAL_P(v); } if (php_swoole_array_get_value(vht, "display_errors", v)) { convert_to_boolean(v); SWOOLE_G(display_errors) = 0; } if (php_swoole_array_get_value(vht, "socket_dontwait", v)) { convert_to_boolean(v); SwooleG.socket_dontwait = Z_BVAL_P(v); } if (php_swoole_array_get_value(vht, "dns_lookup_random", v)) { convert_to_boolean(v); SwooleG.dns_lookup_random = Z_BVAL_P(v); } if (php_swoole_array_get_value(vht, "dns_server", v)) { convert_to_string(v); SwooleG.dns_server_v4 = sw_strndup(Z_STRVAL_P(v), Z_STRLEN_P(v)); } if (php_swoole_array_get_value(vht, "use_async_resolver", v)) { convert_to_boolean(v); SwooleG.use_async_resolver = Z_BVAL_P(v); } if (php_swoole_array_get_value(vht, "enable_coroutine", v)) { convert_to_boolean(v); SwooleG.enable_coroutine = Z_BVAL_P(v); } #if defined(HAVE_REUSEPORT) && defined(HAVE_EPOLL) //reuse port if (php_swoole_array_get_value(vht, "enable_reuse_port", v)) { convert_to_boolean(v); if (Z_BVAL_P(v) && swoole_version_compare(SwooleG.uname.release, "3.9.0") >= 0) { SwooleG.reuse_port = 1; } } #endif sw_zval_ptr_dtor(&zset); } PHP_FUNCTION(swoole_async_dns_lookup) { zval *domain; zval *cb; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &domain, &cb) == FAILURE) { return; } if (Z_TYPE_P(domain) != IS_STRING) { swoole_php_fatal_error(E_WARNING, "invalid domain name."); RETURN_FALSE; } if (Z_STRLEN_P(domain) == 0) { swoole_php_fatal_error(E_WARNING, "domain name empty."); RETURN_FALSE; } dns_request *req = emalloc(sizeof(dns_request)); req->callback = cb; sw_copy_to_stack(req->callback, req->_callback); sw_zval_add_ref(&req->callback); req->domain = domain; sw_copy_to_stack(req->domain, req->_domain); sw_zval_add_ref(&req->domain); /** * Use asynchronous IO */ if (SwooleG.use_async_resolver) { php_swoole_check_reactor(); SW_CHECK_RETURN(swDNSResolver_request(Z_STRVAL_P(domain), php_swoole_dns_callback, (void *) req)); } php_swoole_check_aio(); /** * Use thread pool */ int buf_size; if (Z_STRLEN_P(domain) < SW_IP_MAX_LENGTH) { buf_size = SW_IP_MAX_LENGTH + 1; } else { buf_size = Z_STRLEN_P(domain) + 1; } void *buf = emalloc(buf_size); bzero(buf, buf_size); memcpy(buf, Z_STRVAL_P(domain), Z_STRLEN_P(domain)); php_swoole_check_aio(); SW_CHECK_RETURN(swAio_dns_lookup(req, buf, buf_size)); } static int process_stream_onRead(swReactor *reactor, swEvent *event) { SWOOLE_GET_TSRMLS; process_stream *ps = event->socket->object; char *buf = ps->buffer->str + ps->buffer->length; size_t len = ps->buffer->size - ps->buffer->length; int ret = read(event->fd, buf, len); if (ret > 0) { ps->buffer->length += ret; if (ps->buffer->length == ps->buffer->size) { swString_extend(ps->buffer, ps->buffer->size * 2); } return SW_OK; } else if (ret < 0) { swSysError("read() failed."); return SW_OK; } zval *retval = NULL; zval **args[2]; zval *zdata; SW_MAKE_STD_ZVAL(zdata); SW_ZVAL_STRINGL(zdata, ps->buffer->str, ps->buffer->length, 1); SwooleG.main_reactor->del(SwooleG.main_reactor, ps->fd); swString_free(ps->buffer); args[0] = &zdata; int status; zval *zstatus; SW_MAKE_STD_ZVAL(zstatus); pid_t pid = swWaitpid(ps->pid, &status, WNOHANG); if (pid > 0) { array_init(zstatus); add_assoc_long(zstatus, "code", WEXITSTATUS(status)); add_assoc_long(zstatus, "signal", WTERMSIG(status)); } else { ZVAL_FALSE(zstatus); } args[1] = &zstatus; zval *zcallback = ps->callback; if (zcallback) { if (sw_call_user_function_ex(EG(function_table), NULL, zcallback, &retval, 2, args, 0, NULL TSRMLS_CC) == FAILURE) { swoole_php_fatal_error(E_WARNING, "swoole_async: onAsyncComplete handler error"); } sw_zval_free(zcallback); } else { #ifdef SW_COROUTINE php_context *context = ps->context; sw_zval_add_ref(&zdata); add_assoc_zval(zstatus, "output", zdata); int ret = coro_resume(context, zstatus, &retval); if (ret == CORO_END && retval) { sw_zval_ptr_dtor(&retval); } efree(context); #else return SW_OK; #endif } if (EG(exception)) { zend_exception_error(EG(exception), E_ERROR TSRMLS_CC); } if (retval != NULL) { sw_zval_ptr_dtor(&retval); } sw_zval_ptr_dtor(&zdata); sw_zval_ptr_dtor(&zstatus); close(ps->fd); efree(ps); return SW_OK; } PHP_METHOD(swoole_async, exec) { char *command; zend_size_t command_len; zval *callback; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &command, &command_len, &callback) == FAILURE) { return; } php_swoole_check_reactor(); if (!swReactor_handle_isset(SwooleG.main_reactor, PHP_SWOOLE_FD_PROCESS_STREAM)) { SwooleG.main_reactor->setHandle(SwooleG.main_reactor, PHP_SWOOLE_FD_PROCESS_STREAM | SW_EVENT_READ, process_stream_onRead); SwooleG.main_reactor->setHandle(SwooleG.main_reactor, PHP_SWOOLE_FD_PROCESS_STREAM | SW_EVENT_ERROR, process_stream_onRead); } pid_t pid; int fd = swoole_shell_exec(command, &pid); if (fd < 0) { swoole_php_error(E_WARNING, "Unable to execute '%s'", command); RETURN_FALSE; } swString *buffer = swString_new(1024); if (buffer == NULL) { RETURN_FALSE; } process_stream *ps = emalloc(sizeof(process_stream)); ps->callback = sw_zval_dup(callback); #ifdef SW_COROUTINE ps->context = NULL; #endif sw_zval_add_ref(&ps->callback); ps->fd = fd; ps->pid = pid; ps->buffer = buffer; if (SwooleG.main_reactor->add(SwooleG.main_reactor, ps->fd, PHP_SWOOLE_FD_PROCESS_STREAM | SW_EVENT_READ) < 0) { sw_zval_free(ps->callback); efree(ps); RETURN_FALSE; } else { swConnection *_socket = swReactor_get(SwooleG.main_reactor, ps->fd); _socket->object = ps; RETURN_LONG(pid); } } #ifdef SW_COROUTINE PHP_FUNCTION(swoole_coroutine_exec) { char *command; zend_size_t command_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &command, &command_len) == FAILURE) { return; } coro_check(TSRMLS_C); php_swoole_check_reactor(); if (!swReactor_handle_isset(SwooleG.main_reactor, PHP_SWOOLE_FD_PROCESS_STREAM)) { SwooleG.main_reactor->setHandle(SwooleG.main_reactor, PHP_SWOOLE_FD_PROCESS_STREAM | SW_EVENT_READ, process_stream_onRead); SwooleG.main_reactor->setHandle(SwooleG.main_reactor, PHP_SWOOLE_FD_PROCESS_STREAM | SW_EVENT_ERROR, process_stream_onRead); } pid_t pid; int fd = swoole_shell_exec(command, &pid); if (fd < 0) { swoole_php_error(E_WARNING, "Unable to execute '%s'", command); RETURN_FALSE; } swString *buffer = swString_new(1024); if (buffer == NULL) { RETURN_FALSE; } process_stream *ps = emalloc(sizeof(process_stream)); ps->callback = NULL; ps->context = emalloc(sizeof(php_context)); ps->fd = fd; ps->pid = pid; ps->buffer = buffer; if (SwooleG.main_reactor->add(SwooleG.main_reactor, ps->fd, PHP_SWOOLE_FD_PROCESS_STREAM | SW_EVENT_READ) < 0) { efree(ps->context); efree(ps); RETURN_FALSE; } else { swConnection *_socket = swReactor_get(SwooleG.main_reactor, ps->fd); _socket->object = ps; coro_save(ps->context); coro_yield(); } } PHP_FUNCTION(swoole_async_dns_lookup_coro) { zval *domain; double timeout = SW_CLIENT_DEFAULT_TIMEOUT; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|d", &domain, &timeout) == FAILURE) { RETURN_FALSE; } coro_check(TSRMLS_C); if (Z_TYPE_P(domain) != IS_STRING) { swoole_php_fatal_error(E_WARNING, "invalid domain name."); RETURN_FALSE; } if (Z_STRLEN_P(domain) == 0) { swoole_php_fatal_error(E_WARNING, "domain name empty."); RETURN_FALSE; } if (!request_cache_map) { request_cache_map = swHashMap_new(256, NULL); } //find cache dns_cache *cache = swHashMap_find(request_cache_map, Z_STRVAL_P(domain), Z_STRLEN_P(domain)); if (cache != NULL && cache->update_time > (int64_t)swTimer_get_now_msec ) { SW_RETURN_STRINGL((*cache->zaddress).str,(*cache->zaddress).length,1); } dns_request *req = emalloc(sizeof(dns_request)); req->domain = domain; sw_copy_to_stack(req->domain, req->_domain); req->useless = 0; php_context *sw_current_context = emalloc(sizeof(php_context)); sw_current_context->onTimeout = NULL; sw_current_context->state = SW_CORO_CONTEXT_RUNNING; #if PHP_MAJOR_VERSION < 7 sw_current_context->coro_params = req; #else sw_current_context->coro_params.value.ptr = (void *) req; #endif req->context = sw_current_context; php_swoole_check_reactor(); int ret = swDNSResolver_request(Z_STRVAL_P(domain), php_swoole_dns_callback_coro, (void *) req); if (ret == SW_ERR) { SW_CHECK_RETURN(ret); } //add timeout php_swoole_check_timer(timeout); req->timer = SwooleG.timer.add(&SwooleG.timer, (int) (timeout * 1000), 0, sw_current_context, php_swoole_dns_timeout_coro); if (req->timer) { sw_current_context->state = SW_CORO_CONTEXT_IN_DELAYED_TIMEOUT_LIST; } coro_save(sw_current_context); coro_yield(); } #endif