Add support for filters implemented in C++

C++ header files have the .hh-suffix to distinguish them from C
header files, but also to allow a C++ header file (e.g. xyz.hh) for
an existing C header file (e.g. xyz.h).

- cpp.hh        : Basic C++ stuff. Introduces the namespace "maxscale"
                  and its synonym "mxs".
- filter.[hh|cc]: Template class Filter and companion class FilterSession
                  to be used when implementing filters in C++.
- spinlock.h    : Wrapper and lock guard class for SPINLOCK.
This commit is contained in:
Johan Wikman
2016-12-08 22:04:15 +02:00
parent 528531f3b5
commit 855ed415f8
5 changed files with 570 additions and 1 deletions

65
include/maxscale/cpp.hh Normal file
View File

@ -0,0 +1,65 @@
#pragma once
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl.
*
* Change Date: 2019-07-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
/**
* @file cpp.hh
*
* Common stuff for C++ code.
*/
#if !defined(__cplusplus)
#error This file is only to be included by C++ code.
#endif
#include <maxscale/cdefs.h>
#include <exception>
#include <new>
#include <maxscale/log_manager.h>
/**
* All classes of MaxScale are defined in the namespace @c maxscale.
*
* Third party plugins should not place any definitions inside the namespace
* to avoid any name clashes in the future. An exception are template
* specializations.
*/
namespace maxscale
{
}
/**
* Shorthand for the @c maxscale namespace.
*/
namespace mxs = maxscale;
/**
* If a statement is placed inside this macro, then no exceptions will
* escape. Typical use-case is for preventing exceptions to escape across
* a C-API.
*
* @code{.cpp}
*
* void* cAPI()
* {
* void* rv = NULL;
* MXS_EXCEPTION_GUARD(rv = new Something);
* return rv;
* }
* @endcode
*/
#define MXS_EXCEPTION_GUARD(statement)\
do { try { statement; }\
catch (const std::bad_alloc&) { MXS_OOM(); }\
catch (const std::exception& x) { MXS_ERROR("Caught standard exception: %s", x.what()); }\
catch (...) { MXS_ERROR("Caught unknown exception."); } } while (false)

321
include/maxscale/filter.hh Normal file
View File

