Allow serializing SM state
It is useful in some applications to get a snapshot of the stream management state for storage outside the process, in order to recover from crashes and other things. The get_sm_state already present is not suitable for this because it returns a live object tied in to the current context and such, and containing much unneeded internal-api data. Introduce xmpp_sm_state_set_callback which is called every time the state changes with a serialized state, and a dual xmpp_sm_state_restore which takes in this serialized state and sets up a new sm_state based on that. The serialization is considered opaque from the PoV of the API, but is based on CBOR to facilitate easy debugging.
This commit is contained in:
committed by
Steffen Jaeckel
parent
5690c4ed89
commit
f3fa9f557b
159
tests/test_serialize_sm.c
Normal file
159
tests/test_serialize_sm.c
Normal file
@@ -0,0 +1,159 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* test_serialize_sm.c
|
||||
** libstrophe XMPP client library -- test routines for the send queue
|
||||
**
|
||||
** Copyright (C) 2024 Stephen Paul Weber
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "strophe.h"
|
||||
#include "common.h"
|
||||
|
||||
#include "test.h"
|
||||
|
||||
void callback(xmpp_conn_t *conn, void *ctx, const unsigned char *sm_state, size_t sm_state_len)
|
||||
{
|
||||
int *callback_count = ctx;
|
||||
(*callback_count)++;
|
||||
COMPARE_BUF("\x1a\x00\x00\x00\x00", 5, sm_state, 5);
|
||||
COMPARE_BUF("\x1a\x00\x00\x00\x00", 5, sm_state+10, 5);
|
||||
COMPARE_BUF("\x7a\x00\x00\x00\x04SMID", 9, sm_state+15, 9);
|
||||
if (*callback_count == 1) {
|
||||
COMPARE_BUF("\x1a\x00\x00\x00\x00", 5, sm_state+5, 5);
|
||||
COMPARE_BUF("\x9a\x00\x00\x00\x02", 5, sm_state+24, 5);
|
||||
COMPARE_BUF("\x7a\x00\x00\x00\x03""foo", 8, sm_state+29, 8);
|
||||
COMPARE_BUF("\x7a\x00\x00\x00\x1a<r xmlns='urn:xmpp:sm:3'/>", 31, sm_state+37, 31);
|
||||
COMPARE_BUF("\xba\x00\x00\x00\x00", 5, sm_state+68, 5);
|
||||
ENSURE_EQ(sm_state_len, 73);
|
||||
|
||||
xmpp_conn_t *newconn = xmpp_conn_new(conn->ctx);
|
||||
xmpp_sm_state_restore(newconn, sm_state, sm_state_len);
|
||||
ENSURE_EQ(newconn->sm_state->sm_sent_nr, 0);
|
||||
ENSURE_EQ(newconn->sm_state->sm_handled_nr, 0);
|
||||
COMPARE(newconn->sm_state->id, "SMID");
|
||||
ENSURE_EQ(newconn->send_queue_len, 2);
|
||||
ENSURE_EQ(newconn->send_queue_user_len, 2);
|
||||
ENSURE_EQ((size_t)(newconn->sm_state->sm_queue.head), 0);
|
||||
xmpp_conn_release(newconn);
|
||||
}
|
||||
if (*callback_count == 2) {
|
||||
COMPARE_BUF("\x1a\x00\x00\x00\x01", 5, sm_state+5, 5);
|
||||
COMPARE_BUF("\x9a\x00\x00\x00\x01", 5, sm_state+24, 5);
|
||||
COMPARE_BUF("\x7a\x00\x00\x00\x1a<r xmlns='urn:xmpp:sm:3'/>", 31, sm_state+29, 31);
|
||||
COMPARE_BUF("\xba\x00\x00\x00\x01", 5, sm_state+60, 5);
|
||||
COMPARE_BUF("\x1a\x00\x00\x00\x00", 5, sm_state+65, 5);
|
||||
COMPARE_BUF("\x7a\x00\x00\x00\x03""foo", 8, sm_state+70, 8);
|
||||
ENSURE_EQ(sm_state_len, 78);
|
||||
|
||||
xmpp_conn_t *newconn = xmpp_conn_new(conn->ctx);
|
||||
xmpp_sm_state_restore(newconn, sm_state, sm_state_len);
|
||||
ENSURE_EQ(newconn->sm_state->sm_sent_nr, 1);
|
||||
ENSURE_EQ(newconn->sm_state->sm_handled_nr, 0);
|
||||
COMPARE(newconn->sm_state->id, "SMID");
|
||||
ENSURE_EQ(newconn->send_queue_len, 1);
|
||||
ENSURE_EQ(newconn->send_queue_user_len, 1);
|
||||
ENSURE_EQ(newconn->sm_state->sm_queue.head->sm_h, 0);
|
||||
xmpp_conn_release(newconn);
|
||||
}
|
||||
if (*callback_count == 3) {
|
||||
COMPARE_BUF("\x1a\x00\x00\x00\x01", 5, sm_state+5, 5);
|
||||
COMPARE_BUF("\x9a\x00\x00\x00\x00", 5, sm_state+24, 5);
|
||||
COMPARE_BUF("\xba\x00\x00\x00\x01", 5, sm_state+29, 5);
|
||||
COMPARE_BUF("\x1a\x00\x00\x00\x00", 5, sm_state+34, 5);
|
||||
COMPARE_BUF("\x7a\x00\x00\x00\x03""foo", 8, sm_state+39, 8);
|
||||
ENSURE_EQ(sm_state_len, 47);
|
||||
|
||||
xmpp_conn_t *newconn = xmpp_conn_new(conn->ctx);
|
||||
xmpp_sm_state_restore(newconn, sm_state, sm_state_len);
|
||||
ENSURE_EQ(newconn->sm_state->sm_sent_nr, 1);
|
||||
ENSURE_EQ(newconn->sm_state->sm_handled_nr, 0);
|
||||
COMPARE(newconn->sm_state->id, "SMID");
|
||||
ENSURE_EQ(newconn->send_queue_len, 0);
|
||||
ENSURE_EQ(newconn->send_queue_user_len, 0);
|
||||
ENSURE_EQ(newconn->sm_state->sm_queue.head->sm_h, 0);
|
||||
xmpp_conn_release(newconn);
|
||||
}
|
||||
}
|
||||
|
||||
int fake_read(struct conn_interface *intf, void *buff, size_t len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fake_write(struct conn_interface *intf, const void *buff, size_t len)
|
||||
{
|
||||
return len;
|
||||
}
|
||||
|
||||
int fake_flush(struct conn_interface *intf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fake_error_is_recoverable(struct conn_interface *intf, int err)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
xmpp_ctx_t *ctx;
|
||||
xmpp_conn_t *conn;
|
||||
xmpp_log_t *log;
|
||||
xmpp_conn_state_t state;
|
||||
xmpp_sm_state_t *sm_state;
|
||||
char *ret;
|
||||
unsigned int n;
|
||||
int callback_count = 0;
|
||||
|
||||
xmpp_initialize();
|
||||
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
|
||||
ctx = xmpp_ctx_new(NULL, log);
|
||||
conn = xmpp_conn_new(ctx);
|
||||
sm_state = strophe_alloc(ctx, sizeof(*sm_state));
|
||||
memset(sm_state, 0, sizeof(*sm_state));
|
||||
sm_state->ctx = ctx;
|
||||
sm_state->sm_support = sm_state->sm_enabled = sm_state->can_resume = 1;
|
||||
sm_state->id = strophe_strdup(ctx, "SMID");
|
||||
|
||||
xmpp_conn_set_sm_state(conn, sm_state);
|
||||
xmpp_sm_state_set_callback(conn, callback, &callback_count);
|
||||
|
||||
ENSURE_EQ(xmpp_conn_send_queue_len(conn), 0);
|
||||
|
||||
struct conn_interface intf = {
|
||||
fake_read,
|
||||
fake_write,
|
||||
fake_flush,
|
||||
fake_flush,
|
||||
fake_flush,
|
||||
fake_error_is_recoverable,
|
||||
conn
|
||||
};
|
||||
conn->intf = intf;
|
||||
state = conn->state;
|
||||
conn->state = XMPP_STATE_CONNECTED;
|
||||
|
||||
xmpp_send_raw(conn, "foo", 3);
|
||||
ENSURE_EQ(xmpp_conn_send_queue_len(conn), 1);
|
||||
ENSURE_EQ(callback_count, 1);
|
||||
|
||||
xmpp_run_once(ctx, 0);
|
||||
ENSURE_EQ(callback_count, 3);
|
||||
|
||||
conn->state = state;
|
||||
|
||||
xmpp_conn_release(conn);
|
||||
xmpp_ctx_free(ctx);
|
||||
xmpp_shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user