int php_yar_socket_open(yar_transport_interface_t *self, zend_string *address, long options, char **err_msg) /* {{{ */ { yar_socket_data_t *data = (yar_socket_data_t *)self->data; struct timeval tv; php_stream *stream = NULL; zend_string *errstr = NULL; char *persistent_key = NULL; int err; tv.tv_sec = (ulong)(YAR_G(connect_timeout) / 1000); tv.tv_usec = (ulong)((YAR_G(connect_timeout) % 1000)? (YAR_G(connect_timeout) & 1000) * 1000 : 0); if (options & YAR_PROTOCOL_PERSISTENT) { data->persistent = 1; spprintf(&persistent_key, 0, "yar_%s", ZSTR_VAL(address)); } else { data->persistent = 0; } stream = php_stream_xport_create(ZSTR_VAL(address), ZSTR_LEN(address), 0, STREAM_XPORT_CLIENT|STREAM_XPORT_CONNECT, persistent_key, &tv, NULL, &errstr, &err); if (persistent_key) { efree(persistent_key); } if (stream == NULL) { spprintf(err_msg, 0, "Unable to connect to %s (%s)", ZSTR_VAL(address), strerror(errno)); efree(errstr); return 0; } php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, 0, NULL); #if ZEND_DEBUG stream->__exposed++; #endif data->stream = stream; return 1; } /* }}} */
/* {{{ void php_yar_debug(int server_side TSRMLS_DC, const char *format, ...) */ void php_yar_debug(int server_side TSRMLS_DC, const char *format, ...) { va_list args; if (!YAR_G(debug)) { return; } va_start(args, format); if (server_side) { php_verror(NULL, NULL, E_NOTICE, "[Debug Yar_Server]: %s", args TSRMLS_CC); } else { php_verror(NULL, NULL, E_NOTICE, "[Debug Yar_Client]: %s", args TSRMLS_CC); } va_end(args); }
zend_string *php_yar_packager_pack(char *packager_name, zval *pzval, char **msg) /* {{{ */ { char header[8]; smart_str buf = {0}; const yar_packager_t *packager = packager_name ? php_yar_packager_get(packager_name, strlen(packager_name)) : YAR_G(packager); if (!packager) { php_error_docref(NULL, E_ERROR, "unsupported packager %s", packager_name); return 0; } memcpy(header, packager->name, 8); smart_str_alloc(&buf, YAR_PACKAGER_BUFFER_SIZE /* 1M */, 0); smart_str_appendl(&buf, header, 8); packager->pack(packager, pzval, &buf, msg); if (buf.s) { smart_str_0(&buf); return buf.s; } smart_str_free(&buf); return NULL; } /* }}} */
int php_yar_socket_send(yar_transport_interface_t* self, yar_request_t *request, char **msg) /* {{{ */ { fd_set rfds; zend_string *payload; struct timeval tv; int ret = -1, fd, retval; char buf[SEND_BUF_SIZE]; yar_header_t header = {0}; yar_socket_data_t *data = (yar_socket_data_t *)self->data; FD_ZERO(&rfds); if (SUCCESS == php_stream_cast(data->stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void*)&fd, 1) && fd >= 0) { PHP_SAFE_FD_SET(fd, &rfds); } else { spprintf(msg, 0, "Unable cast socket fd form stream (%s)", strerror(errno)); return 0; } if (!(payload = php_yar_request_pack(request, msg))) { return 0; } DEBUG_C(ZEND_ULONG_FMT": pack request by '%.*s', result len '%ld', content: '%.32s'", request->id, 7, ZSTR_VAL(payload), ZSTR_LEN(payload), ZSTR_VAL(payload) + 8); /* for tcp/unix RPC, we need another way to supports auth */ php_yar_protocol_render(&header, request->id, "Yar PHP Client", NULL, ZSTR_LEN(payload), data->persistent? YAR_PROTOCOL_PERSISTENT : 0); memcpy(buf, (char *)&header, sizeof(yar_header_t)); tv.tv_sec = (ulong)(YAR_G(timeout) / 1000); tv.tv_usec = (ulong)((YAR_G(timeout) % 1000)? (YAR_G(timeout) & 1000) * 1000 : 0); retval = php_select(fd+1, NULL, &rfds, NULL, &tv); if (retval == -1) { zend_string_release(payload); spprintf(msg, 0, "select error '%s'", strerror(errno)); return 0; } else if (retval == 0) { zend_string_release(payload); spprintf(msg, 0, "select timeout '%ld' seconds reached", YAR_G(timeout)); return 0; } if (PHP_SAFE_FD_ISSET(fd, &rfds)) { size_t bytes_left = 0, bytes_sent = 0; if (ZSTR_LEN(payload) > (sizeof(buf) - sizeof(yar_header_t))) { memcpy(buf + sizeof(yar_header_t), ZSTR_VAL(payload), sizeof(buf) - sizeof(yar_header_t)); if ((ret = php_stream_xport_sendto(data->stream, buf, sizeof(buf), 0, NULL, 0)) < 0) { zend_string_release(payload); spprintf(msg, 0, "unable to send data"); return 0; } } else { memcpy(buf + sizeof(yar_header_t), ZSTR_VAL(payload), ZSTR_LEN(payload)); if ((ret = php_stream_xport_sendto(data->stream, buf, sizeof(yar_header_t) + ZSTR_LEN(payload), 0, NULL, 0)) < 0) { zend_string_release(payload); spprintf(msg, 0, "unable to send data"); return 0; } } bytes_sent = ret - sizeof(yar_header_t); bytes_left = ZSTR_LEN(payload) - bytes_sent; wait_io: if (bytes_left) { retval = php_select(fd+1, NULL, &rfds, NULL, &tv); if (retval == -1) { zend_string_release(payload); spprintf(msg, 0, "select error '%s'", strerror(errno)); return 0; } else if (retval == 0) { zend_string_release(payload); spprintf(msg, 0, "select timeout %ldms reached", YAR_G(timeout)); return 0; } if (PHP_SAFE_FD_ISSET(fd, &rfds)) { if ((ret = php_stream_xport_sendto(data->stream, ZSTR_VAL(payload) + bytes_sent, bytes_left, 0, NULL, 0)) > 0) { bytes_left -= ret; bytes_sent += ret; } } goto wait_io; } } zend_string_release(payload); return ret < 0? 0 : 1; } /* }}} */
yar_response_t * php_yar_socket_exec(yar_transport_interface_t* self, yar_request_t *request) /* {{{ */ { fd_set rfds; struct timeval tv; yar_header_t *header; yar_response_t *response; int fd, retval, recvd; size_t len = 0, total_recvd = 0; char *msg, buf[RECV_BUF_SIZE], *payload = NULL; yar_socket_data_t *data = (yar_socket_data_t *)self->data; response = ecalloc(1, sizeof(yar_response_t)); FD_ZERO(&rfds); if (SUCCESS == php_stream_cast(data->stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void*)&fd, 1) && fd >= 0) { PHP_SAFE_FD_SET(fd, &rfds); } else { len = snprintf(buf, sizeof(buf), "Unable cast socket fd form stream (%s)", strerror(errno)); php_yar_response_set_error(response, YAR_ERR_TRANSPORT, buf, len); return response; } tv.tv_sec = (ulong)(YAR_G(timeout) / 1000); tv.tv_usec = (ulong)((YAR_G(timeout) % 1000)? (YAR_G(timeout) & 1000) * 1000 : 0); wait_io: retval = php_select(fd+1, &rfds, NULL, NULL, &tv); if (retval == -1) { len = snprintf(buf, sizeof(buf), "Unable to select %d '%s'", fd, strerror(errno)); php_yar_response_set_error(response, YAR_ERR_TRANSPORT, buf, len); return response; } else if (retval == 0) { len = snprintf(buf, sizeof(buf), "select timeout %ldms reached", YAR_G(timeout)); php_yar_response_set_error(response, YAR_ERR_TRANSPORT, buf, len); return response; } if (PHP_SAFE_FD_ISSET(fd, &rfds)) { zval *retval, rret; if (!payload) { if ((recvd = php_stream_xport_recvfrom(data->stream, buf, sizeof(buf), 0, NULL, NULL, NULL)) > 0) { if (!(header = php_yar_protocol_parse(buf))) { php_yar_error(response, YAR_ERR_PROTOCOL, "malformed response header '%.32s'", payload); return response; } payload = emalloc(header->body_len); len = header->body_len; total_recvd = recvd - sizeof(yar_header_t); memcpy(payload, buf + sizeof(yar_header_t), total_recvd); if (recvd < (sizeof(yar_header_t) + len)) { goto wait_io; } } else if (recvd < 0) { /* this should never happen */ goto wait_io; } } else { if ((recvd = php_stream_xport_recvfrom(data->stream, payload + total_recvd, len - total_recvd, 0, NULL, NULL, NULL)) > 0) { total_recvd += recvd; } if (total_recvd < len) { goto wait_io; } } if (len) { if (!(retval = php_yar_packager_unpack(payload, len, &msg, &rret))) { php_yar_response_set_error(response, YAR_ERR_PACKAGER, msg, strlen(msg)); efree(msg); return response; } php_yar_response_map_retval(response, retval); DEBUG_C(ZEND_ULONG_FMT": server response content packaged by '%.*s', len '%ld', content '%.32s'", response->id, 7, payload, header->body_len, payload + 8); efree(payload); zval_ptr_dtor(retval); } else { php_yar_response_set_error(response, YAR_ERR_EMPTY_RESPONSE, ZEND_STRL("empty response")); } return response; } else { goto wait_io; } } /* }}} */
static zval * php_yar_client_parse_response(char *ret, size_t len, int throw_exception TSRMLS_DC) /* {{{ */ { zval *retval, *response; yar_header_t *header; char *err_msg; MAKE_STD_ZVAL(retval); ZVAL_FALSE(retval); if (!(header = php_yar_protocol_parse(&ret, &len, &err_msg TSRMLS_CC))) { php_yar_client_trigger_error(throw_exception TSRMLS_CC, YAR_ERR_PROTOCOL, "%s", err_msg); if (YAR_G(debug)) { php_yar_debug_client("0: malformed response '%s'", ret); } efree(err_msg); return retval; } if (!len || !header->body_len) { php_yar_client_trigger_error(throw_exception TSRMLS_CC, 0, "server responsed empty body"); return retval; } if (YAR_G(debug)) { php_yar_debug_client("%ld: server responsed: packager '%s', len '%ld', content '%s'", header->id, ret, len - 8, ret + 8); } if (!(response = php_yar_packager_unpack(ret, len, &err_msg TSRMLS_CC))) { php_yar_client_trigger_error(throw_exception TSRMLS_CC, YAR_ERR_PACKAGER, "%s", err_msg); efree(err_msg); return retval; } if (response && IS_ARRAY == Z_TYPE_P(response)) { zval **ppzval; uint status; HashTable *ht = Z_ARRVAL_P(response); if (zend_hash_find(ht, ZEND_STRS("s"), (void **)&ppzval) == FAILURE) { } convert_to_long(*ppzval); status = Z_LVAL_PP(ppzval); if (status == YAR_ERR_OKEY) { if (zend_hash_find(ht, ZEND_STRS("o"), (void **)&ppzval) == SUCCESS) { PHPWRITE(Z_STRVAL_PP(ppzval), Z_STRLEN_PP(ppzval)); } } else if (status == YAR_ERR_EXCEPTION) { if (zend_hash_find(ht, ZEND_STRS("e"), (void **)&ppzval) == SUCCESS) { if (throw_exception) { zval *ex, **property; MAKE_STD_ZVAL(ex); object_init_ex(ex, yar_server_exception_ce); if (zend_hash_find(Z_ARRVAL_PP(ppzval), ZEND_STRS("message"), (void **)&property) == SUCCESS) { zend_update_property(yar_server_exception_ce, ex, ZEND_STRL("message"), *property TSRMLS_CC); } if (zend_hash_find(Z_ARRVAL_PP(ppzval), ZEND_STRS("code"), (void **)&property) == SUCCESS) { zend_update_property(yar_server_exception_ce, ex, ZEND_STRL("code"), *property TSRMLS_CC); } if (zend_hash_find(Z_ARRVAL_PP(ppzval), ZEND_STRS("file"), (void **)&property) == SUCCESS) { zend_update_property(yar_server_exception_ce, ex, ZEND_STRL("file"), *property TSRMLS_CC); } if (zend_hash_find(Z_ARRVAL_PP(ppzval), ZEND_STRS("line"), (void **)&property) == SUCCESS) { zend_update_property(yar_server_exception_ce, ex, ZEND_STRL("line"), *property TSRMLS_CC); } if (zend_hash_find(Z_ARRVAL_PP(ppzval), ZEND_STRS("_type"), (void **)&property) == SUCCESS) { zend_update_property(yar_server_exception_ce, ex, ZEND_STRL("_type"), *property TSRMLS_CC); } zend_throw_exception_object(ex TSRMLS_CC); } else { zval **msg, **code; if (zend_hash_find(Z_ARRVAL_PP(ppzval), ZEND_STRS("message"), (void **)&msg) == SUCCESS && zend_hash_find(Z_ARRVAL_PP(ppzval), ZEND_STRS("code"), (void **)&code) == SUCCESS) { convert_to_string_ex(msg); convert_to_long_ex(code); php_yar_client_trigger_error(0 TSRMLS_CC, Z_LVAL_PP(code), "server threw an exception with message `%s`", Z_STRVAL_PP(msg)); } } } } else if (zend_hash_find(ht, ZEND_STRS("e"), (void **)&ppzval) == SUCCESS && IS_STRING == Z_TYPE_PP(ppzval)) { php_yar_client_trigger_error(throw_exception TSRMLS_CC, status, "%s", Z_STRVAL_PP(ppzval)); } if (zend_hash_find(ht, ZEND_STRS("r"), (void **)&ppzval) == SUCCESS) { ZVAL_ZVAL(retval, *ppzval, 1, 0); } zval_ptr_dtor(&response); } else if (response) { zval_ptr_dtor(&response); } return retval; } /* }}} */