@ -0,0 +1,321 @@
#pragma once
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl.
*
* Change Date: 2019-07-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
#include <maxscale/cdefs.h>
#include <maxscale/filter.h>
#include <maxscale/cpp.hh>
namespace maxscale
{
/**
* @class FilterSession filter.hh <maxscale/filter.hh>
*
* FilterSession is a base class for filter sessions. A concrete filter session
* class should be derived from this class and override all relevant functions.
*
* Note that even though this class is intended to be derived from, no functions
* are virtual. That is by design, as the class will be used in a context where
* the concrete class is known. That is, there is no need for the virtual mechanism.
*/
class FilterSession
{
public:
/**
* @class Downstream
*
* An instance of this class represents a component following a filter.
*/
class Downstream
{
public:
Downstream()
{
m_data.instance = NULL;
m_data.session = NULL;
m_data.routeQuery = NULL;
}
Downstream(const DOWNSTREAM& down)
: m_data(down)
{}
/**
* Function for sending a packet from the client to the next component
* in the routing chain towards the backend.
*
* @param pPacket A packet to be delivered towards the backend.
*
* @return Whatever the following component returns.
*/
int routeQuery(GWBUF* pPacket)
{
return m_data.routeQuery(m_data.instance, m_data.session, pPacket);
}
DOWNSTREAM m_data;
};
class Upstream
{
public:
/**
* @class Upstream
*
* An instance of this class represents a component preceeding a filter.
*/
Upstream()
{
m_data.instance = NULL;
m_data.session = NULL;
m_data.clientReply = NULL;
}
Upstream(const UPSTREAM& up)
: m_data(up)
{}
/**
* Function for sending a packet from the backend to the next component
* in the routing chain towards the client.
*
* @param pPacket A packet to be delivered towards the backend.
*
* @return Whatever the following component returns.
*/
int clientReply(GWBUF* pPacket)
{
return m_data.clientReply(m_data.instance, m_data.session, pPacket);
}
UPSTREAM m_data;
};
/**
* The FilterSession instance will be deleted when a client session
* has terminated. Will be called only after @c close() has been called.
*/
~FilterSession();
/**
* Called when a client session has been closed.
*/
void close();
/**
* Called for setting the component following this filter session.
*
* @param down The component following this filter.
*/
void setDownstream(const Downstream& down);
/**
* Called for setting the component preceeding this filter session.
*
* @param up The component preceeding this filter.
*/
void setUpstream(const Upstream& up);
/**
* Called when a packet being is routed to the backend. The filter should
* forward the packet to the downstream component.
*
* @param pPacket A client packet.
*/
int routeQuery(GWBUF* pPacket);
/**
* Called when a packet is routed to the client. The filter should
* forward the packet to the upstream component.
*
* @param pPacket A client packet.
*/
int clientReply(GWBUF* pPacket);
/**
* Called for obtaining diagnostics about the filter session.
*
* @param pDcb The dcb where the diagnostics should be written.
*/
void diagnostics(DCB *pDcb);
protected:
FilterSession(SESSION* pSession);
protected:
SESSION* m_pSession; /*< The SESSION this filter session is associated with. */
Downstream m_down; /*< The downstream component. */
Upstream m_up; /*< The upstream component. */
};
/**
* @class Filter filter.hh <maxscale/filter.hh>
*
* An instantiation of the Filter template is used for creating a filter.
* Filter is an example of the "Curiously recurring template pattern"
* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
* that is used for compile time polymorfism.
*
* The typical way for using the template is as follows:
*
* @code
* class MyFilterSession : public maxscale::FilterSession
* {
* // Override the relevant functions.
* };
*
* class MyFilter : public maxscale::Filter<MyFilter, MyFilterSession>
* {
* public:
* static MyFilter* create(const char* zName, char** pzOptions, FILTER_PARAMETER** ppParams);
*
* MyFilterSession* newSession(SESSION* pSession);
*
* static uint64_t getCapabilities();
* };
* @endcode
*
* The concrete filter class must implement the methods @c create, @c newSession and
* @c getCapabilities, with the prototypes as shown above.
*
* The plugin function @c GetModuleObject is then implemented as follows:
*
* @code
* extern "C" FILTER_OBJECT *GetModuleObject()
* {
* return &MyFilter::s_object;
* };
* @endcode
*/
template<class FilterType, class FilterSessionType>
class Filter
{
public:
static FILTER* createInstance(const char* zName, char** pzOptions, FILTER_PARAMETER** ppParams)
{
FilterType* pFilter = NULL;
MXS_EXCEPTION_GUARD(pFilter = FilterType::create(zName, pzOptions, ppParams));
return reinterpret_cast<FILTER*>(pFilter);
}
static void* newSession(FILTER* pInstance, SESSION* pSession)
{
FilterType* pFilter = reinterpret_cast<FilterType*>(pInstance);
void* pFilterSession;
MXS_EXCEPTION_GUARD(pFilterSession = pFilter->newSession(pSession));
return pFilterSession;
}
static void closeSession(FILTER*, void* pData)
{
FilterSessionType* pFilterSession = reinterpret_cast<FilterSessionType*>(pData);
MXS_EXCEPTION_GUARD(pFilterSession->close());
}
static void freeSession(FILTER*, void* pData)
{
FilterSessionType* pFilterSession = reinterpret_cast<FilterSessionType*>(pData);
MXS_EXCEPTION_GUARD(delete pFilterSession);
}
static void setDownstream(FILTER*, void* pData, DOWNSTREAM* pDownstream)
{
FilterSessionType* pFilterSession = reinterpret_cast<FilterSessionType*>(pData);
typename FilterSessionType::Downstream down(*pDownstream);
MXS_EXCEPTION_GUARD(pFilterSession->setDownstream(down));
}
static void setUpstream(FILTER* pInstance, void* pData, UPSTREAM* pUpstream)
{
FilterSessionType* pFilterSession = reinterpret_cast<FilterSessionType*>(pData);
typename FilterSessionType::Upstream up(*pUpstream);
MXS_EXCEPTION_GUARD(pFilterSession->setUpstream(up));
}
static int routeQuery(FILTER* pInstance, void* pData, GWBUF* pPacket)
{
FilterSessionType* pFilterSession = reinterpret_cast<FilterSessionType*>(pData);
int rv = 0;
MXS_EXCEPTION_GUARD(rv = pFilterSession->routeQuery(pPacket));
return rv;
}
static int clientReply(FILTER* pInstance, void* pData, GWBUF* pPacket)
{
FilterSessionType* pFilterSession = reinterpret_cast<FilterSessionType*>(pData);
int rv = 0;
MXS_EXCEPTION_GUARD(rv = pFilterSession->clientReply(pPacket));
return rv;
}
static void diagnostics(FILTER* pInstance, void* pData, DCB* pDcb)
{
FilterSessionType* pFilterSession = reinterpret_cast<FilterSessionType*>(pData);
MXS_EXCEPTION_GUARD(pFilterSession->diagnostics(pDcb));
}
static uint64_t getCapabilities(void)
{
uint64_t rv = 0;
MXS_EXCEPTION_GUARD(rv = FilterType::getCapabilities());
return rv;
}
static void destroyInstance(FILTER* pInstance)
{
FilterType* pFilter = reinterpret_cast<FilterType*>(pInstance);
MXS_EXCEPTION_GUARD(delete pFilter);
}
static FILTER_OBJECT s_object;
};
template<class FilterType, class FilterSessionType>
FILTER_OBJECT Filter<FilterType, FilterSessionType>::s_object =
{
&Filter<FilterType, FilterSessionType>::createInstance,
&Filter<FilterType, FilterSessionType>::newSession,
&Filter<FilterType, FilterSessionType>::closeSession,
&Filter<FilterType, FilterSessionType>::freeSession,
&Filter<FilterType, FilterSessionType>::setDownstream,
&Filter<FilterType, FilterSessionType>::setUpstream,
&Filter<FilterType, FilterSessionType>::routeQuery,
&Filter<FilterType, FilterSessionType>::clientReply,
&Filter<FilterType, FilterSessionType>::diagnostics,
&Filter<FilterType, FilterSessionType>::getCapabilities,
&Filter<FilterType, FilterSessionType>::destroyInstance,
};
}

