Index: pjproject/trunk/pjlib/src/pj/config.c
===================================================================
--- pjproject/trunk/pjlib/src/pj/config.c	(revision 562)
+++ pjproject/trunk/pjlib/src/pj/config.c	(revision 563)
@@ -22,5 +22,5 @@
 
 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)
Index: pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 562)
+++ pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 563)
@@ -30,4 +30,6 @@
     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;
@@ -96,5 +98,9 @@
     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");
@@ -240,5 +246,5 @@
 	   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[] = {
@@ -252,4 +258,6 @@
 	{ "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},
@@ -385,4 +393,22 @@
 	    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) {
@@ -2047,5 +2073,5 @@
 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;
@@ -2097,14 +2123,38 @@
     }
 
-    /* Add UDP transport */
-    status = pjsua_transport_create(PJSIP_TRANSPORT_UDP,
-				    &app_config.udp_cfg, 
-				    &transport_id);
-    if (status != PJ_SUCCESS)
+
+    /* 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 local account */
-    pjsua_acc_add_local(transport_id, PJ_TRUE, &current_acc);
-    pjsua_acc_set_online_status(current_acc, PJ_TRUE);
+    }
 
 
Index: pjproject/trunk/pjsip/build/Makefile
===================================================================
--- pjproject/trunk/pjsip/build/Makefile	(revision 562)
+++ pjproject/trunk/pjsip/build/Makefile	(revision 563)
@@ -26,5 +26,4 @@
 		   $(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)) \
@@ -41,6 +40,8 @@
 		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
@@ -77,9 +78,26 @@
 
 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,4 +124,7 @@
 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
@@ -132,4 +153,5 @@
 	$(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:
@@ -141,4 +163,5 @@
 	$(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 $@
 
Index: pjproject/trunk/pjsip/build/test_pjsip.dsp
===================================================================
--- pjproject/trunk/pjsip/build/test_pjsip.dsp	(revision 562)
+++ pjproject/trunk/pjsip/build/test_pjsip.dsp	(revision 563)
@@ -118,4 +118,8 @@
 # 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
@@ -127,4 +131,8 @@
 
 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
Index: pjproject/trunk/pjsip/include/pjsip.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsip.h	(revision 562)
+++ pjproject/trunk/pjsip/include/pjsip.h	(revision 563)
@@ -40,4 +40,5 @@
 #include <pjsip/sip_transport_udp.h>
 #include <pjsip/sip_transport_loop.h>
+#include <pjsip/sip_transport_tcp.h>
 #include <pjsip/sip_resolve.h>
 
Index: pjproject/trunk/pjsip/include/pjsip/sip_transport.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsip/sip_transport.h	(revision 562)
+++ pjproject/trunk/pjsip/include/pjsip/sip_transport.h	(revision 563)
@@ -705,5 +705,5 @@
 
     pjsip_transport_type_e  type;	    /**< Transport type.	*/
-    char		    type_name[8];   /**< Type string name.	*/
+    char		   *type_name;      /**< Type string name.	*/
     unsigned		    flag;	    /**< Transport flag.	*/
 
@@ -722,4 +722,9 @@
 				    int addr_len,
 				    pjsip_transport **transport);
+
+    /**
+     * Destroy the listener.
+     */
+    pj_status_t (*destroy)(pjsip_tpfactory *factory);
 
     /*
Index: pjproject/trunk/pjsip/include/pjsip/sip_transport_tcp.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsip/sip_transport_tcp.h	(revision 562)
+++ pjproject/trunk/pjsip/include/pjsip/sip_transport_tcp.h	(revision 563)
@@ -39,9 +39,33 @@
 
 /**
- * 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
@@ -51,5 +75,6 @@
 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);
 
 
Index: pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 562)
+++ pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 563)
@@ -105,6 +105,14 @@
 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;
 
Index: pjproject/trunk/pjsip/src/pjsip/sip_transaction.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip/sip_transaction.c	(revision 562)
+++ pjproject/trunk/pjsip/src/pjsip/sip_transaction.c	(revision 563)
@@ -1300,4 +1300,5 @@
 	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);
     }
 
@@ -2131,7 +2132,23 @@
 	    }
 
-	    /* 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 
Index: pjproject/trunk/pjsip/src/pjsip/sip_transport.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip/sip_transport.c	(revision 562)
+++ pjproject/trunk/pjsip/src/pjsip/sip_transport.c	(revision 563)
@@ -37,4 +37,10 @@
 #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);
@@ -602,4 +608,10 @@
     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,4 +622,6 @@
 {
     int key_len;
+
+    TRACE_((THIS_FILE, "Transport %s is being destroyed", tp->obj_name));
 
     pj_lock_acquire(tp->lock);
@@ -627,4 +641,5 @@
      */
     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);
 
