summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-06-28 16:46:49 +0000
committerBenny Prijono <bennylp@teluu.com>2006-06-28 16:46:49 +0000
commit72a1098e21f5b7d797655afe7ddb275969a192bd (patch)
tree2d6444a4660696829f4ac3c1f436e0d9f3d16310
parentdf5395ea5cff6c902f8a1d79bd2874a6b279288c (diff)
Major improvements in PJSIP to support TCP. The changes fall into these categories: (1) the TCP transport implementation itself (*.[hc]), (2) bug-fix in SIP transaction when using reliable transports, (3) support for TCP transport in PJSUA-LIB/PJSUA, and (4) changes in PJSIP-TEST to support TCP testing.
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@563 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjlib/src/pj/config.c2
-rw-r--r--pjsip-apps/src/pjsua/pjsua_app.c74
-rw-r--r--pjsip/build/Makefile31
-rw-r--r--pjsip/build/test_pjsip.dsp8
-rw-r--r--pjsip/include/pjsip.h1
-rw-r--r--pjsip/include/pjsip/sip_transport.h7
-rw-r--r--pjsip/include/pjsip/sip_transport_tcp.h33
-rw-r--r--pjsip/include/pjsua-lib/pjsua_internal.h12
-rw-r--r--pjsip/src/pjsip/sip_transaction.c23
-rw-r--r--pjsip/src/pjsip/sip_transport.c50
-rw-r--r--pjsip/src/pjsip/sip_transport_loop.c3
-rw-r--r--pjsip/src/pjsip/sip_transport_tcp.c674
-rw-r--r--pjsip/src/pjsua-lib/pjsua_acc.c34
-rw-r--r--pjsip/src/pjsua-lib/pjsua_core.c121
-rw-r--r--pjsip/src/test-pjsip/main.c15
-rw-r--r--pjsip/src/test-pjsip/msg_logger.c12
-rw-r--r--pjsip/src/test-pjsip/msg_test.c191
-rw-r--r--pjsip/src/test-pjsip/test.c195
-rw-r--r--pjsip/src/test-pjsip/test.h37
-rw-r--r--pjsip/src/test-pjsip/transport_loop_test.c29
-rw-r--r--pjsip/src/test-pjsip/transport_tcp_test.c143
-rw-r--r--pjsip/src/test-pjsip/transport_test.c13
-rw-r--r--pjsip/src/test-pjsip/transport_udp_test.c16
-rw-r--r--pjsip/src/test-pjsip/tsx_basic_test.c19
-rw-r--r--pjsip/src/test-pjsip/tsx_bench.c273
-rw-r--r--pjsip/src/test-pjsip/tsx_uac_test.c210
-rw-r--r--pjsip/src/test-pjsip/tsx_uas_test.c168
-rw-r--r--pjsip/src/test-pjsip/txdata_test.c323
-rw-r--r--pjsip/src/test-pjsip/uri_test.c185
29 files changed, 2430 insertions, 472 deletions
diff --git a/pjlib/src/pj/config.c b/pjlib/src/pj/config.c
index 2a6aefd3..9da6c4b3 100644
--- a/pjlib/src/pj/config.c
+++ b/pjlib/src/pj/config.c
@@ -21,7 +21,7 @@
#include <pj/ioqueue.h>
static const char *id = "config.c";
-const char *PJ_VERSION = "0.5.6.0";
+const char *PJ_VERSION = "0.5.6.1";
PJ_DEF(void) pj_dump_config(void)
{
diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c
index caf6f8b7..5468d236 100644
--- a/pjsip-apps/src/pjsua/pjsua_app.c
+++ b/pjsip-apps/src/pjsua/pjsua_app.c
@@ -29,6 +29,8 @@ static struct app_config
pjsua_config cfg;
pjsua_logging_config log_cfg;
pjsua_media_config media_cfg;
+ pj_bool_t no_tcp;
+ pj_bool_t no_udp;
pjsua_transport_config udp_cfg;
pjsua_transport_config rtp_cfg;
@@ -95,7 +97,11 @@ static void usage(void)
puts (" --next-account Add more account");
puts ("");
puts ("Transport Options:");
- puts (" --local-port=port Set TCP/UDP port");
+ puts (" --local-port=port Set TCP/UDP port. This implicitly enables both ");
+ puts (" TCP and UDP transports on the specified port, unless");
+ puts (" if TCP or UDP is disabled.");
+ puts (" --no-tcp Disable TCP transport.");
+ puts (" --no-udp Disable UDP transport.");
puts (" --outbound=url Set the URL of global outbound proxy server");
puts (" May be specified multiple times");
puts (" --use-stun1=host[:port]");
@@ -239,7 +245,7 @@ static pj_status_t parse_args(int argc, char *argv[],
OPT_PLAY_FILE, OPT_RTP_PORT, OPT_ADD_CODEC,
OPT_COMPLEXITY, OPT_QUALITY, OPT_PTIME,
OPT_NEXT_ACCOUNT, OPT_NEXT_CRED, OPT_MAX_CALLS,
- OPT_DURATION,
+ OPT_DURATION, OPT_NO_TCP, OPT_NO_UDP,
};
struct pj_getopt_option long_options[] = {
{ "config-file",1, 0, OPT_CONFIG_FILE},
@@ -251,6 +257,8 @@ static pj_status_t parse_args(int argc, char *argv[],
{ "clock-rate", 1, 0, OPT_CLOCK_RATE},
{ "null-audio", 0, 0, OPT_NULL_AUDIO},
{ "local-port", 1, 0, OPT_LOCAL_PORT},
+ { "no-tcp", 0, 0, OPT_NO_TCP},
+ { "no-udp", 0, 0, OPT_NO_UDP},
{ "proxy", 1, 0, OPT_PROXY},
{ "outbound", 1, 0, OPT_OUTBOUND_PROXY},
{ "registrar", 1, 0, OPT_REGISTRAR},
@@ -384,6 +392,24 @@ static pj_status_t parse_args(int argc, char *argv[],
cfg->udp_cfg.port = (pj_uint16_t)lval;
break;
+ case OPT_NO_UDP: /* no-udp */
+ if (cfg->no_tcp) {
+ PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
+ return PJ_EINVAL;
+ }
+
+ cfg->no_udp = PJ_TRUE;
+ break;
+
+ case OPT_NO_TCP: /* no-tcp */
+ if (cfg->no_udp) {
+ PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
+ return PJ_EINVAL;
+ }
+
+ cfg->no_tcp = PJ_TRUE;
+ break;
+
case OPT_PROXY: /* proxy */
if (pjsua_verify_sip_url(pj_optarg) != 0) {
PJ_LOG(1,(THIS_FILE,
@@ -2046,7 +2072,7 @@ on_exit:
pj_status_t app_init(int argc, char *argv[])
{
- pjsua_transport_id transport_id;
+ pjsua_transport_id transport_id = -1;
unsigned i;
pj_status_t status;
@@ -2096,16 +2122,40 @@ pj_status_t app_init(int argc, char *argv[])
app_config.wav_port = pjsua_player_get_conf_port(app_config.wav_id);
}
- /* Add UDP transport */
- status = pjsua_transport_create(PJSIP_TRANSPORT_UDP,
- &app_config.udp_cfg,
- &transport_id);
- if (status != PJ_SUCCESS)
- goto on_error;
- /* Add local account */
- pjsua_acc_add_local(transport_id, PJ_TRUE, &current_acc);
- pjsua_acc_set_online_status(current_acc, PJ_TRUE);
+ /* Add TCP transport unless it's disabled */
+ if (!app_config.no_tcp) {
+ status = pjsua_transport_create(PJSIP_TRANSPORT_TCP,
+ &app_config.udp_cfg,
+ &transport_id);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Add local account */
+ pjsua_acc_add_local(transport_id, PJ_TRUE, &current_acc);
+ pjsua_acc_set_online_status(current_acc, PJ_TRUE);
+
+ }
+
+
+ /* Add UDP transport unless it's disabled. */
+ if (!app_config.no_udp) {
+ status = pjsua_transport_create(PJSIP_TRANSPORT_UDP,
+ &app_config.udp_cfg,
+ &transport_id);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Add local account */
+ pjsua_acc_add_local(transport_id, PJ_TRUE, &current_acc);
+ pjsua_acc_set_online_status(current_acc, PJ_TRUE);
+ }
+
+ if (transport_id == -1) {
+ PJ_LOG(3,(THIS_FILE, "Error: no transport is configured"));
+ status = -1;
+ goto on_error;
+ }
/* Add accounts */
diff --git a/pjsip/build/Makefile b/pjsip/build/Makefile
index 30e61e10..15ed0fb9 100644
--- a/pjsip/build/Makefile
+++ b/pjsip/build/Makefile
@@ -25,7 +25,6 @@ export _LDFLAGS := $(subst /,$(HOST_PSEP),$(PJSUA_LIB_LIB)) \
$(subst /,$(HOST_PSEP),$(PJSIP_UA_LIB)) \
$(subst /,$(HOST_PSEP),$(PJSIP_SIMPLE_LIB)) \
$(subst /,$(HOST_PSEP),$(PJSIP_LIB)) \
- $(subst /,$(HOST_PSEP),$(PJMEDIA_CODEC_LIB)) \
$(subst /,$(HOST_PSEP),$(PJMEDIA_LIB)) \
$(subst /,$(HOST_PSEP),$(PJLIB_UTIL_LIB)) \
$(subst /,$(HOST_PSEP),$(PJLIB_LIB)) \
@@ -40,8 +39,10 @@ export PJSIP_SRCDIR = ../src/pjsip
export PJSIP_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
sip_errno.o sip_msg.o sip_parser.o sip_tel_uri.o sip_uri.o \
sip_endpoint.o sip_util.o sip_util_proxy.o \
- sip_resolve.o sip_transport.o sip_transport_loop.o sip_transport_udp.o \
- sip_auth_client.o sip_auth_msg.o sip_auth_parser.o sip_auth_server.o \
+ sip_resolve.o sip_transport.o sip_transport_loop.o \
+ sip_transport_udp.o sip_transport_tcp.o \
+ sip_auth_client.o sip_auth_msg.o sip_auth_parser.o \
+ sip_auth_server.o \
sip_transaction.o sip_util_statefull.o \
sip_dialog.o sip_ua_layer.o
export PJSIP_CFLAGS += $(_CFLAGS)
@@ -76,11 +77,28 @@ export PJSUA_LIB_CFLAGS += $(_CFLAGS)
export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT
+
+###############################################################################
+# Defines for building test application
+#
+export TEST_SRCDIR = ../src/test-pjsip
+export TEST_OBJS += dlg_core_test.o msg_err_test.o msg_logger.o msg_test.o \
+ test.o transport_loop_test.o transport_tcp_test.o \
+ transport_test.o transport_udp_test.o \
+ tsx_basic_test.o tsx_bench.o tsx_uac_test.o \
+ tsx_uas_test.o txdata_test.o uri_test.o
+export TEST_OBJS += main.o
+export TEST_CFLAGS += $(_CFLAGS)
+export TEST_LDFLAGS += $(_LDFLAGS)
+export TEST_EXE := ../bin/pjsip-test-$(TARGET_NAME)$(HOST_EXE)
+
+
+export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT
###############################################################################
# Main entry
#
#
-TARGETS := pjsip pjsip-ua pjsip-simple pjsua-lib
+TARGETS := pjsip pjsip-ua pjsip-simple pjsua-lib pjsip-test
.PHONY: $(TARGETS)
@@ -106,6 +124,9 @@ pjsip-simple:
pjsua-lib:
$(MAKE) -f $(RULES_MAK) APP=PJSUA_LIB app=pjsua-lib $(PJSUA_LIB_LIB)
+pjsip-test:
+ $(MAKE) -f $(RULES_MAK) APP=TEST app=pjsip-test $(TEST_EXE)
+
.PHONY: ../lib/pjsip.ko
../lib/pjsip.ko:
echo Making $@
@@ -131,6 +152,7 @@ clean depend:
$(MAKE) -f $(RULES_MAK) APP=PJSIP_UA app=pjsip-ua $@
$(MAKE) -f $(RULES_MAK) APP=PJSIP_SIMPLE app=pjsip-simple $@
$(MAKE) -f $(RULES_MAK) APP=PJSUA_LIB app=pjsua-lib $@
+ $(MAKE) -f $(RULES_MAK) APP=TEST app=pjsip-test $@
realclean:
$(subst @@,$(subst /,$(HOST_PSEP),.pjsip-$(TARGET_NAME).depend),$(HOST_RMR))
@@ -140,6 +162,7 @@ realclean:
$(MAKE) -f $(RULES_MAK) APP=PJSIP app=pjsip $@
$(MAKE) -f $(RULES_MAK) APP=PJSIP_UA app=pjsip-ua $@
$(MAKE) -f $(RULES_MAK) APP=PJSIP_SIMPLE app=pjsip-simple $@
+ $(MAKE) -f $(RULES_MAK) APP=TEST app=pjsip-test $@
$(MAKE) -f $(RULES_MAK) APP=PJSUA_LIB app=pjsua-lib $@
diff --git a/pjsip/build/test_pjsip.dsp b/pjsip/build/test_pjsip.dsp
index bdf1a72d..418ee8a8 100644
--- a/pjsip/build/test_pjsip.dsp
+++ b/pjsip/build/test_pjsip.dsp
@@ -117,6 +117,10 @@ SOURCE="..\src\test-pjsip\transport_loop_test.c"
# End Source File
# Begin Source File
+SOURCE="..\src\test-pjsip\transport_tcp_test.c"
+# End Source File
+# Begin Source File
+
SOURCE="..\src\test-pjsip\transport_test.c"
# End Source File
# Begin Source File
@@ -129,6 +133,10 @@ SOURCE="..\src\test-pjsip\tsx_basic_test.c"
# End Source File
# Begin Source File
+SOURCE="..\src\test-pjsip\tsx_bench.c"
+# End Source File
+# Begin Source File
+
SOURCE="..\src\test-pjsip\tsx_uac_test.c"
# End Source File
# Begin Source File
diff --git a/pjsip/include/pjsip.h b/pjsip/include/pjsip.h
index b7f2dd95..8da2d64c 100644
--- a/pjsip/include/pjsip.h
+++ b/pjsip/include/pjsip.h
@@ -39,6 +39,7 @@
#include <pjsip/sip_transport.h>
#include <pjsip/sip_transport_udp.h>
#include <pjsip/sip_transport_loop.h>
+#include <pjsip/sip_transport_tcp.h>
#include <pjsip/sip_resolve.h>
/* Authentication. */
diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h
index e09583db..002061cf 100644
--- a/pjsip/include/pjsip/sip_transport.h
+++ b/pjsip/include/pjsip/sip_transport.h
@@ -704,7 +704,7 @@ struct pjsip_tpfactory
pj_lock_t *lock; /**< Lock object. */
pjsip_transport_type_e type; /**< Transport type. */
- char type_name[8]; /**< Type string name. */
+ char *type_name; /**< Type string name. */
unsigned flag; /**< Transport flag. */
pj_sockaddr local_addr; /**< Bound address. */
@@ -722,6 +722,11 @@ struct pjsip_tpfactory
int addr_len,
pjsip_transport **transport);
+ /**
+ * Destroy the listener.
+ */
+ pj_status_t (*destroy)(pjsip_tpfactory *factory);
+
/*
* Application may extend this structure..
*/
diff --git a/pjsip/include/pjsip/sip_transport_tcp.h b/pjsip/include/pjsip/sip_transport_tcp.h
index 317d7772..4f5664d4 100644
--- a/pjsip/include/pjsip/sip_transport_tcp.h
+++ b/pjsip/include/pjsip/sip_transport_tcp.h
@@ -38,11 +38,35 @@ PJ_BEGIN_DECL
*/
/**
- * Create, register, and start TCP transport.
+ * The TCP incoming connection backlog number.
+ * Default: 5
+ */
+#ifndef PJSIP_TCP_TRANSPORT_BACKLOG
+# define PJSIP_TCP_TRANSPORT_BACKLOG 5
+#endif
+
+
+/**
+ * Register support for SIP TCP transport by creating TCP listener on
+ * the specified address and port. This function will create an
+ * instance of SIP TCP transport factory and register it to the
+ * transport manager.
*
* @param endpt The SIP endpoint.
- * @param local Local address to bind.
- * @param async_cnt Number of simultaneous async operations.
+ * @param local Optional local address to bind, or specify the
+ * address to bind the server socket to. Both IP
+ * interface address and port fields are optional.
+ * If IP interface address is not specified, socket
+ * will be bound to PJ_INADDR_ANY. If port is not
+ * specified, socket will be bound to any port
+ * selected by the operating system.
+ * @param async_cnt Number of simultaneous asynchronous accept()
+ * operations to be supported. It is recommended that
+ * the number here corresponds to the number of
+ * processors in the system (or the number of SIP
+ * worker threads).
+ * @param p_factory Optional pointer to receive the instance of the
+ * SIP TCP transport factory just created.
*
* @return PJ_SUCCESS when the transport has been successfully
* started and registered to transport manager, or
@@ -50,7 +74,8 @@ PJ_BEGIN_DECL
*/
PJ_DECL(pj_status_t) pjsip_tcp_transport_start(pjsip_endpoint *endpt,
const pj_sockaddr_in *local,
- unsigned async_cnt);
+ unsigned async_cnt,
+ pjsip_tpfactory **p_factory);
PJ_END_DECL
diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h
index 8b061c5b..004a41a0 100644
--- a/pjsip/include/pjsua-lib/pjsua_internal.h
+++ b/pjsip/include/pjsua-lib/pjsua_internal.h
@@ -104,8 +104,16 @@ typedef struct pjsua_acc
*/
typedef struct transport_data
{
- int index;
- pjsip_transport *tp;
+ int index;
+ pjsip_transport_type_e type;
+ pjsip_host_port local_name;
+
+ union {
+ pjsip_transport *tp;
+ pjsip_tpfactory *factory;
+ void *ptr;
+ } data;
+
} transport_data;
diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c
index 017f936d..2b29b91d 100644
--- a/pjsip/src/pjsip/sip_transaction.c
+++ b/pjsip/src/pjsip/sip_transaction.c
@@ -1299,6 +1299,7 @@ PJ_DEF(pj_status_t) pjsip_tsx_create_uas( pjsip_module *tsx_user,
pjsip_transport_add_ref(tsx->transport);
pj_memcpy(&tsx->addr, &tsx->res_addr.addr, tsx->res_addr.addr_len);
tsx->addr_len = tsx->res_addr.addr_len;
+ tsx->is_reliable = PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport);
}
@@ -2130,9 +2131,25 @@ static pj_status_t tsx_on_state_proceeding_uas( pjsip_transaction *tsx,
pjsip_tx_data_add_ref( tdata );
}
- /* Start timer H for transaction termination */
- pjsip_endpt_schedule_timer(tsx->endpt,&tsx->timeout_timer,
- &timeout_timer_val);
+ /* For INVITE, start timer H for transaction termination
+ * regardless whether transport is reliable or not.
+ * For non-INVITE, start timer J with the value of 64*T1 for
+ * non-reliable transports, and zero for reliable transports.
+ */
+ if (tsx->method.id == PJSIP_INVITE_METHOD) {
+ /* Start timer H for INVITE */
+ pjsip_endpt_schedule_timer(tsx->endpt,&tsx->timeout_timer,
+ &timeout_timer_val);
+ } else if (!tsx->is_reliable) {
+ /* Start timer J on 64*T1 seconds for non-INVITE */
+ pjsip_endpt_schedule_timer(tsx->endpt,&tsx->timeout_timer,
+ &timeout_timer_val);
+ } else {
+ /* Start timer J on zero seconds for non-INVITE */
+ pj_time_val zero_time = { 0, 0 };
+ pjsip_endpt_schedule_timer(tsx->endpt,&tsx->timeout_timer,
+ &zero_time);
+ }
/* For INVITE, if unreliable transport is used, retransmission
* timer G will be scheduled (retransmission).
diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c
index 9583e049..1e9a4f05 100644
--- a/pjsip/src/pjsip/sip_transport.c
+++ b/pjsip/src/pjsip/sip_transport.c
@@ -36,6 +36,12 @@
#define THIS_FILE "sip_transport.c"
+#if 0
+# define TRACE_(x) PJ_LOG(5,x)
+#else
+# define TRACE_(x)
+#endif
+
/* Prototype. */
static pj_status_t mod_on_tx_msg(pjsip_tx_data *tdata);
@@ -601,6 +607,12 @@ PJ_DEF(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr,
pj_hash_set(tp->pool, mgr->table, &tp->key, key_len, 0, tp);
pj_lock_release(mgr->lock);
+ TRACE_((THIS_FILE,"Transport %s registered: type=%s, remote=%s:%d",
+ tp->obj_name,
+ pjsip_transport_get_type_name(tp->key.type),
+ pj_inet_ntoa(((pj_sockaddr_in*)&tp->key.rem_addr)->sin_addr),
+ pj_ntohs(((pj_sockaddr_in*)&tp->key.rem_addr)->sin_port)));
+
return PJ_SUCCESS;
}
@@ -610,6 +622,8 @@ static pj_status_t destroy_transport( pjsip_tpmgr *mgr,
{
int key_len;
+ TRACE_((THIS_FILE, "Transport %s is being destroyed", tp->obj_name));
+
pj_lock_acquire(tp->lock);
pj_lock_acquire(mgr->lock);
@@ -626,6 +640,7 @@ static pj_status_t destroy_transport( pjsip_tpmgr *mgr,
* Unregister from hash table.
*/
key_len = sizeof(tp->key.type) + tp->addr_len;
+ pj_assert(pj_hash_get(mgr->table, &tp->key, key_len, NULL) != NULL);
pj_hash_set(tp->pool, mgr->table, &tp->key, key_len, 0, NULL);
pj_lock_release(mgr->lock);
@@ -643,6 +658,8 @@ PJ_DEF(pj_status_t) pjsip_transport_shutdown(pjsip_transport *tp)
pjsip_tpmgr *mgr;
pj_status_t status;
+ TRACE_((THIS_FILE, "Transport %s shutting down", tp->obj_name));
+
pj_lock_acquire(tp->lock);
mgr = tp->tpmgr;
@@ -807,12 +824,16 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_destroy( pjsip_tpmgr *mgr )
{
pj_hash_iterator_t itr_val;
pj_hash_iterator_t *itr;
+ pjsip_tpfactory *factory;
pjsip_endpoint *endpt = mgr->endpt;
PJ_LOG(5, (THIS_FILE, "Destroying transport manager"));
pj_lock_acquire(mgr->lock);
+ /*
+ * Destroy all transports.
+ */
itr = pj_hash_first(mgr->table, &itr_val);
while (itr != NULL) {
pj_hash_iterator_t *next;
@@ -827,6 +848,18 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_destroy( pjsip_tpmgr *mgr )
itr = next;
}
+ /*
+ * Destroy all factories/listeners.
+ */
+ factory = mgr->factory_list.next;
+ while (factory != &mgr->factory_list) {
+ pjsip_tpfactory *next = factory->next;
+
+ factory->destroy(factory);
+
+ factory = next;
+ }
+
pj_lock_release(mgr->lock);
pj_lock_destroy(mgr->lock);
@@ -842,7 +875,7 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_destroy( pjsip_tpmgr *mgr )
*/
//pj_assert(pj_atomic_get(mgr->tdata_counter) == 0);
if (pj_atomic_get(mgr->tdata_counter) != 0) {
- PJ_LOG(4,(THIS_FILE, "Warning: %d transmit buffers are not freed!",
+ PJ_LOG(3,(THIS_FILE, "Warning: %d transmit buffer(s) not freed!",
pj_atomic_get(mgr->tdata_counter)));
}
#endif
@@ -879,10 +912,10 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr,
current_pkt[remaining_len] = '\0';
/* Process all message fragments. */
- while (total_processed < remaining_len) {
+ while (remaining_len > 0) {
pjsip_msg *msg;
- pj_size_t msg_fragment_size = 0;
+ pj_size_t msg_fragment_size;
/* Initialize default fragment size. */
msg_fragment_size = remaining_len;
@@ -1038,6 +1071,11 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr,
pjsip_tpfactory *factory;
pj_status_t status;
+ TRACE_((THIS_FILE,"Acquiring transport type=%s, remote=%s:%d",
+ pjsip_transport_get_type_name(type),
+ pj_inet_ntoa(((pj_sockaddr_in*)remote)->sin_addr),
+ pj_ntohs(((pj_sockaddr_in*)remote)->sin_port)));
+
pj_lock_acquire(mgr->lock);
key_len = sizeof(key.type) + addr_len;
@@ -1083,6 +1121,8 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr,
pjsip_transport_add_ref(transport);
pj_lock_release(mgr->lock);
*tp = transport;
+
+ TRACE_((THIS_FILE, "Transport %s acquired", transport->obj_name));
return PJ_SUCCESS;
}
@@ -1100,9 +1140,13 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr,
if (factory == &mgr->factory_list) {
/* No factory can create the transport! */
pj_lock_release(mgr->lock);
+ TRACE_((THIS_FILE, "No suitable factory was found either"));
return PJSIP_EUNSUPTRANSPORT;
}
+ TRACE_((THIS_FILE, "%s, creating new one from factory",
+ (transport?"Transport is shutdown":"No transport found")));
+
/* Request factory to create transport. */
status = factory->create_transport(factory, mgr, mgr->endpt,
remote, addr_len, tp);
diff --git a/pjsip/src/pjsip/sip_transport_loop.c b/pjsip/src/pjsip/sip_transport_loop.c
index d5ac676a..3dc19200 100644
--- a/pjsip/src/pjsip/sip_transport_loop.c
+++ b/pjsip/src/pjsip/sip_transport_loop.c
@@ -73,7 +73,8 @@ struct recv_list *create_incoming_packet( struct loop_transport *loop,
struct recv_list *pkt;
pool = pjsip_endpt_create_pool(loop->base.endpt, "rdata",
- PJSIP_POOL_RDATA_LEN, PJSIP_POOL_RDATA_INC);
+ PJSIP_POOL_RDATA_LEN,
+ PJSIP_POOL_RDATA_INC+5);
if (!pool)
return NULL;
diff --git a/pjsip/src/pjsip/sip_transport_tcp.c b/pjsip/src/pjsip/sip_transport_tcp.c
index c168ccb0..42339c25 100644
--- a/pjsip/src/pjsip/sip_transport_tcp.c
+++ b/pjsip/src/pjsip/sip_transport_tcp.c
@@ -20,6 +20,7 @@
#include <pjsip/sip_endpoint.h>
#include <pjsip/sip_errno.h>
#include <pj/compat/socket.h>
+#include <pj/addr_resolv.h>
#include <pj/assert.h>
#include <pj/ioqueue.h>
#include <pj/lock.h>
@@ -33,15 +34,21 @@
#define MAX_ASYNC_CNT 16
#define POOL_LIS_INIT 4000
-#define POOL_LIS_INC 4000
+#define POOL_LIS_INC 4001
#define POOL_TP_INIT 4000
-#define POOL_TP_INC 4000
+#define POOL_TP_INC 4002
struct tcp_listener;
struct tcp_transport;
+/*
+ * This structure is "descendant" of pj_ioqueue_op_key_t, and it is used to
+ * track pending/asynchronous accept() operation. TCP transport may have
+ * more than one pending accept() operations, depending on the value of
+ * async_cnt.
+ */
struct pending_accept
{
pj_ioqueue_op_key_t op_key;
@@ -52,18 +59,16 @@ struct pending_accept
pj_sockaddr_in remote_addr;
};
-struct pending_connect
-{
- pj_ioqueue_op_key_t op_key;
- struct tcp_transport *transport;
-};
-
+/*
+ * This is the TCP listener, which is a "descendant" of pjsip_tpfactory (the
+ * SIP transport factory).
+ */
struct tcp_listener
{
pjsip_tpfactory factory;
- char name[PJ_MAX_OBJ_NAME];
- pj_bool_t active;
+ char obj_name[PJ_MAX_OBJ_NAME];
+ pj_bool_t is_registered;
pjsip_endpoint *endpt;
pjsip_tpmgr *tpmgr;
pj_sock_t sock;
@@ -73,23 +78,35 @@ struct tcp_listener
};
-struct pending_tdata
+/*
+ * This structure is used to keep delayed transmit operation in a list.
+ * A delayed transmission occurs when application sends tx_data when
+ * the TCP connect/establishment is still in progress. These delayed
+ * transmission will be "flushed" once the socket is connected (either
+ * successfully or with errors).
+ */
+struct delayed_tdata
{
- PJ_DECL_LIST_MEMBER(struct pending_tdata);
+ PJ_DECL_LIST_MEMBER(struct delayed_tdata);
pjsip_tx_data_op_key *tdata_op_key;
};
+/*
+ * This structure describes the TCP transport, and it's descendant of
+ * pjsip_transport.
+ */
struct tcp_transport
{
pjsip_transport base;
+ pj_bool_t is_server;
struct tcp_listener *listener;
pj_bool_t is_registered;
pj_bool_t is_closing;
+ pj_status_t close_reason;
pj_sock_t sock;
pj_ioqueue_key_t *key;
pj_bool_t has_pending_connect;
- struct pending_connect connect_op;
/* TCP transport can only have one rdata!
@@ -99,19 +116,24 @@ struct tcp_transport
pjsip_rx_data rdata;
/* Pending transmission list. */
- struct pending_tdata tx_list;
+ struct delayed_tdata delayed_list;
};
-/*
- * This callback is called when #pj_ioqueue_accept completes.
+/****************************************************************************
+ * PROTOTYPES
*/
+
+/* This callback is called when pending accept() operation completes. */
static void on_accept_complete( pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
pj_sock_t sock,
pj_status_t status);
-static pj_status_t lis_destroy(struct tcp_listener *listener);
+/* This callback is called by transport manager to destroy listener */
+static pj_status_t lis_destroy(pjsip_tpfactory *factory);
+
+/* This callback is called by transport manager to create transport */
static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
pjsip_tpmgr *mgr,
pjsip_endpoint *endpt,
@@ -119,12 +141,12 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
int addr_len,
pjsip_transport **transport);
-
-static pj_status_t create_tcp_transport(struct tcp_listener *listener,
- pj_sock_t sock,
- const pj_sockaddr_in *local,
- const pj_sockaddr_in *remote,
- struct tcp_transport **p_tcp);
+/* Common function to create and initialize transport */
+static pj_status_t tcp_create(struct tcp_listener *listener,
+ pj_sock_t sock, pj_bool_t is_server,
+ const pj_sockaddr_in *local,
+ const pj_sockaddr_in *remote,
+ struct tcp_transport **p_tcp);
static void tcp_perror(const char *sender, const char *title,
@@ -138,18 +160,41 @@ static void tcp_perror(const char *sender, const char *title,
}
+static void sockaddr_to_host_port( pj_pool_t *pool,
+ pjsip_host_port *host_port,
+ const pj_sockaddr_in *addr )
+{
+ host_port->host.ptr = pj_pool_alloc(pool, 48);
+ host_port->host.slen = pj_ansi_sprintf( host_port->host.ptr, "%s",
+ pj_inet_ntoa(addr->sin_addr));
+ host_port->port = pj_ntohs(addr->sin_port);
+}
+
+
+
+/****************************************************************************
+ * The TCP listener/transport factory.
+ */
+
+/*
+ * This is the public API to create, initialize, register, and start the
+ * TCP listener.
+ */
PJ_DEF(pj_status_t) pjsip_tcp_transport_start( pjsip_endpoint *endpt,
const pj_sockaddr_in *local,
- unsigned async_cnt)
+ unsigned async_cnt,
+ pjsip_tpfactory **p_factory)
{
pj_pool_t *pool;
struct tcp_listener *listener;
pj_ioqueue_callback listener_cb;
+ pj_sockaddr_in *listener_addr;
+ int addr_len;
unsigned i;
pj_status_t status;
/* Sanity check */
- PJ_ASSERT_RETURN(endpt && local && async_cnt, PJ_EINVAL);
+ PJ_ASSERT_RETURN(endpt && async_cnt, PJ_EINVAL);
pool = pjsip_endpt_create_pool(endpt, "tcplis", POOL_LIS_INIT,
@@ -158,14 +203,15 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start( pjsip_endpoint *endpt,
listener = pj_pool_zalloc(pool, sizeof(struct tcp_listener));
- pj_ansi_sprintf(listener->name, "tcp:%d", (int)pj_ntohs(local->sin_port));
listener->factory.pool = pool;
listener->factory.type = PJSIP_TRANSPORT_TCP;
- pj_ansi_strcpy(listener->factory.type_name, "tcp");
+ listener->factory.type_name = "tcp";
listener->factory.flag =
pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TCP);
listener->sock = PJ_INVALID_SOCKET;
+ pj_ansi_strcpy(listener->obj_name, "tcp");
+
status = pj_lock_create_recursive_mutex(pool, "tcplis",
&listener->factory.lock);
if (status != PJ_SUCCESS)
@@ -177,11 +223,52 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start( pjsip_endpoint *endpt,
if (status != PJ_SUCCESS)
goto on_error;
- pj_memcpy(&listener->factory.local_addr, local, sizeof(pj_sockaddr_in));
- status = pj_sock_bind(listener->sock, local, sizeof(*local));
+ listener_addr = (pj_sockaddr_in*)&listener->factory.local_addr;
+ if (local) {
+ pj_memcpy(listener_addr, local, sizeof(pj_sockaddr_in));
+ } else {
+ pj_sockaddr_in_init(listener_addr, NULL, 0);
+ }
+
+ status = pj_sock_bind(listener->sock, listener_addr,
+ sizeof(pj_sockaddr_in));
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Retrieve the bound address */
+ addr_len = sizeof(pj_sockaddr_in);
+ status = pj_sock_getsockname(listener->sock, listener_addr, &addr_len);
if (status != PJ_SUCCESS)
goto on_error;
+ /* If the address returns 0.0.0.0, use the first interface address
+ * as the transport's address.
+ */
+ if (listener_addr->sin_addr.s_addr == 0) {
+ const pj_str_t *hostname;
+ struct pj_hostent he;
+
+ hostname = pj_gethostname();
+ status = pj_gethostbyname(hostname, &he);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ listener_addr->sin_addr = *(pj_in_addr*)he.h_addr;
+ }
+
+ pj_ansi_sprintf(listener->obj_name, "tcp:%d",
+ (int)pj_ntohs(listener_addr->sin_port));
+
+ /* Save the address name */
+ sockaddr_to_host_port(listener->factory.pool,
+ &listener->factory.addr_name, listener_addr);
+
+ /* Start listening to the address */
+ status = pj_sock_listen(listener->sock, PJSIP_TCP_TRANSPORT_BACKLOG);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+
/* Register socket to ioqeuue */
pj_memset(&listener_cb, 0, sizeof(listener_cb));
listener_cb.on_accept_complete = &on_accept_complete;
@@ -191,7 +278,21 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start( pjsip_endpoint *endpt,
if (status != PJ_SUCCESS)
goto on_error;
- /* Start pending accept() operation */
+ /* Register to transport manager */
+ listener->endpt = endpt;
+ listener->tpmgr = pjsip_endpt_get_tpmgr(endpt);
+ listener->factory.create_transport = lis_create_transport;
+ listener->factory.destroy = lis_destroy;
+ listener->is_registered = PJ_TRUE;
+ status = pjsip_tpmgr_register_tpfactory(listener->tpmgr,
+ &listener->factory);
+ if (status != PJ_SUCCESS) {
+ listener->is_registered = PJ_FALSE;
+ goto on_error;
+ }
+
+
+ /* Start pending accept() operations */
if (async_cnt > MAX_ASYNC_CNT) async_cnt = MAX_ASYNC_CNT;
listener->async_cnt = async_cnt;
@@ -200,45 +301,34 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start( pjsip_endpoint *endpt,
sizeof(listener->accept_op[i].op_key));
listener->accept_op[i].listener = listener;
- status = pj_ioqueue_accept(listener->key,
- &listener->accept_op[i].op_key,
- &listener->accept_op[i].new_sock,
- &listener->accept_op[i].local_addr,
- &listener->accept_op[i].remote_addr,
- &listener->accept_op[i].addr_len);
- if (status != PJ_SUCCESS && status != PJ_EPENDING)
- goto on_error;
+ on_accept_complete(listener->key, &listener->accept_op[i].op_key,
+ listener->sock, PJ_EPENDING);
}
- /* Register to transport manager */
- listener->endpt = endpt;
- listener->tpmgr = pjsip_endpt_get_tpmgr(endpt);
- listener->factory.create_transport = lis_create_transport;
- status = pjsip_tpmgr_register_tpfactory(listener->tpmgr,
- &listener->factory);
- if (status != PJ_SUCCESS)
- goto on_error;
-
- /* Done! */
- listener->active = PJ_TRUE;
+ PJ_LOG(4,(listener->obj_name,
+ "SIP TCP listener ready for incoming connections at %s:%d",
+ pj_inet_ntoa(listener_addr->sin_addr),
+ (int)pj_ntohs(listener_addr->sin_port)));
- PJ_LOG(4,(listener->name,
- "SIP TCP transport listening for incoming connections at %s:%d",
- pj_inet_ntoa(local->sin_addr), (int)pj_ntohs(local->sin_port)));
+ /* Return the pointer to user */
+ if (p_factory) *p_factory = &listener->factory;
return PJ_SUCCESS;
on_error:
- lis_destroy(listener);
+ lis_destroy(&listener->factory);
return status;
}
-static pj_status_t lis_destroy(struct tcp_listener *listener)
+/* This callback is called by transport manager to destroy listener */
+static pj_status_t lis_destroy(pjsip_tpfactory *factory)
{
- if (listener->active) {
+ struct tcp_listener *listener = (struct tcp_listener *)factory;
+
+ if (listener->is_registered) {
pjsip_tpmgr_unregister_tpfactory(listener->tpmgr, &listener->factory);
- listener->active = PJ_FALSE;
+ listener->is_registered = PJ_FALSE;
}
if (listener->key) {
@@ -258,9 +348,12 @@ static pj_status_t lis_destroy(struct tcp_listener *listener)
}
if (listener->factory.pool) {
- PJ_LOG(4,(listener->name, "SIP TCP transport destroyed"));
- pj_pool_release(listener->factory.pool);
+ pj_pool_t *pool = listener->factory.pool;
+
+ PJ_LOG(4,(listener->obj_name, "SIP TCP listener destroyed"));
+
listener->factory.pool = NULL;
+ pj_pool_release(pool);
}
return PJ_SUCCESS;
@@ -288,8 +381,12 @@ static pj_status_t tcp_send_msg(pjsip_transport *transport,
/* Called by transport manager to shutdown */
static pj_status_t tcp_shutdown(pjsip_transport *transport);
-/* Called by transport manager to destroy */
-static pj_status_t tcp_destroy(pjsip_transport *transport);
+/* Called by transport manager to destroy transport */
+static pj_status_t tcp_destroy_transport(pjsip_transport *transport);
+
+/* Utility to destroy transport */
+static pj_status_t tcp_destroy(pjsip_transport *transport,
+ pj_status_t reason);
/* Callback from ioqueue on incoming packet */
static void on_read_complete(pj_ioqueue_key_t *key,
@@ -306,25 +403,15 @@ static void on_connect_complete(pj_ioqueue_key_t *key,
pj_status_t status);
-static void sockaddr_to_host_port( pj_pool_t *pool,
- pjsip_host_port *host_port,
- const pj_sockaddr_in *addr )
-{
- host_port->host.ptr = pj_pool_alloc(pool, 48);
- host_port->host.slen = pj_ansi_sprintf( host_port->host.ptr, "%s",
- pj_inet_ntoa(addr->sin_addr));
- host_port->port = pj_ntohs(addr->sin_port);
-}
-
-
/*
- * Utilities to create TCP transport.
+ * Common function to create TCP transport, called when pending accept() and
+ * pending connect() complete.
*/
-static pj_status_t create_tcp_transport(struct tcp_listener *listener,
- pj_sock_t sock,
- const pj_sockaddr_in *local,
- const pj_sockaddr_in *remote,
- struct tcp_transport **p_tcp)
+static pj_status_t tcp_create( struct tcp_listener *listener,
+ pj_sock_t sock, pj_bool_t is_server,
+ const pj_sockaddr_in *local,
+ const pj_sockaddr_in *remote,
+ struct tcp_transport **p_tcp)
{
struct tcp_transport *tcp;
pj_pool_t *pool;
@@ -332,28 +419,37 @@ static pj_status_t create_tcp_transport(struct tcp_listener *listener,
pj_ioqueue_callback tcp_callback;
pj_status_t status;
- pool = pjsip_endpt_create_pool(listener->endpt, "tcp",
+
+ PJ_ASSERT_RETURN(sock != PJ_INVALID_SOCKET, PJ_EINVAL);
+
+
+ pool = pjsip_endpt_create_pool(listener->endpt, "tcp",
POOL_TP_INIT, POOL_TP_INC);
+ PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
/*
* Create and initialize basic transport structure.
*/
tcp = pj_pool_zalloc(pool, sizeof(*tcp));
tcp->sock = sock;
+ tcp->is_server = is_server;
tcp->listener = listener;
- pj_list_init(&tcp->tx_list);
-
-
- pj_ansi_snprintf(tcp->base.obj_name, PJ_MAX_OBJ_NAME, "tcp%p", tcp);
+ pj_list_init(&tcp->delayed_list);
tcp->base.pool = pool;
+ pj_ansi_snprintf(tcp->base.obj_name, PJ_MAX_OBJ_NAME,
+ (is_server ? "tcps%p" :"tcpc%p"), tcp);
+
status = pj_atomic_create(pool, 0, &tcp->base.ref_cnt);
- if (status != PJ_SUCCESS)
+ if (status != PJ_SUCCESS) {
goto on_error;
+ }
status = pj_lock_create_recursive_mutex(pool, "tcp", &tcp->base.lock);
- if (status != PJ_SUCCESS)
+ if (status != PJ_SUCCESS) {
goto on_error;
+ }
tcp->base.key.type = PJSIP_TRANSPORT_TCP;
pj_memcpy(&tcp->base.key.rem_addr, remote, sizeof(pj_sockaddr_in));
@@ -374,7 +470,7 @@ static pj_status_t create_tcp_transport(struct tcp_listener *listener,
tcp->base.tpmgr = listener->tpmgr;
tcp->base.send_msg = &tcp_send_msg;
tcp->base.do_shutdown = &tcp_shutdown;
- tcp->base.destroy = &tcp_destroy;
+ tcp->base.destroy = &tcp_destroy_transport;
/* Register socket to ioqueue */
@@ -386,37 +482,44 @@ static pj_status_t create_tcp_transport(struct tcp_listener *listener,
ioqueue = pjsip_endpt_get_ioqueue(listener->endpt);
status = pj_ioqueue_register_sock(pool, ioqueue, sock,
tcp, &tcp_callback, &tcp->key);
- if (status != PJ_SUCCESS)
+ if (status != PJ_SUCCESS) {
goto on_error;
+ }
/* Register transport to transport manager */
status = pjsip_transport_register(listener->tpmgr, &tcp->base);
- if (status != PJ_SUCCESS)
+ if (status != PJ_SUCCESS) {
goto on_error;
+ }
tcp->is_registered = PJ_TRUE;
/* Done setting up basic transport. */
*p_tcp = tcp;
+ PJ_LOG(4,(tcp->base.obj_name, "TCP %s transport created",
+ (tcp->is_server ? "server" : "client")));
+
+ return PJ_SUCCESS;
+
on_error:
- tcp_destroy(&tcp->base);
+ tcp_destroy(&tcp->base, status);
return status;
}
-/* Flush all pending send operations */
-static tcp_flush_pending_tx(struct tcp_transport *tcp)
+/* Flush all delayed transmision once the socket is connected. */
+static void tcp_flush_pending_tx(struct tcp_transport *tcp)
{
pj_lock_acquire(tcp->base.lock);
- while (!pj_list_empty(&tcp->tx_list)) {
- struct pending_tdata *pending_tx;
+ while (!pj_list_empty(&tcp->delayed_list)) {
+ struct delayed_tdata *pending_tx;
pjsip_tx_data *tdata;
pj_ioqueue_op_key_t *op_key;
pj_ssize_t size;
pj_status_t status;
- pending_tx = tcp->tx_list.next;
+ pending_tx = tcp->delayed_list.next;
pj_list_erase(pending_tx);
tdata = pending_tx->tdata_op_key->tdata;
@@ -436,29 +539,53 @@ static tcp_flush_pending_tx(struct tcp_transport *tcp)
}
+/* Called by transport manager to destroy transport */
+static pj_status_t tcp_destroy_transport(pjsip_transport *transport)
+{
+ struct tcp_transport *tcp = (struct tcp_transport*)transport;
+
+ /* Transport would have been unregistered by now since this callback
+ * is called by transport manager.
+ */
+ tcp->is_registered = PJ_FALSE;
+
+ return tcp_destroy(transport, tcp->close_reason);
+}
+
/* Destroy TCP transport */
-static pj_status_t tcp_destroy(pjsip_transport *transport)
+static pj_status_t tcp_destroy(pjsip_transport *transport,
+ pj_status_t reason)
{
struct tcp_transport *tcp = (struct tcp_transport*)transport;
- /* Cancel all pending transmits */
- while (!pj_list_empty(&tcp->tx_list)) {
- struct pending_tdata *pending_tx;
+ if (tcp->close_reason == 0)
+ tcp->close_reason = reason;
+
+ if (tcp->is_registered) {
+ tcp->is_registered = PJ_FALSE;
+ pjsip_transport_destroy(transport);
+
+ /* pjsip_transport_destroy will recursively call this function
+ * again.
+ */
+ return PJ_SUCCESS;
+ }
+
+ /* Mark transport as closing */
+ tcp->is_closing = PJ_TRUE;
+
+ /* Cancel all delayed transmits */
+ while (!pj_list_empty(&tcp->delayed_list)) {
+ struct delayed_tdata *pending_tx;
pj_ioqueue_op_key_t *op_key;
- pending_tx = tcp->tx_list.next;
+ pending_tx = tcp->delayed_list.next;
pj_list_erase(pending_tx);
op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key;
- on_write_complete(tcp->key, op_key,
- -PJ_RETURN_OS_ERROR(OSERR_ENOTCONN));
- }
-
- if (tcp->is_registered) {
- pjsip_transport_destroy(transport);
- tcp->is_registered = PJ_FALSE;
+ on_write_complete(tcp->key, op_key, -reason);
}
if (tcp->rdata.tp_info.pool) {
@@ -469,6 +596,12 @@ static pj_status_t tcp_destroy(pjsip_transport *transport)
if (tcp->key) {
pj_ioqueue_unregister(tcp->key);
tcp->key = NULL;
+ tcp->sock = PJ_INVALID_SOCKET;
+ }
+
+ if (tcp->sock != PJ_INVALID_SOCKET) {
+ pj_sock_close(tcp->sock);
+ tcp->sock = PJ_INVALID_SOCKET;
}
if (tcp->base.lock) {
@@ -482,9 +615,26 @@ static pj_status_t tcp_destroy(pjsip_transport *transport)
}
if (tcp->base.pool) {
- PJ_LOG(4,(tcp->base.obj_name, "TCP transport destroyed"));
- pj_pool_release(tcp->base.pool);
+ pj_pool_t *pool;
+
+ if (reason != PJ_SUCCESS) {
+ char errmsg[PJ_ERR_MSG_SIZE];
+
+ pj_strerror(reason, errmsg, sizeof(errmsg));
+ PJ_LOG(4,(tcp->base.obj_name,
+ "TCP transport destroyed with reason %d: %s",
+ reason, errmsg));
+
+ } else {
+
+ PJ_LOG(4,(tcp->base.obj_name,
+ "TCP transport destroyed normally"));
+
+ }
+
+ pool = tcp->base.pool;
tcp->base.pool = NULL;
+ pj_pool_release(pool);
}
return PJ_SUCCESS;
@@ -493,7 +643,8 @@ static pj_status_t tcp_destroy(pjsip_transport *transport)
/*
* This utility function creates receive data buffers and start
- * asynchronous recv() operations from the socket.
+ * asynchronous recv() operations from the socket. It is called after
+ * accept() or connect() operation complete.
*/
static pj_status_t tcp_start_read(struct tcp_transport *tcp)
{
@@ -531,7 +682,7 @@ static pj_status_t tcp_start_read(struct tcp_transport *tcp)
status = pj_ioqueue_recv(tcp->key, &tcp->rdata.tp_info.op_key.op_key,
tcp->rdata.pkt_info.packet, &size,
PJ_IOQUEUE_ALWAYS_ASYNC);
- if (status != PJ_SUCCESS) {
+ if (status != PJ_SUCCESS && status != PJ_EPENDING) {
tcp_perror(tcp->base.obj_name, "ioqueue recv() error", status);
return status;
}
@@ -593,19 +744,19 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
((pj_sockaddr_in*)&listener->factory.local_addr)->sin_addr.s_addr;
/* Create the transport descriptor */
- status = create_tcp_transport(listener, sock, &local_addr,
- (pj_sockaddr_in*)rem_addr, &tcp);
+ status = tcp_create(listener, sock, PJ_FALSE, &local_addr,
+ (pj_sockaddr_in*)rem_addr, &tcp);
if (status != PJ_SUCCESS)
return status;
-
+
+
/* Start asynchronous connect() operation */
tcp->has_pending_connect = PJ_TRUE;
- pj_ioqueue_op_key_init(&tcp->connect_op.op_key,
- sizeof(tcp->connect_op.op_key));
- tcp->connect_op.transport = tcp;
status = pj_ioqueue_connect(tcp->key, rem_addr, sizeof(pj_sockaddr_in));
- if (status != PJ_SUCCESS) {
- tcp_destroy(&tcp->base);
+ if (status == PJ_SUCCESS) {
+ tcp->has_pending_connect = PJ_FALSE;
+ } else if (status != PJ_EPENDING) {
+ tcp_destroy(&tcp->base, status);
return status;
}
@@ -629,6 +780,17 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
}
}
+ if (tcp->has_pending_connect) {
+ PJ_LOG(4,(tcp->base.obj_name,
+ "TCP transport %.*s:%d is connecting to %.*s:%d...",
+ (int)tcp->base.local_name.host.slen,
+ tcp->base.local_name.host.ptr,
+ tcp->base.local_name.port,
+ (int)tcp->base.remote_name.host.slen,
+ tcp->base.remote_name.host.ptr,
+ tcp->base.remote_name.port));
+ }
+
/* Done */
*p_transport = &tcp->base;
@@ -653,31 +815,75 @@ static void on_accept_complete( pj_ioqueue_key_t *key,
listener = pj_ioqueue_get_user_data(key);
accept_op = (struct pending_accept*) op_key;
+ /*
+ * Loop while there is immediate connection or when there is error.
+ */
do {
- if (status != PJ_SUCCESS) {
- tcp_perror(listener->name, "Error in accept()", status);
-
+ if (status == PJ_EPENDING) {
+ /*
+ * This can only happen when this function is called during
+ * initialization to kick off asynchronous accept().
+ */
+
+ } else if (status != PJ_SUCCESS) {
+
+ /*
+ * Error in accept().
+ */
+ tcp_perror(listener->obj_name, "Error in accept()", status);
+
+ /*
+ * Prevent endless accept() error loop by limiting the
+ * number of consecutive errors. Once the number of errors
+ * is equal to maximum, we treat this as permanent error, and
+ * we stop the accept() operation.
+ */
++err_cnt;
- if (err_cnt >= 5) {
- PJ_LOG(1, (listener->name,
+ if (err_cnt >= 10) {
+ PJ_LOG(1, (listener->obj_name,
"Too many errors, listener stopping"));
}
- goto start_next_accept;
- }
+ } else {
- status = create_tcp_transport( listener, sock,
- &accept_op->local_addr,
- &accept_op->remote_addr, &tcp);
- if (status == PJ_SUCCESS) {
- status = tcp_start_read(tcp);
- if (status != PJ_SUCCESS) {
- PJ_LOG(3,(tcp->base.obj_name, "New transport cancelled"));
- tcp_destroy(&tcp->base);
+ if (sock == PJ_INVALID_SOCKET) {
+ sock = accept_op->new_sock;
+ PJ_LOG(4,(listener->obj_name,
+ "Warning: ioqueue reports -1 in on_accept_complete()"
+ " sock argument"));
+ }
+
+ PJ_LOG(4,(listener->obj_name,
+ "TCP listener %.*s:%d: got incoming TCP connection "
+ "from %s:%d, sock=%d",
+ (int)listener->factory.addr_name.host.slen,
+ listener->factory.addr_name.host.ptr,
+ listener->factory.addr_name.port,
+ pj_inet_ntoa(accept_op->remote_addr.sin_addr),
+ pj_ntohs(accept_op->remote_addr.sin_port),
+ sock));
+
+ /*
+ * Incoming connections!
+ * Create TCP transport for the new socket.
+ */
+ status = tcp_create( listener, sock, PJ_TRUE,
+ &accept_op->local_addr,
+ &accept_op->remote_addr, &tcp);
+ if (status == PJ_SUCCESS) {
+ status = tcp_start_read(tcp);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(3,(tcp->base.obj_name, "New transport cancelled"));
+ tcp_destroy(&tcp->base, status);
+ }
}
}
-start_next_accept:
+ /*
+ * Start the next asynchronous accept() operation.
+ */
+ accept_op->addr_len = sizeof(pj_sockaddr_in);
+ accept_op->new_sock = PJ_INVALID_SOCKET;
status = pj_ioqueue_accept(listener->key,
&accept_op->op_key,
@@ -686,27 +892,51 @@ start_next_accept:
&accept_op->remote_addr,
&accept_op->addr_len);
+ /*
+ * Loop while we have immediate connection or when there is error.
+ */
+
} while (status != PJ_EPENDING);
}
-/* Callback from ioqueue when packet is sent */
+/*
+ * Callback from ioqueue when packet is sent.
+ */
static void on_write_complete(pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
pj_ssize_t bytes_sent)
{
- struct tcp_transport *tp = pj_ioqueue_get_user_data(key);
+ struct tcp_transport *tcp = pj_ioqueue_get_user_data(key);
pjsip_tx_data_op_key *tdata_op_key = (pjsip_tx_data_op_key*)op_key;
tdata_op_key->tdata = NULL;
+ /* Check for error/closure */
+ if (bytes_sent <= 0) {
+ pj_status_t status;
+
+ PJ_LOG(5,(tcp->base.obj_name, "TCP send() error, sent=%d",
+ bytes_sent));
+
+ status = (bytes_sent == 0) ? PJ_RETURN_OS_ERROR(OSERR_ENOTCONN) :
+ -bytes_sent;
+ if (tcp->close_reason==PJ_SUCCESS) tcp->close_reason = status;
+ pjsip_transport_shutdown(&tcp->base);
+ }
+
if (tdata_op_key->callback) {
- tdata_op_key->callback(&tp->base, tdata_op_key->token, bytes_sent);
+ /*
+ * Notify sip_transport.c that packet has been sent.
+ */
+ tdata_op_key->callback(&tcp->base, tdata_op_key->token, bytes_sent);
}
}
-/* This callback is called by transport manager to send SIP message */
+/*
+ * This callback is called by transport manager to send SIP message
+ */
static pj_status_t tcp_send_msg(pjsip_transport *transport,
pjsip_tx_data *tdata,
const pj_sockaddr_t *rem_addr,
@@ -718,7 +948,8 @@ static pj_status_t tcp_send_msg(pjsip_transport *transport,
{
struct tcp_transport *tcp = (struct tcp_transport*)transport;
pj_ssize_t size;
- pj_status_t status;
+ pj_bool_t delayed = PJ_FALSE;
+ pj_status_t status = PJ_SUCCESS;
/* Sanity check */
PJ_ASSERT_RETURN(transport && tdata, PJ_EINVAL);
@@ -737,38 +968,74 @@ static pj_status_t tcp_send_msg(pjsip_transport *transport,
tdata->op_key.callback = callback;
/* If asynchronous connect() has not completed yet, just put the
- * transmit data in the pending transmission list.
+ * transmit data in the pending transmission list since we can not
+ * use the socket yet.
*/
- pj_lock_acquire(tcp->base.lock);
-
if (tcp->has_pending_connect) {
- struct pending_tdata *pending_tdata;
- /* Pust to list */
- pending_tdata = pj_pool_alloc(tdata->pool, sizeof(*pending_tdata));
- pending_tdata->tdata_op_key = &tdata->op_key;
+ /*
+ * Looks like connect() is still in progress. Check again (this time
+ * with holding the lock) to be sure.
+ */
+ pj_lock_acquire(tcp->base.lock);
- pj_list_push_back(&tcp->tx_list, pending_tdata);
- status = PJ_EPENDING;
+ if (tcp->has_pending_connect) {
+ struct delayed_tdata *delayed_tdata;
- } else {
- /* send to ioqueue! */
+ /*
+ * connect() is still in progress. Put the transmit data to
+ * the delayed list.
+ */
+ delayed_tdata = pj_pool_alloc(tdata->pool,
+ sizeof(*delayed_tdata));
+ delayed_tdata->tdata_op_key = &tdata->op_key;
+
+ pj_list_push_back(&tcp->delayed_list, delayed_tdata);
+ status = PJ_EPENDING;
+
+ /* Prevent pj_ioqueue_send() to be called below */
+ delayed = PJ_TRUE;
+ }
+
+ pj_lock_release(tcp->base.lock);
+ }
+
+ if (!delayed) {
+ /*
+ * Transport is ready to go. Send the packet to ioqueue to be
+ * sent asynchronously.
+ */
size = tdata->buf.cur - tdata->buf.start;
status = pj_ioqueue_send(tcp->key,
(pj_ioqueue_op_key_t*)&tdata->op_key,
tdata->buf.start, &size, 0);
- if (status != PJ_EPENDING)
+ if (status != PJ_EPENDING) {
+ /* Not pending (could be immediate success or error) */
tdata->op_key.tdata = NULL;
- }
- pj_lock_release(tcp->base.lock);
+ /* Shutdown transport on closure/errors */
+ if (size <= 0) {
+
+ PJ_LOG(5,(tcp->base.obj_name, "TCP send() error, sent=%d",
+ size));
+
+ if (status == PJ_SUCCESS)
+ status = PJ_RETURN_OS_ERROR(OSERR_ENOTCONN);
+ if (tcp->close_reason==PJ_SUCCESS) tcp->close_reason = status;
+ pjsip_transport_shutdown(&tcp->base);
+ }
+ }
+ }
return status;
}
-/* This callback is called by transport manager to shutdown transport */
+/*
+ * This callback is called by transport manager to shutdown transport.
+ * This normally is only used by UDP transport.
+ */
static pj_status_t tcp_shutdown(pjsip_transport *transport)
{
@@ -779,7 +1046,9 @@ static pj_status_t tcp_shutdown(pjsip_transport *transport)
}
-/* Callback from ioqueue on incoming packet */
+/*
+ * Callback from ioqueue that an incoming data is received from the socket.
+ */
static void on_read_complete(pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
pj_ssize_t bytes_read)
@@ -787,13 +1056,14 @@ static void on_read_complete(pj_ioqueue_key_t *key,
enum { MAX_IMMEDIATE_PACKET = 10 };
pjsip_rx_data_op_key *rdata_op_key = (pjsip_rx_data_op_key*) op_key;
pjsip_rx_data *rdata = rdata_op_key->rdata;
- struct tcp_transport *tp = (struct tcp_transport*)rdata->tp_info.transport;
+ struct tcp_transport *tcp =
+ (struct tcp_transport*)rdata->tp_info.transport;
int i;
pj_status_t status;
/* Don't do anything if transport is closing. */
- if (tp->is_closing) {
- tp->is_closing++;
+ if (tcp->is_closing) {
+ tcp->is_closing++;
return;
}
@@ -806,7 +1076,9 @@ static void on_read_complete(pj_ioqueue_key_t *key,
for (i=0;; ++i) {
pj_uint32_t flags;
- /* Report the packet to transport manager. */
+ /* Houston, we have packet! Report the packet to transport manager
+ * to be parsed.
+ */
if (bytes_read > 0) {
pj_size_t size_eaten;
@@ -815,6 +1087,10 @@ static void on_read_complete(pj_ioqueue_key_t *key,
rdata->pkt_info.zero = 0;
pj_gettimeofday(&rdata->pkt_info.timestamp);
+ /* Report to transport manager.
+ * The transport manager will tell us how many bytes of the packet
+ * have been processed (as valid SIP message).
+ */
size_eaten =
pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr,
rdata);
@@ -833,24 +1109,45 @@ static void on_read_complete(pj_ioqueue_key_t *key,
} else if (bytes_read == 0) {
/* Transport is closed */
- PJ_LOG(4,(tp->base.obj_name, "tcp connection closed"));
- tcp_destroy(&tp->base);
+ PJ_LOG(4,(tcp->base.obj_name, "TCP connection closed"));
+
+ /* We can not destroy the transport since high level objects may
+ * still keep reference to this transport. So we can only
+ * instruct transport manager to gracefully start the shutdown
+ * procedure for this transport.
+ */
+ if (tcp->close_reason==PJ_SUCCESS)
+ tcp->close_reason = PJ_RETURN_OS_ERROR(OSERR_ENOTCONN);
+ pjsip_transport_shutdown(&tcp->base);
+
return;
- } else if (bytes_read < 0) {
+ //} else if (bytes_read < 0) {
+ } else if (-bytes_read != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) &&
+ -bytes_read != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) &&
+ -bytes_read != PJ_STATUS_FROM_OS(OSERR_ECONNRESET))
+ {
/* Report error to endpoint. */
PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt,
rdata->tp_info.transport->obj_name,
- -bytes_read, "tcp recv() error"));
+ -bytes_read, "TCP recv() error"));
+
+ /* We can not destroy the transport since high level objects may
+ * still keep reference to this transport. So we can only
+ * instruct transport manager to gracefully start the shutdown
+ * procedure for this transport.
+ */
+ if (tcp->close_reason==PJ_SUCCESS) tcp->close_reason = -bytes_read;
+ pjsip_transport_shutdown(&tcp->base);
- /* Transport error, close transport */
- tcp_destroy(&tp->base);
return;
}
if (i >= MAX_IMMEDIATE_PACKET) {
- /* Force ioqueue_recv() to return PJ_EPENDING */
+ /* Receive quota reached. Force ioqueue_recv() to
+ * return PJ_EPENDING
+ */
flags = PJ_IOQUEUE_ALWAYS_ASYNC;
} else {
flags = 0;
@@ -867,6 +1164,7 @@ static void on_read_complete(pj_ioqueue_key_t *key,
&bytes_read, flags);
if (status == PJ_SUCCESS) {
+
/* Continue loop. */
pj_assert(i < MAX_IMMEDIATE_PACKET);
@@ -879,30 +1177,56 @@ static void on_read_complete(pj_ioqueue_key_t *key,
rdata->tp_info.transport->obj_name,
status, "tcp recv() error"));
- /* Transport error, close transport */
- tcp_destroy(&tp->base);
+ /* We can not destroy the transport since high level objects may
+ * still keep reference to this transport. So we can only
+ * instruct transport manager to gracefully start the shutdown
+ * procedure for this transport.
+ */
+ if (tcp->close_reason==PJ_SUCCESS) tcp->close_reason = status;
+ pjsip_transport_shutdown(&tcp->base);
+
return;
}
}
}
-/* Callback from ioqueue when connect completes */
+/*
+ * Callback from ioqueue when asynchronous connect() operation completes.
+ */
static void on_connect_complete(pj_ioqueue_key_t *key,
pj_status_t status)
{
- struct pending_connect *connect_op = (struct pending_connect *)key;
- struct tcp_transport *tcp = connect_op->transport;
+ struct tcp_transport *tcp;
pj_sockaddr_in addr;
int addrlen;
+ tcp = pj_ioqueue_get_user_data(key);
+
+ PJ_LOG(4,(tcp->base.obj_name,
+ "TCP transport %.*s:%d is connected to %.*s:%d",
+ (int)tcp->base.local_name.host.slen,
+ tcp->base.local_name.host.ptr,
+ tcp->base.local_name.port,
+ (int)tcp->base.remote_name.host.slen,
+ tcp->base.remote_name.host.ptr,
+ tcp->base.remote_name.port));
+
/* Mark that pending connect() operation has completed. */
tcp->has_pending_connect = PJ_FALSE;
/* Check connect() status */
if (status != PJ_SUCCESS) {
+
tcp_perror(tcp->base.obj_name, "TCP connect() error", status);
- tcp_destroy(&tcp->base);
+
+ /* We can not destroy the transport since high level objects may
+ * still keep reference to this transport. So we can only
+ * instruct transport manager to gracefully start the shutdown
+ * procedure for this transport.
+ */
+ if (tcp->close_reason==PJ_SUCCESS) tcp->close_reason = status;
+ pjsip_transport_shutdown(&tcp->base);
return;
}
@@ -925,7 +1249,13 @@ static void on_connect_complete(pj_ioqueue_key_t *key,
/* Start pending read */
status = tcp_start_read(tcp);
if (status != PJ_SUCCESS) {
- tcp_destroy(&tcp->base);
+ /* We can not destroy the transport since high level objects may
+ * still keep reference to this transport. So we can only
+ * instruct transport manager to gracefully start the shutdown
+ * procedure for this transport.
+ */
+ if (tcp->close_reason==PJ_SUCCESS) tcp->close_reason = status;
+ pjsip_transport_shutdown(&tcp->base);
return;
}
diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c
index 57c6d33f..8e11f2a2 100644
--- a/pjsip/src/pjsua-lib/pjsua_acc.c
+++ b/pjsip/src/pjsua-lib/pjsua_acc.c
@@ -77,18 +77,18 @@ static void update_acc_contact(unsigned acc_id,
unsigned tp_id)
{
pjsua_acc *acc = &pjsua_var.acc[acc_id];
- pjsip_transport *tp = pjsua_var.tpdata[tp_id].tp;
+ struct transport_data *t = &pjsua_var.tpdata[tp_id];
char uri[80];
/* Transport must be valid */
- pj_assert(tp != NULL);
+ pj_assert(t->data.ptr != NULL);
/* Build URI for the account */
pj_ansi_sprintf(uri, "<sip:%.*s:%d;transport=%s>",
- (int)tp->local_name.host.slen,
- tp->local_name.host.ptr,
- tp->local_name.port,
- pjsip_transport_get_type_name(tp->key.type));
+ (int)t->local_name.host.slen,
+ t->local_name.host.ptr,
+ t->local_name.port,
+ pjsip_transport_get_type_name(t->type));
pj_strdup2(pjsua_var.pool, &acc->real_contact, uri);
@@ -144,7 +144,7 @@ static pj_status_t initialize_acc(unsigned acc_id)
}
PJ_TODO(attach_account_to_transport);
- if (pjsua_var.tpdata[0].tp)
+ if (pjsua_var.tpdata[0].data.ptr)
update_acc_contact(acc_id, 0);
/* Build account route-set from outbound proxies and route set from
@@ -220,7 +220,8 @@ PJ_DEF(pj_status_t) pjsua_acc_add( const pjsua_acc_config *cfg,
PJ_ETOOMANY);
/* Must have a transport */
- PJ_ASSERT_RETURN(pjsua_var.tpdata[0].tp != NULL, PJ_EINVALIDOP);
+ PJ_TODO(associate_acc_with_transport);
+ PJ_ASSERT_RETURN(pjsua_var.tpdata[0].data.ptr != NULL, PJ_EINVALIDOP);
PJSUA_LOCK();
@@ -281,20 +282,23 @@ PJ_DEF(pj_status_t) pjsua_acc_add_local( pjsua_transport_id tid,
pjsua_acc_id *p_acc_id)
{
pjsua_acc_config cfg;
- pjsip_transport *tp;
+ struct transport_data *t = &pjsua_var.tpdata[tid];
char uri[62];
+ /* ID must be valid */
+ PJ_ASSERT_RETURN(tid>=0 && tid<PJ_ARRAY_SIZE(pjsua_var.tpdata), PJ_EINVAL);
+
/* Transport must be valid */
- tp = pjsua_var.tpdata[tid].tp;
- PJ_ASSERT_RETURN(tp != NULL, PJ_EINVAL);
+ PJ_ASSERT_RETURN(t->data.ptr != NULL, PJ_EINVAL);
pjsua_acc_config_default(&cfg);
/* Build URI for the account */
- pj_ansi_sprintf(uri, "<sip:%.*s:%d>",
- (int)tp->local_name.host.slen,
- tp->local_name.host.ptr,
- tp->local_name.port);
+ pj_ansi_sprintf(uri, "<sip:%.*s:%d;transport=%s>",
+ (int)t->local_name.host.slen,
+ t->local_name.host.ptr,
+ t->local_name.port,
+ pjsip_transport_get_type_name(t->type));
cfg.id = pj_str(uri);
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index 72c14c00..30bfdd9a 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -729,7 +729,7 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
/* Find empty transport slot */
for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
- if (pjsua_var.tpdata[id].tp == NULL)
+ if (pjsua_var.tpdata[id].data.ptr == NULL)
break;
}
@@ -741,7 +741,9 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
/* Create the transport */
if (type == PJSIP_TRANSPORT_UDP) {
-
+ /*
+ * Create UDP transport.
+ */
pjsua_transport_config config;
pj_sock_t sock = PJ_INVALID_SOCKET;
pj_sockaddr_in pub_addr;
@@ -773,14 +775,56 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
goto on_return;
}
+
+ /* Save the transport */
+ pjsua_var.tpdata[id].type = type;
+ pjsua_var.tpdata[id].local_name = tp->local_name;
+ pjsua_var.tpdata[id].data.tp = tp;
+
+ } else if (type == PJSIP_TRANSPORT_TCP) {
+ /*
+ * Create TCP transport.
+ */
+ pjsua_transport_config config;
+ pjsip_tpfactory *tcp;
+ pj_sockaddr_in local_addr;
+
+ /* Supply default config if it's not specified */
+ if (cfg == NULL) {
+ pjsua_transport_config_default(&config);
+ cfg = &config;
+ }
+
+ /* Init local address */
+ pj_sockaddr_in_init(&local_addr, 0, 0);
+
+ if (cfg->port)
+ local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
+
+ if (cfg->ip_addr.s_addr)
+ local_addr.sin_addr.s_addr = cfg->ip_addr.s_addr;
+
+ /* Create the TCP transport */
+ status = pjsip_tcp_transport_start(pjsua_var.endpt, &local_addr, 1,
+ &tcp);
+
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Error creating SIP TCP listener",
+ status);
+ goto on_return;
+ }
+
+ /* Save the transport */
+ pjsua_var.tpdata[id].type = type;
+ pjsua_var.tpdata[id].local_name = tcp->addr_name;
+ pjsua_var.tpdata[id].data.factory = tcp;
+
} else {
status = PJSIP_EUNSUPTRANSPORT;
pjsua_perror(THIS_FILE, "Error creating transport", status);
goto on_return;
}
- /* Save the transport */
- pjsua_var.tpdata[id].tp = tp;
/* Return the ID */
if (p_id) *p_id = id;
@@ -807,7 +851,7 @@ PJ_DEF(pj_status_t) pjsua_transport_register( pjsip_transport *tp,
/* Find empty transport slot */
for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
- if (pjsua_var.tpdata[id].tp == NULL)
+ if (pjsua_var.tpdata[id].data.ptr == NULL)
break;
}
@@ -818,7 +862,9 @@ PJ_DEF(pj_status_t) pjsua_transport_register( pjsip_transport *tp,
}
/* Save the transport */
- pjsua_var.tpdata[id].tp = tp;
+ pjsua_var.tpdata[id].type = tp->key.type;
+ pjsua_var.tpdata[id].local_name = tp->local_name;
+ pjsua_var.tpdata[id].data.tp = tp;
/* Return the ID */
if (p_id) *p_id = id;
@@ -842,7 +888,7 @@ PJ_DEF(pj_status_t) pjsua_enum_transports( pjsua_transport_id id[],
for (i=0, count=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata) && count<*p_count;
++i)
{
- if (!pjsua_var.tpdata[i].tp)
+ if (!pjsua_var.tpdata[i].data.ptr)
continue;
id[count++] = i;
@@ -862,7 +908,7 @@ PJ_DEF(pj_status_t) pjsua_enum_transports( pjsua_transport_id id[],
PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id,
pjsua_transport_info *info)
{
- pjsip_transport *tp;
+ struct transport_data *t = &pjsua_var.tpdata[id];
pj_memset(info, 0, sizeof(*info));
@@ -870,25 +916,50 @@ PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id,
PJ_ASSERT_RETURN(id>=0 && id<PJ_ARRAY_SIZE(pjsua_var.tpdata), PJ_EINVAL);
/* Make sure that transport exists */
- PJ_ASSERT_RETURN(pjsua_var.tpdata[id].tp != NULL, PJ_EINVAL);
+ PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
PJSUA_LOCK();
- tp = pjsua_var.tpdata[id].tp;
- if (tp == NULL) {
- PJSUA_UNLOCK();
- return PJ_EINVALIDOP;
- }
+ if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_UDP) {
+
+ pjsip_transport *tp = t->data.tp;
+
+ if (tp == NULL) {
+ PJSUA_UNLOCK();
+ return PJ_EINVALIDOP;
+ }
+
+ info->id = id;
+ info->type = tp->key.type;
+ info->type_name = pj_str(tp->type_name);
+ info->info = pj_str(tp->info);
+ info->flag = tp->flag;
+ info->addr_len = tp->addr_len;
+ info->local_addr = tp->local_addr;
+ info->local_name = tp->local_name;
+ info->usage_count = pj_atomic_get(tp->ref_cnt);
+
+ } else if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_TCP) {
+
+ pjsip_tpfactory *factory = t->data.factory;
+
+ if (factory == NULL) {
+ PJSUA_UNLOCK();
+ return PJ_EINVALIDOP;
+ }
- info->id = id;
- info->type = tp->key.type;
- info->type_name = pj_str(tp->type_name);
- info->info = pj_str(tp->info);
- info->flag = tp->flag;
- info->addr_len = tp->addr_len;
- info->local_addr = tp->local_addr;
- info->local_name = tp->local_name;
- info->usage_count = pj_atomic_get(tp->ref_cnt);
+ info->id = id;
+ info->type = t->type;
+ info->type_name = pj_str("TCP");
+ info->info = pj_str("TCP transport");
+ info->flag = factory->flag;
+ info->addr_len = sizeof(factory->local_addr);
+ info->local_addr = factory->local_addr;
+ info->local_name = factory->addr_name;
+ info->usage_count = 0;
+
+ }
+
PJSUA_UNLOCK();
@@ -906,7 +977,7 @@ PJ_DEF(pj_status_t) pjsua_transport_set_enable( pjsua_transport_id id,
PJ_ASSERT_RETURN(id>=0 && id<PJ_ARRAY_SIZE(pjsua_var.tpdata), PJ_EINVAL);
/* Make sure that transport exists */
- PJ_ASSERT_RETURN(pjsua_var.tpdata[id].tp != NULL, PJ_EINVAL);
+ PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
/* To be done!! */
@@ -926,7 +997,7 @@ PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id,
PJ_ASSERT_RETURN(id>=0 && id<PJ_ARRAY_SIZE(pjsua_var.tpdata), PJ_EINVAL);
/* Make sure that transport exists */
- PJ_ASSERT_RETURN(pjsua_var.tpdata[id].tp != NULL, PJ_EINVAL);
+ PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
/* To be done!! */
diff --git a/pjsip/src/test-pjsip/main.c b/pjsip/src/test-pjsip/main.c
index 7994504d..ddfb1e7d 100644
--- a/pjsip/src/test-pjsip/main.c
+++ b/pjsip/src/test-pjsip/main.c
@@ -21,6 +21,8 @@
#include <string.h>
#include <stdlib.h>
+extern const char *system_name;
+
static void usage()
{
puts("Usage: test-pjsip");
@@ -57,6 +59,15 @@ int main(int argc, char *argv[])
return 1;
}
log_level = atoi(*opt_arg);
+ } else if (strcmp(*opt_arg, "-s") == 0 ||
+ strcmp(*opt_arg, "--system") == 0)
+ {
+ ++opt_arg;
+ if (!opt_arg) {
+ usage();
+ return 1;
+ }
+ system_name = *opt_arg;
} else {
usage();
return 1;
@@ -67,9 +78,9 @@ int main(int argc, char *argv[])
retval = test_main();
- if (argc != 1) {
+ if (interractive) {
char s[10];
- printf("<Press ENTER to quit>\n");
+ printf("<Press ENTER to quit>\n"); fflush(stdout);
fgets(s, sizeof(s), stdin);
}
diff --git a/pjsip/src/test-pjsip/msg_logger.c b/pjsip/src/test-pjsip/msg_logger.c
index a4eac0da..e517ae05 100644
--- a/pjsip/src/test-pjsip/msg_logger.c
+++ b/pjsip/src/test-pjsip/msg_logger.c
@@ -27,13 +27,15 @@ static pj_bool_t msg_log_enabled;
static pj_bool_t on_rx_msg(pjsip_rx_data *rdata)
{
if (msg_log_enabled) {
- PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s:%d:\n"
- "%s\n"
+ PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s:%s:%d:\n"
+ "%.*s\n"
"--end msg--",
rdata->msg_info.len,
pjsip_rx_data_get_info(rdata),
+ rdata->tp_info.transport->type_name,
rdata->pkt_info.src_name,
rdata->pkt_info.src_port,
+ rdata->msg_info.len,
rdata->msg_info.msg_buf));
}
@@ -43,13 +45,15 @@ static pj_bool_t on_rx_msg(pjsip_rx_data *rdata)
static pj_status_t on_tx_msg(pjsip_tx_data *tdata)
{
if (msg_log_enabled) {
- PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s:%d:\n"
- "%s\n"
+ PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s:%s:%d:\n"
+ "%.*s\n"
"--end msg--",
(tdata->buf.cur - tdata->buf.start),
pjsip_tx_data_get_info(tdata),
+ tdata->tp_info.transport->type_name,
tdata->tp_info.dst_name,
tdata->tp_info.dst_port,
+ (tdata->buf.cur - tdata->buf.start),
tdata->buf.start));
}
return PJ_SUCCESS;
diff --git a/pjsip/src/test-pjsip/msg_test.c b/pjsip/src/test-pjsip/msg_test.c
index f5debe84..ebe43025 100644
--- a/pjsip/src/test-pjsip/msg_test.c
+++ b/pjsip/src/test-pjsip/msg_test.c
@@ -21,7 +21,11 @@
#include <pjlib.h>
#define POOL_SIZE 8000
-#define LOOP 10000
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+# define LOOP 10000
+#else
+# define LOOP 100000
+#endif
#define AVERAGE_MSG_LEN 800
#define THIS_FILE "msg_test.c"
@@ -35,8 +39,6 @@ static pjsip_msg *create_msg1(pj_pool_t *pool);
#define FLAG_PARSE_ONLY 4
#define FLAG_PRINT_ONLY 8
-static int flag = 0;
-
struct test_msg
{
char msg[1024];
@@ -101,8 +103,12 @@ struct test_msg
}
};
-static pj_highprec_t detect_len, parse_len, print_len;
-static pj_timestamp detect_time, parse_time, print_time;
+static struct
+{
+ int flag;
+ pj_highprec_t detect_len, parse_len, print_len;
+ pj_timestamp detect_time, parse_time, print_time;
+} var;
static pj_status_t test_entry( pj_pool_t *pool, struct test_msg *entry )
{
@@ -121,17 +127,17 @@ static pj_status_t test_entry( pj_pool_t *pool, struct test_msg *entry )
entry->len = pj_native_strlen(entry->msg);
- if (flag & FLAG_PARSE_ONLY)
+ if (var.flag & FLAG_PARSE_ONLY)
goto parse_msg;
- if (flag & FLAG_PRINT_ONLY) {
+ if (var.flag & FLAG_PRINT_ONLY) {
if (print_msg == NULL)
print_msg = entry->creator(pool);
goto print_msg;
}
/* Detect message. */
- detect_len = detect_len + entry->len;
+ var.detect_len = var.detect_len + entry->len;
pj_get_timestamp(&t1);
status = pjsip_find_msg(entry->msg, entry->len, PJ_FALSE, &msg_size);
if (status != PJ_SUCCESS) {
@@ -148,14 +154,14 @@ static pj_status_t test_entry( pj_pool_t *pool, struct test_msg *entry )
}
pj_get_timestamp(&t2);
pj_sub_timestamp(&t2, &t1);
- pj_add_timestamp(&detect_time, &t2);
+ pj_add_timestamp(&var.detect_time, &t2);
- if (flag & FLAG_DETECT_ONLY)
+ if (var.flag & FLAG_DETECT_ONLY)
return PJ_SUCCESS;
/* Parse message. */
parse_msg:
- parse_len = parse_len + entry->len;
+ var.parse_len = var.parse_len + entry->len;
pj_get_timestamp(&t1);
pj_list_init(&err_list);
parsed_msg = pjsip_parse_msg(pool, entry->msg, entry->len, &err_list);
@@ -171,9 +177,9 @@ parse_msg:
}
pj_get_timestamp(&t2);
pj_sub_timestamp(&t2, &t1);
- pj_add_timestamp(&parse_time, &t2);
+ pj_add_timestamp(&var.parse_time, &t2);
- if (flag & FLAG_PARSE_ONLY)
+ if (var.flag & FLAG_PARSE_ONLY)
return PJ_SUCCESS;
/* Create reference message. */
@@ -306,9 +312,9 @@ parse_msg:
/* Print message. */
print_msg:
- print_len = print_len + entry->len;
+ var.print_len = var.print_len + entry->len;
pj_get_timestamp(&t1);
- if (flag && FLAG_PRINT_ONLY)
+ if (var.flag && FLAG_PRINT_ONLY)
ref_msg = print_msg;
len = pjsip_msg_print(ref_msg, msgbuf1, PJSIP_MAX_PKT_LEN);
if (len < 1) {
@@ -317,7 +323,7 @@ print_msg:
}
pj_get_timestamp(&t2);
pj_sub_timestamp(&t2, &t1);
- pj_add_timestamp(&print_time, &t2);
+ pj_add_timestamp(&var.print_time, &t2);
status = PJ_SUCCESS;
@@ -674,17 +680,14 @@ static pjsip_msg *create_msg1(pj_pool_t *pool)
/*****************************************************************************/
-int msg_test(void)
+static pj_status_t simple_test(void)
{
+ unsigned i;
pj_status_t status;
- pj_pool_t *pool;
- int i, loop;
- pj_timestamp zero;
- pj_time_val elapsed;
- pj_highprec_t avg_detect, avg_parse, avg_print, kbytes;
PJ_LOG(3,(THIS_FILE, " simple test.."));
for (i=0; i<PJ_ARRAY_SIZE(test_array); ++i) {
+ pj_pool_t *pool;
pool = pjsip_endpt_create_pool(endpt, NULL, POOL_SIZE, POOL_SIZE);
status = test_entry( pool, &test_array[i] );
pjsip_endpt_release_pool(endpt, pool);
@@ -693,10 +696,24 @@ int msg_test(void)
return status;
}
- PJ_LOG(3,(THIS_FILE, " benchmarking.."));
- detect_len = parse_len = print_len = 0;
- zero.u64 = detect_time.u64 = parse_time.u64 = print_time.u64 = 0;
-
+ return PJ_SUCCESS;
+}
+
+
+static int msg_benchmark(unsigned *p_detect, unsigned *p_parse,
+ unsigned *p_print)
+{
+ pj_status_t status;
+ pj_pool_t *pool;
+ int i, loop;
+ pj_timestamp zero;
+ pj_time_val elapsed;
+ pj_highprec_t avg_detect, avg_parse, avg_print, kbytes;
+
+
+ pj_memset(&var, 0, sizeof(var));
+ zero.u64 = 0;
+
for (loop=0; loop<LOOP; ++loop) {
for (i=0; i<PJ_ARRAY_SIZE(test_array); ++i) {
pool = pjsip_endpt_create_pool(endpt, NULL, POOL_SIZE, POOL_SIZE);
@@ -708,52 +725,144 @@ int msg_test(void)
}
}
- kbytes = detect_len;
+ kbytes = var.detect_len;
pj_highprec_mod(kbytes, 1000000);
pj_highprec_div(kbytes, 100000);
- elapsed = pj_elapsed_time(&zero, &detect_time);
- avg_detect = pj_elapsed_usec(&zero, &detect_time);
+ elapsed = pj_elapsed_time(&zero, &var.detect_time);
+ avg_detect = pj_elapsed_usec(&zero, &var.detect_time);
pj_highprec_mul(avg_detect, AVERAGE_MSG_LEN);
- pj_highprec_div(avg_detect, detect_len);
+ pj_highprec_div(avg_detect, var.detect_len);
avg_detect = 1000000 / avg_detect;
PJ_LOG(3,(THIS_FILE,
" %u.%u MB detected in %d.%03ds (avg=%d msg detection/sec)",
- (unsigned)(detect_len/1000000), (unsigned)kbytes,
+ (unsigned)(var.detect_len/1000000), (unsigned)kbytes,
elapsed.sec, elapsed.msec,
(unsigned)avg_detect));
+ *p_detect = (unsigned)avg_detect;
- kbytes = parse_len;
+ kbytes = var.parse_len;
pj_highprec_mod(kbytes, 1000000);
pj_highprec_div(kbytes, 100000);
- elapsed = pj_elapsed_time(&zero, &parse_time);
- avg_parse = pj_elapsed_usec(&zero, &parse_time);
+ elapsed = pj_elapsed_time(&zero, &var.parse_time);
+ avg_parse = pj_elapsed_usec(&zero, &var.parse_time);
pj_highprec_mul(avg_parse, AVERAGE_MSG_LEN);
- pj_highprec_div(avg_parse, parse_len);
+ pj_highprec_div(avg_parse, var.parse_len);
avg_parse = 1000000 / avg_parse;
PJ_LOG(3,(THIS_FILE,
" %u.%u MB parsed in %d.%03ds (avg=%d msg parsing/sec)",
- (unsigned)(parse_len/1000000), (unsigned)kbytes,
+ (unsigned)(var.parse_len/1000000), (unsigned)kbytes,
elapsed.sec, elapsed.msec,
(unsigned)avg_parse));
+ *p_parse = (unsigned)avg_parse;
- kbytes = print_len;
+ kbytes = var.print_len;
pj_highprec_mod(kbytes, 1000000);
pj_highprec_div(kbytes, 100000);
- elapsed = pj_elapsed_time(&zero, &print_time);
- avg_print = pj_elapsed_usec(&zero, &print_time);
+ elapsed = pj_elapsed_time(&zero, &var.print_time);
+ avg_print = pj_elapsed_usec(&zero, &var.print_time);
pj_highprec_mul(avg_print, AVERAGE_MSG_LEN);
- pj_highprec_div(avg_print, print_len);
+ pj_highprec_div(avg_print, var.print_len);
avg_print = 1000000 / avg_print;
PJ_LOG(3,(THIS_FILE,
" %u.%u MB printed in %d.%03ds (avg=%d msg print/sec)",
- (unsigned)(print_len/1000000), (unsigned)kbytes,
+ (unsigned)(var.print_len/1000000), (unsigned)kbytes,
elapsed.sec, elapsed.msec,
(unsigned)avg_print));
+ *p_print = (unsigned)avg_print;
return status;
}
/*****************************************************************************/
+
+int msg_test(void)
+{
+ enum { COUNT = 4, DETECT=0, PARSE=1, PRINT=2 };
+ struct {
+ unsigned detect;
+ unsigned parse;
+ unsigned print;
+ } run[COUNT];
+ unsigned i, max, avg_len;
+ char desc[250];
+ pj_status_t status;
+
+
+ status = simple_test();
+ if (status != PJ_SUCCESS)
+ return status;
+
+ for (i=0; i<COUNT; ++i) {
+ PJ_LOG(3,(THIS_FILE, " benchmarking (%d of %d)..", i+1, COUNT));
+ status = msg_benchmark(&run[i].detect, &run[i].parse, &run[i].print);
+ if (status != PJ_SUCCESS)
+ return status;
+ }
+
+ /* Calculate average message length */
+ for (i=0, avg_len=0; i<PJ_ARRAY_SIZE(test_array); ++i) {
+ avg_len += test_array[i].len;
+ }
+ avg_len /= PJ_ARRAY_SIZE(test_array);
+
+
+ /* Print maximum detect/sec */
+ for (i=0, max=0; i<COUNT; ++i)
+ if (run[i].detect > max) max = run[i].detect;
+
+ PJ_LOG(3,("", " Maximum message detection/sec=%u", max));
+
+ pj_ansi_sprintf(desc, "Number of SIP messages "
+ "can be pre-parse by <tt>pjsip_find_msg()</tt> "
+ "per second (tested with %d message sets with "
+ "average message length of "
+ "%d bytes)", PJ_ARRAY_SIZE(test_array), avg_len);
+ report_ival("msg-detect-per-sec", max, "msg/sec", desc);
+
+ /* Print maximum parse/sec */
+ for (i=0, max=0; i<COUNT; ++i)
+ if (run[i].parse > max) max = run[i].parse;
+
+ PJ_LOG(3,("", " Maximum message parsing/sec=%u", max));
+
+ pj_ansi_sprintf(desc, "Number of SIP messages "
+ "can be <b>parsed</b> by <tt>pjsip_parse_msg()</tt> "
+ "per second (tested with %d message sets with "
+ "average message length of "
+ "%d bytes)", PJ_ARRAY_SIZE(test_array), avg_len);
+ report_ival("msg-parse-per-sec", max, "msg/sec", desc);
+
+ /* Msg parsing bandwidth */
+ report_ival("msg-parse-bandwidth-mb", avg_len*max/1000000, "MB/sec",
+ "Message parsing bandwidth in megabytes (number of megabytes"
+ " worth of SIP messages that can be parsed per second). "
+ "The value is derived from msg-parse-per-sec above.");
+
+
+ /* Print maximum print/sec */
+ for (i=0, max=0; i<COUNT; ++i)
+ if (run[i].print > max) max = run[i].print;
+
+ PJ_LOG(3,("", " Maximum message print/sec=%u", max));
+
+ pj_ansi_sprintf(desc, "Number of SIP messages "
+ "can be <b>printed</b> by <tt>pjsip_msg_print()</tt>"
+ " per second (tested with %d message sets with "
+ "average message length of "
+ "%d bytes)", PJ_ARRAY_SIZE(test_array), avg_len);
+
+ report_ival("msg-print-per-sec", max, "msg/sec", desc);
+
+ /* Msg print bandwidth */
+ report_ival("msg-printed-bandwidth-mb", avg_len*max/1000000, "MB/sec",
+ "Message print bandwidth in megabytes (total size of "
+ "SIP messages printed per second). "
+ "The value is derived from msg-print-per-sec above.");
+
+
+ return PJ_SUCCESS;
+}
+
diff --git a/pjsip/src/test-pjsip/test.c b/pjsip/src/test-pjsip/test.c
index b8707332..3ae58daa 100644
--- a/pjsip/src/test-pjsip/test.c
+++ b/pjsip/src/test-pjsip/test.c
@@ -33,11 +33,24 @@
if (rc!=0) goto on_return; \
} while (0)
+#define DO_TSX_TEST(test, param) \
+ do { \
+ PJ_LOG(3, (THIS_FILE, "Running %s(%s)...", #test, (param)->tp_type)); \
+ rc = test(param); \
+ PJ_LOG(3, (THIS_FILE, \
+ "%s(%d)", \
+ (rc ? "..ERROR" : "..success"), rc)); \
+ if (rc!=0) goto on_return; \
+ } while (0)
pjsip_endpoint *endpt;
int log_level = 3;
+static pj_oshandle_t fd_report;
+const char *system_name = "Unknown";
+static char buf[1024];
+
void app_perror(const char *msg, pj_status_t rc)
{
char errbuf[256];
@@ -75,11 +88,142 @@ pj_status_t register_static_modules(pj_size_t *count, pjsip_module **modules)
return PJ_SUCCESS;
}
+static pj_status_t init_report(void)
+{
+ char tmp[80];
+ pj_time_val timestamp;
+ pj_parsed_time date_time;
+ pj_ssize_t len;
+ pj_status_t status;
+
+ pj_ansi_sprintf(tmp, "pjsip-static-bench-%s-%s.htm", PJ_OS_NAME, PJ_CC_NAME);
+
+ status = pj_file_open(NULL, tmp, PJ_O_WRONLY, &fd_report);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Title */
+ len = pj_ansi_sprintf(buf, "<HTML>\n"
+ " <HEAD>\n"
+ " <TITLE>PJSIP %s (%s) - Static Benchmark</TITLE>\n"
+ " </HEAD>\n"
+ "<BODY>\n"
+ "\n",
+ PJ_VERSION,
+ (PJ_DEBUG ? "Debug" : "Release"));
+ pj_file_write(fd_report, buf, &len);
+
+
+ /* Title */
+ len = pj_ansi_sprintf(buf, "<H1>PJSIP %s (%s) - Static Benchmark</H1>\n",
+ PJ_VERSION,
+ (PJ_DEBUG ? "Debug" : "Release"));
+ pj_file_write(fd_report, buf, &len);
+
+ len = pj_ansi_sprintf(buf, "<P>Below is the benchmark result generated "
+ "by <b>test-pjsip</b> program. The program "
+ "is single-threaded only.</P>\n");
+ pj_file_write(fd_report, buf, &len);
+
+
+ /* Write table heading */
+ len = pj_ansi_sprintf(buf, "<TABLE border=\"1\" cellpadding=\"4\">\n"
+ " <TR><TD bgColor=\"aqua\" align=\"center\">Variable</TD>\n"
+ " <TD bgColor=\"aqua\" align=\"center\">Value</TD>\n"
+ " <TD bgColor=\"aqua\" align=\"center\">Description</TD>\n"
+ " </TR>\n");
+ pj_file_write(fd_report, buf, &len);
+
+
+ /* Write version */
+ report_sval("version", PJ_VERSION, "", "PJLIB/PJSIP version");
+
+
+ /* Debug or release */
+ report_sval("build-type", (PJ_DEBUG ? "Debug" : "Release"), "", "Build type");
+
+
+ /* Write timestamp */
+ pj_gettimeofday(&timestamp);
+ report_ival("timestamp", timestamp.sec, "", "System timestamp of the test");
+
+
+ /* Write time of day */
+ pj_time_decode(&timestamp, &date_time);
+ len = pj_ansi_sprintf(tmp, "%04d-%02d-%02d %02d:%02d:%02d",
+ date_time.year, date_time.mon+1, date_time.day,
+ date_time.hour, date_time.min, date_time.sec);
+ report_sval("date-time", tmp, "", "Date/time of the test");
+
+
+ /* Write System */
+ report_sval("system", system_name, "", "System description");
+
+
+ /* Write OS type */
+ report_sval("os-family", PJ_OS_NAME, "", "Operating system family");
+
+
+ /* Write CC name */
+ len = pj_ansi_sprintf(tmp, "%s-%d.%d.%d", PJ_CC_NAME,
+ PJ_CC_VER_1, PJ_CC_VER_2, PJ_CC_VER_2);
+ report_sval("cc-name", tmp, "", "Compiler name and version");
+
+
+ return PJ_SUCCESS;
+}
+
+void report_sval(const char *name, const char* value, const char *valname,
+ const char *desc)
+{
+ pj_ssize_t len;
+
+ len = pj_ansi_sprintf(buf, " <TR><TD><TT>%s</TT></TD>\n"
+ " <TD align=\"right\"><B>%s %s</B></TD>\n"
+ " <TD>%s</TD>\n"
+ " </TR>\n",
+ name, value, valname, desc);
+ pj_file_write(fd_report, buf, &len);
+}
+
+
+void report_ival(const char *name, int value, const char *valname,
+ const char *desc)
+{
+ pj_ssize_t len;
+
+ len = pj_ansi_sprintf(buf, " <TR><TD><TT>%s</TT></TD>\n"
+ " <TD align=\"right\"><B>%d %s</B></TD>\n"
+ " <TD>%s</TD>\n"
+ " </TR>\n",
+ name, value, valname, desc);
+ pj_file_write(fd_report, buf, &len);
+
+}
+
+static void close_report(void)
+{
+ pj_ssize_t len;
+
+ if (fd_report) {
+ len = pj_ansi_sprintf(buf, "</TABLE>\n</BODY>\n</HTML>\n");
+ pj_file_write(fd_report, buf, &len);
+
+ pj_file_close(fd_report);
+ }
+}
+
+
int test_main(void)
{
pj_status_t rc;
pj_caching_pool caching_pool;
const char *filename;
+ unsigned i, tsx_test_cnt=0;
+ struct tsx_test_param tsx_test[10];
+ pj_status_t status;
+ pjsip_transport *tp;
+ pjsip_tpfactory *tpfactory;
int line;
pj_log_set_level(log_level);
@@ -93,6 +237,10 @@ int test_main(void)
return rc;
}
+ status = init_report();
+ if (status != PJ_SUCCESS)
+ return status;
+
pj_dump_config();
pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 );
@@ -124,6 +272,11 @@ int test_main(void)
rc);
goto on_return;
}
+ tsx_test[tsx_test_cnt].port = 5060;
+ tsx_test[tsx_test_cnt].tp_type = "loop-dgram";
+ tsx_test[tsx_test_cnt].type = PJSIP_TRANSPORT_LOOP_DGRAM;
+ ++tsx_test_cnt;
+
#if INCLUDE_URI_TEST
DO_TEST(uri_test());
@@ -138,6 +291,10 @@ int test_main(void)
DO_TEST(txdata_test());
#endif
+#if INCLUDE_TSX_BENCH
+ DO_TEST(tsx_bench());
+#endif
+
#if INCLUDE_UDP_TEST
DO_TEST(transport_udp_test());
#endif
@@ -146,15 +303,43 @@ int test_main(void)
DO_TEST(transport_loop_test());
#endif
+#if INCLUDE_TCP_TEST
+ DO_TEST(transport_tcp_test());
+#endif
+
+
#if INCLUDE_TSX_TEST
- DO_TEST(tsx_basic_test());
- DO_TEST(tsx_uac_test());
- DO_TEST(tsx_uas_test());
+ status = pjsip_udp_transport_start(endpt, NULL, NULL, 1, &tp);
+ if (status == PJ_SUCCESS) {
+ tsx_test[tsx_test_cnt].port = tp->local_name.port;
+ tsx_test[tsx_test_cnt].tp_type = "udp";
+ tsx_test[tsx_test_cnt].type = PJSIP_TRANSPORT_UDP;
+ ++tsx_test_cnt;
+ }
+
+ status = pjsip_tcp_transport_start(endpt, NULL, 1, &tpfactory);
+ if (status == PJ_SUCCESS) {
+ tsx_test[tsx_test_cnt].port = tpfactory->addr_name.port;
+ tsx_test[tsx_test_cnt].tp_type = "tcp";
+ tsx_test[tsx_test_cnt].type = PJSIP_TRANSPORT_TCP;
+ ++tsx_test_cnt;
+ } else {
+ app_perror("Unable to create TCP", status);
+ rc = -4;
+ goto on_return;
+ }
+
+
+ for (i=0; i<tsx_test_cnt; ++i) {
+ DO_TSX_TEST(tsx_basic_test, &tsx_test[i]);
+ DO_TSX_TEST(tsx_uac_test, &tsx_test[i]);
+ DO_TSX_TEST(tsx_uas_test, &tsx_test[i]);
+ }
#endif
on_return:
-
+ flush_events(500);
pjsip_endpt_destroy(endpt);
pj_caching_pool_destroy(&caching_pool);
@@ -169,6 +354,8 @@ on_return:
else
PJ_LOG(3,(THIS_FILE, "Test completed with error(s)"));
+ report_ival("test-status", rc, "", "Overall test status/result (0==success)");
+ close_report();
return rc;
}
diff --git a/pjsip/src/test-pjsip/test.h b/pjsip/src/test-pjsip/test.h
index f2b6b91d..969b341a 100644
--- a/pjsip/src/test-pjsip/test.h
+++ b/pjsip/src/test-pjsip/test.h
@@ -31,13 +31,22 @@ extern pjsip_endpoint *endpt;
#define INCLUDE_TRANSPORT_GROUP 1
#define INCLUDE_TSX_GROUP 1
+/*
+ * Include tests that normally would fail under certain gcc
+ * optimization levels.
+ */
+#ifndef INCLUDE_GCC_TEST
+# define INCLUDE_GCC_TEST 0
+#endif
#define INCLUDE_URI_TEST INCLUDE_MESSAGING_GROUP
#define INCLUDE_MSG_TEST INCLUDE_MESSAGING_GROUP
#define INCLUDE_TXDATA_TEST INCLUDE_MESSAGING_GROUP
+#define INCLUDE_TSX_BENCH INCLUDE_MESSAGING_GROUP
#define INCLUDE_UDP_TEST INCLUDE_TRANSPORT_GROUP
#define INCLUDE_LOOP_TEST INCLUDE_TRANSPORT_GROUP
+#define INCLUDE_TCP_TEST INCLUDE_TRANSPORT_GROUP
#define INCLUDE_TSX_TEST INCLUDE_TSX_GROUP
@@ -47,17 +56,28 @@ int uri_test(void);
int msg_test(void);
int msg_err_test(void);
int txdata_test(void);
+int tsx_bench(void);
int transport_udp_test(void);
int transport_loop_test(void);
-int tsx_basic_test(void);
-int tsx_uac_test(void);
-int tsx_uas_test(void);
+int transport_tcp_test(void);
+
+struct tsx_test_param
+{
+ int type;
+ int port;
+ char *tp_type;
+};
+
+int tsx_basic_test(struct tsx_test_param *param);
+int tsx_uac_test(struct tsx_test_param *param);
+int tsx_uas_test(struct tsx_test_param *param);
/* Transport test helpers (transport_test.c). */
int generic_transport_test(pjsip_transport *tp);
int transport_send_recv_test( pjsip_transport_type_e tp_type,
pjsip_transport *ref_tp,
- char *target_url );
+ char *target_url,
+ int *p_usec_rtt);
int transport_rt_test( pjsip_transport_type_e tp_type,
pjsip_transport *ref_tp,
char *target_url,
@@ -68,10 +88,15 @@ int test_main(void);
/* Test utilities. */
void app_perror(const char *msg, pj_status_t status);
-int init_msg_logger(void);
-int msg_logger_set_enabled(pj_bool_t enabled);
+int init_msg_logger(void);
+int msg_logger_set_enabled(pj_bool_t enabled);
void flush_events(unsigned duration);
+
+void report_ival(const char *name, int value, const char *valname, const char *desc);
+void report_sval(const char *name, const char* value, const char *valname, const char *desc);
+
+
/* Settings. */
extern int log_level;
diff --git a/pjsip/src/test-pjsip/transport_loop_test.c b/pjsip/src/test-pjsip/transport_loop_test.c
index 351fb7b6..3e378832 100644
--- a/pjsip/src/test-pjsip/transport_loop_test.c
+++ b/pjsip/src/test-pjsip/transport_loop_test.c
@@ -25,10 +25,13 @@
static int datagram_loop_test()
{
+ enum { LOOP = 8 };
pjsip_transport *loop;
int i, pkt_lost;
pj_sockaddr_in addr;
pj_status_t status;
+ long ref_cnt;
+ unsigned rtt[LOOP], min_rtt;
PJ_LOG(3,(THIS_FILE, "testing datagram loop transport"));
@@ -40,19 +43,33 @@ static int datagram_loop_test()
return -20;
}
+ /* Get initial reference counter */
+ ref_cnt = pj_atomic_get(loop->ref_cnt);
+
/* Test basic transport attributes */
status = generic_transport_test(loop);
if (status != PJ_SUCCESS)
return status;
/* Basic transport's send/receive loopback test. */
- for (i=0; i<2; ++i) {
+ for (i=0; i<LOOP; ++i) {
status = transport_send_recv_test(PJSIP_TRANSPORT_LOOP_DGRAM, loop,
- "sip:bob@130.0.0.1;transport=loop-dgram");
+ "sip:bob@130.0.0.1;transport=loop-dgram",
+ &rtt[i]);
if (status != 0)
return status;
}
+ min_rtt = 0xFFFFFFF;
+ for (i=0; i<LOOP; ++i)
+ if (rtt[i] < min_rtt) min_rtt = rtt[i];
+
+ report_ival("loop-rtt-usec", min_rtt, "usec",
+ "Best Loopback transport round trip time, in microseconds "
+ "(time from sending request until response is received. "
+ "Tests were performed on local machine only)");
+
+
/* Multi-threaded round-trip test. */
status = transport_rt_test(PJSIP_TRANSPORT_LOOP_DGRAM, loop,
"sip:bob@130.0.0.1;transport=loop-dgram",
@@ -84,9 +101,11 @@ static int datagram_loop_test()
/* Restore delay. */
pjsip_loop_set_delay(loop, 0);
- /* Check that reference counter is one. */
- if (pj_atomic_get(loop->ref_cnt) != 1) {
- return -50;
+ /* Check reference counter. */
+ if (pj_atomic_get(loop->ref_cnt) != ref_cnt) {
+ PJ_LOG(3,(THIS_FILE, " error: ref counter is not %d (%d)",
+ ref_cnt, pj_atomic_get(loop->ref_cnt)));
+ return -51;
}
/* Decrement reference. */
diff --git a/pjsip/src/test-pjsip/transport_tcp_test.c b/pjsip/src/test-pjsip/transport_tcp_test.c
new file mode 100644
index 00000000..70f1bc92
--- /dev/null
+++ b/pjsip/src/test-pjsip/transport_tcp_test.c
@@ -0,0 +1,143 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "test.h"
+#include <pjsip.h>
+#include <pjlib.h>
+
+#define THIS_FILE "transport_tcp_test.c"
+
+
+/*
+ * TCP transport test.
+ */
+int transport_tcp_test(void)
+{
+ enum { SEND_RECV_LOOP = 8 };
+ pjsip_tpfactory *tpfactory;
+ pjsip_transport *tcp;
+ pj_sockaddr_in rem_addr;
+ pj_status_t status;
+ char url[64];
+ unsigned rtt[SEND_RECV_LOOP], min_rtt;
+ int i, pkt_lost;
+
+ /* Start TCP listener on arbitrary port. */
+ status = pjsip_tcp_transport_start(endpt, NULL, 1, &tpfactory);
+ if (status != PJ_SUCCESS) {
+ app_perror(" Error: unable to start TCP transport", status);
+ return -10;
+ }
+
+
+ /* Get the listener address */
+ status = pj_sockaddr_in_init(&rem_addr, &tpfactory->addr_name.host,
+ (pj_uint16_t)tpfactory->addr_name.port);
+ if (status != PJ_SUCCESS) {
+ app_perror(" Error: possibly invalid TCP address name", status);
+ return -14;
+ }
+
+ pj_ansi_sprintf(url, "sip:alice@%s:%d;transport=tcp",
+ pj_inet_ntoa(rem_addr.sin_addr),
+ pj_ntohs(rem_addr.sin_port));
+
+
+ /* Acquire one TCP transport. */
+ status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_TCP,
+ &rem_addr, sizeof(rem_addr),
+ &tcp);
+ if (status != PJ_SUCCESS || tcp == NULL) {
+ app_perror(" Error: unable to acquire TCP transport", status);
+ return -17;
+ }
+
+ /* After pjsip_endpt_acquire_transport, TCP transport must have
+ * reference counter 1.
+ */
+ if (pj_atomic_get(tcp->ref_cnt) != 1)
+ return -20;
+
+ /* Test basic transport attributes */
+ status = generic_transport_test(tcp);
+ if (status != PJ_SUCCESS)
+ return status;
+
+
+ /* Check again that reference counter is 1. */
+ if (pj_atomic_get(tcp->ref_cnt) != 1)
+ return -70;
+
+ /* Basic transport's send/receive loopback test. */
+ for (i=0; i<SEND_RECV_LOOP; ++i) {
+ status = transport_send_recv_test(PJSIP_TRANSPORT_TCP, tcp, url, &rtt[i]);
+
+ if (status != 0) {
+ pjsip_transport_dec_ref(tcp);
+ flush_events(500);
+ return -72;
+ }
+ }
+
+ min_rtt = 0xFFFFFFF;
+ for (i=0; i<SEND_RECV_LOOP; ++i)
+ if (rtt[i] < min_rtt) min_rtt = rtt[i];
+
+ report_ival("tcp-rtt-usec", min_rtt, "usec",
+ "Best TCP transport round trip time, in microseconds "
+ "(time from sending request until response is received. "
+ "Tests were performed on local machine only, and after "
+ "TCP socket has been established by previous test)");
+
+
+ /* Multi-threaded round-trip test. */
+ status = transport_rt_test(PJSIP_TRANSPORT_TCP, tcp, url, &pkt_lost);
+ if (status != 0) {
+ pjsip_transport_dec_ref(tcp);
+ return status;
+ }
+
+ if (pkt_lost != 0)
+ PJ_LOG(3,(THIS_FILE, " note: %d packet(s) was lost", pkt_lost));
+
+ /* Check again that reference counter is still 1. */
+ if (pj_atomic_get(tcp->ref_cnt) != 1)
+ return -80;
+
+ /* Destroy this transport. */
+ pjsip_transport_dec_ref(tcp);
+
+ /* Force destroy this transport. */
+ status = pjsip_transport_destroy(tcp);
+ if (status != PJ_SUCCESS)
+ return -90;
+
+ /* Unregister factory */
+ status = pjsip_tpmgr_unregister_tpfactory(pjsip_endpt_get_tpmgr(endpt),
+ tpfactory);
+ if (status != PJ_SUCCESS)
+ return -95;
+
+ /* Flush events. */
+ PJ_LOG(3,(THIS_FILE, " Flushing events, 1 second..."));
+ flush_events(1000);
+
+ /* Done */
+ return 0;
+}
diff --git a/pjsip/src/test-pjsip/transport_test.c b/pjsip/src/test-pjsip/transport_test.c
index 0cd0d225..5aa491a2 100644
--- a/pjsip/src/test-pjsip/transport_test.c
+++ b/pjsip/src/test-pjsip/transport_test.c
@@ -173,7 +173,8 @@ static void send_msg_callback(pjsip_send_state *stateless_data,
/* Test that we receive loopback message. */
int transport_send_recv_test( pjsip_transport_type_e tp_type,
pjsip_transport *ref_tp,
- char *target_url )
+ char *target_url,
+ int *p_usec_rtt)
{
pj_bool_t msg_log_enabled;
pj_status_t status;
@@ -220,6 +221,8 @@ int transport_send_recv_test( pjsip_transport_type_e tp_type,
pj_get_timestamp(&my_send_time);
/* Send the message (statelessly). */
+ PJ_LOG(5,(THIS_FILE, "Sending request to %.*s",
+ (int)target.slen, target.ptr));
status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL,
&send_msg_callback);
if (status != PJ_SUCCESS) {
@@ -228,9 +231,9 @@ int transport_send_recv_test( pjsip_transport_type_e tp_type,
send_status = status;
}
- /* Set the timeout (1 second from now) */
+ /* Set the timeout (2 seconds from now) */
pj_gettimeofday(&timeout);
- timeout.sec += 1;
+ timeout.sec += 2;
/* Loop handling events until we get status */
do {
@@ -268,7 +271,10 @@ int transport_send_recv_test( pjsip_transport_type_e tp_type,
if (status == PJ_SUCCESS) {
unsigned usec_rt;
usec_rt = pj_elapsed_usec(&my_send_time, &my_recv_time);
+
PJ_LOG(3,(THIS_FILE, " round-trip = %d usec", usec_rt));
+
+ *p_usec_rtt = usec_rt;
}
/* Restore message logging. */
@@ -516,7 +522,6 @@ int transport_rt_test( pjsip_transport_type_e tp_type,
unsigned total_sent;
unsigned total_recv;
-
PJ_LOG(3,(THIS_FILE, " multithreaded round-trip test (%d threads)...",
THREADS));
PJ_LOG(3,(THIS_FILE, " this will take approx %d seconds, please wait..",
diff --git a/pjsip/src/test-pjsip/transport_udp_test.c b/pjsip/src/test-pjsip/transport_udp_test.c
index 4db0a7ca..34bb02c4 100644
--- a/pjsip/src/test-pjsip/transport_udp_test.c
+++ b/pjsip/src/test-pjsip/transport_udp_test.c
@@ -29,11 +29,12 @@
*/
int transport_udp_test(void)
{
- enum { SEND_RECV_LOOP = 2 };
+ enum { SEND_RECV_LOOP = 8 };
pjsip_transport *udp_tp, *tp;
pj_sockaddr_in addr, rem_addr;
pj_str_t s;
pj_status_t status;
+ unsigned rtt[SEND_RECV_LOOP], min_rtt;
int i, pkt_lost;
pj_sockaddr_in_init(&addr, NULL, TEST_UDP_PORT);
@@ -79,11 +80,22 @@ int transport_udp_test(void)
pj_sockaddr_in_init(&rem_addr, pj_cstr(&s, "127.0.0.1"), TEST_UDP_PORT);
for (i=0; i<SEND_RECV_LOOP; ++i) {
status = transport_send_recv_test(PJSIP_TRANSPORT_UDP, tp,
- "sip:alice@127.0.0.1:"TEST_UDP_PORT_STR);
+ "sip:alice@127.0.0.1:"TEST_UDP_PORT_STR,
+ &rtt[i]);
if (status != 0)
return status;
}
+ min_rtt = 0xFFFFFFF;
+ for (i=0; i<SEND_RECV_LOOP; ++i)
+ if (rtt[i] < min_rtt) min_rtt = rtt[i];
+
+ report_ival("udp-rtt-usec", min_rtt, "usec",
+ "Best UDP transport round trip time, in microseconds "
+ "(time from sending request until response is received. "
+ "Tests were performed on local machine only)");
+
+
/* Multi-threaded round-trip test. */
status = transport_rt_test(PJSIP_TRANSPORT_UDP, tp,
"sip:alice@127.0.0.1:"TEST_UDP_PORT_STR,
diff --git a/pjsip/src/test-pjsip/tsx_basic_test.c b/pjsip/src/test-pjsip/tsx_basic_test.c
index 6be0ea2a..afe72279 100644
--- a/pjsip/src/test-pjsip/tsx_basic_test.c
+++ b/pjsip/src/test-pjsip/tsx_basic_test.c
@@ -23,6 +23,10 @@
#define THIS_FILE "tsx_basic_test.c"
+static char TARGET_URI[128];
+static char FROM_URI[128];
+
+
/* Test transaction layer. */
static int tsx_layer_test(void)
{
@@ -33,8 +37,8 @@ static int tsx_layer_test(void)
PJ_LOG(3,(THIS_FILE, " transaction layer test"));
- target = pj_str("sip:alice@localhost");
- from = pj_str("sip:bob@localhost");
+ target = pj_str(TARGET_URI);
+ from = pj_str(FROM_URI);
status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &target,
&from, &target, NULL, NULL, -1, NULL,
@@ -77,8 +81,8 @@ static int double_terminate(void)
PJ_LOG(3,(THIS_FILE, " double terminate test"));
- target = pj_str("sip:alice@localhost;transport=loop-dgram");
- from = pj_str("sip:bob@localhost");
+ target = pj_str(TARGET_URI);
+ from = pj_str(FROM_URI);
/* Create request. */
status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &target,
@@ -131,10 +135,15 @@ static int double_terminate(void)
return PJ_SUCCESS;
}
-int tsx_basic_test(void)
+int tsx_basic_test(struct tsx_test_param *param)
{
int status;
+ pj_ansi_sprintf(TARGET_URI, "sip:bob@127.0.0.1:%d;transport=%s",
+ param->port, param->tp_type);
+ pj_ansi_sprintf(FROM_URI, "sip:alice@127.0.0.1:%d;transport=%s",
+ param->port, param->tp_type);
+
status = tsx_layer_test();
if (status != 0)
return status;
diff --git a/pjsip/src/test-pjsip/tsx_bench.c b/pjsip/src/test-pjsip/tsx_bench.c
new file mode 100644
index 00000000..53c950cb
--- /dev/null
+++ b/pjsip/src/test-pjsip/tsx_bench.c
@@ -0,0 +1,273 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjsip.h>
+#include <pjlib.h>
+
+#define THIS_FILE "tsx_uas_test.c"
+
+
+static pjsip_module mod_tsx_user;
+
+static int uac_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed)
+{
+ unsigned i;
+ pjsip_tx_data *request;
+ pjsip_transaction **tsx;
+ pj_timestamp t1, t2, elapsed;
+ pj_status_t status;
+
+ /* Create the request first. */
+ pj_str_t str_target = pj_str("sip:someuser@someprovider.com");
+ pj_str_t str_from = pj_str("\"Local User\" <sip:localuser@serviceprovider.com>");
+ pj_str_t str_to = pj_str("\"Remote User\" <sip:remoteuser@serviceprovider.com>");
+ pj_str_t str_contact = str_from;
+
+ status = pjsip_endpt_create_request(endpt, &pjsip_invite_method,
+ &str_target, &str_from, &str_to,
+ &str_contact, NULL, -1, NULL,
+ &request);
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to create request", status);
+ return status;
+ }
+
+ /* Create transaction array */
+ tsx = pj_pool_zalloc(request->pool, working_set * sizeof(pj_pool_t*));
+
+ pj_memset(&mod_tsx_user, 0, sizeof(mod_tsx_user));
+ mod_tsx_user.id = -1;
+
+ /* Benchmark */
+ elapsed.u64 = 0;
+ pj_get_timestamp(&t1);
+ for (i=0; i<working_set; ++i) {
+ status = pjsip_tsx_create_uac(&mod_tsx_user, request, &tsx[i]);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ }
+ pj_get_timestamp(&t2);
+ pj_sub_timestamp(&t2, &t1);
+ pj_add_timestamp(&elapsed, &t2);
+
+ p_elapsed->u64 = elapsed.u64;
+ status = PJ_SUCCESS;
+
+on_error:
+ for (i=0; i<working_set; ++i) {
+ if (tsx[i]) {
+ pjsip_tsx_terminate(tsx[i], 601);
+ tsx[i] = NULL;
+ }
+ }
+ pjsip_tx_data_dec_ref(request);
+ flush_events(2000);
+ return status;
+}
+
+
+
+static int uas_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed)
+{
+ unsigned i;
+ pjsip_tx_data *request;
+ pjsip_via_hdr *via;
+ pjsip_rx_data rdata;
+ pj_sockaddr_in remote;
+ pjsip_transaction **tsx;
+ pj_timestamp t1, t2, elapsed;
+ char branch_buf[80] = PJSIP_RFC3261_BRANCH_ID "0000000000";
+ pj_status_t status;
+
+ /* Create the request first. */
+ pj_str_t str_target = pj_str("sip:someuser@someprovider.com");
+ pj_str_t str_from = pj_str("\"Local User\" <sip:localuser@serviceprovider.com>");
+ pj_str_t str_to = pj_str("\"Remote User\" <sip:remoteuser@serviceprovider.com>");
+ pj_str_t str_contact = str_from;
+
+ status = pjsip_endpt_create_request(endpt, &pjsip_invite_method,
+ &str_target, &str_from, &str_to,
+ &str_contact, NULL, -1, NULL,
+ &request);
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to create request", status);
+ return status;
+ }
+
+ /* Create Via */
+ via = pjsip_via_hdr_create(request->pool);
+ via->sent_by.host = pj_str("192.168.0.7");
+ via->sent_by.port = 5061;
+ via->transport = pj_str("udp");
+ via->rport_param = 1;
+ via->recvd_param = pj_str("192.168.0.7");
+ pjsip_msg_insert_first_hdr(request->msg, (pjsip_hdr*)via);
+
+
+ /* Create "dummy" rdata from the tdata */
+ pj_memset(&rdata, 0, sizeof(pjsip_rx_data));
+ rdata.tp_info.pool = request->pool;
+ rdata.msg_info.msg = request->msg;
+ rdata.msg_info.from = pjsip_msg_find_hdr(request->msg, PJSIP_H_FROM, NULL);
+ rdata.msg_info.to = pjsip_msg_find_hdr(request->msg, PJSIP_H_TO, NULL);
+ rdata.msg_info.cseq = pjsip_msg_find_hdr(request->msg, PJSIP_H_CSEQ, NULL);
+ rdata.msg_info.cid = pjsip_msg_find_hdr(request->msg, PJSIP_H_FROM, NULL);
+ rdata.msg_info.via = via;
+
+ pj_sockaddr_in_init(&remote, 0, 0);
+ status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_LOOP_DGRAM,
+ &remote, sizeof(pj_sockaddr_in),
+ &rdata.tp_info.transport);
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to get loop transport", status);
+ return status;
+ }
+
+
+ /* Create transaction array */
+ tsx = pj_pool_zalloc(request->pool, working_set * sizeof(pj_pool_t*));
+
+ pj_memset(&mod_tsx_user, 0, sizeof(mod_tsx_user));
+ mod_tsx_user.id = -1;
+
+
+ /* Benchmark */
+ elapsed.u64 = 0;
+ pj_get_timestamp(&t1);
+ for (i=0; i<working_set; ++i) {
+ via->branch_param.ptr = branch_buf;
+ via->branch_param.slen = PJSIP_RFC3261_BRANCH_LEN +
+ pj_ansi_sprintf(branch_buf+PJSIP_RFC3261_BRANCH_LEN,
+ "-%d", i);
+ status = pjsip_tsx_create_uas(&mod_tsx_user, &rdata, &tsx[i]);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ }
+ pj_get_timestamp(&t2);
+ pj_sub_timestamp(&t2, &t1);
+ pj_add_timestamp(&elapsed, &t2);
+
+ p_elapsed->u64 = elapsed.u64;
+ status = PJ_SUCCESS;
+
+on_error:
+ for (i=0; i<working_set; ++i) {
+ if (tsx[i]) {
+ pjsip_tsx_terminate(tsx[i], 601);
+ tsx[i] = NULL;
+ }
+ }
+ pjsip_tx_data_dec_ref(request);
+ flush_events(2000);
+ return status;
+}
+
+
+
+int tsx_bench(void)
+{
+ enum { WORKING_SET=PJSIP_MAX_TSX_COUNT, REPEAT = 4 };
+ unsigned i, speed;
+ pj_timestamp usec[REPEAT], min, freq;
+ char desc[250];
+ int status;
+
+ status = pj_get_timestamp_freq(&freq);
+ if (status != PJ_SUCCESS)
+ return status;
+
+
+ /*
+ * Benchmark UAC
+ */
+ PJ_LOG(3,(THIS_FILE, " benchmarking UAC transaction creation:"));
+ for (i=0; i<REPEAT; ++i) {
+ PJ_LOG(3,(THIS_FILE, " test %d of %d..",
+ i+1, REPEAT));
+ status = uac_tsx_bench(WORKING_SET, &usec[i]);
+ if (status != PJ_SUCCESS)
+ return status;
+ }
+
+ min.u64 = PJ_UINT64(0xFFFFFFFFFFFFFFF);
+ for (i=0; i<REPEAT; ++i) {
+ if (usec[i].u64 < min.u64) min.u64 = usec[i].u64;
+ }
+
+
+ /* Report time */
+ pj_ansi_sprintf(desc, "Time to create %d UAC transactions, in miliseconds",
+ WORKING_SET);
+ report_ival("create-uac-time", (unsigned)(min.u64 * 1000 / freq.u64), "msec", desc);
+
+
+ /* Write speed */
+ speed = (unsigned)(freq.u64 * WORKING_SET / min.u64);
+ PJ_LOG(3,(THIS_FILE, " UAC created at %d tsx/sec", speed));
+
+ pj_ansi_sprintf(desc, "Number of UAC transactions that potentially can be created per second "
+ "with <tt>pjsip_tsx_create_uac()</tt>, based on the time "
+ "to create %d simultaneous transactions above.",
+ WORKING_SET);
+
+ report_ival("create-uac-tsx-per-sec",
+ speed, "tsx/sec", desc);
+
+
+
+ /*
+ * Benchmark UAS
+ */
+ PJ_LOG(3,(THIS_FILE, " benchmarking UAS transaction creation:"));
+ for (i=0; i<REPEAT; ++i) {
+ PJ_LOG(3,(THIS_FILE, " test %d of %d..",
+ i+1, REPEAT));
+ status = uas_tsx_bench(WORKING_SET, &usec[i]);
+ if (status != PJ_SUCCESS)
+ return status;
+ }
+
+ min.u64 = PJ_UINT64(0xFFFFFFFFFFFFFFF);
+ for (i=0; i<REPEAT; ++i) {
+ if (usec[i].u64 < min.u64) min.u64 = usec[i].u64;
+ }
+
+
+ /* Report time */
+ pj_ansi_sprintf(desc, "Time to create %d UAS transactions, in miliseconds",
+ WORKING_SET);
+ report_ival("create-uas-time", (unsigned)(min.u64 * 1000 / freq.u64), "msec", desc);
+
+
+ /* Write speed */
+ speed = (unsigned)(freq.u64 * WORKING_SET / min.u64);
+ PJ_LOG(3,(THIS_FILE, " UAS created at %d tsx/sec", speed));
+
+ pj_ansi_sprintf(desc, "Number of UAS transactions that potentially can be created per second "
+ "with <tt>pjsip_tsx_create_uas()</tt>, based on the time "
+ "to create %d simultaneous transactions above.",
+ WORKING_SET);
+
+ report_ival("create-uas-tsx-per-sec",
+ speed, "tsx/sec", desc);
+
+ return PJ_SUCCESS;
+}
+
diff --git a/pjsip/src/test-pjsip/tsx_uac_test.c b/pjsip/src/test-pjsip/tsx_uac_test.c
index d2717fec..b97b01a9 100644
--- a/pjsip/src/test-pjsip/tsx_uac_test.c
+++ b/pjsip/src/test-pjsip/tsx_uac_test.c
@@ -91,6 +91,10 @@ static char *TEST9_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAC-Test9";
#define TEST4_RETRANSMIT_CNT 3
#define TEST5_RETRANSMIT_CNT 3
+static char TARGET_URI[128];
+static char FROM_URI[128];
+static unsigned tp_flag;
+static struct tsx_test_param *test_param;
static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e);
static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata);
@@ -140,7 +144,12 @@ static pj_bool_t test_complete;
static pjsip_transport *loop;
/* General timer entry to be used by tests. */
-static pj_timer_entry timer;
+static struct my_timer
+{
+ pj_timer_entry entry;
+ char key_buf[1024];
+ pj_str_t tsx_key;
+} timer;
/*
* This is the handler to receive state changed notification from the
@@ -166,6 +175,41 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
tsx->status_code, PJSIP_SC_TSX_TIMEOUT));
test_complete = -710;
}
+
+
+ /* If transport is reliable, then there must not be any
+ * retransmissions.
+ */
+ if (tp_flag & PJSIP_TRANSPORT_RELIABLE) {
+ if (recv_count != 1) {
+ PJ_LOG(3,(THIS_FILE,
+ " error: there were %d (re)transmissions",
+ recv_count));
+ test_complete = -715;
+ }
+ } else {
+ /* Check the number of transmissions, which must be
+ * 6 for INVITE and 10 for non-INVITE
+ */
+ if (tsx->method.id==PJSIP_INVITE_METHOD && recv_count != 7) {
+ PJ_LOG(3,(THIS_FILE,
+ " error: there were %d (re)transmissions",
+ recv_count));
+ test_complete = -716;
+ } else
+ if (tsx->method.id==PJSIP_OPTIONS_METHOD && recv_count != 11) {
+ PJ_LOG(3,(THIS_FILE,
+ " error: there were %d (re)transmissions",
+ recv_count));
+ test_complete = -717;
+ } else
+ if (tsx->method.id!=PJSIP_INVITE_METHOD &&
+ tsx->method.id!=PJSIP_OPTIONS_METHOD)
+ {
+ PJ_LOG(3,(THIS_FILE, " error: unexpected method"));
+ test_complete = -718;
+ }
+ }
}
} else if (pj_strcmp2(&tsx->branch, TEST2_BRANCH_ID)==0) {
@@ -515,7 +559,8 @@ static void send_response_callback( pj_timer_heap_t *timer_heap,
static void terminate_tsx_callback( pj_timer_heap_t *timer_heap,
struct pj_timer_entry *entry)
{
- pjsip_transaction *tsx = entry->user_data;
+ struct my_timer *m = (struct my_timer *)entry;
+ pjsip_transaction *tsx = pjsip_tsx_layer_find_tsx(&m->tsx_key, PJ_FALSE);
int status_code = entry->id;
if (tsx) {
@@ -720,9 +765,9 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata)
if (r->res_addr.transport)
pjsip_transport_add_ref(r->res_addr.transport);
- timer.cb = &send_response_callback;
- timer.user_data = r;
- pjsip_endpt_schedule_timer(endpt, &timer, &delay);
+ timer.entry.cb = &send_response_callback;
+ timer.entry.user_data = r;
+ pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay);
return PJ_TRUE;
@@ -768,11 +813,11 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata)
PJSIP_ROLE_UAC, &pjsip_invite_method,
rdata);
- timer.user_data = pjsip_tsx_layer_find_tsx(&key, PJ_FALSE);
- timer.id = 301;
- timer.cb = &terminate_tsx_callback;
+ pj_strcpy(&timer.tsx_key, &key);
+ timer.entry.id = 301;
+ timer.entry.cb = &terminate_tsx_callback;
- pjsip_endpt_schedule_timer(endpt, &timer, &delay);
+ pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay);
}
if (recv_count > 2) {
@@ -841,9 +886,9 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata)
if (r->res_addr.transport)
pjsip_transport_add_ref(r->res_addr.transport);
- timer.cb = &send_response_callback;
- timer.user_data = r;
- pjsip_endpt_schedule_timer(endpt, &timer, &delay);
+ timer.entry.cb = &send_response_callback;
+ timer.entry.user_data = r;
+ pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay);
} else if (method->id == PJSIP_ACK_METHOD) {
@@ -859,11 +904,11 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata)
PJSIP_ROLE_UAC, &pjsip_invite_method,
rdata);
- timer.user_data = pjsip_tsx_layer_find_tsx(&key, PJ_FALSE);
- timer.id = 302;
- timer.cb = &terminate_tsx_callback;
+ pj_strcpy(&timer.tsx_key, &key);
+ timer.entry.id = 302;
+ timer.entry.cb = &terminate_tsx_callback;
- pjsip_endpt_schedule_timer(endpt, &timer, &delay);
+ pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay);
}
if (recv_count > 2) {
@@ -980,10 +1025,23 @@ static int perform_tsx_test(int dummy, char *target_uri, char *from_uri,
}
pjsip_tx_data_dec_ref(tdata);
return test_complete;
- }
- /* Allow transaction to destroy itself */
- flush_events(500);
+ } else {
+ pj_time_val now;
+
+ /* Allow transaction to destroy itself */
+ flush_events(500);
+
+ /* Wait until test completes */
+ pj_gettimeofday(&now);
+
+ if (PJ_TIME_VAL_LT(now, timeout)) {
+ pj_time_val interval;
+ interval = timeout;
+ PJ_TIME_VAL_SUB(interval, now);
+ flush_events(PJ_TIME_VAL_MSEC(interval));
+ }
+ }
/* Make sure transaction has been destroyed. */
if (pjsip_tsx_layer_find_tsx(&tsx_key, PJ_FALSE) != NULL) {
@@ -1052,8 +1110,7 @@ static int tsx_uac_retransmit_test(void)
pjsip_loop_set_recv_delay(loop, sub_test[i].delay, NULL);
/* Do the test. */
- status = perform_tsx_test(-500, "sip:bob@127.0.0.1;transport=loop-dgram",
- "sip:alice@127.0.0.1;transport=loop-dgram",
+ status = perform_tsx_test(-500, TARGET_URI, FROM_URI,
TEST1_BRANCH_ID,
35, sub_test[i].method);
if (status != 0)
@@ -1093,9 +1150,8 @@ static int tsx_resolve_error_test(void)
PJ_LOG(3,(THIS_FILE, " variant a: immediate resolving error"));
status = perform_tsx_test(-800,
- "sip:bob@unresolved-host;transport=loop-dgram",
- "sip:alice@127.0.0.1;transport=loop-dgram",
- TEST2_BRANCH_ID, 10,
+ "sip:bob@unresolved-host",
+ FROM_URI, TEST2_BRANCH_ID, 10,
&pjsip_options_method);
if (status != 0)
return status;
@@ -1105,20 +1161,22 @@ static int tsx_resolve_error_test(void)
*/
PJ_LOG(3,(THIS_FILE, " variant b: error via callback"));
- /* Set loop transport to return delayed error. */
- pjsip_loop_set_failure(loop, 2, NULL);
- pjsip_loop_set_send_callback_delay(loop, 10, NULL);
+ /* This only applies to "loop-dgram" transport */
+ if (test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) {
+ /* Set loop transport to return delayed error. */
+ pjsip_loop_set_failure(loop, 2, NULL);
+ pjsip_loop_set_send_callback_delay(loop, 10, NULL);
- status = perform_tsx_test(-800, "sip:bob@127.0.0.1;transport=loop-dgram",
- "sip:alice@127.0.0.1;transport=loop-dgram",
- TEST2_BRANCH_ID, 2,
- &pjsip_options_method);
- if (status != 0)
- return status;
+ status = perform_tsx_test(-800, TARGET_URI, FROM_URI,
+ TEST2_BRANCH_ID, 2,
+ &pjsip_options_method);
+ if (status != 0)
+ return status;
- /* Restore loop transport settings. */
- pjsip_loop_set_failure(loop, 0, NULL);
- pjsip_loop_set_send_callback_delay(loop, 0, NULL);
+ /* Restore loop transport settings. */
+ pjsip_loop_set_failure(loop, 0, NULL);
+ pjsip_loop_set_send_callback_delay(loop, 0, NULL);
+ }
return status;
}
@@ -1143,8 +1201,7 @@ static int tsx_terminate_resolving_test(void)
pjsip_loop_set_send_callback_delay(loop, 100, &prev_delay);
/* Start the test. */
- status = perform_tsx_test(-900, "sip:127.0.0.1;transport=loop-dgram",
- "sip:127.0.0.1;transport=loop-dgram",
+ status = perform_tsx_test(-900, TARGET_URI, FROM_URI,
TEST3_BRANCH_ID, 2, &pjsip_options_method);
/* Restore delay. */
@@ -1186,8 +1243,7 @@ static int tsx_retransmit_fail_test(void)
pjsip_loop_set_failure(loop, 0, 0);
/* Start the test. */
- status = perform_tsx_test(-1000, "sip:127.0.0.1;transport=loop-dgram",
- "sip:127.0.0.1;transport=loop-dgram",
+ status = perform_tsx_test(-1000, TARGET_URI, FROM_URI,
TEST4_BRANCH_ID, 6, &pjsip_options_method);
if (status != 0)
@@ -1218,8 +1274,7 @@ static int tsx_terminate_after_retransmit_test(void)
PJ_LOG(3,(THIS_FILE, " test5: terminate after retransmissions"));
/* Do the test. */
- status = perform_tsx_test(-1100, "sip:bob@127.0.0.1;transport=loop-dgram",
- "sip:alice@127.0.0.1;transport=loop-dgram",
+ status = perform_tsx_test(-1100, TARGET_URI, FROM_URI,
TEST5_BRANCH_ID,
6, &pjsip_options_method);
@@ -1249,18 +1304,20 @@ static int perform_generic_test( const char *title,
/* Do the test. */
for (i=0; i<PJ_ARRAY_SIZE(delay); ++i) {
- PJ_LOG(3,(THIS_FILE, " variant %c: with %d ms transport delay",
- ('a'+i), delay[i]));
+ if (test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) {
+ PJ_LOG(3,(THIS_FILE, " variant %c: with %d ms transport delay",
+ ('a'+i), delay[i]));
- pjsip_loop_set_delay(loop, delay[i]);
+ pjsip_loop_set_delay(loop, delay[i]);
+ }
- status = perform_tsx_test(-1200,
- "sip:bob@127.0.0.1;transport=loop-dgram",
- "sip:alice@127.0.0.1;transport=loop-dgram",
- branch_id,
- 10, method);
+ status = perform_tsx_test(-1200, TARGET_URI, FROM_URI,
+ branch_id, 10, method);
if (status != 0)
return status;
+
+ if (test_param->type != PJSIP_TRANSPORT_LOOP_DGRAM)
+ break;
}
pjsip_loop_set_delay(loop, 0);
@@ -1276,11 +1333,23 @@ static int perform_generic_test( const char *title,
**
*****************************************************************************
*/
-int tsx_uac_test(void)
+int tsx_uac_test(struct tsx_test_param *param)
{
pj_sockaddr_in addr;
pj_status_t status;
+ timer.tsx_key.ptr = timer.key_buf;
+
+ test_param = param;
+
+ /* Get transport flag */
+ tp_flag = pjsip_transport_get_flag_from_type(test_param->type);
+
+ pj_ansi_sprintf(TARGET_URI, "sip:bob@127.0.0.1:%d;transport=%s",
+ param->port, param->tp_type);
+ pj_ansi_sprintf(FROM_URI, "sip:alice@127.0.0.1:%d;transport=%s",
+ param->port, param->tp_type);
+
/* Check if loop transport is configured. */
status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_LOOP_DGRAM,
&addr, sizeof(addr), &loop);
@@ -1316,15 +1385,23 @@ int tsx_uac_test(void)
if (status != 0)
return status;
- /* TEST4_BRANCH_ID: Transport failed after several retransmissions */
- status = tsx_retransmit_fail_test();
- if (status != 0)
- return status;
+ /* TEST4_BRANCH_ID: Transport failed after several retransmissions.
+ * Only applies to loop transport.
+ */
+ if (test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) {
+ status = tsx_retransmit_fail_test();
+ if (status != 0)
+ return status;
+ }
- /* TEST5_BRANCH_ID: Terminate transaction after several retransmissions */
- status = tsx_terminate_after_retransmit_test();
- if (status != 0)
- return status;
+ /* TEST5_BRANCH_ID: Terminate transaction after several retransmissions
+ * Only applicable to non-reliable transports.
+ */
+ if ((tp_flag & PJSIP_TRANSPORT_RELIABLE) == 0) {
+ status = tsx_terminate_after_retransmit_test();
+ if (status != 0)
+ return status;
+ }
/* TEST6_BRANCH_ID: Successfull non-invite transaction */
status = perform_generic_test("test6: successfull non-invite transaction",
@@ -1352,8 +1429,21 @@ int tsx_uac_test(void)
if (status != 0)
return status;
-
pjsip_transport_dec_ref(loop);
+ flush_events(500);
+
+ /* Unregister modules. */
+ status = pjsip_endpt_unregister_module(endpt, &tsx_user);
+ if (status != PJ_SUCCESS) {
+ app_perror(" Error: unable to unregister module", status);
+ return -31;
+ }
+ status = pjsip_endpt_unregister_module(endpt, &msg_receiver);
+ if (status != PJ_SUCCESS) {
+ app_perror(" Error: unable to unregister module", status);
+ return -41;
+ }
+
return 0;
}
diff --git a/pjsip/src/test-pjsip/tsx_uas_test.c b/pjsip/src/test-pjsip/tsx_uas_test.c
index 3ff2e18e..42cb075f 100644
--- a/pjsip/src/test-pjsip/tsx_uas_test.c
+++ b/pjsip/src/test-pjsip/tsx_uas_test.c
@@ -35,6 +35,8 @@
** TEST1_BRANCH_ID
** Test that non-INVITE transaction returns 2xx response to the correct
** transport and correctly terminates the transaction.
+ ** This also checks that transaction is destroyed immediately after
+ ** it sends final response when reliable transport is used.
**
** TEST2_BRANCH_ID
** As above, for non-2xx final response.
@@ -53,6 +55,7 @@
**
** TEST6_BRANCH_ID
** As above, in COMPLETED state, with first sending provisional response.
+ ** (Only applicable for non-reliable transports).
**
** TEST7_BRANCH_ID
** INVITE transaction MUST retransmit non-2xx final response.
@@ -65,6 +68,7 @@
** ACK is received. (Note: PJSIP also retransmit 2xx final response
** until it's terminated by user).
** Transaction also MUST terminate in T4 seconds.
+ ** (Only applicable for non-reliable transports).
**
** TEST11_BRANCH_ID
** Test scenario where transport fails before response is sent (i.e.
@@ -128,6 +132,12 @@ static char *TEST13_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAS-Test13";
#define TEST6_TITLE "test6: retransmit last response in COMPLETED state"
+static char TARGET_URI[128];
+static char FROM_URI[128];
+static struct tsx_test_param *test_param;
+static unsigned tp_flag;
+
+
#define TEST_TIMEOUT_ERROR -30
#define MAX_ALLOWED_DIFF 150
@@ -521,7 +531,9 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
/* Check that status code is status_code. */
if (tsx->status_code != TEST6_STATUS_CODE) {
- PJ_LOG(3,(THIS_FILE, " error: incorrect status code"));
+ PJ_LOG(3,(THIS_FILE, " error: incorrect status code %d "
+ "(expecting %d)", tsx->status_code,
+ TEST6_STATUS_CODE));
test_complete = -140;
}
@@ -577,6 +589,26 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
test_complete = -151;
}
+ /* Check the number of retransmissions */
+ if (tp_flag & PJSIP_TRANSPORT_RELIABLE) {
+
+ if (tsx->retransmit_count != 0) {
+ PJ_LOG(3,(THIS_FILE, " error: should not retransmit"));
+ test_complete = -1510;
+ }
+
+ } else {
+
+ if (tsx->retransmit_count != 10) {
+ PJ_LOG(3,(THIS_FILE,
+ " error: incorrect retransmit count %d "
+ "(expecting 10)",
+ tsx->retransmit_count));
+ test_complete = -1510;
+ }
+
+ }
+
} else if (tsx->state == PJSIP_TSX_STATE_COMPLETED) {
/* Check that status code is status_code. */
@@ -814,6 +846,8 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
/* On received request, create UAS. */
pjsip_transaction *tsx;
+ PJ_LOG(4,(THIS_FILE, " received request (probably retransmission)"));
+
status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx);
if (status != PJ_SUCCESS) {
app_perror(" error: unable to create transaction", status);
@@ -830,13 +864,17 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
send_response(rdata, tsx, TEST5_PROVISIONAL_CODE);
} else if (pj_strcmp2(&branch_param, TEST6_BRANCH_ID) == 0) {
+ PJ_LOG(4,(THIS_FILE, " sending provisional response"));
send_response(rdata, tsx, TEST6_PROVISIONAL_CODE);
+ PJ_LOG(4,(THIS_FILE, " sending final response"));
send_response(rdata, tsx, TEST6_STATUS_CODE);
}
} else {
/* Verify the response received. */
+ PJ_LOG(4,(THIS_FILE, " received response number %d", recv_count));
+
++recv_count;
if (pj_strcmp2(&branch_param, TEST4_BRANCH_ID) == 0) {
@@ -869,7 +907,8 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
case 2:
case 3:
if (code != TEST6_STATUS_CODE) {
- PJ_LOG(3,(THIS_FILE, " error: invalid code!"));
+ PJ_LOG(3,(THIS_FILE, " error: invalid code %d "
+ "(expecting %d)", code, TEST6_STATUS_CODE));
test_complete = -136;
}
break;
@@ -1183,6 +1222,8 @@ static int perform_test( char *target_uri, char *from_uri,
pjsip_tx_data_add_ref(tdata);
/* (Re)Send the request. */
+ PJ_LOG(4,(THIS_FILE, " (re)sending request %d", sent_cnt));
+
status = pjsip_endpt_send_request_stateless(endpt, tdata, 0, 0);
if (status != PJ_SUCCESS) {
app_perror(" Error: unable to send request", status);
@@ -1255,25 +1296,25 @@ static int perform_test( char *target_uri, char *from_uri,
*/
static int tsx_basic_final_response_test(void)
{
+ unsigned duration;
int status;
PJ_LOG(3,(THIS_FILE," test1: basic sending 2xx final response"));
- status = perform_test("sip:129.0.0.1;transport=loop-dgram",
- "sip:129.0.0.1;transport=loop-dgram",
- TEST1_BRANCH_ID,
- 33, /* Test duration must be greater than 32 secs */
- &pjsip_options_method, 1, 0, 0);
+ /* Test duration must be greater than 32 secs if unreliable transport
+ * is used.
+ */
+ duration = (tp_flag & PJSIP_TRANSPORT_RELIABLE) ? 1 : 33;
+
+ status = perform_test(TARGET_URI, FROM_URI, TEST1_BRANCH_ID,
+ duration, &pjsip_options_method, 1, 0, 0);
if (status != 0)
return status;
PJ_LOG(3,(THIS_FILE," test2: basic sending non-2xx final response"));
- status = perform_test("sip:129.0.0.1;transport=loop-dgram",
- "sip:129.0.0.1;transport=loop-dgram",
- TEST2_BRANCH_ID,
- 33, /* Test duration must be greater than 32 secs */
- &pjsip_options_method, 1, 0, 0);
+ status = perform_test(TARGET_URI, FROM_URI, TEST2_BRANCH_ID,
+ duration, &pjsip_options_method, 1, 0, 0);
if (status != 0)
return status;
@@ -1289,14 +1330,15 @@ static int tsx_basic_final_response_test(void)
*/
static int tsx_basic_provisional_response_test(void)
{
+ unsigned duration;
int status;
PJ_LOG(3,(THIS_FILE," test3: basic sending 2xx final response"));
- status = perform_test("sip:129.0.0.1;transport=loop-dgram",
- "sip:129.0.0.1;transport=loop-dgram",
- TEST3_BRANCH_ID,
- 35,
+ duration = (tp_flag & PJSIP_TRANSPORT_RELIABLE) ? 1 : 33;
+ duration += 2;
+
+ status = perform_test(TARGET_URI, FROM_URI, TEST3_BRANCH_ID, duration,
&pjsip_options_method, 1, 0, 0);
return status;
@@ -1320,10 +1362,7 @@ static int tsx_retransmit_last_response_test(const char *title,
PJ_LOG(3,(THIS_FILE," %s", title));
- status = perform_test("sip:129.0.0.1;transport=loop-dgram",
- "sip:129.0.0.1;transport=loop-dgram",
- branch_id,
- 5,
+ status = perform_test(TARGET_URI, FROM_URI, branch_id, 5,
&pjsip_options_method,
request_cnt, 1000, 1);
if (status && status != TEST_TIMEOUT_ERROR)
@@ -1357,9 +1396,7 @@ static int tsx_final_response_retransmission_test(void)
PJ_LOG(3,(THIS_FILE,
" test7: INVITE non-2xx final response retransmission"));
- status = perform_test("sip:129.0.0.1;transport=loop-dgram",
- "sip:129.0.0.1;transport=loop-dgram",
- TEST7_BRANCH_ID,
+ status = perform_test(TARGET_URI, FROM_URI, TEST7_BRANCH_ID,
33, /* Test duration must be greater than 32 secs */
&pjsip_invite_method, 1, 0, 0);
if (status != 0)
@@ -1368,9 +1405,7 @@ static int tsx_final_response_retransmission_test(void)
PJ_LOG(3,(THIS_FILE,
" test8: INVITE 2xx final response retransmission"));
- status = perform_test("sip:129.0.0.1;transport=loop-dgram",
- "sip:129.0.0.1;transport=loop-dgram",
- TEST8_BRANCH_ID,
+ status = perform_test(TARGET_URI, FROM_URI, TEST8_BRANCH_ID,
33, /* Test duration must be greater than 32 secs */
&pjsip_invite_method, 1, 0, 0);
if (status != 0)
@@ -1394,9 +1429,7 @@ static int tsx_ack_test(void)
PJ_LOG(3,(THIS_FILE,
" test9: receiving ACK for non-2xx final response"));
- status = perform_test("sip:129.0.0.1;transport=loop-dgram",
- "sip:129.0.0.1;transport=loop-dgram",
- TEST9_BRANCH_ID,
+ status = perform_test(TARGET_URI, FROM_URI, TEST9_BRANCH_ID,
20, /* allow 5 retransmissions */
&pjsip_invite_method, 1, 0, 0);
if (status != 0)
@@ -1443,11 +1476,8 @@ static int tsx_transport_failure_test(void)
pjsip_loop_set_failure(loop, 0, NULL);
pjsip_loop_set_delay(loop, tests[i].transport_delay);
- status = perform_test("sip:129.0.0.1;transport=loop-dgram",
- "sip:129.0.0.1;transport=loop-dgram",
- tests[i].branch_id,
- 0,
- &pjsip_invite_method, 1, 0, 1);
+ status = perform_test(TARGET_URI, FROM_URI, tests[i].branch_id,
+ 0, &pjsip_invite_method, 1, 0, 1);
if (status && status != TEST_TIMEOUT_ERROR)
return status;
if (!status) {
@@ -1494,19 +1524,26 @@ static int tsx_transport_failure_test(void)
**
*****************************************************************************
*/
-int tsx_uas_test(void)
+int tsx_uas_test(struct tsx_test_param *param)
{
pj_sockaddr_in addr;
pj_status_t status;
+ test_param = param;
+ tp_flag = pjsip_transport_get_flag_from_type(param->type);
+
+ pj_ansi_sprintf(TARGET_URI, "sip:bob@127.0.0.1:%d;transport=%s",
+ param->port, param->tp_type);
+ pj_ansi_sprintf(FROM_URI, "sip:alice@127.0.0.1:%d;transport=%s",
+ param->port, param->tp_type);
+
/* Check if loop transport is configured. */
status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_LOOP_DGRAM,
&addr, sizeof(addr), &loop);
if (status != PJ_SUCCESS) {
PJ_LOG(3,(THIS_FILE, " Error: loop transport is not configured!"));
- return -1;
+ return -10;
}
-
/* Register modules. */
status = pjsip_endpt_register_module(endpt, &tsx_user);
if (status != PJ_SUCCESS) {
@@ -1550,42 +1587,67 @@ int tsx_uas_test(void)
if (status != 0)
return status;
- /* TEST6_BRANCH_ID: retransmit last response in PROCEEDING state
+ /* TEST6_BRANCH_ID: retransmit last response in COMPLETED state
+ * This only applies to non-reliable transports,
+ * since UAS transaction is destroyed as soon
+ * as final response is sent for reliable transports.
*/
- status = tsx_retransmit_last_response_test(TEST6_TITLE,
- TEST6_BRANCH_ID,
- TEST6_REQUEST_COUNT,
- TEST6_STATUS_CODE);
- if (status != 0)
- return status;
+ if ((tp_flag & PJSIP_TRANSPORT_RELIABLE) == 0) {
+ status = tsx_retransmit_last_response_test(TEST6_TITLE,
+ TEST6_BRANCH_ID,
+ TEST6_REQUEST_COUNT,
+ TEST6_STATUS_CODE);
+ if (status != 0)
+ return status;
+ }
/* TEST7_BRANCH_ID: INVITE non-2xx final response retransmission test
* TEST8_BRANCH_ID: INVITE 2xx final response retransmission test
*/
-
status = tsx_final_response_retransmission_test();
if (status != 0)
return status;
/* TEST9_BRANCH_ID: retransmission of non-2xx INVITE final response must
* cease when ACK is received
+ * Only applicable for non-reliable transports.
*/
- status = tsx_ack_test();
- if (status != 0)
- return status;
+ if ((tp_flag & PJSIP_TRANSPORT_RELIABLE) == 0) {
+ status = tsx_ack_test();
+ if (status != 0)
+ return status;
+ }
+
/* TEST10_BRANCH_ID: test transport failure in TRYING state.
* TEST11_BRANCH_ID: test transport failure in PROCEEDING state.
* TEST12_BRANCH_ID: test transport failure in CONNECTED state.
* TEST13_BRANCH_ID: test transport failure in CONFIRMED state.
*/
- status = tsx_transport_failure_test();
- if (status != 0)
- return status;
+ /* Only valid for loop-dgram */
+ if (param->type == PJSIP_TRANSPORT_LOOP_DGRAM) {
+ status = tsx_transport_failure_test();
+ if (status != 0)
+ return status;
+ }
- pjsip_transport_dec_ref(loop);
- return 0;
+ /* Register modules. */
+ status = pjsip_endpt_unregister_module(endpt, &tsx_user);
+ if (status != PJ_SUCCESS) {
+ app_perror(" Error: unable to unregister module", status);
+ return -8;
+ }
+ status = pjsip_endpt_unregister_module(endpt, &msg_sender);
+ if (status != PJ_SUCCESS) {
+ app_perror(" Error: unable to unregister module", status);
+ return -9;
+ }
+
+ if (loop)
+ pjsip_transport_dec_ref(loop);
+
+ return 0;
}
diff --git a/pjsip/src/test-pjsip/txdata_test.c b/pjsip/src/test-pjsip/txdata_test.c
index d39fad42..a7f0fb0b 100644
--- a/pjsip/src/test-pjsip/txdata_test.c
+++ b/pjsip/src/test-pjsip/txdata_test.c
@@ -21,11 +21,19 @@
#include <pjsip.h>
#include <pjlib.h>
-#define HFIND(msg,h,H) ((pjsip_##h##_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_##H, NULL))
#define THIS_FILE "txdata_test.c"
+#define HFIND(msg,h,H) ((pjsip_##h##_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_##H, NULL))
+
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+# define LOOP 10000
+#else
+# define LOOP 100000
+#endif
+
+
/*
* This tests various core message creation functions.
*/
@@ -323,6 +331,77 @@ static int core_txdata_test(void)
return 0;
}
+
+
+/*
+ * This test demonstrate the bug as reported in:
+ * http://bugzilla.pjproject.net/show_bug.cgi?id=49
+ */
+static int gcc_test()
+{
+ char msgbuf[512];
+ pj_str_t target = pj_str("sip:alice@wonderland:5061;x-param=param%201"
+ "?X-Hdr-1=Header%201"
+ "&X-Empty-Hdr=");
+ pjsip_tx_data *tdata;
+ pjsip_parser_err_report err_list;
+ pjsip_msg *msg;
+ int len;
+ pj_status_t status;
+
+ PJ_LOG(3,(THIS_FILE, " header param in URI to create request"));
+
+ /* Create request with header param in target URI. */
+ status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &target,
+ &target, &target, &target, NULL, -1,
+ NULL, &tdata);
+ if (status != 0) {
+ app_perror(" error: Unable to create request", status);
+ return -200;
+ }
+
+ /* Print and parse the request.
+ * We'll check that header params are not present in
+ */
+ len = pjsip_msg_print(tdata->msg, msgbuf, sizeof(msgbuf));
+ if (len < 1) {
+ PJ_LOG(3,(THIS_FILE, " error: printing message"));
+ pjsip_tx_data_dec_ref(tdata);
+ return -250;
+ }
+ msgbuf[len] = '\0';
+
+ PJ_LOG(5,(THIS_FILE, "%d bytes request created:--begin-msg--\n"
+ "%s\n"
+ "--end-msg--", len, msgbuf));
+
+ /* Now parse the message. */
+ pj_list_init(&err_list);
+ msg = pjsip_parse_msg( tdata->pool, msgbuf, len, &err_list);
+ if (msg == NULL) {
+ pjsip_parser_err_report *e;
+
+ PJ_LOG(3,(THIS_FILE, " error: parsing message message"));
+
+ e = err_list.next;
+ while (e != &err_list) {
+ PJ_LOG(3,(THIS_FILE, " %s in line %d col %d hname=%.*s",
+ pj_exception_id_name(e->except_code),
+ e->line, e->col+1,
+ (int)e->hname.slen,
+ e->hname.ptr));
+ e = e->next;
+ }
+
+ pjsip_tx_data_dec_ref(tdata);
+ return -255;
+ }
+
+ pjsip_tx_data_dec_ref(tdata);
+ return 0;
+}
+
+
/* This tests the request creating functions against the following
* requirements:
* - header params in URI creates header in the request.
@@ -345,6 +424,8 @@ static int txdata_test_uri_params(void)
pjsip_tx_data *tdata;
pjsip_sip_uri *uri;
pjsip_param *param;
+ pjsip_via_hdr *via;
+ pjsip_parser_err_report err_list;
pjsip_msg *msg;
int len;
pj_status_t status;
@@ -360,6 +441,11 @@ static int txdata_test_uri_params(void)
return -200;
}
+ /* Fill up the Via header to prevent syntax error on parsing */
+ via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
+ via->transport = pj_str("TCP");
+ via->sent_by.host = pj_str("127.0.0.1");
+
/* Print and parse the request.
* We'll check that header params are not present in
*/
@@ -376,11 +462,25 @@ static int txdata_test_uri_params(void)
"--end-msg--", len, msgbuf));
/* Now parse the message. */
- msg = pjsip_parse_msg( tdata->pool, msgbuf, len, NULL);
+ pj_list_init(&err_list);
+ msg = pjsip_parse_msg( tdata->pool, msgbuf, len, &err_list);
if (msg == NULL) {
- app_perror(" error: parsing message message", status);
+ pjsip_parser_err_report *e;
+
+ PJ_LOG(3,(THIS_FILE, " error: parsing message message"));
+
+ e = err_list.next;
+ while (e != &err_list) {
+ PJ_LOG(3,(THIS_FILE, " %s in line %d col %d hname=%.*s",
+ pj_exception_id_name(e->except_code),
+ e->line, e->col+1,
+ (int)e->hname.slen,
+ e->hname.ptr));
+ e = e->next;
+ }
+
pjsip_tx_data_dec_ref(tdata);
- return -250;
+ return -256;
}
/* Check the existence of port, other_param, and header param.
@@ -512,18 +612,233 @@ static int txdata_test_uri_params(void)
return 0;
}
+
+/*
+ * create request benchmark
+ */
+static int create_request_bench(pj_timestamp *p_elapsed)
+{
+ enum { COUNT = 100 };
+ unsigned i, j;
+ pjsip_tx_data *tdata[COUNT];
+ pj_timestamp t1, t2, elapsed;
+ pj_status_t status;
+
+ pj_str_t str_target = pj_str("sip:someuser@someprovider.com");
+ pj_str_t str_from = pj_str("\"Local User\" <sip:localuser@serviceprovider.com>");
+ pj_str_t str_to = pj_str("\"Remote User\" <sip:remoteuser@serviceprovider.com>");
+ pj_str_t str_contact = str_from;
+
+ elapsed.u64 = 0;
+
+ for (i=0; i<LOOP; i+=COUNT) {
+ pj_memset(tdata, 0, sizeof(tdata));
+
+ pj_get_timestamp(&t1);
+
+ for (j=0; j<COUNT; ++j) {
+ status = pjsip_endpt_create_request(endpt, &pjsip_invite_method,
+ &str_target, &str_from, &str_to,
+ &str_contact, NULL, -1, NULL,
+ &tdata[j]);
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to create request", status);
+ goto on_error;
+ }
+ }
+
+ pj_get_timestamp(&t2);
+ pj_sub_timestamp(&t2, &t1);
+ pj_add_timestamp(&elapsed, &t2);
+
+ for (j=0; j<COUNT; ++j)
+ pjsip_tx_data_dec_ref(tdata[j]);
+ }
+
+ p_elapsed->u64 = elapsed.u64;
+ return PJ_SUCCESS;
+
+on_error:
+ for (i=0; i<COUNT; ++i) {
+ if (tdata[i])
+ pjsip_tx_data_dec_ref(tdata[i]);
+ }
+ return -400;
+}
+
+
+
+/*
+ * create response benchmark
+ */
+static int create_response_bench(pj_timestamp *p_elapsed)
+{
+ enum { COUNT = 100 };
+ unsigned i, j;
+ pjsip_via_hdr *via;
+ pjsip_rx_data rdata;
+ pjsip_tx_data *request;
+ pjsip_tx_data *tdata[COUNT];
+ pj_timestamp t1, t2, elapsed;
+ pj_status_t status;
+
+ /* Create the request first. */
+ pj_str_t str_target = pj_str("sip:someuser@someprovider.com");
+ pj_str_t str_from = pj_str("\"Local User\" <sip:localuser@serviceprovider.com>");
+ pj_str_t str_to = pj_str("\"Remote User\" <sip:remoteuser@serviceprovider.com>");
+ pj_str_t str_contact = str_from;
+
+ status = pjsip_endpt_create_request(endpt, &pjsip_invite_method,
+ &str_target, &str_from, &str_to,
+ &str_contact, NULL, -1, NULL,
+ &request);
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to create request", status);
+ return status;
+ }
+
+ /* Create several Via headers */
+ via = pjsip_via_hdr_create(request->pool);
+ via->sent_by.host = pj_str("192.168.0.7");
+ via->sent_by.port = 5061;
+ via->transport = pj_str("udp");
+ via->rport_param = 0;
+ via->branch_param = pj_str("012345678901234567890123456789");
+ via->recvd_param = pj_str("192.168.0.7");
+ pjsip_msg_insert_first_hdr(request->msg, pjsip_hdr_clone(request->pool, via));
+ pjsip_msg_insert_first_hdr(request->msg, pjsip_hdr_clone(request->pool, via));
+ pjsip_msg_insert_first_hdr(request->msg, (pjsip_hdr*)via);
+
+
+ /* Create "dummy" rdata from the tdata */
+ pj_memset(&rdata, 0, sizeof(pjsip_rx_data));
+ rdata.tp_info.pool = request->pool;
+ rdata.msg_info.msg = request->msg;
+ rdata.msg_info.from = pjsip_msg_find_hdr(request->msg, PJSIP_H_FROM, NULL);
+ rdata.msg_info.to = pjsip_msg_find_hdr(request->msg, PJSIP_H_TO, NULL);
+ rdata.msg_info.cseq = pjsip_msg_find_hdr(request->msg, PJSIP_H_CSEQ, NULL);
+ rdata.msg_info.cid = pjsip_msg_find_hdr(request->msg, PJSIP_H_FROM, NULL);
+ rdata.msg_info.via = via;
+
+ /*
+ * Now benchmark create_response
+ */
+ elapsed.u64 = 0;
+
+ for (i=0; i<LOOP; i+=COUNT) {
+ pj_memset(tdata, 0, sizeof(tdata));
+
+ pj_get_timestamp(&t1);
+
+ for (j=0; j<COUNT; ++j) {
+ status = pjsip_endpt_create_response(endpt, &rdata, 200, NULL, &tdata[j]);
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to create request", status);
+ goto on_error;
+ }
+ }
+
+ pj_get_timestamp(&t2);
+ pj_sub_timestamp(&t2, &t1);
+ pj_add_timestamp(&elapsed, &t2);
+
+ for (j=0; j<COUNT; ++j)
+ pjsip_tx_data_dec_ref(tdata[j]);
+ }
+
+ p_elapsed->u64 = elapsed.u64;
+ pjsip_tx_data_dec_ref(request);
+ return PJ_SUCCESS;
+
+on_error:
+ for (i=0; i<COUNT; ++i) {
+ if (tdata[i])
+ pjsip_tx_data_dec_ref(tdata[i]);
+ }
+ return -400;
+}
+
+
int txdata_test(void)
{
+ enum { REPEAT = 4 };
+ unsigned i, msgs;
+ pj_timestamp usec[REPEAT], min, freq;
int status;
+ status = pj_get_timestamp_freq(&freq);
+ if (status != PJ_SUCCESS)
+ return status;
+
status = core_txdata_test();
if (status != 0)
return status;
+#if INCLUDE_GCC_TEST
+ status = gcc_test();
+ if (status != 0)
+ return status;
+#endif
status = txdata_test_uri_params();
if (status != 0)
return status;
+
+ /*
+ * Benchmark create_request()
+ */
+ PJ_LOG(3,(THIS_FILE, " benchmarking request creation:"));
+ for (i=0; i<REPEAT; ++i) {
+ PJ_LOG(3,(THIS_FILE, " test %d of %d..",
+ i+1, REPEAT));
+ status = create_request_bench(&usec[i]);
+ if (status != PJ_SUCCESS)
+ return status;
+ }
+
+ min.u64 = PJ_UINT64(0xFFFFFFFFFFFFFFF);
+ for (i=0; i<REPEAT; ++i) {
+ if (usec[i].u64 < min.u64) min.u64 = usec[i].u64;
+ }
+
+ msgs = (unsigned)(freq.u64 * LOOP / min.u64);
+
+ PJ_LOG(3,(THIS_FILE, " Requests created at %d requests/sec", msgs));
+
+ report_ival("create-request-per-sec",
+ msgs, "msg/sec",
+ "Number of typical request messages that can be created "
+ "per second with <tt>pjsip_endpt_create_request()</tt>");
+
+
+ /*
+ * Benchmark create_response()
+ */
+ PJ_LOG(3,(THIS_FILE, " benchmarking response creation:"));
+ for (i=0; i<REPEAT; ++i) {
+ PJ_LOG(3,(THIS_FILE, " test %d of %d..",
+ i+1, REPEAT));
+ status = create_response_bench(&usec[i]);
+ if (status != PJ_SUCCESS)
+ return status;
+ }
+
+ min.u64 = PJ_UINT64(0xFFFFFFFFFFFFFFF);
+ for (i=0; i<REPEAT; ++i) {
+ if (usec[i].u64 < min.u64) min.u64 = usec[i].u64;
+ }
+
+ msgs = (unsigned)(freq.u64 * LOOP / min.u64);
+
+ PJ_LOG(3,(THIS_FILE, " Responses created at %d responses/sec", msgs));
+
+ report_ival("create-response-per-sec",
+ msgs, "msg/sec",
+ "Number of typical response messages that can be created "
+ "per second with <tt>pjsip_endpt_create_response()</tt>");
+
+
return 0;
}
+
diff --git a/pjsip/src/test-pjsip/uri_test.c b/pjsip/src/test-pjsip/uri_test.c
index a221f4ca..916a30f4 100644
--- a/pjsip/src/test-pjsip/uri_test.c
+++ b/pjsip/src/test-pjsip/uri_test.c
@@ -32,12 +32,19 @@
#define PARAM_CHAR ALPHANUM MARK "[]/:&+$"
#define POOL_SIZE 8000
-#define LOOP_COUNT 10000
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+# define LOOP_COUNT 10000
+#else
+# define LOOP_COUNT 40000
+#endif
#define AVERAGE_URL_LEN 80
#define THREAD_COUNT 4
-static pj_highprec_t parse_len, print_len, cmp_len;
-static pj_timestamp parse_time, print_time, cmp_time;
+static struct
+{
+ pj_highprec_t parse_len, print_len, cmp_len;
+ pj_timestamp parse_time, print_time, cmp_time;
+} var;
/* URI creator functions. */
@@ -686,7 +693,7 @@ static pj_status_t do_uri_test(pj_pool_t *pool, struct uri_test *entry)
/* Parse URI text. */
pj_get_timestamp(&t1);
- parse_len = parse_len + entry->len;
+ var.parse_len = var.parse_len + entry->len;
parsed_uri = pjsip_parse_uri(pool, entry->str, entry->len, 0);
if (!parsed_uri) {
/* Parsing failed. If the entry says that this is expected, then
@@ -702,7 +709,7 @@ static pj_status_t do_uri_test(pj_pool_t *pool, struct uri_test *entry)
}
pj_get_timestamp(&t2);
pj_sub_timestamp(&t2, &t1);
- pj_add_timestamp(&parse_time, &t2);
+ pj_add_timestamp(&var.parse_time, &t2);
/* Create the reference URI. */
ref_uri = entry->creator(pool);
@@ -720,10 +727,10 @@ static pj_status_t do_uri_test(pj_pool_t *pool, struct uri_test *entry)
s1.ptr[len] = '\0';
s1.slen = len;
- print_len = print_len + len;
+ var.print_len = var.print_len + len;
pj_get_timestamp(&t2);
pj_sub_timestamp(&t2, &t1);
- pj_add_timestamp(&print_time, &t2);
+ pj_add_timestamp(&var.print_time, &t2);
len = pjsip_uri_print( PJSIP_URI_IN_OTHER, ref_uri, s2.ptr, PJSIP_MAX_URL_SIZE);
if (len < 1) {
@@ -755,10 +762,10 @@ static pj_status_t do_uri_test(pj_pool_t *pool, struct uri_test *entry)
}
}
- cmp_len = cmp_len + len;
+ var.cmp_len = var.cmp_len + len;
pj_get_timestamp(&t2);
pj_sub_timestamp(&t2, &t1);
- pj_add_timestamp(&cmp_time, &t2);
+ pj_add_timestamp(&var.cmp_time, &t2);
/* Compare text. */
if (entry->printed) {
@@ -785,16 +792,12 @@ on_return:
return status;
}
-int uri_test()
+
+static int simple_uri_test(void)
{
- unsigned i, loop;
+ unsigned i;
pj_pool_t *pool;
pj_status_t status;
- pj_timestamp zero;
- pj_time_val elapsed;
- pj_highprec_t avg_parse, avg_print, avg_cmp, kbytes;
-
- zero.u32.hi = zero.u32.lo = 0;
PJ_LOG(3,(THIS_FILE, " simple test"));
pool = pjsip_endpt_create_pool(endpt, "", POOL_SIZE, POOL_SIZE);
@@ -803,16 +806,31 @@ int uri_test()
if (status != PJ_SUCCESS) {
PJ_LOG(3,(THIS_FILE, " error %d when testing entry %d",
status, i));
- goto on_return;
+ return status;
}
}
pjsip_endpt_release_pool(endpt, pool);
- PJ_LOG(3,(THIS_FILE, " benchmarking..."));
- parse_len = print_len = cmp_len = 0;
- parse_time.u32.hi = parse_time.u32.lo = 0;
- print_time.u32.hi = print_time.u32.lo = 0;
- cmp_time.u32.hi = cmp_time.u32.lo = 0;
+ return 0;
+}
+
+static int uri_benchmark(unsigned *p_parse, unsigned *p_print, unsigned *p_cmp)
+{
+ unsigned i, loop;
+ pj_pool_t *pool;
+ pj_status_t status;
+ pj_timestamp zero;
+ pj_time_val elapsed;
+ pj_highprec_t avg_parse, avg_print, avg_cmp, kbytes;
+
+ pj_memset(&var, 0, sizeof(var));
+
+ zero.u32.hi = zero.u32.lo = 0;
+
+ var.parse_len = var.print_len = var.cmp_len = 0;
+ var.parse_time.u32.hi = var.parse_time.u32.lo = 0;
+ var.print_time.u32.hi = var.print_time.u32.lo = 0;
+ var.cmp_time.u32.hi = var.cmp_time.u32.lo = 0;
for (loop=0; loop<LOOP_COUNT; ++loop) {
pool = pjsip_endpt_create_pool(endpt, "", POOL_SIZE, POOL_SIZE);
for (i=0; i<PJ_ARRAY_SIZE(uri_test_array); ++i) {
@@ -827,55 +845,144 @@ int uri_test()
pjsip_endpt_release_pool(endpt, pool);
}
- kbytes = parse_len;
+ kbytes = var.parse_len;
pj_highprec_mod(kbytes, 1000000);
pj_highprec_div(kbytes, 100000);
- elapsed = pj_elapsed_time(&zero, &parse_time);
- avg_parse = pj_elapsed_usec(&zero, &parse_time);
+ elapsed = pj_elapsed_time(&zero, &var.parse_time);
+ avg_parse = pj_elapsed_usec(&zero, &var.parse_time);
pj_highprec_mul(avg_parse, AVERAGE_URL_LEN);
- pj_highprec_div(avg_parse, parse_len);
+ pj_highprec_div(avg_parse, var.parse_len);
avg_parse = 1000000 / avg_parse;
PJ_LOG(3,(THIS_FILE,
" %u.%u MB of urls parsed in %d.%03ds (avg=%d urls/sec)",
- (unsigned)(parse_len/1000000), (unsigned)kbytes,
+ (unsigned)(var.parse_len/1000000), (unsigned)kbytes,
elapsed.sec, elapsed.msec,
(unsigned)avg_parse));
- kbytes = print_len;
+ *p_parse = (unsigned)avg_parse;
+
+ kbytes = var.print_len;
pj_highprec_mod(kbytes, 1000000);
pj_highprec_div(kbytes, 100000);
- elapsed = pj_elapsed_time(&zero, &print_time);
- avg_print = pj_elapsed_usec(&zero, &print_time);
+ elapsed = pj_elapsed_time(&zero, &var.print_time);
+ avg_print = pj_elapsed_usec(&zero, &var.print_time);
pj_highprec_mul(avg_print, AVERAGE_URL_LEN);
- pj_highprec_div(avg_print, parse_len);
+ pj_highprec_div(avg_print, var.parse_len);
avg_print = 1000000 / avg_print;
PJ_LOG(3,(THIS_FILE,
" %u.%u MB of urls printed in %d.%03ds (avg=%d urls/sec)",
- (unsigned)(print_len/1000000), (unsigned)kbytes,
+ (unsigned)(var.print_len/1000000), (unsigned)kbytes,
elapsed.sec, elapsed.msec,
(unsigned)avg_print));
- kbytes = cmp_len;
+ *p_print = (unsigned)avg_print;
+
+ kbytes = var.cmp_len;
pj_highprec_mod(kbytes, 1000000);
pj_highprec_div(kbytes, 100000);
- elapsed = pj_elapsed_time(&zero, &cmp_time);
- avg_cmp = pj_elapsed_usec(&zero, &cmp_time);
+ elapsed = pj_elapsed_time(&zero, &var.cmp_time);
+ avg_cmp = pj_elapsed_usec(&zero, &var.cmp_time);
pj_highprec_mul(avg_cmp, AVERAGE_URL_LEN);
- pj_highprec_div(avg_cmp, cmp_len);
+ pj_highprec_div(avg_cmp, var.cmp_len);
avg_cmp = 1000000 / avg_cmp;
PJ_LOG(3,(THIS_FILE,
" %u.%u MB of urls compared in %d.%03ds (avg=%d urls/sec)",
- (unsigned)(cmp_len/1000000), (unsigned)kbytes,
+ (unsigned)(var.cmp_len/1000000), (unsigned)kbytes,
elapsed.sec, elapsed.msec,
(unsigned)avg_cmp));
- PJ_LOG(3,(THIS_FILE, " multithreaded test"));
-
+ *p_cmp = (unsigned)avg_cmp;
on_return:
return status;
}
+
+/*****************************************************************************/
+
+int uri_test(void)
+{
+ enum { COUNT = 4, DETECT=0, PARSE=1, PRINT=2 };
+ struct {
+ unsigned parse;
+ unsigned print;
+ unsigned cmp;
+ } run[COUNT];
+ unsigned i, max, avg_len;
+ char desc[200];
+ pj_status_t status;
+
+ status = simple_uri_test();
+ if (status != PJ_SUCCESS)
+ return status;
+
+ for (i=0; i<COUNT; ++i) {
+ PJ_LOG(3,(THIS_FILE, " benchmarking (%d of %d)...", i+1, COUNT));
+ status = uri_benchmark(&run[i].parse, &run[i].print, &run[i].cmp);
+ if (status != PJ_SUCCESS)
+ return status;
+ }
+
+ /* Calculate average URI length */
+ for (i=0, avg_len=0; i<PJ_ARRAY_SIZE(uri_test_array); ++i) {
+ avg_len += uri_test_array[i].len;
+ }
+ avg_len /= PJ_ARRAY_SIZE(uri_test_array);
+
+
+ /*
+ * Print maximum parse/sec
+ */
+ for (i=0, max=0; i<COUNT; ++i)
+ if (run[i].parse > max) max = run[i].parse;
+
+ PJ_LOG(3,("", " Maximum URI parse/sec=%u", max));
+
+ pj_ansi_sprintf(desc, "Number of SIP/TEL URIs that can be <B>parsed</B> with "
+ "<tt>pjsip_parse_uri()</tt> per second "
+ "(tested with %d URI set, with average length of "
+ "%d chars)",
+ PJ_ARRAY_SIZE(uri_test_array), avg_len);
+
+ report_ival("uri-parse-per-sec", max, "URI/sec", desc);
+
+ /* URI parsing bandwidth */
+ report_ival("uri-parse-bandwidth-mb", avg_len*max/1000000, "MB/sec",
+ "URI parsing bandwidth in megabytes (number of megabytes "
+ "worth of URI that can be parsed per second)");
+
+
+ /* Print maximum print/sec */
+ for (i=0, max=0; i<COUNT; ++i)
+ if (run[i].print > max) max = run[i].print;
+
+ PJ_LOG(3,("", " Maximum URI print/sec=%u", max));
+
+ pj_ansi_sprintf(desc, "Number of SIP/TEL URIs that can be <B>printed</B> with "
+ "<tt>pjsip_uri_print()</tt> per second "
+ "(tested with %d URI set, with average length of "
+ "%d chars)",
+ PJ_ARRAY_SIZE(uri_test_array), avg_len);
+
+ report_ival("uri-print-per-sec", max, "URI/sec", desc);
+
+ /* Print maximum detect/sec */
+ for (i=0, max=0; i<COUNT; ++i)
+ if (run[i].cmp > max) max = run[i].cmp;
+
+ PJ_LOG(3,("", " Maximum URI comparison/sec=%u", max));
+
+ pj_ansi_sprintf(desc, "Number of SIP/TEL URIs that can be <B>compared</B> with "
+ "<tt>pjsip_uri_cmp()</tt> per second "
+ "(tested with %d URI set, with average length of "
+ "%d chars)",
+ PJ_ARRAY_SIZE(uri_test_array), avg_len);
+
+ report_ival("uri-cmp-per-sec", max, "URI/sec", desc);
+
+ return PJ_SUCCESS;
+}
+