Add session manager
This commit is contained in:
@@ -7,11 +7,13 @@ add_library(session_manager
|
|||||||
session_address.cpp
|
session_address.cpp
|
||||||
session_pool.cpp
|
session_pool.cpp
|
||||||
session_table.cpp
|
session_table.cpp
|
||||||
session_timer.cpp)
|
session_timer.cpp
|
||||||
|
session_manager.cpp)
|
||||||
target_include_directories(session_manager PUBLIC ${CMAKE_SOURCE_DIR}/deps/uthash)
|
target_include_directories(session_manager PUBLIC ${CMAKE_SOURCE_DIR}/deps/uthash)
|
||||||
target_include_directories(session_manager PUBLIC ${CMAKE_SOURCE_DIR}/deps/timeout)
|
target_include_directories(session_manager PUBLIC ${CMAKE_SOURCE_DIR}/deps/timeout)
|
||||||
target_include_directories(session_manager PUBLIC ${CMAKE_SOURCE_DIR}/src/packet)
|
target_include_directories(session_manager PUBLIC ${CMAKE_SOURCE_DIR}/src/packet)
|
||||||
target_include_directories(session_manager PUBLIC ${CMAKE_SOURCE_DIR}/src/session)
|
target_include_directories(session_manager PUBLIC ${CMAKE_SOURCE_DIR}/src/session)
|
||||||
|
target_include_directories(session_manager PUBLIC ${CMAKE_SOURCE_DIR}/src/timestamp)
|
||||||
target_link_libraries(session_manager timeout)
|
target_link_libraries(session_manager timeout)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include "session_timer.h"
|
#include "session_timer.h"
|
||||||
#include "session_private.h"
|
#include "session_private.h"
|
||||||
|
|
||||||
static void session_expire(struct session *sess)
|
static void session_expire(struct session *sess, void *arg)
|
||||||
{
|
{
|
||||||
printf("=== session %lu expired ===\n", session_get_id(sess));
|
printf("=== session %lu expired ===\n", session_get_id(sess));
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ TEST(SESSION_TIMER, ADD_DEL)
|
|||||||
|
|
||||||
session_init(&sess);
|
session_init(&sess);
|
||||||
session_set_id(&sess, 1);
|
session_set_id(&sess, 1);
|
||||||
session_set_expirecb(&sess, session_expire, 1000);
|
session_set_expirecb(&sess, session_expire, NULL, 1000);
|
||||||
|
|
||||||
session_timer_add_session(timer, &sess);
|
session_timer_add_session(timer, &sess);
|
||||||
session_timer_del_session(timer, &sess);
|
session_timer_del_session(timer, &sess);
|
||||||
@@ -39,9 +39,9 @@ TEST(SESSION_TIMER, EXPIRE)
|
|||||||
session_set_id(&sess1, 1);
|
session_set_id(&sess1, 1);
|
||||||
session_set_id(&sess2, 2);
|
session_set_id(&sess2, 2);
|
||||||
session_set_id(&sess3, 3);
|
session_set_id(&sess3, 3);
|
||||||
session_set_expirecb(&sess1, session_expire, 5);
|
session_set_expirecb(&sess1, session_expire, NULL, 5);
|
||||||
session_set_expirecb(&sess2, session_expire, 5);
|
session_set_expirecb(&sess2, session_expire, NULL, 5);
|
||||||
session_set_expirecb(&sess3, session_expire, 10);
|
session_set_expirecb(&sess3, session_expire, NULL, 10);
|
||||||
|
|
||||||
session_timer_add_session(timer, &sess1);
|
session_timer_add_session(timer, &sess1);
|
||||||
session_timer_add_session(timer, &sess2);
|
session_timer_add_session(timer, &sess2);
|
||||||
@@ -78,9 +78,9 @@ TEST(SESSION_TIMER, BEFORE_EXPIRE_DEL)
|
|||||||
session_set_id(&sess1, 1);
|
session_set_id(&sess1, 1);
|
||||||
session_set_id(&sess2, 2);
|
session_set_id(&sess2, 2);
|
||||||
session_set_id(&sess3, 3);
|
session_set_id(&sess3, 3);
|
||||||
session_set_expirecb(&sess1, session_expire, 5);
|
session_set_expirecb(&sess1, session_expire, NULL, 5);
|
||||||
session_set_expirecb(&sess2, session_expire, 5);
|
session_set_expirecb(&sess2, session_expire, NULL, 5);
|
||||||
session_set_expirecb(&sess3, session_expire, 10);
|
session_set_expirecb(&sess3, session_expire, NULL, 10);
|
||||||
|
|
||||||
session_timer_add_session(timer, &sess1);
|
session_timer_add_session(timer, &sess1);
|
||||||
session_timer_add_session(timer, &sess2);
|
session_timer_add_session(timer, &sess2);
|
||||||
@@ -122,9 +122,9 @@ TEST(SESSION_TIMER, BEFORE_EXPIRE_UPDATE)
|
|||||||
session_set_id(&sess1, 1);
|
session_set_id(&sess1, 1);
|
||||||
session_set_id(&sess2, 2);
|
session_set_id(&sess2, 2);
|
||||||
session_set_id(&sess3, 3);
|
session_set_id(&sess3, 3);
|
||||||
session_set_expirecb(&sess1, session_expire, 5);
|
session_set_expirecb(&sess1, session_expire, NULL, 5);
|
||||||
session_set_expirecb(&sess2, session_expire, 5);
|
session_set_expirecb(&sess2, session_expire, NULL, 5);
|
||||||
session_set_expirecb(&sess3, session_expire, 10);
|
session_set_expirecb(&sess3, session_expire, NULL, 10);
|
||||||
|
|
||||||
session_timer_add_session(timer, &sess1);
|
session_timer_add_session(timer, &sess1);
|
||||||
session_timer_add_session(timer, &sess2);
|
session_timer_add_session(timer, &sess2);
|
||||||
@@ -137,7 +137,7 @@ TEST(SESSION_TIMER, BEFORE_EXPIRE_UPDATE)
|
|||||||
{
|
{
|
||||||
printf("update timer 2\n");
|
printf("update timer 2\n");
|
||||||
session_timer_del_session(timer, &sess2);
|
session_timer_del_session(timer, &sess2);
|
||||||
session_set_expirecb(&sess2, session_expire, 8);
|
session_set_expirecb(&sess2, session_expire, NULL, 8);
|
||||||
session_timer_add_session(timer, &sess2);
|
session_timer_add_session(timer, &sess2);
|
||||||
}
|
}
|
||||||
do
|
do
|
||||||
|
|||||||
@@ -300,13 +300,15 @@ void session_free_ex_data(struct session *sess, uint8_t idx)
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
// session expire
|
// session expire
|
||||||
void session_set_expirecb(struct session *sess, session_expire_cb fn, uint64_t abs_expire_ts)
|
void session_set_expirecb(struct session *sess, session_expire_cb expire_cb, void *expire_arg, uint64_t expire_abs_ts)
|
||||||
{
|
{
|
||||||
struct timeout *timeout = &sess->timeout;
|
struct timeout *timeout = &sess->timeout;
|
||||||
|
|
||||||
timeout_init(timeout, TIMEOUT_ABS);
|
timeout_init(timeout, TIMEOUT_ABS);
|
||||||
timeout_setcb(timeout, fn, sess);
|
timeout_setcb(timeout, NULL, sess);
|
||||||
sess->abs_expire_ts = abs_expire_ts;
|
sess->expire_cb = expire_cb;
|
||||||
|
sess->expire_arg = expire_arg;
|
||||||
|
sess->expire_abs_ts = expire_abs_ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
void session_del_expirecb(struct session *sess)
|
void session_del_expirecb(struct session *sess)
|
||||||
@@ -314,14 +316,15 @@ void session_del_expirecb(struct session *sess)
|
|||||||
struct timeout *timeout = &sess->timeout;
|
struct timeout *timeout = &sess->timeout;
|
||||||
|
|
||||||
timeout_init(timeout, 0);
|
timeout_init(timeout, 0);
|
||||||
sess->abs_expire_ts = 0;
|
sess->expire_cb = NULL;
|
||||||
|
sess->expire_arg = NULL;
|
||||||
|
sess->expire_abs_ts = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void session_run_expirecb(struct session *sess)
|
void session_run_expirecb(struct session *sess)
|
||||||
{
|
{
|
||||||
struct timeout *timeout = &sess->timeout;
|
if (sess->expire_cb)
|
||||||
if (timeout->callback.fn)
|
|
||||||
{
|
{
|
||||||
timeout->callback.fn(timeout->callback.arg);
|
sess->expire_cb(sess, sess->expire_arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,10 +132,10 @@ void session_free_ex_data(struct session *sess, uint8_t idx);
|
|||||||
* session expire
|
* session expire
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
typedef void (*session_expire_cb)(struct session *sess);
|
typedef void (*session_expire_cb)(struct session *sess, void *arg);
|
||||||
|
|
||||||
// session timer
|
// session timer
|
||||||
void session_set_expirecb(struct session *sess, session_expire_cb fn, uint64_t abs_timeout_ts);
|
void session_set_expirecb(struct session *sess, session_expire_cb expire_cb, void *expire_arg, uint64_t expire_abs_ts);
|
||||||
void session_del_expirecb(struct session *sess);
|
void session_del_expirecb(struct session *sess);
|
||||||
void session_run_expirecb(struct session *sess);
|
void session_run_expirecb(struct session *sess);
|
||||||
|
|
||||||
|
|||||||
252
src/session/session_manager.cpp
Normal file
252
src/session/session_manager.cpp
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "timestamp.h"
|
||||||
|
#include "session_manager.h"
|
||||||
|
#include "session_pool.h"
|
||||||
|
#include "session_table.h"
|
||||||
|
#include "session_timer.h"
|
||||||
|
#include "session_private.h"
|
||||||
|
|
||||||
|
struct session_manager
|
||||||
|
{
|
||||||
|
struct session_pool *sess_pool;
|
||||||
|
struct session_table *sess_table;
|
||||||
|
struct session_timer *sess_timer;
|
||||||
|
|
||||||
|
session_event_cb event_cb;
|
||||||
|
void *arg;
|
||||||
|
|
||||||
|
struct session *queue_head_ptr;
|
||||||
|
struct session *queue_tail_ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define HANDSHAKE_TIMEOUT_MS (5 * 1000)
|
||||||
|
#define DATA_TIMEOUT_MS (60 * 1000)
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* private API
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
static void session_manager_handle_new_session(struct session_manager *mgr, struct session *sess, const struct packet *pkt, struct session_address *addr)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
static void session_manager_handle_old_session(struct session_manager *mgr, struct session *sess, const struct packet *pkt, struct session_address *addr)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enqueue ready session to queue tail
|
||||||
|
static void session_manager_enqueue_ready_session(struct session_manager *mgr, struct session *sess)
|
||||||
|
{
|
||||||
|
if (mgr == NULL || sess == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mgr->queue_head_ptr == NULL)
|
||||||
|
{
|
||||||
|
mgr->queue_head_ptr = sess;
|
||||||
|
mgr->queue_tail_ptr = sess;
|
||||||
|
sess->next_ready_ptr = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mgr->queue_tail_ptr->next_ready_ptr = sess;
|
||||||
|
mgr->queue_tail_ptr = sess;
|
||||||
|
sess->next_ready_ptr = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dequeue ready session from queue head
|
||||||
|
struct session *session_manager_ready_session_dequeue(struct session_manager *mgr)
|
||||||
|
{
|
||||||
|
if (mgr == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct session *sess = mgr->queue_head_ptr;
|
||||||
|
if (sess == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mgr->queue_head_ptr == mgr->queue_tail_ptr)
|
||||||
|
{
|
||||||
|
mgr->queue_head_ptr = NULL;
|
||||||
|
mgr->queue_tail_ptr = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mgr->queue_head_ptr = sess->next_ready_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
sess->next_ready_ptr = NULL;
|
||||||
|
return sess;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handshake expire set to discard
|
||||||
|
static void handshake_expire_cb(struct session *sess, void *arg)
|
||||||
|
{
|
||||||
|
struct session_manager *mgr = (struct session_manager *)arg;
|
||||||
|
assert(mgr != NULL);
|
||||||
|
|
||||||
|
session_set_state(sess, SESSION_STATE_DISCARD);
|
||||||
|
session_push_event(sess, SESSION_EVENT_DISCARD);
|
||||||
|
session_manager_enqueue_ready_session(mgr, sess);
|
||||||
|
}
|
||||||
|
|
||||||
|
// data expire set to closing
|
||||||
|
static void data_expire_cb(struct session *sess, void *arg)
|
||||||
|
{
|
||||||
|
struct session_manager *mgr = (struct session_manager *)arg;
|
||||||
|
assert(mgr != NULL);
|
||||||
|
|
||||||
|
session_set_state(sess, SESSION_STATE_CLOSING);
|
||||||
|
session_push_event(sess, SESSION_EVENT_CLOSING);
|
||||||
|
session_manager_enqueue_ready_session(mgr, sess);
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* public API
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
struct session_manager *session_manager_create(uint64_t max_session_num)
|
||||||
|
{
|
||||||
|
struct session_manager *mgr = (struct session_manager *)calloc(1, sizeof(struct session_manager));
|
||||||
|
if (mgr == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mgr->sess_pool = session_pool_create(max_session_num);
|
||||||
|
if (mgr->sess_pool == NULL)
|
||||||
|
{
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
mgr->sess_table = session_table_create();
|
||||||
|
if (mgr->sess_table == NULL)
|
||||||
|
{
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
mgr->sess_timer = session_timer_create();
|
||||||
|
if (mgr->sess_timer == NULL)
|
||||||
|
{
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
mgr->queue_head_ptr = NULL;
|
||||||
|
mgr->queue_tail_ptr = NULL;
|
||||||
|
|
||||||
|
return mgr;
|
||||||
|
|
||||||
|
error:
|
||||||
|
session_manager_destroy(mgr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void session_manager_destroy(struct session_manager *mgr)
|
||||||
|
{
|
||||||
|
if (mgr)
|
||||||
|
{
|
||||||
|
session_timer_destroy(mgr->sess_timer);
|
||||||
|
session_table_destroy(mgr->sess_table);
|
||||||
|
session_pool_destroy(mgr->sess_pool);
|
||||||
|
free(mgr);
|
||||||
|
mgr = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void session_manager_set_session_eventcb(struct session_manager *mgr, session_event_cb cb, void *arg)
|
||||||
|
{
|
||||||
|
mgr->event_cb = cb;
|
||||||
|
mgr->arg = arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct session *session_manager_find_session_by_id(struct session_manager *mgr, uint64_t id)
|
||||||
|
{
|
||||||
|
struct session *sess = session_table_find_session_by_id(mgr->sess_table, id);
|
||||||
|
if (sess)
|
||||||
|
{
|
||||||
|
session_set_last_time(sess, timestamp_get_msec());
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
return sess;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct session *session_manager_find_session_by_packet(struct session_manager *mgr, const struct packet *pkt)
|
||||||
|
{
|
||||||
|
struct session_address addr;
|
||||||
|
session_address_init(&addr, pkt);
|
||||||
|
|
||||||
|
struct session *sess = session_table_find_session_by_addr(mgr->sess_table, &addr);
|
||||||
|
if (sess == NULL)
|
||||||
|
{
|
||||||
|
if (session_pool_get_count(mgr->sess_pool) == 1)
|
||||||
|
{
|
||||||
|
struct session *oldest_sess = session_table_find_oldest_session(mgr->sess_table);
|
||||||
|
assert(oldest_sess == NULL);
|
||||||
|
session_set_state(oldest_sess, SESSION_STATE_DISCARD);
|
||||||
|
session_push_event(oldest_sess, SESSION_EVENT_DISCARD);
|
||||||
|
session_manager_enqueue_ready_session(mgr, oldest_sess);
|
||||||
|
}
|
||||||
|
|
||||||
|
sess = session_pool_alloc(mgr->sess_pool);
|
||||||
|
assert(sess != NULL);
|
||||||
|
|
||||||
|
session_manager_handle_new_session(mgr, sess, pkt, &addr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
session_manager_handle_old_session(mgr, sess, pkt, &addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sess;
|
||||||
|
}
|
||||||
|
|
||||||
|
void session_manager_dispatch(struct session_manager *mgr)
|
||||||
|
{
|
||||||
|
uint32_t event;
|
||||||
|
struct session *sess;
|
||||||
|
|
||||||
|
void *cb_arg = mgr->arg;
|
||||||
|
session_event_cb event_cb = mgr->event_cb;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
sess = session_timer_expire_session(mgr->sess_timer, timestamp_get_msec());
|
||||||
|
if (sess == NULL)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
session_run_expirecb(sess);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
sess = session_manager_ready_session_dequeue(mgr);
|
||||||
|
if (sess == NULL)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (session_pop_event(sess, &event) == false)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event_cb)
|
||||||
|
{
|
||||||
|
event_cb(sess, event, cb_arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
36
src/session/session_manager.h
Normal file
36
src/session/session_manager.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#ifndef _SESSION_MANAGER_H
|
||||||
|
#define _SESSION_MANAGER_H
|
||||||
|
|
||||||
|
#ifdef __cpluscplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "session.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* session manager = session pool + session table + session timer
|
||||||
|
*
|
||||||
|
* session pool : alloc and free session
|
||||||
|
* session table : find session by session id or session addr
|
||||||
|
* session timer : session timeout
|
||||||
|
* session manager: manage session pool, session table and session timer
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct session_manager;
|
||||||
|
struct session_manager *session_manager_create(uint64_t max_session_num);
|
||||||
|
void session_manager_destroy(struct session_manager *mgr);
|
||||||
|
|
||||||
|
typedef void (*session_event_cb)(struct session *sess, uint32_t event, void *arg);
|
||||||
|
void session_manager_set_session_eventcb(struct session_manager *mgr, session_event_cb cb, void *arg);
|
||||||
|
|
||||||
|
struct session *session_manager_find_session_by_id(struct session_manager *mgr, uint64_t id);
|
||||||
|
struct session *session_manager_find_session_by_packet(struct session_manager *mgr, const struct packet *pkt);
|
||||||
|
|
||||||
|
void session_manager_dispatch(struct session_manager *mgr);
|
||||||
|
|
||||||
|
#ifdef __cpluscplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -10,18 +10,11 @@ extern "C"
|
|||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "timeout.h"
|
||||||
#include "uthash.h"
|
#include "uthash.h"
|
||||||
#include "session.h"
|
#include "session.h"
|
||||||
#include "session_address.h"
|
#include "session_address.h"
|
||||||
|
|
||||||
#define TIMEOUT_CB_OVERRIDE
|
|
||||||
struct timeout_cb
|
|
||||||
{
|
|
||||||
session_expire_cb fn;
|
|
||||||
struct session *arg;
|
|
||||||
};
|
|
||||||
#include "timeout.h"
|
|
||||||
|
|
||||||
#define EX_DATA_MAX_COUNT 128
|
#define EX_DATA_MAX_COUNT 128
|
||||||
#define SESSION_EVENT_QUEUE_SIZE 256
|
#define SESSION_EVENT_QUEUE_SIZE 256
|
||||||
|
|
||||||
@@ -72,7 +65,9 @@ struct session
|
|||||||
|
|
||||||
// session timer
|
// session timer
|
||||||
struct timeout timeout;
|
struct timeout timeout;
|
||||||
uint64_t abs_expire_ts;
|
session_expire_cb expire_cb;
|
||||||
|
void *expire_arg;
|
||||||
|
uint64_t expire_abs_ts;
|
||||||
|
|
||||||
/******************************
|
/******************************
|
||||||
* Session Pool Zone
|
* Session Pool Zone
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ void session_timer_destroy(struct session_timer *timer)
|
|||||||
void session_timer_add_session(struct session_timer *timer, struct session *sess)
|
void session_timer_add_session(struct session_timer *timer, struct session *sess)
|
||||||
{
|
{
|
||||||
struct timeout *timeout = &sess->timeout;
|
struct timeout *timeout = &sess->timeout;
|
||||||
timeouts_add(timer->timeouts, timeout, sess->abs_expire_ts);
|
timeouts_add(timer->timeouts, timeout, sess->expire_abs_ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void session_timer_del_session(struct session_timer *timer, struct session *sess)
|
void session_timer_del_session(struct session_timer *timer, struct session *sess)
|
||||||
|
|||||||
Reference in New Issue
Block a user