@@ -643,4 +658,6 @@
     pjsip_tpmgr *mgr;
     pj_status_t status;
+
+    TRACE_((THIS_FILE, "Transport %s shutting down", tp->obj_name));
 
     pj_lock_acquire(tp->lock);
@@ -808,4 +825,5 @@
     pj_hash_iterator_t itr_val;
     pj_hash_iterator_t *itr;
+    pjsip_tpfactory *factory;
     pjsip_endpoint *endpt = mgr->endpt;
     
@@ -814,4 +832,7 @@
     pj_lock_acquire(mgr->lock);
 
+    /*
+     * Destroy all transports.
+     */
     itr = pj_hash_first(mgr->table, &itr_val);
     while (itr != NULL) {
@@ -828,4 +849,16 @@
     }
 
+    /*
+     * 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);
@@ -843,5 +876,5 @@
     //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)));
     }
@@ -880,8 +913,8 @@
 
     /* 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. */
@@ -1039,4 +1072,9 @@
     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);
 
@@ -1084,4 +1122,6 @@
 	pj_lock_release(mgr->lock);
 	*tp = transport;
+
+	TRACE_((THIS_FILE, "Transport %s acquired", transport->obj_name));
 	return PJ_SUCCESS;
     }
@@ -1101,6 +1141,10 @@
 	/* 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. */
Index: pjproject/trunk/pjsip/src/pjsip/sip_transport_loop.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip/sip_transport_loop.c	(revision 562)
+++ pjproject/trunk/pjsip/src/pjsip/sip_transport_loop.c	(revision 563)
@@ -74,5 +74,6 @@
 
     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;
Index: pjproject/trunk/pjsip/src/pjsip/sip_transport_tcp.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip/sip_transport_tcp.c	(revision 562)
+++ pjproject/trunk/pjsip/src/pjsip/sip_transport_tcp.c	(revision 563)
@@ -21,4 +21,5 @@
 #include <pjsip/sip_errno.h>
 #include <pj/compat/socket.h>
+#include <pj/addr_resolv.h>
 #include <pj/assert.h>
 #include <pj/ioqueue.h>
@@ -34,7 +35,7 @@
 #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
 
 
@@ -43,4 +44,10 @@
 
 
+/*
+ * 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
 {
@@ -53,16 +60,14 @@
 };
 
-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;
@@ -74,21 +79,33 @@
 
 
-struct pending_tdata
-{
-    PJ_DECL_LIST_MEMBER(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 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;
 
 
@@ -100,11 +117,13 @@
 
     /* 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, 
@@ -112,5 +131,8 @@
 				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,
@@ -120,10 +142,10 @@
 					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);
 
 
@@ -139,16 +161,39 @@
 
 
+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);
 
 
@@ -159,11 +204,12 @@
 
     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", 
@@ -178,8 +224,49 @@
 	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 */
@@ -192,5 +279,19 @@
 	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;
@@ -201,43 +302,32 @@
 	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;
-    }
-
-    /* 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->name, 
-	     "SIP TCP transport listening for incoming connections at %s:%d",
-	     pj_inet_ntoa(local->sin_addr), (int)pj_ntohs(local->sin_port)));
+	on_accept_complete(listener->key, &listener->accept_op[i].op_key,
+			   listener->sock, PJ_EPENDING);
+    }
+
+    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)));
+
+    /* 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)
-{
-    if (listener->active) {
+/* This callback is called by transport manager to destroy listener */
+static pj_status_t lis_destroy(pjsip_tpfactory *factory)
+{
+    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;
     }
 
@@ -259,7 +349,10 @@
 
     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);
     }
 
@@ -289,6 +382,10 @@
 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 */
@@ -307,23 +404,13 @@
 
 
-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.
- */
-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 TCP transport, called when pending accept() and
+ * pending connect() complete.
+ */
+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;
@@ -333,7 +420,13 @@
     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.
@@ -341,18 +434,21 @@
     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;