View File

@ -0,0 +1,122 @@
#pragma once
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl.
*
* Change Date: 2019-07-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
#include <maxscale/cdefs.h>
#include <maxscale/spinlock.h>
#include <maxscale/cpp.hh>
namespace maxscale
{
class SpinLockGuard;
/**
* @class SpinLock spinlock.hh <maxscale/spinlock.hh>
*
* The class SpinLock is a simple class that wraps a regular SPINLOCK
* and provides equivalent functionality.
*/
class SpinLock
{
public:
friend class SpinLockGuard;
/**
* Creates a spinlock.
*/
SpinLock()
{
spinlock_init(&m_lock);
}
~SpinLock()
{
}
/**
* Acquires the spinlock.
*/
void acquire()
{
spinlock_acquire(&m_lock);
}
/**
* Releases the spinlock.
*/
void release()
{
spinlock_release(&m_lock);
}
private:
SpinLock(const SpinLock&) /* = delete */;
SpinLock& operator = (const SpinLock&) /* = delete */;
private:
SPINLOCK m_lock;
};
/**
* @class SpinLockGuard spinlock.hh <maxscale/spinlock.hh>
*
* The class SpinLockGuard is a spinlock wrapper that provides a RAII-style
* mechanism for owning a spinlock for the duration of a scoped block. When
* a SpinLockGuard object is created, it attempts to take ownership of the
* spinlock it is given. When control leaves the scope in which the
* SpinLockGuard object was created, the SpinLockGuard is destructed and the
* mutex is released.
*/
class SpinLockGuard
{
public:
/**
* Creates the guard and locks the provided lock.
*
* @param lock The spinlock to lock.
*/
SpinLockGuard(SPINLOCK& lock)
: m_lock(lock)
{
spinlock_acquire(&m_lock);
}
/**
* Creates the guard and locks the provided lock.
*
* @param lock The spinlock to lock.
*/
SpinLockGuard(SpinLock& lock)
: m_lock(lock.m_lock)
{
spinlock_acquire(&m_lock);
}
/**
* Destroys the guard and unlocks the lock that was provided when
* the guard was created.
*/
~SpinLockGuard()
{
spinlock_release(&m_lock);
}
private:
SpinLockGuard(const SpinLockGuard&) /* = delete */;
SpinLockGuard& operator = (const SpinLockGuard&) /* = delete */;
SPINLOCK& m_lock;
};
}

