--- embedaddon/php/ext/mysqlnd/mysqlnd_net.c 2012/02/21 23:47:58 1.1.1.1 +++ embedaddon/php/ext/mysqlnd/mysqlnd_net.c 2012/05/29 12:34:41 1.1.1.2 @@ -12,11 +12,13 @@ | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Authors: Georg Richter | - | Andrey Hristov | + | Authors: Andrey Hristov | | Ulf Wendel | + | Georg Richter | +----------------------------------------------------------------------+ */ + +/* $Id: mysqlnd_net.c,v 1.1.1.2 2012/05/29 12:34:41 misho Exp $ */ #include "php.h" #include "php_globals.h" #include "mysqlnd.h" @@ -24,7 +26,7 @@ #include "mysqlnd_wireprotocol.h" #include "mysqlnd_statistics.h" #include "mysqlnd_debug.h" -#include "ext/standard/sha1.h" +#include "mysqlnd_ext_plugin.h" #include "php_network.h" #include "zend_ini.h" #ifdef MYSQLND_COMPRESSION_ENABLED @@ -59,48 +61,88 @@ mysqlnd_set_sock_no_delay(php_stream * stream TSRMLS_D /* }}} */ -/* {{{ mysqlnd_net::network_read */ +/* {{{ mysqlnd_net::network_read_ex */ static enum_func_status -MYSQLND_METHOD(mysqlnd_net, network_read)(MYSQLND * conn, zend_uchar * buffer, size_t count TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_net, network_read_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count, + MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC) { enum_func_status return_value = PASS; size_t to_read = count, ret; - size_t old_chunk_size = conn->net->stream->chunk_size; - DBG_ENTER("mysqlnd_net::network_read"); - DBG_INF_FMT("count=%u", count); - conn->net->stream->chunk_size = MIN(to_read, conn->net->options.net_read_buffer_size); + size_t old_chunk_size = net->stream->chunk_size; + zend_uchar * p = buffer; + + DBG_ENTER("mysqlnd_net::network_read_ex"); + DBG_INF_FMT("count="MYSQLND_SZ_T_SPEC, count); + + net->stream->chunk_size = MIN(to_read, net->options.net_read_buffer_size); while (to_read) { - if (!(ret = php_stream_read(conn->net->stream, (char *) buffer, to_read))) { + if (!(ret = php_stream_read(net->stream, (char *) p, to_read))) { DBG_ERR_FMT("Error while reading header from socket"); return_value = FAIL; break; } - buffer += ret; + p += ret; to_read -= ret; } - MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_BYTES_RECEIVED, count - to_read); - conn->net->stream->chunk_size = old_chunk_size; + MYSQLND_INC_CONN_STATISTIC_W_VALUE(stats, STAT_BYTES_RECEIVED, count - to_read); + net->stream->chunk_size = old_chunk_size; DBG_RETURN(return_value); } /* }}} */ -/* {{{ mysqlnd_net::network_write */ +/* {{{ mysqlnd_net::network_write_ex */ static size_t -MYSQLND_METHOD(mysqlnd_net, network_write)(MYSQLND * const conn, const zend_uchar * const buf, size_t count TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_net, network_write_ex)(MYSQLND_NET * const net, const zend_uchar * const buffer, const size_t count, + MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC) { size_t ret; - DBG_ENTER("mysqlnd_net::network_write"); - ret = php_stream_write(conn->net->stream, (char *)buf, count); + DBG_ENTER("mysqlnd_net::network_write_ex"); + ret = php_stream_write(net->stream, (char *)buffer, count); DBG_RETURN(ret); } /* }}} */ +/* {{{ mysqlnd_net::open_pipe */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_net, open_pipe)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len, + const zend_bool persistent, + MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC) +{ +#if PHP_API_VERSION < 20100412 + unsigned int streams_options = ENFORCE_SAFE_MODE; +#else + unsigned int streams_options = 0; +#endif + DBG_ENTER("mysqlnd_net::open_pipe"); + if (persistent) { + streams_options |= STREAM_OPEN_PERSISTENT; + } + streams_options |= IGNORE_URL; + net->stream = php_stream_open_wrapper((char*) scheme + sizeof("pipe://") - 1, "r+", streams_options, NULL); + if (!net->stream) { + SET_CLIENT_ERROR(*error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, "Unknown errror while connecting"); + DBG_RETURN(FAIL); + } + /* + Streams are not meant for C extensions! Thus we need a hack. Every connected stream will + be registered as resource (in EG(regular_list). So far, so good. However, it won't be + unregistered yntil the script ends. So, we need to take care of that. + */ + net->stream->in_free = 1; + zend_hash_index_del(&EG(regular_list), net->stream->rsrc_id); + net->stream->in_free = 0; + DBG_RETURN(PASS); +} +/* }}} */ -/* {{{ mysqlnd_net::connect */ + +/* {{{ mysqlnd_net::open_tcp_or_unix */ static enum_func_status -MYSQLND_METHOD(mysqlnd_net, connect)(MYSQLND_NET * net, const char * const scheme, size_t scheme_len, zend_bool persistent, char **errstr, int * errcode TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_net, open_tcp_or_unix)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len, + const zend_bool persistent, + MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC) { #if PHP_API_VERSION < 20100412 unsigned int streams_options = ENFORCE_SAFE_MODE; @@ -110,27 +152,17 @@ MYSQLND_METHOD(mysqlnd_net, connect)(MYSQLND_NET * net unsigned int streams_flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT; char * hashed_details = NULL; int hashed_details_len = 0; + char * errstr = NULL; + int errcode = 0; struct timeval tv; - DBG_ENTER("mysqlnd_net::connect"); + DBG_ENTER("mysqlnd_net::open_tcp_or_unix"); + if (persistent) { - hashed_details_len = spprintf(&hashed_details, 0, "%p", net); + hashed_details_len = mnd_sprintf(&hashed_details, 0, "%p", net); DBG_INF_FMT("hashed_details=%s", hashed_details); } - net->packet_no = net->compressed_envelope_packet_no = 0; - - if (net->stream) { - /* close before opening a new one */ - DBG_INF_FMT("Freeing stream. abstract=%p", net->stream->abstract); - if (net->persistent) { - php_stream_free(net->stream, PHP_STREAM_FREE_CLOSE_PERSISTENT | PHP_STREAM_FREE_RSRC_DTOR); - } else { - php_stream_free(net->stream, PHP_STREAM_FREE_CLOSE); - } - net->stream = NULL; - } - if (net->options.timeout_connect) { tv.tv_sec = net->options.timeout_connect; tv.tv_usec = 0; @@ -139,16 +171,20 @@ MYSQLND_METHOD(mysqlnd_net, connect)(MYSQLND_NET * net DBG_INF_FMT("calling php_stream_xport_create"); net->stream = php_stream_xport_create(scheme, scheme_len, streams_options, streams_flags, hashed_details, (net->options.timeout_connect) ? &tv : NULL, - NULL /*ctx*/, errstr, errcode); - - if (*errstr || !net->stream) { + NULL /*ctx*/, &errstr, &errcode); + if (errstr || !net->stream) { + DBG_ERR("Error"); if (hashed_details) { - efree(hashed_details); /* allocated by spprintf */ + mnd_sprintf_free(hashed_details); } - *errcode = CR_CONNECTION_ERROR; + errcode = CR_CONNECTION_ERROR; + SET_CLIENT_ERROR(*error_info, errcode? errcode:CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, errstr); + if (errstr) { + /* no mnd_ since we don't allocate it */ + efree(errstr); + } DBG_RETURN(FAIL); } - if (hashed_details) { /* If persistent, the streams register it in EG(persistent_list). @@ -171,22 +207,32 @@ MYSQLND_METHOD(mysqlnd_net, connect)(MYSQLND_NET * net /* Shut-up the streams, they don't know what they are doing */ net->stream->__exposed = 1; #endif - efree(hashed_details); + mnd_sprintf_free(hashed_details); } + /* Streams are not meant for C extensions! Thus we need a hack. Every connected stream will be registered as resource (in EG(regular_list). So far, so good. However, it won't be - unregistered till the script ends. So, we need to take care of that. + unregistered yntil the script ends. So, we need to take care of that. */ net->stream->in_free = 1; zend_hash_index_del(&EG(regular_list), net->stream->rsrc_id); net->stream->in_free = 0; - if (!net->options.timeout_read) { - /* should always happen because read_timeout cannot be set via API */ - net->options.timeout_read = (unsigned int) MYSQLND_G(net_read_timeout); - } + DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ mysqlnd_net::connect_ex */ +static void +MYSQLND_METHOD(mysqlnd_net, post_connect_set_opt)(MYSQLND_NET * const net, + const char * const scheme, const size_t scheme_len, + MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC) +{ + DBG_ENTER("mysqlnd_net::post_connect_set_opt"); if (net->options.timeout_read) { + struct timeval tv; DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net->options.timeout_read); tv.tv_sec = net->options.timeout_read; tv.tv_usec = 0; @@ -198,13 +244,33 @@ MYSQLND_METHOD(mysqlnd_net, connect)(MYSQLND_NET * net mysqlnd_set_sock_no_delay(net->stream TSRMLS_CC); } - { - unsigned int buf_size = MYSQLND_G(net_read_buffer_size); /* this is long, cast to unsigned int*/ - net->m.set_client_option(net, MYSQLND_OPT_NET_READ_BUFFER_SIZE, (char *)&buf_size TSRMLS_CC); - } + DBG_VOID_RETURN; +} +/* }}} */ - DBG_RETURN(PASS); +/* {{{ mysqlnd_net::connect_ex */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_net, connect_ex)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len, + const zend_bool persistent, + MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC) +{ + enum_func_status ret = FAIL; + func_mysqlnd_net__open_stream open_stream = NULL; + DBG_ENTER("mysqlnd_net::connect_ex"); + + net->packet_no = net->compressed_envelope_packet_no = 0; + + net->m.close_stream(net, conn_stats, error_info TSRMLS_CC); + + open_stream = (scheme_len > (sizeof("pipe://") - 1) && !memcmp(scheme, "pipe://", sizeof("pipe://") - 1))? net->m.open_pipe: + net->m.open_tcp_or_unix; + + if (PASS == (ret = open_stream(net, scheme, scheme_len, persistent, conn_stats, error_info TSRMLS_CC))) { + net->m.post_connect_set_opt(net, scheme, scheme_len, conn_stats, error_info TSRMLS_CC); + } + + DBG_RETURN(ret); } /* }}} */ @@ -218,35 +284,33 @@ MYSQLND_METHOD(mysqlnd_net, connect)(MYSQLND_NET * net #define STORE_HEADER_SIZE(safe_storage, buffer) COPY_HEADER((safe_storage), (buffer)) #define RESTORE_HEADER_SIZE(buffer, safe_storage) STORE_HEADER_SIZE((safe_storage), (buffer)) -/* {{{ mysqlnd_net::send */ + +/* {{{ mysqlnd_net::send_ex */ /* - IMPORTANT : It's expected that buf has place in the beginning for MYSQLND_HEADER_SIZE !!!! + IMPORTANT : It's expected that buffer has place in the beginning for MYSQLND_HEADER_SIZE !!!! This is done for performance reasons in the caller of this function. Otherwise we will have to do send two TCP packets, or do new alloc and memcpy. Neither are quick, thus the clients of this function are obligated to do what they are asked for. `count` is actually the length of the payload data. Thus : - count + MYSQLND_HEADER_SIZE = sizeof(buf) (not the pointer but the actual buffer) + count + MYSQLND_HEADER_SIZE = sizeof(buffer) (not the pointer but the actual buffer) */ -size_t -MYSQLND_METHOD(mysqlnd_net, send)(MYSQLND * const conn, char * const buf, size_t count TSRMLS_DC) +static size_t +MYSQLND_METHOD(mysqlnd_net, send_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count, + MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC) { zend_uchar safe_buf[((MYSQLND_HEADER_SIZE) + (sizeof(zend_uchar)) - 1) / (sizeof(zend_uchar))]; - zend_uchar *safe_storage = safe_buf; - MYSQLND_NET *net = conn->net; - size_t old_chunk_size = net->stream->chunk_size; - size_t ret, packets_sent = 1; + zend_uchar * safe_storage = safe_buf; + size_t bytes_sent, packets_sent = 1; size_t left = count; - zend_uchar *p = (zend_uchar *) buf; + zend_uchar * p = (zend_uchar *) buffer; zend_uchar * compress_buf = NULL; size_t to_be_sent; - DBG_ENTER("mysqlnd_net::send"); - DBG_INF_FMT("conn=%llu count=%lu compression=%u", conn->thread_id, count, net->compressed); + DBG_ENTER("mysqlnd_net::send_ex"); + DBG_INF_FMT("count=" MYSQLND_SZ_T_SPEC " compression=%u", count, net->compressed); - net->stream->chunk_size = MYSQLND_MAX_PACKET_SIZE; - if (net->compressed == TRUE) { size_t comp_buf_size = MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE + MIN(left, MYSQLND_MAX_PACKET_SIZE); DBG_INF_FMT("compress_buf_size="MYSQLND_SZ_T_SPEC, comp_buf_size); @@ -280,7 +344,8 @@ MYSQLND_METHOD(mysqlnd_net, send)(MYSQLND * const conn int3store(compress_buf, payload_size); int1store(compress_buf + 3, net->packet_no); DBG_INF_FMT("writing "MYSQLND_SZ_T_SPEC" bytes to the network", payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE); - ret = conn->net->m.network_write(conn, compress_buf, payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE TSRMLS_CC); + bytes_sent = net->m.network_write_ex(net, compress_buf, payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, + conn_stats, error_info TSRMLS_CC); net->compressed_envelope_packet_no++; #if WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY if (res == Z_OK) { @@ -311,7 +376,7 @@ MYSQLND_METHOD(mysqlnd_net, send)(MYSQLND * const conn STORE_HEADER_SIZE(safe_storage, p); int3store(p, to_be_sent); int1store(p + 3, net->packet_no); - ret = conn->net->m.network_write(conn, p, to_be_sent + MYSQLND_HEADER_SIZE TSRMLS_CC); + bytes_sent = net->m.network_write_ex(net, p, to_be_sent + MYSQLND_HEADER_SIZE, conn_stats, error_info TSRMLS_CC); RESTORE_HEADER_SIZE(p, safe_storage); net->compressed_envelope_packet_no++; } @@ -327,26 +392,25 @@ MYSQLND_METHOD(mysqlnd_net, send)(MYSQLND * const conn indeed it then loop once more, then to_be_sent will become 0, left will stay 0. Empty packet will be sent and this loop will end. */ - } while (ret && (left > 0 || to_be_sent == MYSQLND_MAX_PACKET_SIZE)); + } while (bytes_sent && (left > 0 || to_be_sent == MYSQLND_MAX_PACKET_SIZE)); DBG_INF_FMT("packet_size="MYSQLND_SZ_T_SPEC" packet_no=%u", left, net->packet_no); - /* Even for zero size payload we have to send a packet */ - if (!ret) { - DBG_ERR_FMT("Can't %u send bytes", count); - conn->state = CONN_QUIT_SENT; - SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone); - } - MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn->stats, + MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, STAT_BYTES_SENT, count + packets_sent * MYSQLND_HEADER_SIZE, STAT_PROTOCOL_OVERHEAD_OUT, packets_sent * MYSQLND_HEADER_SIZE, STAT_PACKETS_SENT, packets_sent); - net->stream->chunk_size = old_chunk_size; if (compress_buf) { mnd_efree(compress_buf); } - DBG_RETURN(ret); + + /* Even for zero size payload we have to send a packet */ + if (!bytes_sent) { + DBG_ERR_FMT("Can't %u send bytes", count); + SET_CLIENT_ERROR(*error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone); + } + DBG_RETURN(bytes_sent); } /* }}} */ @@ -416,19 +480,19 @@ mysqlnd_create_read_buffer(size_t count TSRMLS_DC) /* }}} */ -/* {{{ mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer */ +/* {{{ mysqlnd_net::read_compressed_packet_from_stream_and_fill_read_buffer */ static enum_func_status -mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer(MYSQLND * conn, size_t net_payload_size TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_net, read_compressed_packet_from_stream_and_fill_read_buffer) + (MYSQLND_NET * net, size_t net_payload_size, MYSQLND_STATS * conn_stats, MYSQLND_ERROR_INFO * error_info TSRMLS_DC) { - MYSQLND_NET * net = conn->net; size_t decompressed_size; enum_func_status ret = PASS; zend_uchar * compressed_data = NULL; zend_uchar comp_header[COMPRESSED_HEADER_SIZE]; - DBG_ENTER("mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer"); + DBG_ENTER("mysqlnd_net::read_compressed_packet_from_stream_and_fill_read_buffe"); /* Read the compressed header */ - if (FAIL == conn->net->m.network_read(conn, comp_header, COMPRESSED_HEADER_SIZE TSRMLS_CC)) { + if (FAIL == net->m.network_read_ex(net, comp_header, COMPRESSED_HEADER_SIZE, conn_stats, error_info TSRMLS_CC)) { DBG_RETURN(FAIL); } decompressed_size = uint3korr(comp_header); @@ -438,7 +502,7 @@ mysqlnd_read_compressed_packet_from_stream_and_fill_re if (decompressed_size) { compressed_data = mnd_emalloc(net_payload_size); - if (FAIL == conn->net->m.network_read(conn, compressed_data, net_payload_size TSRMLS_CC)) { + if (FAIL == net->m.network_read_ex(net, compressed_data, net_payload_size, conn_stats, error_info TSRMLS_CC)) { ret = FAIL; goto end; } @@ -450,7 +514,7 @@ mysqlnd_read_compressed_packet_from_stream_and_fill_re } else { DBG_INF_FMT("The server decided not to compress the data. Our job is easy. Copying %u bytes", net_payload_size); net->uncompressed_data = mysqlnd_create_read_buffer(net_payload_size TSRMLS_CC); - if (FAIL == conn->net->m.network_read(conn, net->uncompressed_data->data, net_payload_size TSRMLS_CC)) { + if (FAIL == net->m.network_read_ex(net, net->uncompressed_data->data, net_payload_size, conn_stats, error_info TSRMLS_CC)) { ret = FAIL; goto end; } @@ -467,8 +531,8 @@ end: /* {{{ mysqlnd_net::decode */ static enum_func_status -MYSQLND_METHOD(mysqlnd_net, decode)(zend_uchar * uncompressed_data, size_t uncompressed_data_len, - const zend_uchar * const compressed_data, size_t compressed_data_len TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_net, decode)(zend_uchar * uncompressed_data, const size_t uncompressed_data_len, + const zend_uchar * const compressed_data, const size_t compressed_data_len TSRMLS_DC) { #ifdef MYSQLND_COMPRESSION_ENABLED int error; @@ -492,7 +556,7 @@ MYSQLND_METHOD(mysqlnd_net, decode)(zend_uchar * uncom /* {{{ mysqlnd_net::encode */ static enum_func_status MYSQLND_METHOD(mysqlnd_net, encode)(zend_uchar * compress_buffer, size_t * compress_buffer_len, - const zend_uchar * const uncompressed_data, size_t uncompressed_data_len TSRMLS_DC) + const zend_uchar * const uncompressed_data, const size_t uncompressed_data_len TSRMLS_DC) { #ifdef MYSQLND_COMPRESSION_ENABLED int error; @@ -516,15 +580,15 @@ MYSQLND_METHOD(mysqlnd_net, encode)(zend_uchar * compr /* }}} */ -/* {{{ mysqlnd_net::receive */ +/* {{{ mysqlnd_net::receive_ex */ static enum_func_status -MYSQLND_METHOD(mysqlnd_net, receive)(MYSQLND * conn, zend_uchar * buffer, size_t count TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_net, receive_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count, + MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC) { size_t to_read = count; zend_uchar * p = buffer; - MYSQLND_NET * net = conn->net; - DBG_ENTER("mysqlnd_net::receive"); + DBG_ENTER("mysqlnd_net::receive_ex"); #ifdef MYSQLND_COMPRESSION_ENABLED if (net->compressed) { if (net->uncompressed_data) { @@ -546,7 +610,7 @@ MYSQLND_METHOD(mysqlnd_net, receive)(MYSQLND * conn, z size_t net_payload_size; zend_uchar packet_no; - if (FAIL == net->m.network_read(conn, net_header, MYSQLND_HEADER_SIZE TSRMLS_CC)) { + if (FAIL == net->m.network_read_ex(net, net_header, MYSQLND_HEADER_SIZE, conn_stats, error_info TSRMLS_CC)) { DBG_RETURN(FAIL); } net_payload_size = uint3korr(net_header); @@ -564,7 +628,7 @@ MYSQLND_METHOD(mysqlnd_net, receive)(MYSQLND * conn, z DBG_INF_FMT("HEADER: hwd_packet_no=%u size=%3u", packet_no, (unsigned long) net_payload_size); #endif /* Now let's read from the wire, decompress it and fill the read buffer */ - mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer(conn, net_payload_size TSRMLS_CC); + net->m.read_compressed_packet_from_stream_and_fill_read_buffer(net, net_payload_size, conn_stats, error_info TSRMLS_CC); /* Now a bit of recursion - read from the read buffer, @@ -572,12 +636,12 @@ MYSQLND_METHOD(mysqlnd_net, receive)(MYSQLND * conn, z is not enough, then the recursive call will try to satisfy it until it is satisfied. */ - DBG_RETURN(net->m.receive(conn, p, to_read TSRMLS_CC)); + DBG_RETURN(net->m.receive_ex(net, p, to_read, conn_stats, error_info TSRMLS_CC)); } DBG_RETURN(PASS); } #endif /* MYSQLND_COMPRESSION_ENABLED */ - DBG_RETURN(net->m.network_read(conn, p, to_read TSRMLS_CC)); + DBG_RETURN(net->m.network_read_ex(net, p, to_read, conn_stats, error_info TSRMLS_CC)); } /* }}} */ @@ -668,13 +732,11 @@ MYSQLND_METHOD(mysqlnd_net, set_client_option)(MYSQLND case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: net->options.ssl_verify_peer = value? ((*(zend_bool *)value)? TRUE:FALSE): FALSE; break; -#ifdef WHEN_SUPPORTED_BY_MYSQLI case MYSQL_OPT_READ_TIMEOUT: - DBG_INF("MYSQL_OPT_READ_TIMEOUT"); net->options.timeout_read = *(unsigned int*) value; break; +#ifdef WHEN_SUPPORTED_BY_MYSQLI case MYSQL_OPT_WRITE_TIMEOUT: - DBG_INF("MYSQL_OPT_WRITE_TIMEOUT"); net->options.timeout_write = *(unsigned int*) value; break; #endif @@ -744,7 +806,7 @@ static enum_func_status MYSQLND_METHOD(mysqlnd_net, enable_ssl)(MYSQLND_NET * const net TSRMLS_DC) { #ifdef MYSQLND_SSL_SUPPORTED - php_stream_context *context = php_stream_context_alloc(); + php_stream_context *context = php_stream_context_alloc(TSRMLS_C); DBG_ENTER("mysqlnd_net::enable_ssl"); if (!context) { DBG_RETURN(FAIL); @@ -753,19 +815,16 @@ MYSQLND_METHOD(mysqlnd_net, enable_ssl)(MYSQLND_NET * if (net->options.ssl_key) { zval key_zval; ZVAL_STRING(&key_zval, net->options.ssl_key, 0); - DBG_INF("key"); php_stream_context_set_option(context, "ssl", "local_pk", &key_zval); } if (net->options.ssl_verify_peer) { zval verify_peer_zval; ZVAL_TRUE(&verify_peer_zval); - DBG_INF("verify peer"); php_stream_context_set_option(context, "ssl", "verify_peer", &verify_peer_zval); } if (net->options.ssl_cert) { zval cert_zval; ZVAL_STRING(&cert_zval, net->options.ssl_cert, 0); - DBG_INF_FMT("local_cert=%s", net->options.ssl_cert); php_stream_context_set_option(context, "ssl", "local_cert", &cert_zval); if (!net->options.ssl_key) { php_stream_context_set_option(context, "ssl", "local_pk", &cert_zval); @@ -774,13 +833,11 @@ MYSQLND_METHOD(mysqlnd_net, enable_ssl)(MYSQLND_NET * if (net->options.ssl_ca) { zval cafile_zval; ZVAL_STRING(&cafile_zval, net->options.ssl_ca, 0); - DBG_INF_FMT("cafile=%s", net->options.ssl_ca); php_stream_context_set_option(context, "ssl", "cafile", &cafile_zval); } if (net->options.ssl_capath) { zval capath_zval; ZVAL_STRING(&capath_zval, net->options.ssl_capath, 0); - DBG_INF_FMT("capath=%s", net->options.ssl_capath); php_stream_context_set_option(context, "ssl", "cafile", &capath_zval); } if (net->options.ssl_passphrase) { @@ -791,7 +848,6 @@ MYSQLND_METHOD(mysqlnd_net, enable_ssl)(MYSQLND_NET * if (net->options.ssl_cipher) { zval cipher_zval; ZVAL_STRING(&cipher_zval, net->options.ssl_cipher, 0); - DBG_INF_FMT("ciphers=%s", net->options.ssl_cipher); php_stream_context_set_option(context, "ssl", "ciphers", &cipher_zval); } php_stream_context_set(net->stream, context); @@ -838,7 +894,7 @@ MYSQLND_METHOD(mysqlnd_net, disable_ssl)(MYSQLND_NET * /* }}} */ -/* {{{ mysqlnd_net::set_client_option */ +/* {{{ mysqlnd_net::free_contents */ static void MYSQLND_METHOD(mysqlnd_net, free_contents)(MYSQLND_NET * net TSRMLS_DC) { @@ -875,70 +931,72 @@ MYSQLND_METHOD(mysqlnd_net, free_contents)(MYSQLND_NET } /* }}} */ -static -MYSQLND_CLASS_METHODS_START(mysqlnd_net) - MYSQLND_METHOD(mysqlnd_net, connect), - MYSQLND_METHOD(mysqlnd_net, send), - MYSQLND_METHOD(mysqlnd_net, receive), - MYSQLND_METHOD(mysqlnd_net, set_client_option), - MYSQLND_METHOD(mysqlnd_net, network_read), - MYSQLND_METHOD(mysqlnd_net, network_write), - MYSQLND_METHOD(mysqlnd_net, decode), - MYSQLND_METHOD(mysqlnd_net, encode), - MYSQLND_METHOD(mysqlnd_net, consume_uneaten_data), - MYSQLND_METHOD(mysqlnd_net, free_contents), - MYSQLND_METHOD(mysqlnd_net, enable_ssl), - MYSQLND_METHOD(mysqlnd_net, disable_ssl) -MYSQLND_CLASS_METHODS_END; - -/* {{{ mysqlnd_net_init */ -PHPAPI MYSQLND_NET * -mysqlnd_net_init(zend_bool persistent TSRMLS_DC) +/* {{{ mysqlnd_net::close_stream */ +static void +MYSQLND_METHOD(mysqlnd_net, close_stream)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC) { - size_t alloc_size = sizeof(MYSQLND_NET) + mysqlnd_plugin_count() * sizeof(void *); - MYSQLND_NET * net = mnd_pecalloc(1, alloc_size, persistent); - - DBG_ENTER("mysqlnd_net_init"); - DBG_INF_FMT("persistent=%u", persistent); - if (net) { - net->persistent = persistent; - net->m = mysqlnd_mysqlnd_net_methods; - - { - unsigned int buf_size = MYSQLND_G(net_cmd_buffer_size); /* this is long, cast to unsigned int*/ - net->m.set_client_option(net, MYSQLND_OPT_NET_CMD_BUFFER_SIZE, (char *) &buf_size TSRMLS_CC); + DBG_ENTER("mysqlnd_net::close_stream"); + if (net && net->stream) { + zend_bool pers = net->persistent; + DBG_INF_FMT("Freeing stream. abstract=%p", net->stream->abstract); + if (pers) { + if (EG(active)) { + php_stream_free(net->stream, PHP_STREAM_FREE_CLOSE_PERSISTENT | PHP_STREAM_FREE_RSRC_DTOR); + } else { + /* + otherwise we will crash because the EG(persistent_list) has been freed already, + before the modules are shut down + */ + php_stream_free(net->stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR); + } + } else { + php_stream_free(net->stream, PHP_STREAM_FREE_CLOSE); } + net->stream = NULL; } - DBG_RETURN(net); + + DBG_VOID_RETURN; } /* }}} */ -/* {{{ mysqlnd_net_free */ -PHPAPI void -mysqlnd_net_free(MYSQLND_NET * const net TSRMLS_DC) +/* {{{ mysqlnd_net::init */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_net, init)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC) { - DBG_ENTER("mysqlnd_net_free"); + unsigned int buf_size; + DBG_ENTER("mysqlnd_net::init"); + buf_size = MYSQLND_G(net_cmd_buffer_size); /* this is long, cast to unsigned int*/ + net->m.set_client_option(net, MYSQLND_OPT_NET_CMD_BUFFER_SIZE, (char *) &buf_size TSRMLS_CC); + + buf_size = MYSQLND_G(net_read_buffer_size); /* this is long, cast to unsigned int*/ + net->m.set_client_option(net, MYSQLND_OPT_NET_READ_BUFFER_SIZE, (char *)&buf_size TSRMLS_CC); + + buf_size = MYSQLND_G(net_read_timeout); /* this is long, cast to unsigned int*/ + net->m.set_client_option(net, MYSQL_OPT_READ_TIMEOUT, (char *)&buf_size TSRMLS_CC); + + DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ mysqlnd_net::dtor */ +static void +MYSQLND_METHOD(mysqlnd_net, dtor)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC) +{ + DBG_ENTER("mysqlnd_net::dtor"); if (net) { zend_bool pers = net->persistent; - + net->m.free_contents(net TSRMLS_CC); + net->m.close_stream(net, stats, error_info TSRMLS_CC); if (net->cmd_buffer.buffer) { DBG_INF("Freeing cmd buffer"); mnd_pefree(net->cmd_buffer.buffer, pers); net->cmd_buffer.buffer = NULL; } - if (net->stream) { - DBG_INF_FMT("Freeing stream. abstract=%p", net->stream->abstract); - if (pers) { - php_stream_free(net->stream, PHP_STREAM_FREE_CLOSE_PERSISTENT | PHP_STREAM_FREE_RSRC_DTOR); - } else { - php_stream_free(net->stream, PHP_STREAM_FREE_CLOSE); - } - net->stream = NULL; - } mnd_pefree(net, pers); } DBG_VOID_RETURN; @@ -946,26 +1004,59 @@ mysqlnd_net_free(MYSQLND_NET * const net TSRMLS_DC) /* }}} */ -/* {{{ _mysqlnd_plugin_get_plugin_net_data */ -PHPAPI void ** _mysqlnd_plugin_get_plugin_net_data(const MYSQLND_NET * net, unsigned int plugin_id TSRMLS_DC) +MYSQLND_CLASS_METHODS_START(mysqlnd_net) + MYSQLND_METHOD(mysqlnd_net, init), + MYSQLND_METHOD(mysqlnd_net, dtor), + MYSQLND_METHOD(mysqlnd_net, connect_ex), + MYSQLND_METHOD(mysqlnd_net, close_stream), + MYSQLND_METHOD(mysqlnd_net, open_pipe), + MYSQLND_METHOD(mysqlnd_net, open_tcp_or_unix), + NULL, /* unused 1 */ + NULL, /* unused 2 */ + MYSQLND_METHOD(mysqlnd_net, post_connect_set_opt), + MYSQLND_METHOD(mysqlnd_net, set_client_option), + MYSQLND_METHOD(mysqlnd_net, decode), + MYSQLND_METHOD(mysqlnd_net, encode), + MYSQLND_METHOD(mysqlnd_net, consume_uneaten_data), + MYSQLND_METHOD(mysqlnd_net, free_contents), + MYSQLND_METHOD(mysqlnd_net, enable_ssl), + MYSQLND_METHOD(mysqlnd_net, disable_ssl), + MYSQLND_METHOD(mysqlnd_net, network_read_ex), + MYSQLND_METHOD(mysqlnd_net, network_write_ex), + MYSQLND_METHOD(mysqlnd_net, send_ex), + MYSQLND_METHOD(mysqlnd_net, receive_ex), +#ifdef MYSQLND_COMPRESSION_ENABLED + MYSQLND_METHOD(mysqlnd_net, read_compressed_packet_from_stream_and_fill_read_buffer) +#else + NULL +#endif +MYSQLND_CLASS_METHODS_END; + + +/* {{{ mysqlnd_net_init */ +PHPAPI MYSQLND_NET * +mysqlnd_net_init(zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info TSRMLS_DC) { - DBG_ENTER("_mysqlnd_plugin_get_plugin_net_data"); - DBG_INF_FMT("plugin_id=%u", plugin_id); - if (!net || plugin_id >= mysqlnd_plugin_count()) { - return NULL; - } - DBG_RETURN((void *)((char *)net + sizeof(MYSQLND_NET) + plugin_id * sizeof(void *))); + MYSQLND_NET * net; + DBG_ENTER("mysqlnd_net_init"); + net = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_io_channel(persistent, stats, error_info TSRMLS_CC); + DBG_RETURN(net); } /* }}} */ -/* {{{ mysqlnd_net_get_methods */ -PHPAPI struct st_mysqlnd_net_methods * -mysqlnd_net_get_methods() +/* {{{ mysqlnd_net_free */ +PHPAPI void +mysqlnd_net_free(MYSQLND_NET * const net, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info TSRMLS_DC) { - return &mysqlnd_mysqlnd_net_methods; + DBG_ENTER("mysqlnd_net_free"); + if (net) { + net->m.dtor(net, stats, error_info TSRMLS_CC); + } + DBG_VOID_RETURN; } /* }}} */ + /*