@@ -375,5 +471,5 @@
     tcp->base.send_msg = &tcp_send_msg;
     tcp->base.do_shutdown = &tcp_shutdown;
-    tcp->base.destroy = &tcp_destroy;
+    tcp->base.destroy = &tcp_destroy_transport;
 
 
@@ -387,11 +483,13 @@
     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;
@@ -400,16 +498,21 @@
     *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;
@@ -417,5 +520,5 @@
 	pj_status_t status;
 
-	pending_tx = tcp->tx_list.next;
+	pending_tx = tcp->delayed_list.next;
 	pj_list_erase(pending_tx);
 
@@ -437,27 +540,51 @@
 
 
+/* 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);
     }
 
@@ -470,4 +597,10 @@
 	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;
     }
 
@@ -483,7 +616,24 @@
 
     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);
     }
 
@@ -494,5 +644,6 @@
 /*
  * 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)
@@ -532,5 +683,5 @@
 			     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;
@@ -594,17 +745,17 @@
 
     /* 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;
     }
@@ -630,4 +781,15 @@
     }
 
+    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;
@@ -654,29 +816,73 @@
     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;
-	}
-
-	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);
+	} else {
+
+	    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, 
@@ -687,25 +893,49 @@
 				   &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);
-    }
-}
-
-
-/* This callback is called by transport manager to send SIP message */
+	/*
+	 * 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 
+ */
 static pj_status_t tcp_send_msg(pjsip_transport *transport, 
 				pjsip_tx_data *tdata,
@@ -719,5 +949,6 @@
     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 */
@@ -738,20 +969,41 @@
 
     /* 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;
-
-	pj_list_push_back(&tcp->tx_list, pending_tdata);
-	status = PJ_EPENDING;
-
-    } else {
-	/* send to ioqueue! */
+
+	/*
+	 * Looks like connect() is still in progress. Check again (this time
+	 * with holding the lock) to be sure.
+	 */
+	pj_lock_acquire(tcp->base.lock);
+
+	if (tcp->has_pending_connect) {
+	    struct delayed_tdata *delayed_tdata;
+
+	    /*
+	     * 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, 
@@ -759,9 +1011,21 @@
 				 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;
@@ -769,5 +1033,8 @@
 
 
-/* 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)
 {
@@ -780,5 +1047,7 @@
 
 
-/* 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, 
@@ -788,11 +1057,12 @@
     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;
     }
@@ -807,5 +1077,7 @@
 	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;
@@ -816,4 +1088,8 @@
 	    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, 
@@ -834,22 +1110,43 @@
 
 	    /* 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"));
-
-	    /* Transport error, close transport */
-	    tcp_destroy(&tp->base);
+				   -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);
+
 	    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 {
@@ -868,4 +1165,5 @@
 
 	if (status == PJ_SUCCESS) {
+
 	    /* Continue loop. */
 	    pj_assert(i < MAX_IMMEDIATE_PACKET);
@@ -880,6 +1178,12 @@
 				   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;
 	}
@@ -888,13 +1192,25 @@
 
 
-/* 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;
@@ -902,6 +1218,14 @@
     /* 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;
     }
@@ -926,5 +1250,11 @@
     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;
     }
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_acc.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_acc.c	(revision 562)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_acc.c	(revision 563)
@@ -78,16 +78,16 @@
 {
     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));
 
 
@@ -145,5 +145,5 @@
 
     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);
 
@@ -221,5 +221,6 @@
 
     /* 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();
@@ -282,18 +283,21 @@
 {
     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);
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 562)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 563)
@@ -730,5 +730,5 @@
     /* 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;
     }
@@ -742,5 +742,7 @@
     /* Create the transport */
     if (type == PJSIP_TRANSPORT_UDP) {
-
+	/*
+	 * Create UDP transport.
+	 */
 	pjsua_transport_config config;
 	pj_sock_t sock = PJ_INVALID_SOCKET;
@@ -774,4 +776,48 @@
 	}
 
+
+	/* 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;
@@ -780,6 +826,4 @@
     }
 
-    /* Save the transport */
-    pjsua_var.tpdata[id].tp = tp;
 
     /* Return the ID */
@@ -808,5 +852,5 @@
     /* 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;
     }
@@ -819,5 +863,7 @@
 
     /* 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 */
@@ -843,5 +889,5 @@
 	 ++i) 
     {
-	if (!pjsua_var.tpdata[i].tp)
+	if (!pjsua_var.tpdata[i].data.ptr)
 	    continue;
 
@@ -863,5 +909,5 @@
 					      pjsua_transport_info *info)
 {
-    pjsip_transport *tp;
+    struct transport_data *t = &pjsua_var.tpdata[id];
 
     pj_memset(info, 0, sizeof(*info));
@@ -871,23 +917,48 @@
 
     /* 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);
+	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 = 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();
@@ -907,5 +978,5 @@
 
     /* 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);
 
 
@@ -927,5 +998,5 @@
 
     /* 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);
 
 
Index: pjproject/trunk/pjsip/src/test-pjsip/main.c
===================================================================
--- pjproject/trunk/pjsip/src/test-pjsip/main.c	(revision 562)
+++ pjproject/trunk/pjsip/src/test-pjsip/main.c	(revision 563)
@@ -21,4 +21,6 @@
 #include <string.h>
 #include <stdlib.h>
+
+extern const char *system_name;
 
 static void usage()
@@ -58,4 +60,13 @@
 	    }
 	    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();
@@ -68,7 +79,7 @@
     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);
     }
Index: pjproject/trunk/pjsip/src/test-pjsip/msg_logger.c
===================================================================
--- pjproject/trunk/pjsip/src/test-pjsip/msg_logger.c	(revision 562)
+++ pjproject/trunk/pjsip/src/test-pjsip/msg_logger.c	(revision 563)
@@ -28,11 +28,13 @@
 {
     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));
     }
@@ -44,11 +46,13 @@
 {
     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));
     }
Index: pjproject/trunk/pjsip/src/test-pjsip/msg_test.c
===================================================================
--- pjproject/trunk/pjsip/src/test-pjsip/msg_test.c	(revision 562)
+++ pjproject/trunk/pjsip/src/test-pjsip/msg_test.c	(revision 563)
@@ -22,5 +22,9 @@
 
 #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,6 +39,4 @@
 #define FLAG_PARSE_ONLY		4
 #define FLAG_PRINT_ONLY		8
-
-static int flag = 0;
 
 struct test_msg
@@ -102,6 +104,10 @@
 };
 
-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 )
@@ -122,8 +128,8 @@
     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);
@@ -132,5 +138,5 @@
 
     /* 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);
@@ -149,12 +155,12 @@
     pj_get_timestamp(&t2);
     pj_sub_timestamp(&t2, &t1);
-    pj_add_timestamp(&detect_time, &t2);
-
-    if (flag & FLAG_DETECT_ONLY)
+    pj_add_timestamp(&var.detect_time, &t2);
+
+    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);
@@ -172,7 +178,7 @@
     pj_get_timestamp(&t2);
     pj_sub_timestamp(&t2, &t1);
-    pj_add_timestamp(&parse_time, &t2);
-
-    if (flag & FLAG_PARSE_ONLY)
+    pj_add_timestamp(&var.parse_time, &t2);
+
+    if (var.flag & FLAG_PARSE_ONLY)
 	return PJ_SUCCESS;
 
@@ -307,7 +313,7 @@
     /* 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);
@@ -318,5 +324,5 @@
     pj_get_timestamp(&t2);
     pj_sub_timestamp(&t2, &t1);
-    pj_add_timestamp(&print_time, &t2);
+    pj_add_timestamp(&var.print_time, &t2);
 
 
@@ -675,5 +681,26 @@
 /*****************************************************************************/
 
-int msg_test(void)
+static pj_status_t simple_test(void)
+{
+    unsigned i;
+    pj_status_t status;
+
+    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);
+
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+static int msg_benchmark(unsigned *p_detect, unsigned *p_parse, 
+			 unsigned *p_print)
 {
     pj_status_t status;
@@ -684,18 +711,8 @@
     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) {
-	pool = pjsip_endpt_create_pool(endpt, NULL, POOL_SIZE, POOL_SIZE);
-	status = test_entry( pool, &test_array[i] );
-	pjsip_endpt_release_pool(endpt, pool);
-
-	if (status != PJ_SUCCESS)
-	    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;
-    
+
+    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) {
@@ -709,51 +726,143 @@
     }
 
-    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));
-
-    kbytes = parse_len;
+    *p_detect = (unsigned)avg_detect;
+
+    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));
-
-    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_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;
+}
+
Index: pjproject/trunk/pjsip/src/test-pjsip/test.c
===================================================================
--- pjproject/trunk/pjsip/src/test-pjsip/test.c	(revision 562)
+++ pjproject/trunk/pjsip/src/test-pjsip/test.c	(revision 563)
@@ -34,8 +34,21 @@
 			} 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)