View File

@ -1,4 +1,4 @@
add_library(maxscale-common SHARED adminusers.c alloc.c authenticator.c atomic.c buffer.c config.c config_runtime.c dcb.c filter.c externcmd.c gwbitmask.c gwdirs.c hashtable.c hint.c housekeeper.c load_utils.c log_manager.cc maxscale_pcre2.c memlog.c misc.c mlist.c modutil.c monitor.c queuemanager.c query_classifier.c poll.c random_jkiss.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c skygw_utils.cc statistics.c listener.c gw_ssl.c mysql_utils.c mysql_binlog.c modulecmd.c ) add_library(maxscale-common SHARED adminusers.c alloc.c authenticator.c atomic.c buffer.c config.c config_runtime.c dcb.c filter.c filter.cc externcmd.c gwbitmask.c gwdirs.c hashtable.c hint.c housekeeper.c load_utils.c log_manager.cc maxscale_pcre2.c memlog.c misc.c mlist.c modutil.c monitor.c queuemanager.c query_classifier.c poll.c random_jkiss.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c skygw_utils.cc statistics.c listener.c gw_ssl.c mysql_utils.c mysql_binlog.c modulecmd.c )
target_link_libraries(maxscale-common ${MARIADB_CONNECTOR_LIBRARIES} ${LZMA_LINK_FLAGS} ${PCRE2_LIBRARIES} ${CURL_LIBRARIES} ssl pthread crypt dl crypto inih z rt m stdc++) target_link_libraries(maxscale-common ${MARIADB_CONNECTOR_LIBRARIES} ${LZMA_LINK_FLAGS} ${PCRE2_LIBRARIES} ${CURL_LIBRARIES} ssl pthread crypt dl crypto inih z rt m stdc++)

61
server/core/filter.cc Normal file
View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl.
*
* Change Date: 2019-07-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
#include <maxscale/filter.hh>
namespace maxscale
{
//
// FilterSession
//
FilterSession::FilterSession(SESSION* pSession)
: m_pSession(pSession)
{
}
FilterSession::~FilterSession()
{
}
void FilterSession::close()
{
}
void FilterSession::setDownstream(const Downstream& down)
{
m_down = down;
}
void FilterSession::setUpstream(const Upstream& up)
{
m_up = up;
}
int FilterSession::routeQuery(GWBUF* pPacket)
{
return m_down.routeQuery(pPacket);
}
int FilterSession::clientReply(GWBUF* pPacket)
{
return m_up.clientReply(pPacket);
}
void FilterSession::diagnostics(DCB* pDcb)
{
}
}