2015-05-08 18:02:28 -07:00
/*
* FreeSWITCH Modular Media Switching Software Library / Soft - Switch Application
2016-04-11 11:54:38 -04:00
* Copyright ( C ) 2005 - 2016 , Anthony Minessale II < anthm @ freeswitch . org >
2015-05-08 18:02:28 -07:00
*
* Version : MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 ( the " License " ) ; you may not use this file except in compliance with
* the License . You may obtain a copy of the License at
* http : //www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an " AS IS " basis ,
* WITHOUT WARRANTY OF ANY KIND , either express or implied . See the License
* for the specific language governing rights and limitations under the
* License .
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft - Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II < anthm @ freeswitch . org >
* Portions created by the Initial Developer are Copyright ( C )
* the Initial Developer . All Rights Reserved .
*
* Contributor ( s ) :
*
* William King < william . king @ quentustech . com >
2016-04-11 11:54:38 -04:00
* Christopher Rienzo < chris . rienzo @ citrix . com >
2015-05-08 18:02:28 -07:00
*
* mod_hiredis . c - - redis client built using the C client library hiredis
*
*/
# include <mod_hiredis.h>
2016-04-11 11:54:38 -04:00
2016-10-18 15:30:25 -04:00
/* auth if password is set */
static switch_status_t hiredis_context_auth ( hiredis_context_t * context )
{
switch_status_t status = SWITCH_STATUS_SUCCESS ;
if ( ! zstr ( context - > connection - > password ) ) {
redisReply * response = redisCommand ( context - > context , " AUTH %s " , context - > connection - > password ) ;
if ( ! response | | response - > type = = REDIS_REPLY_ERROR ) {
status = SWITCH_STATUS_FALSE ;
}
if ( response ) {
freeReplyObject ( response ) ;
}
}
return status ;
}
2016-04-11 11:54:38 -04:00
/* reconnect to redis server */
static switch_status_t hiredis_context_reconnect ( hiredis_context_t * context )
{
redisFree ( context - > context ) ;
context - > context = redisConnectWithTimeout ( context - > connection - > host , context - > connection - > port , context - > connection - > timeout ) ;
2016-10-18 15:30:25 -04:00
if ( context - > context & & ! context - > context - > err & & hiredis_context_auth ( context ) = = SWITCH_STATUS_SUCCESS ) {
2016-04-11 11:54:38 -04:00
return SWITCH_STATUS_SUCCESS ;
}
return SWITCH_STATUS_FALSE ;
}
/* Return a context back to the pool */
2016-04-12 11:18:11 -04:00
static void hiredis_context_release ( hiredis_context_t * context , switch_core_session_t * session )
2016-04-11 11:54:38 -04:00
{
if ( switch_queue_push ( context - > connection - > context_pool , context ) ! = SWITCH_STATUS_SUCCESS ) {
2016-04-12 11:18:11 -04:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_CRIT , " hiredis: failed to release back to pool [%s, %d] \n " , context - > connection - > host , context - > connection - > port ) ;
2016-04-11 11:54:38 -04:00
} else {
2016-04-12 11:18:11 -04:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " hiredis: release back to pool [%s, %d] \n " , context - > connection - > host , context - > connection - > port ) ;
2016-04-11 11:54:38 -04:00
}
}
/* Grab a context from the pool, reconnect/connect as needed */
2016-04-12 11:18:11 -04:00
static hiredis_context_t * hiredis_connection_get_context ( hiredis_connection_t * conn , switch_core_session_t * session )
2016-04-11 11:54:38 -04:00
{
void * val = NULL ;
2016-04-12 11:18:11 -04:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " hiredis: waiting for [%s, %d] \n " , conn - > host , conn - > port ) ;
2016-04-11 11:54:38 -04:00
if ( switch_queue_pop_timeout ( conn - > context_pool , & val , conn - > timeout_us ) = = SWITCH_STATUS_SUCCESS ) {
hiredis_context_t * context = ( hiredis_context_t * ) val ;
if ( ! context - > context ) {
2016-04-12 11:18:11 -04:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_INFO , " hiredis: attempting[%s, %d] \n " , conn - > host , conn - > port ) ;
2016-04-11 11:54:38 -04:00
context - > context = redisConnectWithTimeout ( conn - > host , conn - > port , conn - > timeout ) ;
2016-10-18 15:30:25 -04:00
if ( context - > context & & ! context - > context - > err & & hiredis_context_auth ( context ) = = SWITCH_STATUS_SUCCESS ) {
2016-04-12 11:18:11 -04:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " hiredis: connection success[%s, %d] \n " , conn - > host , conn - > port ) ;
2016-04-11 11:54:38 -04:00
return context ;
} else {
2016-04-12 11:18:11 -04:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_ERROR , " hiredis: connection error[%s, %d] (%s) \n " , conn - > host , conn - > port , context - > context - > errstr ) ;
hiredis_context_release ( context , session ) ;
2016-04-11 11:54:38 -04:00
return NULL ;
}
} else if ( context - > context - > err ) {
2016-04-12 11:18:11 -04:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_INFO , " hiredis: reconnecting[%s, %d] \n " , conn - > host , conn - > port ) ;
2016-04-11 11:54:38 -04:00
if ( hiredis_context_reconnect ( context ) = = SWITCH_STATUS_SUCCESS ) {
2016-04-12 11:18:11 -04:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " hiredis: reconnection success[%s, %d] \n " , conn - > host , conn - > port ) ;
2016-04-11 11:54:38 -04:00
return context ;
} else {
2016-04-12 11:18:11 -04:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_ERROR , " hiredis: reconnection error[%s, %d] (%s) \n " , conn - > host , conn - > port , context - > context - > errstr ) ;
hiredis_context_release ( context , session ) ;
2016-04-11 11:54:38 -04:00
return NULL ;
}
} else {
2016-10-05 21:25:40 -04:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " hiredis: recycled from pool[%s, %d] \n " , conn - > host , conn - > port ) ;
2016-04-11 11:54:38 -04:00
return context ;
}
} else {
2016-04-12 11:18:11 -04:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_ERROR , " hiredis: timed out waiting for [%s, %d] \n " , conn - > host , conn - > port ) ;
2016-04-11 11:54:38 -04:00
}
return NULL ;
}
2016-10-18 15:30:25 -04:00
switch_status_t hiredis_profile_create ( hiredis_profile_t * * new_profile , char * name , uint8_t ignore_connect_fail , uint8_t ignore_error )
2015-05-08 18:02:28 -07:00
{
hiredis_profile_t * profile = NULL ;
switch_memory_pool_t * pool = NULL ;
2016-04-11 11:54:38 -04:00
switch_core_new_memory_pool ( & pool ) ;
2015-05-08 18:02:28 -07:00
profile = switch_core_alloc ( pool , sizeof ( hiredis_profile_t ) ) ;
profile - > pool = pool ;
profile - > name = name ? switch_core_strdup ( profile - > pool , name ) : " default " ;
profile - > conn_head = NULL ;
2016-04-12 09:44:17 -04:00
profile - > ignore_connect_fail = ignore_connect_fail ;
2016-10-18 15:30:25 -04:00
profile - > ignore_error = ignore_error ;
2015-05-08 18:02:28 -07:00
switch_core_hash_insert ( mod_hiredis_globals . profiles , name , ( void * ) profile ) ;
* new_profile = profile ;
2016-04-11 11:54:38 -04:00
2015-05-08 18:02:28 -07:00
return SWITCH_STATUS_SUCCESS ;
}
switch_status_t hiredis_profile_destroy ( hiredis_profile_t * * old_profile )
{
hiredis_profile_t * profile = NULL ;
if ( ! old_profile | | ! * old_profile ) {
return SWITCH_STATUS_SUCCESS ;
} else {
profile = * old_profile ;
* old_profile = NULL ;
}
switch_core_hash_delete ( mod_hiredis_globals . profiles , profile - > name ) ;
switch_core_destroy_memory_pool ( & ( profile - > pool ) ) ;
return SWITCH_STATUS_SUCCESS ;
}
2016-04-11 11:54:38 -04:00
switch_status_t hiredis_profile_connection_add ( hiredis_profile_t * profile , char * host , char * password , uint32_t port , uint32_t timeout_ms , uint32_t max_contexts )
2015-05-08 18:02:28 -07:00
{
hiredis_connection_t * connection = NULL , * new_conn = NULL ;
new_conn = switch_core_alloc ( profile - > pool , sizeof ( hiredis_connection_t ) ) ;
new_conn - > host = host ? switch_core_strdup ( profile - > pool , host ) : " localhost " ;
new_conn - > password = password ? switch_core_strdup ( profile - > pool , password ) : NULL ;
new_conn - > port = port ? port : 6379 ;
2016-04-11 11:54:38 -04:00
new_conn - > pool = profile - > pool ;
2015-05-08 18:02:28 -07:00
2016-04-11 11:54:38 -04:00
/* create fixed size context pool */
max_contexts = max_contexts > 0 ? max_contexts : 3 ;
if ( switch_queue_create ( & new_conn - > context_pool , max_contexts , new_conn - > pool ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " hiredis: failed to allocate context pool \n " ) ;
return SWITCH_STATUS_GENERR ;
} else {
int i = 0 ;
for ( i = 0 ; i < max_contexts ; i + + ) {
hiredis_context_t * new_context = switch_core_alloc ( new_conn - > pool , sizeof ( hiredis_context_t ) ) ;
new_context - > connection = new_conn ;
new_context - > context = NULL ;
switch_queue_push ( new_conn - > context_pool , new_context ) ;
}
}
2015-05-08 18:02:28 -07:00
if ( timeout_ms ) {
2016-04-11 11:54:38 -04:00
new_conn - > timeout_us = timeout_ms * 1000 ;
new_conn - > timeout . tv_sec = timeout_ms / 1000 ;
new_conn - > timeout . tv_usec = ( timeout_ms % 1000 ) * 1000 ;
2015-05-08 18:02:28 -07:00
} else {
2016-04-11 11:54:38 -04:00
new_conn - > timeout_us = 500 * 1000 ;
2015-05-08 18:02:28 -07:00
new_conn - > timeout . tv_sec = 0 ;
new_conn - > timeout . tv_usec = 500 * 1000 ;
}
2016-04-11 11:54:38 -04:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_NOTICE , " hiredis: adding conn[%s,%d], pool size = %d \n " , new_conn - > host , new_conn - > port , max_contexts ) ;
2015-05-08 18:02:28 -07:00
if ( profile - > conn_head ! = NULL ) {
/* Adding 'another' connection */
connection = profile - > conn_head ;
2016-04-11 11:54:38 -04:00
while ( connection - > next ! = NULL ) {
2015-05-08 18:02:28 -07:00
connection = connection - > next ;
}
connection - > next = new_conn ;
} else {
profile - > conn_head = new_conn ;
}
return SWITCH_STATUS_SUCCESS ;
}
2016-04-12 11:18:11 -04:00
static hiredis_context_t * hiredis_profile_get_context ( hiredis_profile_t * profile , hiredis_connection_t * initial_conn , switch_core_session_t * session )
2015-05-08 18:02:28 -07:00
{
2016-04-11 11:54:38 -04:00
hiredis_connection_t * conn = initial_conn ? initial_conn : profile - > conn_head ;
hiredis_context_t * context ;
2015-05-08 18:02:28 -07:00
while ( conn ) {
2016-04-12 11:18:11 -04:00
context = hiredis_connection_get_context ( conn , session ) ;
2016-04-11 11:54:38 -04:00
if ( context ) {
/* successful redis connection */
return context ;
2015-05-08 18:02:28 -07:00
}
2016-04-11 11:54:38 -04:00
conn = conn - > next ;
2015-05-08 18:02:28 -07:00
}
2016-04-12 11:18:11 -04:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_ERROR , " hiredis: unable to connect \n " ) ;
2016-04-11 11:54:38 -04:00
return NULL ;
2015-05-08 18:02:28 -07:00
}
2016-04-12 11:18:11 -04:00
static switch_status_t hiredis_context_execute_sync ( hiredis_context_t * context , const char * data , char * * resp , switch_core_session_t * session )
2015-05-08 18:02:28 -07:00
{
2016-04-12 15:23:49 -04:00
redisReply * response ;
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " hiredis: %s \n " , data ) ;
response = redisCommand ( context - > context , data ) ;
2015-09-09 10:39:00 -07:00
if ( ! response ) {
2016-04-11 11:54:38 -04:00
* resp = NULL ;
2015-09-09 10:39:00 -07:00
return SWITCH_STATUS_GENERR ;
}
2016-04-11 11:54:38 -04:00
2015-05-08 18:02:28 -07:00
switch ( response - > type ) {
case REDIS_REPLY_STATUS : /* fallthrough */
case REDIS_REPLY_STRING :
2016-04-11 11:54:38 -04:00
* resp = strdup ( response - > str ) ;
2015-05-08 18:02:28 -07:00
break ;
case REDIS_REPLY_INTEGER :
2016-04-11 11:54:38 -04:00
* resp = switch_mprintf ( " %lld " , response - > integer ) ;
2015-05-08 18:02:28 -07:00
break ;
2016-10-18 15:30:25 -04:00
case REDIS_REPLY_ERROR :
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_ERROR , " hiredis: error response[%s][%d] \n " , response - > str , response - > type ) ;
2015-05-08 18:02:28 -07:00
freeReplyObject ( response ) ;
2016-04-11 11:54:38 -04:00
* resp = NULL ;
2015-05-08 18:02:28 -07:00
return SWITCH_STATUS_GENERR ;
2016-10-18 15:30:25 -04:00
default :
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_WARNING , " hiredis: unsupported response[%s][%d] \n " , response - > str , response - > type ) ;
freeReplyObject ( response ) ;
* resp = NULL ;
return SWITCH_STATUS_IGNORE ;
2015-05-08 18:02:28 -07:00
}
freeReplyObject ( response ) ;
return SWITCH_STATUS_SUCCESS ;
}
2016-04-12 11:18:11 -04:00
switch_status_t hiredis_profile_execute_sync ( hiredis_profile_t * profile , const char * data , char * * resp , switch_core_session_t * session )
2016-04-11 11:54:38 -04:00
{
hiredis_context_t * context = NULL ;
int reconnected = 0 ;
2016-04-12 11:18:11 -04:00
context = hiredis_profile_get_context ( profile , NULL , session ) ;
2016-04-11 11:54:38 -04:00
while ( context ) {
2016-04-12 11:18:11 -04:00
if ( hiredis_context_execute_sync ( context , data , resp , session ) = = SWITCH_STATUS_SUCCESS ) {
2016-04-11 11:54:38 -04:00
/* got result */
2016-04-12 11:18:11 -04:00
hiredis_context_release ( context , session ) ;
2016-04-11 11:54:38 -04:00
return SWITCH_STATUS_SUCCESS ;
} else if ( context - > context - > err ) {
/* have a bad connection, try a single reconnect attempt before moving on to alternate connection */
if ( reconnected | | hiredis_context_reconnect ( context ) ! = SWITCH_STATUS_SUCCESS ) {
/* try alternate connection */
2016-04-12 11:18:11 -04:00
hiredis_context_t * new_context = hiredis_profile_get_context ( profile , context - > connection , session ) ;
hiredis_context_release ( context , session ) ;
2016-04-11 11:54:38 -04:00
context = new_context ;
if ( context ) {
2016-04-12 11:18:11 -04:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " hiredis: got alternate connection to [%s, %d] \n " , context - > connection - > host , context - > connection - > port ) ;
2016-04-11 11:54:38 -04:00
} else {
2016-04-12 11:18:11 -04:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " hiredis: no more alternate connections to try \n " ) ;
2016-04-11 11:54:38 -04:00
}
reconnected = 0 ;
} else {
2016-04-12 11:18:11 -04:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " hiredis: reconnection success[%s, %d] \n " , context - > connection - > host , context - > connection - > port ) ;
2016-04-11 11:54:38 -04:00
reconnected = 1 ;
}
} else {
/* no problem with context, so don't retry */
2016-04-12 11:18:11 -04:00
hiredis_context_release ( context , session ) ;
2016-04-11 11:54:38 -04:00
return SWITCH_STATUS_GENERR ;
}
}
2016-04-12 09:44:17 -04:00
return SWITCH_STATUS_SOCKERR ;
2016-04-11 11:54:38 -04:00
}
2015-05-08 18:02:28 -07:00
/* For Emacs:
* Local Variables :
* mode : c
* indent - tabs - mode : t
* tab - width : 4
* c - basic - offset : 4
* End :
* For VIM :
* vim : set softtabstop = 4 shiftwidth = 4 tabstop = 4 noet :
*/