@@ -76,4 +89,130 @@
 }
 
+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)
 {
@@ -81,4 +220,9 @@
     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;
 
@@ -93,4 +237,8 @@
 	return rc;
     }
+
+    status = init_report();
+    if (status != PJ_SUCCESS)
+	return status;
 
     pj_dump_config();
@@ -125,4 +273,9 @@
 	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
@@ -139,4 +292,8 @@
 #endif
 
+#if INCLUDE_TSX_BENCH
+    DO_TEST(tsx_bench());
+#endif
+
 #if INCLUDE_UDP_TEST
     DO_TEST(transport_udp_test());
@@ -147,13 +304,41 @@
 #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);
@@ -170,4 +355,6 @@
 	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;
 }
Index: pjproject/trunk/pjsip/src/test-pjsip/test.h
===================================================================
--- pjproject/trunk/pjsip/src/test-pjsip/test.h	(revision 562)
+++ pjproject/trunk/pjsip/src/test-pjsip/test.h	(revision 563)
@@ -32,4 +32,11 @@
 #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
 
 
@@ -37,6 +44,8 @@
 #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
 
@@ -48,9 +57,19 @@
 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). */
@@ -58,5 +77,6 @@
 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,
@@ -69,7 +89,12 @@
 /* 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. */
Index: pjproject/trunk/pjsip/src/test-pjsip/transport_loop_test.c
===================================================================
--- pjproject/trunk/pjsip/src/test-pjsip/transport_loop_test.c	(revision 562)
+++ pjproject/trunk/pjsip/src/test-pjsip/transport_loop_test.c	(revision 563)
@@ -26,8 +26,11 @@
 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"));
