Merge branch '2.2' into develop

This commit is contained in:
Markus Mäkelä 2018-09-12 22:14:56 +03:00
commit 7ec2f77708
No known key found for this signature in database
GPG Key ID: 72D48FCE664F7B19
21 changed files with 254 additions and 105 deletions

View File

@ -39,6 +39,7 @@ For more details, please refer to:
the master. There is also limited capability for rejoining nodes.
For more details, please refer to:
* [MariaDB MaxScale 2.2.14 Release Notes](Release-Notes/MaxScale-2.2.14-Release-Notes.md)
* [MariaDB MaxScale 2.2.13 Release Notes](Release-Notes/MaxScale-2.2.13-Release-Notes.md)
* [MariaDB MaxScale 2.2.12 Release Notes](Release-Notes/MaxScale-2.2.12-Release-Notes.md)
* [MariaDB MaxScale 2.2.11 Release Notes](Release-Notes/MaxScale-2.2.11-Release-Notes.md)

View File

@ -2,8 +2,8 @@
Release 2.2.14 is a GA release.
This document describes the changes in release 2.2.14, when compared to the
previous release in the same series.
This document describes the changes in release 2.2.14, when compared to
release 2.2.13.
For any problems you encounter, please consider submitting a bug
report on [our Jira](https://jira.mariadb.org/projects/MXS).
@ -14,7 +14,10 @@ report on [our Jira](https://jira.mariadb.org/projects/MXS).
## Bug fixes
* [MXS-2041](https://jira.mariadb.org/browse/MXS-2041) Crash on failure to create schemarouter session
* [MXS-2040](https://jira.mariadb.org/browse/MXS-2040) Default monitor timeouts are too short
* [MXS-2037](https://jira.mariadb.org/browse/MXS-2037) % wildcards not working with source in Named Server Filter
* [MXS-2036](https://jira.mariadb.org/browse/MXS-2036) A slave with sql thread stopped causes wrong master after failover
* [MXS-2034](https://jira.mariadb.org/browse/MXS-2034) query_retry_timeout was not set
* [MXS-2027](https://jira.mariadb.org/browse/MXS-2027) LOAD DATA LOCAL INFILE is not ignored by protocol modules
* [MXS-2024](https://jira.mariadb.org/browse/MXS-2024) Crash in reauthenticate_client

View File

@ -1,11 +1,11 @@
#!/bin/bash
major=`cmake -P ../../VERSION.cmake -L|grep 'MAXSCALE_VERSION_MAJOR'|sed 's/.*=//'`
minor=`cmake -P ../../VERSION.cmake -L|grep 'MAXSCALE_VERSION_MINOR'|sed 's/.*=//'`
patch=`cmake -P ../../VERSION.cmake -L|grep 'MAXSCALE_VERSION_PATCH'|sed 's/.*=//'`
maturity=`cmake -P ../../VERSION.cmake -L|grep 'MAXSCALE_MATURITY'|sed 's/.*=//'`
major="`cd ../../ && cmake -P ./VERSION.cmake -L|grep 'MAXSCALE_VERSION_MAJOR'|sed 's/.*=//'`"
minor="`cd ../../ && cmake -P ./VERSION.cmake -L|grep 'MAXSCALE_VERSION_MINOR'|sed 's/.*=//'`"
patch="`cd ../../ && cmake -P ./VERSION.cmake -L|grep 'MAXSCALE_VERSION_PATCH'|sed 's/.*=//'`"
maturity="`cd ../../ && cmake -P ./VERSION.cmake -L|grep 'MAXSCALE_MATURITY'|sed 's/.*=//'`"
VERSION=${major}.${minor}.${patch}
VERSION="${major}.${minor}.${patch}"
cat <<EOF > MaxScale-$VERSION-Release-Notes.md
# MariaDB MaxScale ${VERSION} Release Notes

View File

@ -4,7 +4,7 @@ if (BUILD_MAXCTRL)
if (NPM_FOUND AND NODEJS_FOUND AND NODEJS_VERSION VERSION_GREATER "6.0.0")
include(configure_version.cmake)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/lib/version.js.in ${CMAKE_CURRENT_BINARY_DIR}/lib/version.js @ONLY)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/maxctrl/maxctrl
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build.sh ${CMAKE_SOURCE_DIR}

View File

@ -1,2 +0,0 @@
include(../VERSION.cmake)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/lib/version.js.in ${CMAKE_CURRENT_BINARY_DIR}/lib/version.js @ONLY)

View File

@ -6,7 +6,7 @@
"main": "maxctrl.js",
"scripts": {
"test": "nyc mocha --exit --timeout 15000 --slow 10000",
"preinstall": "cmake -P configure_version.cmake"
"preinstall": "test -f lib/version.js || cp lib/version.js.in lib/version.js"
},
"keywords": [
"maxscale"

View File

@ -1058,5 +1058,9 @@ add_test_executable(mxs1985_kill_hang.cpp mxs1985_kill_hang replication LABELS R
# https://jira.mariadb.org/browse/MXS-1113
add_test_executable(mxs1113_schemarouter_ps.cpp mxs1113_schemarouter_ps mxs1113_schemarouter_ps LABELS schemarouter BREAKS_REPL)
# MXS-2037: Wildcards not working with source in Named Server Filter
# https://jira.mariadb.org/browse/MXS-2037
add_test_executable(mxs2037_namedserver_wildcards.cpp mxs2037_namedserver_wildcards mxs2037_namedserver_wildcards LABELS namedserverfilter LIGHT REPL_BACKEND)
configure_file(templates.h.in ${CMAKE_CURRENT_BINARY_DIR}/templates.h @ONLY)

View File

@ -0,0 +1,95 @@
[maxscale]
threads=###threads###
log_warning=1
[namedserverfilter]
type=filter
module=namedserverfilter
match01=SELECT
target01=server1
source=127.%.%.%
[MySQL Monitor]
type=monitor
module=mysqlmon
###repl51###
servers=server1,server2,server3,server4
user=maxskysql
passwd=skysql
monitor_interval=1000
[RW Split Router]
type=service
router=readwritesplit
servers=server1,server2,server3,server4
user=maxskysql
passwd=skysql
filters=namedserverfilter
[CLI]
type=service
router=cli
[CLI Listener]
type=listener
service=CLI
protocol=maxscaled
socket=default
[Read Connection Router Slave]
type=service
router=readconnroute
router_options=slave
servers=server1,server2,server3,server4
user=maxskysql
passwd=skysql
[Read Connection Router Master]
type=service
router=readconnroute
router_options=master
servers=server1,server2,server3,server4
user=maxskysql
passwd=skysql
[RW Split Listener]
type=listener
service=RW Split Router
protocol=MySQLClient
port=4006
[Read Connection Listener Slave]
type=listener
service=Read Connection Router Slave
protocol=MySQLClient
port=4009
[Read Connection Listener Master]
type=listener
service=Read Connection Router Master
protocol=MySQLClient
port=4008
[server1]
type=server
address=###node_server_IP_1###
port=###node_server_port_1###
protocol=MySQLBackend
[server2]
type=server
address=###node_server_IP_2###
port=###node_server_port_2###
protocol=MySQLBackend
[server3]
type=server
address=###node_server_IP_3###
port=###node_server_port_3###
protocol=MySQLBackend
[server4]
type=server
address=###node_server_IP_4###
port=###node_server_port_4###
protocol=MySQLBackend

View File

@ -22,6 +22,7 @@ int main(int argc, char* argv[])
char* version;
TestConnections::multiple_maxscales(true);
TestConnections* Test = new TestConnections(argc, argv);
Test->set_timeout(10);

View File

@ -39,6 +39,7 @@ int main(int argc, char* argv[])
bool passive;
char str[1024];
TestConnections::multiple_maxscales(true);
TestConnections* Test = new TestConnections(argc, argv);
// Test->set_timeout(10);

View File

@ -0,0 +1,23 @@
/**
* MXS-2037: Wildcards not working with source in NamedServerFilter
*
* https://jira.mariadb.org/browse/MXS-2037
*
* This test only tests that ip addresses with wildcards are accepted by
* NamedServerFilter. The actual matching functionality is not tested
* because the client IPs can change with the different test environments
* and that would make it complicated to check if the matching is correct.
*/
#include "testconnections.h"
int main(int argc, char** argv)
{
TestConnections test(argc, argv);
test.set_timeout(10);
test.maxscales->connect_maxscale(0);
test.add_result(execute_query(test.maxscales->conn_rwsplit[0], "select 1"), "Can't connect to backend");
test.maxscales->close_maxscale_connections(0);
return test.global_result;
}

View File

@ -21,13 +21,13 @@ int main(int argc, char** argv)
test.repl->connect();
delete_slave_binlogs(test);
test.maxscales->wait_for_monitor();
test.maxscales->wait_for_monitor(2);
basic_test(test);
print_gtids(test);
// Part 1
int node0_id = prepare_test_1(test);
test.maxscales->wait_for_monitor();
test.maxscales->wait_for_monitor(2);
check_test_1(test, node0_id);
if (test.global_result != 0)
@ -37,7 +37,7 @@ int main(int argc, char** argv)
// Part 2
prepare_test_2(test);
test.maxscales->wait_for_monitor();
test.maxscales->wait_for_monitor(2);
check_test_2(test);
if (test.global_result != 0)
@ -47,7 +47,7 @@ int main(int argc, char** argv)
// Part 3
prepare_test_3(test);
test.maxscales->wait_for_monitor();
test.maxscales->wait_for_monitor(2);
check_test_3(test);
return test.global_result;

View File

@ -25,6 +25,7 @@ static bool manual_debug = false;
static std::string required_repl_version;
static std::string required_galera_version;
static bool restart_galera = false;
static bool multiple_maxscales = false;
}
static void signal_set(int sig, void (* handler)(int))
@ -57,6 +58,11 @@ void TestConnections::skip_maxscale_start(bool value)
maxscale::start = !value;
}
void TestConnections::multiple_maxscales(bool value)
{
maxscale::multiple_maxscales = value;
}
void TestConnections::require_repl_version(const char* version)
{
maxscale::required_repl_version = version;
@ -666,9 +672,15 @@ void TestConnections::process_template(int m, const char* template_name, const c
void TestConnections::init_maxscales()
{
for (int i = 0; i < maxscales->N; i++)
// Always initialize the first MaxScale
init_maxscale(0);
if (maxscale::multiple_maxscales)
{
init_maxscale(i);
for (int i = 1; i < maxscales->N; i++)
{
init_maxscale(i);
}
}
}

View File

@ -250,6 +250,9 @@ public:
/** Skip initial start of MaxScale */
static void skip_maxscale_start(bool value);
/** Prepare multiple maxscale instances */
static void multiple_maxscales(bool value);
/** Test requires a certain backend version */
static void require_repl_version(const char* version);
static void require_galera_version(const char* version);

View File

@ -344,6 +344,7 @@ MXS_DOWNSTREAM* filter_apply(const SFilterDef& filter, MXS_SESSION* session, MXS
if ((me = (MXS_DOWNSTREAM*)MXS_CALLOC(1, sizeof(MXS_DOWNSTREAM))) == NULL)
{
MXS_OOM();
return NULL;
}
me->instance = filter->filter;
@ -351,6 +352,7 @@ MXS_DOWNSTREAM* filter_apply(const SFilterDef& filter, MXS_SESSION* session, MXS
if ((me->session = filter->obj->newSession(me->instance, session)) == NULL)
{
MXS_ERROR("Failed to create filter session for '%s'", filter->name.c_str());
MXS_FREE(me);
return NULL;
}

View File

@ -23,9 +23,9 @@ MXS_BEGIN_DECLS
#define MON_ARG_MAX 8192
#define DEFAULT_CONNECT_TIMEOUT 3
#define DEFAULT_READ_TIMEOUT 3
#define DEFAULT_WRITE_TIMEOUT 3
#define DEFAULT_CONNECT_TIMEOUT 3
#define DEFAULT_READ_TIMEOUT 3
#define DEFAULT_WRITE_TIMEOUT 3
#define DEFAULT_CONNECTION_ATTEMPTS 1
#define DEFAULT_MONITOR_INTERVAL 2000 // in milliseconds

View File

@ -44,7 +44,8 @@
#define MYSQL57_PASSWORD "authentication_string"
// Query used with 10.0 or older
#define NEW_LOAD_DBUSERS_QUERY "SELECT u.user, u.host, d.db, u.select_priv, u.%s \
#define NEW_LOAD_DBUSERS_QUERY \
"SELECT u.user, u.host, d.db, u.select_priv, u.%s \
FROM mysql.user AS u LEFT JOIN mysql.db AS d \
ON (u.user = d.user AND u.host = d.host) WHERE u.plugin IN ('', 'mysql_native_password') %s \
UNION \
@ -53,87 +54,87 @@
ON (u.user = t.user AND u.host = t.host) WHERE u.plugin IN ('', 'mysql_native_password') %s"
// Used with 10.2 or newer, supports composite roles
const char* mariadb_102_users_query =
// `t` is users that are not roles
"WITH RECURSIVE t AS ( "
" SELECT u.user, u.host, d.db, u.select_priv, u.password AS password, u.is_role, u.default_role"
" FROM mysql.user AS u LEFT JOIN mysql.db AS d "
" ON (u.user = d.user AND u.host = d.host) "
" UNION "
" SELECT u.user, u.host, t.db, u.select_priv, u.password AS password, u.is_role, u.default_role "
" FROM mysql.user AS u LEFT JOIN mysql.tables_priv AS t "
" ON (u.user = t.user AND u.host = t.host)"
"), users AS ("
// Select the root row, the actual user
" SELECT t.user, t.host, t.db, t.select_priv, t.password, t.default_role AS role FROM t"
" WHERE t.is_role <> 'Y'"
" UNION"
// Recursively select all roles for the users
" SELECT u.user, u.host, t.db, t.select_priv, u.password, r.role FROM t"
" JOIN users AS u"
" ON (t.user = u.role)"
" LEFT JOIN mysql.roles_mapping AS r"
" ON (t.user = r.user)"
")"
"SELECT DISTINCT t.user, t.host, t.db, t.select_priv, t.password FROM users AS t %s";
const char* mariadb_102_users_query
= // `t` is users that are not roles
"WITH RECURSIVE t AS ( "
" SELECT u.user, u.host, d.db, u.select_priv, u.password AS password, u.is_role, u.default_role"
" FROM mysql.user AS u LEFT JOIN mysql.db AS d "
" ON (u.user = d.user AND u.host = d.host) "
" UNION "
" SELECT u.user, u.host, t.db, u.select_priv, u.password AS password, u.is_role, u.default_role "
" FROM mysql.user AS u LEFT JOIN mysql.tables_priv AS t "
" ON (u.user = t.user AND u.host = t.host)"
"), users AS ("
// Select the root row, the actual user
" SELECT t.user, t.host, t.db, t.select_priv, t.password, t.default_role AS role FROM t"
" WHERE t.is_role <> 'Y'"
" UNION"
// Recursively select all roles for the users
" SELECT u.user, u.host, t.db, t.select_priv, u.password, r.role FROM t"
" JOIN users AS u"
" ON (t.user = u.role)"
" LEFT JOIN mysql.roles_mapping AS r"
" ON (t.user = r.user)"
")"
"SELECT DISTINCT t.user, t.host, t.db, t.select_priv, t.password FROM users AS t %s";
// Query used with MariaDB 10.1, supports basic roles
const char* mariadb_users_query =
// First, select all users
"SELECT t.user, t.host, t.db, t.select_priv, t.password FROM "
"( "
" SELECT u.user, u.host, d.db, u.select_priv, u.password AS password, u.is_role "
" FROM mysql.user AS u LEFT JOIN mysql.db AS d "
" ON (u.user = d.user AND u.host = d.host) "
" UNION "
" SELECT u.user, u.host, t.db, u.select_priv, u.password AS password, u.is_role "
" FROM mysql.user AS u LEFT JOIN mysql.tables_priv AS t "
" ON (u.user = t.user AND u.host = t.host) "
") AS t "
// Discard any users that are roles
"WHERE t.is_role <> 'Y' %s "
"UNION "
// Then select all users again
"SELECT r.user, r.host, u.db, u.select_priv, t.password FROM "
"( "
" SELECT u.user, u.host, d.db, u.select_priv, u.password AS password, u.default_role "
" FROM mysql.user AS u LEFT JOIN mysql.db AS d "
" ON (u.user = d.user AND u.host = d.host) "
" UNION "
" SELECT u.user, u.host, t.db, u.select_priv, u.password AS password, u.default_role "
" FROM mysql.user AS u LEFT JOIN mysql.tables_priv AS t "
" ON (u.user = t.user AND u.host = t.host) "
") AS t "
// Join it to the roles_mapping table to only have users with roles
"JOIN mysql.roles_mapping AS r "
"ON (r.user = t.user AND r.host = t.host) "
// Then join it into itself to get the privileges of the role with the name of the user
"JOIN "
"( "
" SELECT u.user, u.host, d.db, u.select_priv, u.password AS password, u.is_role "
" FROM mysql.user AS u LEFT JOIN mysql.db AS d "
" ON (u.user = d.user AND u.host = d.host) "
" UNION "
" SELECT u.user, u.host, t.db, u.select_priv, u.password AS password, u.is_role "
" FROM mysql.user AS u LEFT JOIN mysql.tables_priv AS t "
" ON (u.user = t.user AND u.host = t.host) "
") AS u "
"ON (u.user = r.role AND u.is_role = 'Y') "
// We only care about users that have a default role assigned
"WHERE t.default_role = u.user %s;";
const char* mariadb_users_query
= // First, select all users
"SELECT t.user, t.host, t.db, t.select_priv, t.password FROM "
"( "
" SELECT u.user, u.host, d.db, u.select_priv, u.password AS password, u.is_role "
" FROM mysql.user AS u LEFT JOIN mysql.db AS d "
" ON (u.user = d.user AND u.host = d.host) "
" UNION "
" SELECT u.user, u.host, t.db, u.select_priv, u.password AS password, u.is_role "
" FROM mysql.user AS u LEFT JOIN mysql.tables_priv AS t "
" ON (u.user = t.user AND u.host = t.host) "
") AS t "
// Discard any users that are roles
"WHERE t.is_role <> 'Y' %s "
"UNION "
// Then select all users again
"SELECT r.user, r.host, u.db, u.select_priv, t.password FROM "
"( "
" SELECT u.user, u.host, d.db, u.select_priv, u.password AS password, u.default_role "
" FROM mysql.user AS u LEFT JOIN mysql.db AS d "
" ON (u.user = d.user AND u.host = d.host) "
" UNION "
" SELECT u.user, u.host, t.db, u.select_priv, u.password AS password, u.default_role "
" FROM mysql.user AS u LEFT JOIN mysql.tables_priv AS t "
" ON (u.user = t.user AND u.host = t.host) "
") AS t "
// Join it to the roles_mapping table to only have users with roles
"JOIN mysql.roles_mapping AS r "
"ON (r.user = t.user AND r.host = t.host) "
// Then join it into itself to get the privileges of the role with the name of the user
"JOIN "
"( "
" SELECT u.user, u.host, d.db, u.select_priv, u.password AS password, u.is_role "
" FROM mysql.user AS u LEFT JOIN mysql.db AS d "
" ON (u.user = d.user AND u.host = d.host) "
" UNION "
" SELECT u.user, u.host, t.db, u.select_priv, u.password AS password, u.is_role "
" FROM mysql.user AS u LEFT JOIN mysql.tables_priv AS t "
" ON (u.user = t.user AND u.host = t.host) "
") AS u "
"ON (u.user = r.role AND u.is_role = 'Y') "
// We only care about users that have a default role assigned
"WHERE t.default_role = u.user %s;";
static int get_users(SERV_LISTENER *listener, bool skip_local);
static MYSQL *gw_mysql_init(void);
static int gw_mysql_set_timeouts(MYSQL* handle);
static char *mysql_format_user_entry(void *data);
static bool get_hostname(DCB *dcb, char *client_hostname, size_t size);
static int get_users(SERV_LISTENER* listener, bool skip_local);
static MYSQL* gw_mysql_init(void);
static int gw_mysql_set_timeouts(MYSQL* handle);
static char* mysql_format_user_entry(void* data);
static bool get_hostname(DCB* dcb, char* client_hostname, size_t size);
static char* get_mariadb_102_users_query(bool include_root)
{
const char *root = include_root ? "" : " WHERE t.user <> 'root'";
const char* root = include_root ? "" : " WHERE t.user <> 'root'";
size_t n_bytes = snprintf(NULL, 0, mariadb_102_users_query, root);
char *rval = static_cast<char*>(MXS_MALLOC(n_bytes + 1));
char* rval = static_cast<char*>(MXS_MALLOC(n_bytes + 1));
MXS_ABORT_IF_NULL(rval);
snprintf(rval, n_bytes + 1, mariadb_102_users_query, root);
@ -152,13 +153,13 @@ static char* get_mariadb_users_query(bool include_root)
return rval;
}
static char* get_users_query(const char *server_version, int version, bool include_root, bool is_mariadb)
static char* get_users_query(const char* server_version, int version, bool include_root, bool is_mariadb)
{
if (is_mariadb) // 10.1.1 or newer, supports default roles
{
return version >= 100202 ?
get_mariadb_102_users_query(include_root) :
get_mariadb_users_query(include_root);
return version >= 100202
? get_mariadb_102_users_query(include_root)
: get_mariadb_users_query(include_root);
}
// Either an older MariaDB version or a MySQL variant, use the legacy query
@ -935,7 +936,7 @@ int get_users_from_server(MYSQL* con, SERVER_REF* server_ref, SERVICE* service,
mxs_mysql_set_server_version(con, server_ref->server);
}
char *query = get_users_query(server_ref->server->version_string,
char* query = get_users_query(server_ref->server->version_string,
server_ref->server->version,
service->enable_root,
roles_are_available(con, service, server_ref->server));
@ -946,8 +947,8 @@ int get_users_from_server(MYSQL* con, SERVER_REF* server_ref, SERVICE* service,
if (query)
{
if (mxs_mysql_query(con, "USE mysql") == 0 && // Set default database in case we use CTEs
mxs_mysql_query(con, query) == 0)
if (mxs_mysql_query(con, "USE mysql") == 0 // Set default database in case we use CTEs
&& mxs_mysql_query(con, query) == 0)
{
MYSQL_RES* result = mysql_store_result(con);

View File

@ -52,6 +52,7 @@ TeeSession* TeeSession::create(Tee* my_instance, MXS_SESSION* session)
if ((match && (md_match = pcre2_match_data_create_from_pattern(match, NULL)) == NULL)
|| (exclude && (md_exclude = pcre2_match_data_create_from_pattern(exclude, NULL)) == NULL))
{
MXS_OOM();
return NULL;
}
@ -59,6 +60,9 @@ TeeSession* TeeSession::create(Tee* my_instance, MXS_SESSION* session)
(MySQLProtocol*)session->client_dcb->protocol,
my_instance->get_service())) == NULL)
{
MXS_ERROR("Failed to create local client connection to '%s'%s",
my_instance->get_service()->name,
my_instance->get_service()->ports ? "" : ": Service has no network listeners");
return NULL;
}
}

View File

@ -667,7 +667,7 @@ static inline bool session_ok_to_route(DCB* dcb)
return rval;
}
static inline bool expecting_resultset(MySQLProtocol* proto)
static inline bool expecting_text_result(MySQLProtocol* proto)
{
return proto->current_command == MXS_COM_QUERY
|| proto->current_command == MXS_COM_STMT_EXECUTE
@ -861,7 +861,7 @@ static int gw_read_and_write(DCB* dcb)
if (collecting_resultset(proto, capabilities))
{
if (expecting_resultset(proto))
if (expecting_text_result(proto))
{
if (mxs_mysql_is_result_set(read_buffer))
{

View File

@ -37,7 +37,7 @@ static std::string extract_error(GWBUF* buffer)
{
size_t replylen = MYSQL_GET_PAYLOAD_LEN(GWBUF_DATA(buffer));
char replybuf[replylen];
gwbuf_copy_data(buffer, 0, gwbuf_length(buffer), (uint8_t*)replybuf);
gwbuf_copy_data(buffer, 0, sizeof(replybuf), (uint8_t*)replybuf);
std::string err;
std::string msg;
err.append(replybuf + 8, 5);

View File

@ -641,7 +641,8 @@ void SchemaRouterSession::handleError(GWBUF* pMessage,
break;
case ERRACT_REPLY_CLIENT:
if (m_client->session->state == SESSION_STATE_ROUTER_READY)
// The session pointer can be NULL if the creation fails when filters are being set up
if (m_client->session && m_client->session->state == SESSION_STATE_ROUTER_READY)
{
m_client->func.write(m_client, gwbuf_clone(pMessage));
}