@@ -41,4 +44,7 @@
     }
 
+    /* Get initial reference counter */
+    ref_cnt = pj_atomic_get(loop->ref_cnt);
+
     /* Test basic transport attributes */
     status = generic_transport_test(loop);
@@ -47,10 +53,21 @@
 
     /* 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. */
@@ -85,7 +102,9 @@
     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;
     }
 
Index: pjproject/trunk/pjsip/src/test-pjsip/transport_tcp_test.c
===================================================================
--- pjproject/trunk/pjsip/src/test-pjsip/transport_tcp_test.c	(revision 563)
+++ pjproject/trunk/pjsip/src/test-pjsip/transport_tcp_test.c	(revision 563)
@@ -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;
+}
Index: pjproject/trunk/pjsip/src/test-pjsip/transport_test.c
===================================================================
--- pjproject/trunk/pjsip/src/test-pjsip/transport_test.c	(revision 562)
+++ pjproject/trunk/pjsip/src/test-pjsip/transport_test.c	(revision 563)
@@ -174,5 +174,6 @@
 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;
@@ -221,4 +222,6 @@
 
     /* 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);
@@ -229,7 +232,7 @@
     }
 
-    /* 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 */
@@ -269,5 +272,8 @@
 	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;
     }
 
@@ -517,5 +523,4 @@
     unsigned total_recv;
 
-
     PJ_LOG(3,(THIS_FILE, "  multithreaded round-trip test (%d threads)...",
 		  THREADS));
Index: pjproject/trunk/pjsip/src/test-pjsip/transport_udp_test.c
===================================================================
--- pjproject/trunk/pjsip/src/test-pjsip/transport_udp_test.c	(revision 562)
+++ pjproject/trunk/pjsip/src/test-pjsip/transport_udp_test.c	(revision 563)
@@ -30,9 +30,10 @@
 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;
 
@@ -80,8 +81,19 @@
     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. */
Index: pjproject/trunk/pjsip/src/test-pjsip/tsx_basic_test.c
===================================================================
--- pjproject/trunk/pjsip/src/test-pjsip/tsx_basic_test.c	(revision 562)
+++ pjproject/trunk/pjsip/src/test-pjsip/tsx_basic_test.c	(revision 563)
@@ -24,4 +24,8 @@
 #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)
@@ -34,6 +38,6 @@
     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,
@@ -78,6 +82,6 @@
     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. */
@@ -132,7 +136,12 @@
 }
 
-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();
Index: pjproject/trunk/pjsip/src/test-pjsip/tsx_bench.c
===================================================================
--- pjproject/trunk/pjsip/src/test-pjsip/tsx_bench.c	(revision 563)
+++ pjproject/trunk/pjsip/src/test-pjsip/tsx_bench.c	(revision 563)
@@ -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;
+}
+
Index: pjproject/trunk/pjsip/src/test-pjsip/tsx_uac_test.c
===================================================================
--- pjproject/trunk/pjsip/src/test-pjsip/tsx_uac_test.c	(revision 562)
+++ pjproject/trunk/pjsip/src/test-pjsip/tsx_uac_test.c	(revision 563)
@@ -92,4 +92,8 @@
 #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);
@@ -141,5 +145,10 @@
 
 /* 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;
 
 /*
@@ -167,4 +176,39 @@
 		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;
+		}
+	    }
 	}
 
@@ -516,5 +560,6 @@
 				    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;
 
@@ -721,7 +766,7 @@
 	    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;
@@ -769,9 +814,9 @@
 				     rdata);
 
-		timer.user_data = pjsip_tsx_layer_find_tsx(&key, PJ_FALSE);
-		timer.id = 301;
-		timer.cb = &terminate_tsx_callback;
-
-		pjsip_endpt_schedule_timer(endpt, &timer, &delay);
+		pj_strcpy(&timer.tsx_key, &key);
+		timer.entry.id = 301;
+		timer.entry.cb = &terminate_tsx_callback;
+
+		pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay);
 	    }
 
@@ -842,7 +887,7 @@
 		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) {
@@ -860,9 +905,9 @@
 				     rdata);
 
-		timer.user_data = pjsip_tsx_layer_find_tsx(&key, PJ_FALSE);
-		timer.id = 302;
-		timer.cb = &terminate_tsx_callback;
-
-		pjsip_endpt_schedule_timer(endpt, &timer, &delay);
+		pj_strcpy(&timer.tsx_key, &key);
+		timer.entry.id = 302;
+		timer.entry.cb = &terminate_tsx_callback;
+
+		pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay);
 	    }
 
@@ -981,8 +1026,21 @@
 	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. */
@@ -1053,6 +1111,5 @@
 
 	/* 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);
@@ -1094,7 +1151,6 @@
 
     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)
@@ -1106,18 +1162,20 @@
     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);
-
-    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;
-
-    /* Restore loop transport settings. */
-    pjsip_loop_set_failure(loop, 0, NULL);
-    pjsip_loop_set_send_callback_delay(loop, 0, 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, 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);
+    }
 
     return status;
@@ -1144,6 +1202,5 @@
 
     /* 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);
 
@@ -1187,6 +1244,5 @@
 
 	/* 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);
 
@@ -1219,6 +1275,5 @@
 
     /* 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);
@@ -1250,16 +1305,18 @@
     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]));
-
-	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);
+	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]);
+	}
+
+	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;
     }
 
@@ -1277,8 +1334,20 @@
  *****************************************************************************
  */
-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. */
@@ -1317,13 +1386,21 @@
 	return status;
 
-    /* TEST4_BRANCH_ID: Transport failed after several retransmissions */
-    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;
+    /* 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 
+     *			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 */
@@ -1353,6 +1430,19 @@
 	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;
 }
Index: pjproject/trunk/pjsip/src/test-pjsip/tsx_uas_test.c
===================================================================
--- pjproject/trunk/pjsip/src/test-pjsip/tsx_uas_test.c	(revision 562)
+++ pjproject/trunk/pjsip/src/test-pjsip/tsx_uas_test.c	(revision 563)
@@ -36,4 +36,6 @@
  **	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
@@ -54,4 +56,5 @@
  ** TEST6_BRANCH_ID
  **	As above, in COMPLETED state, with first sending provisional response.
+ **	(Only applicable for non-reliable transports).
  **
  ** TEST7_BRANCH_ID
@@ -66,4 +69,5 @@
  **	until it's terminated by user).
  **     Transaction also MUST terminate in T4 seconds.
+ **	(Only applicable for non-reliable transports).
  **
  ** TEST11_BRANCH_ID
@@ -129,4 +133,10 @@
 
 
+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
@@ -522,5 +532,7 @@
 	    /* 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;
 	    }
@@ -576,4 +588,24 @@
 		PJ_LOG(3,(THIS_FILE, "    error: incorrect prev_state"));
 		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;
+		}
+
 	    }
 
@@ -815,4 +847,6 @@
 	    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) {
@@ -831,5 +865,7 @@
 
 	    } 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);
 	    }
@@ -838,4 +874,6 @@
 	    /* Verify the response received. */
 	    
+	    PJ_LOG(4,(THIS_FILE, "    received response number %d", recv_count));
+
 	    ++recv_count;
 
@@ -870,5 +908,6 @@
 		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;
 		    }
@@ -1184,4 +1223,6 @@
 
 	    /* (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) {
@@ -1256,13 +1297,16 @@
 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;
@@ -1270,9 +1314,6 @@
     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;
@@ -1290,12 +1331,13 @@
 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);
 
@@ -1321,8 +1363,5 @@
     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);
@@ -1358,7 +1397,5 @@
 	      "  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);
@@ -1369,7 +1406,5 @@
 	      "  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);
@@ -1395,7 +1430,5 @@
 	      "  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);
@@ -1444,9 +1477,6 @@
 	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;
@@ -1495,8 +1525,16 @@
  *****************************************************************************
  */
-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. */
@@ -1505,7 +1543,6 @@
     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);
@@ -1551,17 +1588,21 @@
 	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)
@@ -1570,8 +1611,12 @@
     /* 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.
@@ -1580,12 +1625,29 @@
      * TEST13_BRANCH_ID: test transport failure in CONFIRMED state.
      */
-    status = tsx_transport_failure_test();
-    if (status != 0)
-	return status;
-
-
-    pjsip_transport_dec_ref(loop);
+    /* Only valid for loop-dgram */
+    if (param->type == PJSIP_TRANSPORT_LOOP_DGRAM) {
+	status = tsx_transport_failure_test();
+	if (status != 0)
+	    return status;
+    }
+
+
+    /* 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;
-
 }
 
Index: pjproject/trunk/pjsip/src/test-pjsip/txdata_test.c
===================================================================
--- pjproject/trunk/pjsip/src/test-pjsip/txdata_test.c	(revision 562)
+++ pjproject/trunk/pjsip/src/test-pjsip/txdata_test.c	(revision 563)
@@ -22,7 +22,15 @@
 #include <pjlib.h>
 
+
+#define THIS_FILE   "txdata_test.c"
+
+
 #define HFIND(msg,h,H) ((pjsip_##h##_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_##H, NULL))
 
-#define THIS_FILE   "txdata_test.c"
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+#   define LOOP	    10000
+#else
+#   define LOOP	    100000
+#endif
 
 
@@ -323,4 +331,75 @@
     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
@@ -346,4 +425,6 @@
     pjsip_sip_uri *uri;
     pjsip_param *param;
+    pjsip_via_hdr *via;
+    pjsip_parser_err_report err_list;
     pjsip_msg *msg;
     int len;
@@ -361,4 +442,9 @@
     }
 
+    /* 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
@@ -377,9 +463,23 @@
 
     /* 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_tx_data_dec_ref(tdata);
-	return -250;
+	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 -256;
     }
 
@@ -513,7 +613,161 @@
 }
 
+
+/*
+ * 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();
@@ -521,4 +775,9 @@
 	return status;
 
+#if INCLUDE_GCC_TEST
+    status = gcc_test();
+    if (status != 0)
+	return status;
+#endif
 
     status = txdata_test_uri_params();
@@ -526,4 +785,60 @@
 	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;
 }
+ 
Index: pjproject/trunk/pjsip/src/test-pjsip/uri_test.c
===================================================================
--- pjproject/trunk/pjsip/src/test-pjsip/uri_test.c	(revision 562)
+++ pjproject/trunk/pjsip/src/test-pjsip/uri_test.c	(revision 563)
@@ -33,10 +33,17 @@
 
 #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;
 
 
@@ -687,5 +694,5 @@
     /* 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) {
@@ -703,5 +710,5 @@
     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. */
@@ -721,8 +728,8 @@
     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);
@@ -756,8 +763,8 @@
     }
 
-    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. */
@@ -786,14 +793,10 @@
 }
 
-int uri_test()
-{
-    unsigned i, loop;
+
+static int simple_uri_test(void)
+{
+    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"));
@@ -804,14 +807,29 @@
 	    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);
@@ -828,51 +846,54 @@
     }
 
-    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:
@@ -880,2 +901,88 @@
 }
 
+
+/*****************************************************************************/
+
+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;
+}
+
