Added loop transport to test transaction

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@107 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjlib/include/pj/compat/socket.h b/pjlib/include/pj/compat/socket.h
index c45cd3f..1ef7fff 100644
--- a/pjlib/include/pj/compat/socket.h
+++ b/pjlib/include/pj/compat/socket.h
@@ -79,9 +79,11 @@
 #ifdef PJ_WIN32
 #  define OSERR_EWOULDBLOCK    WSAEWOULDBLOCK
 #  define OSERR_EINPROGRESS    WSAEINPROGRESS
+#  define OSERR_ECONNRESET     WSAECONNRESET
 #else
 #  define OSERR_EWOULDBLOCK    EWOULDBLOCK
 #  define OSERR_EINPROGRESS    EINPROGRESS
+#  define OSERR_ECONNRESET     ECONNRESET
 #endif
 
 
diff --git a/pjlib/include/pj/list.h b/pjlib/include/pj/list.h
index fc0223c..4f2e514 100644
--- a/pjlib/include/pj/list.h
+++ b/pjlib/include/pj/list.h
@@ -117,6 +117,19 @@
 
 
 /**
+ * Insert the node to the back of the list. This is just an alias for
+ * #pj_list_insert_before().
+ *
+ * @param list	The list. 
+ * @param node	The element to be inserted.
+ */
+PJ_INLINE(void) pj_list_push_back(pj_list_type *list, pj_list_type *node)
+{
+    pj_list_insert_before(list, node);
+}
+
+
+/**
  * Inserts all nodes in \a nodes to the target list.
  *
  * @param lst	    The target list.
@@ -136,6 +149,20 @@
  */
 PJ_IDECL(void) pj_list_insert_after(pj_list_type *pos, pj_list_type *node);
 
+
+/**
+ * Insert the node to the front of the list. This is just an alias for
+ * #pj_list_insert_after().
+ *
+ * @param list	The list. 
+ * @param node	The element to be inserted.
+ */
+PJ_INLINE(void) pj_list_push_front(pj_list_type *list, pj_list_type *node)
+{
+    pj_list_insert_after(list, node);
+}
+
+
 /**
  * Insert all nodes in \a nodes to the target list.
  *
diff --git a/pjlib/src/pj/os_core_win32.c b/pjlib/src/pj/os_core_win32.c
index eac778f..7633e7b 100644
--- a/pjlib/src/pj/os_core_win32.c
+++ b/pjlib/src/pj/os_core_win32.c
@@ -862,8 +862,13 @@
 
 struct pj_rwmutex_t
 {
-    pj_mutex_t *read_lock, *write_lock;
-    int reader_count;
+    pj_mutex_t *read_lock;
+    /* write_lock must use semaphore, because write_lock may be released
+     * by thread other than the thread that acquire the write_lock in the
+     * first place.
+     */
+    pj_sem_t   *write_lock;
+    pj_int32_t  reader_count;
 };
 
 /*
@@ -885,7 +890,7 @@
     if (status != PJ_SUCCESS)
 	return status;
 
-    status = pj_mutex_create_recursive(pool, name, &rwmutex->write_lock);
+    status = pj_sem_create(pool, name, 1, 1, &rwmutex->write_lock);
     if (status != PJ_SUCCESS) {
 	pj_mutex_destroy(rwmutex->read_lock);
 	return status;
@@ -907,12 +912,17 @@
     PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
 
     status = pj_mutex_lock(mutex->read_lock);
-    if (status != PJ_SUCCESS)
+    if (status != PJ_SUCCESS) {
+	pj_assert(!"This pretty much is unexpected");
 	return status;
+    }
 
     mutex->reader_count++;
+
+    pj_assert(mutex->reader_count < 0x7FFFFFF0L);
+
     if (mutex->reader_count == 1)
-	pj_mutex_lock(mutex->write_lock);
+	pj_sem_wait(mutex->write_lock);
 
     status = pj_mutex_unlock(mutex->read_lock);
     return status;
@@ -925,7 +935,7 @@
 PJ_DEF(pj_status_t) pj_rwmutex_lock_write(pj_rwmutex_t *mutex)
 {
     PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-    return pj_mutex_lock(mutex->write_lock);
+    return pj_sem_wait(mutex->write_lock);
 }
 
 /*
@@ -939,14 +949,16 @@
     PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
 
     status = pj_mutex_lock(mutex->read_lock);
-    if (status != PJ_SUCCESS)
+    if (status != PJ_SUCCESS) {
+	pj_assert(!"This pretty much is unexpected");
 	return status;
+    }
 
     pj_assert(mutex->reader_count >= 1);
 
     --mutex->reader_count;
     if (mutex->reader_count == 0)
-	pj_mutex_unlock(mutex->write_lock);
+	pj_sem_post(mutex->write_lock);
 
     status = pj_mutex_unlock(mutex->read_lock);
     return status;
@@ -959,7 +971,8 @@
 PJ_DEF(pj_status_t) pj_rwmutex_unlock_write(pj_rwmutex_t *mutex)
 {
     PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-    return pj_mutex_unlock(mutex->write_lock);
+    pj_assert(mutex->reader_count <= 1);
+    return pj_sem_post(mutex->write_lock);
 }
 
 
@@ -971,7 +984,7 @@
 {
     PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
     pj_mutex_destroy(mutex->read_lock);
-    pj_mutex_destroy(mutex->write_lock);
+    pj_sem_destroy(mutex->write_lock);
     return PJ_SUCCESS;
 }
 
diff --git a/pjsip/build/pjsip_core.dsp b/pjsip/build/pjsip_core.dsp
index 264799c..608d7a6 100644
--- a/pjsip/build/pjsip_core.dsp
+++ b/pjsip/build/pjsip_core.dsp
@@ -124,7 +124,15 @@
 # Begin Source File

 

 SOURCE=..\src\pjsip\sip_transaction.c

+

+!IF  "$(CFG)" == "pjsip_core - Win32 Release"

+

 # PROP Exclude_From_Build 1

+

+!ELSEIF  "$(CFG)" == "pjsip_core - Win32 Debug"

+

+!ENDIF 

+

 # End Source File

 # Begin Source File

 

@@ -132,6 +140,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=..\src\pjsip\sip_transport_loop.c

+# End Source File

+# Begin Source File

+

 SOURCE=..\src\pjsip\sip_transport_udp.c

 # End Source File

 # Begin Source File

@@ -144,6 +156,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=..\src\pjsip\sip_util_proxy.c

+# End Source File

+# Begin Source File

+

 SOURCE=..\src\pjsip\sip_util_statefull.c

 # PROP Exclude_From_Build 1

 # End Source File

@@ -221,6 +237,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=..\include\pjsip\sip_transport_loop.h

+# End Source File

+# Begin Source File

+

 SOURCE=..\include\pjsip\sip_transport_udp.h

 # End Source File

 # Begin Source File

diff --git a/pjsip/build/test_pjsip.dsp b/pjsip/build/test_pjsip.dsp
index 1c8ad9f..649a8ae 100644
--- a/pjsip/build/test_pjsip.dsp
+++ b/pjsip/build/test_pjsip.dsp
@@ -101,6 +101,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE="..\src\test-pjsip\transport_loop_test.c"

+# End Source File

+# Begin Source File

+

 SOURCE="..\src\test-pjsip\transport_test.c"

 # End Source File

 # Begin Source File

@@ -109,6 +113,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE="..\src\test-pjsip\tsx_uac_test.c"

+# End Source File

+# Begin Source File

+

 SOURCE="..\src\test-pjsip\txdata_test.c"

 # End Source File

 # Begin Source File

diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h
index 592aea4..158d4a7 100644
--- a/pjsip/include/pjsip/sip_config.h
+++ b/pjsip/include/pjsip/sip_config.h
@@ -45,6 +45,14 @@
 #define PJSIP_RFC3261_BRANCH_ID		"z9hG4bK"
 #define PJSIP_RFC3261_BRANCH_LEN	7
 
+/* Transaction related constants. */
+#define PJSIP_POOL_TSX_LAYER_LEN	4000
+#define PJSIP_POOL_TSX_LAYER_INC	4000
+#define PJSIP_MAX_TSX_COUNT		(16*1024)
+#define PJSIP_POOL_TSX_LEN		1536 //768
+#define PJSIP_POOL_TSX_INC		256
+#define PJSIP_MAX_TSX_KEY_LEN		(PJSIP_MAX_URL_SIZE*2)
+
 /* Message/URL related constants. */
 #define PJSIP_MAX_CALL_ID_LEN		PJ_GUID_STRING_LENGTH
 #define PJSIP_MAX_TAG_LEN		PJ_GUID_STRING_LENGTH
@@ -52,12 +60,6 @@
 #define PJSIP_MAX_URL_SIZE		256
 #define PJSIP_MAX_HNAME_LEN		64
 
-/* Transction related constants. */
-#define PJSIP_MAX_TSX_COUNT		(16*1024)
-#define PJSIP_POOL_LEN_TSX		1536 //768
-#define PJSIP_POOL_INC_TSX		256
-#define PJSIP_MAX_TSX_KEY_LEN		(PJSIP_MAX_URL_SIZE*2)
-
 /* Dialog related constants. */
 #define PJSIP_MAX_DIALOG_COUNT		(16*1024)
 #define PJSIP_POOL_LEN_DIALOG		1200
diff --git a/pjsip/include/pjsip/sip_endpoint.h b/pjsip/include/pjsip/sip_endpoint.h
index d9d03a9..527cd88 100644
--- a/pjsip/include/pjsip/sip_endpoint.h
+++ b/pjsip/include/pjsip/sip_endpoint.h
@@ -209,7 +209,7 @@
  * @param endpt	    The endpoint.
  * @param pool	    The pool to be destroyed.
  */
-PJ_DECL(void) pjsip_endpt_destroy_pool( pjsip_endpoint *endpt,
+PJ_DECL(void) pjsip_endpt_release_pool( pjsip_endpoint *endpt,
 					pj_pool_t *pool );
 
 /**
diff --git a/pjsip/include/pjsip/sip_errno.h b/pjsip/include/pjsip/sip_errno.h
index 4d50c75..253e6c0 100644
--- a/pjsip/include/pjsip/sip_errno.h
+++ b/pjsip/include/pjsip/sip_errno.h
@@ -151,6 +151,11 @@
  * Expecting response message.
  */
 #define PJSIP_ENOTRESPONSEMSG	(PJSIP_ERRNO_START_PJSIP + 31)	/* 171031 */
+/**
+ * @hideinitializer
+ * Invalid header field.
+ */
+#define PJSIP_EINVALIDHDR	(PJSIP_ERRNO_START_PJSIP + 32)	/* 171032 */
 
 
 /************************************************************
diff --git a/pjsip/include/pjsip/sip_msg.h b/pjsip/include/pjsip/sip_msg.h
index e186604..29bd976 100644
--- a/pjsip/include/pjsip/sip_msg.h
+++ b/pjsip/include/pjsip/sip_msg.h
@@ -499,9 +499,13 @@
      *  For incoming messages, the parser will fill in this member with the
      *  content type found in Content-Type header.
      *
-     *  For outgoing messages, application must fill in this member with
+     *  For outgoing messages, application may fill in this member with
      *  appropriate value, because the stack will generate Content-Type header
      *  based on the value specified here.
+     *
+     *  If the content_type is empty, no Content-Type AND Content-Length header
+     *  will be added to the message. The stack assumes that application adds
+     *  these headers themselves.
      */
     pjsip_media_type content_type;
 
@@ -543,6 +547,19 @@
     int (*print_body)(struct pjsip_msg_body *msg_body, 
 		      char *buf, pj_size_t size);
 
+    /** Clone the data part only of this message body. Note that this only
+     *  duplicates the data part of the body instead of the whole message
+     *  body. If application wants to duplicate the entire message body
+     *  structure, it must call #pjsip_msg_body_clone().
+     *
+     *  @param pool	    Pool used to clone the data.
+     *  @param data	    The data inside message body, to be cloned.
+     *  @param len	    The length of the data.
+     *
+     *  @return		    New data duplicated from the original data.
+     */
+    void* (*clone_data)(pj_pool_t *pool, const void *data, unsigned len);
+
 } pjsip_msg_body;
 
 /**
@@ -562,6 +579,22 @@
 				    char *buf, pj_size_t size);
 
 /**
+ * Clone the message body in src_body to the dst_body. This will duplicate
+ * the contents of the message body using the \a clone_data member of the
+ * source message body.
+ *
+ * @param pool		Pool to use to duplicate the message body.
+ * @param dst_body	Destination message body.
+ * @param src_body	Source message body to duplicate.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_msg_body_clone(pj_pool_t *pool,
+					  pjsip_msg_body *dst_body,
+					  const pjsip_msg_body *src_body );
+					   
+
+/**
  * @}
  */
 
diff --git a/pjsip/include/pjsip/sip_transaction.h b/pjsip/include/pjsip/sip_transaction.h
index c0627aa..5d2a512 100644
--- a/pjsip/include/pjsip/sip_transaction.h
+++ b/pjsip/include/pjsip/sip_transaction.h
@@ -25,7 +25,7 @@
  */
 
 #include <pjsip/sip_msg.h>
-#include <pjsip/sip_resolve.h>
+#include <pjsip/sip_util.h>
 #include <pj/timer.h>
 
 PJ_BEGIN_DECL
@@ -36,42 +36,26 @@
  * @{
  */
 
-/* Forward decl. */
-struct pjsip_transaction;
-
-
 /**
- * Transaction state.
+ * This enumeration represents transaction state.
  */
 typedef enum pjsip_tsx_state_e
 {
-    PJSIP_TSX_STATE_NULL,
-    PJSIP_TSX_STATE_CALLING,
-    PJSIP_TSX_STATE_TRYING,
-    PJSIP_TSX_STATE_PROCEEDING,
-    PJSIP_TSX_STATE_COMPLETED,
-    PJSIP_TSX_STATE_CONFIRMED,
-    PJSIP_TSX_STATE_TERMINATED,
-    PJSIP_TSX_STATE_DESTROYED,
-    PJSIP_TSX_STATE_MAX,
+    PJSIP_TSX_STATE_NULL,	/**< For UAC, before any message is sent.   */
+    PJSIP_TSX_STATE_CALLING,	/**< For UAC, just after request is sent.   */
+    PJSIP_TSX_STATE_TRYING,	/**< For UAS, just after request is received.*/
+    PJSIP_TSX_STATE_PROCEEDING,	/**< For UAS/UAC, after provisional response.*/
+    PJSIP_TSX_STATE_COMPLETED,	/**< For UAS/UAC, after final response.	    */
+    PJSIP_TSX_STATE_CONFIRMED,	/**< For UAS, after ACK is received.	    */
+    PJSIP_TSX_STATE_TERMINATED,	/**< For UAS/UAC, before it's destroyed.    */
+    PJSIP_TSX_STATE_DESTROYED,	/**< For UAS/UAC, will be destroyed now.    */
+    PJSIP_TSX_STATE_MAX,	/**< Number of states.			    */
 } pjsip_tsx_state_e;
 
 
 /**
- * State of the transport in the transaction.
- * The transport is progressing independently of the transaction.
- */
-typedef enum pjsip_tsx_transport_state_e
-{
-    PJSIP_TSX_TRANSPORT_STATE_NULL,
-    PJSIP_TSX_TRANSPORT_STATE_RESOLVING,
-    PJSIP_TSX_TRANSPORT_STATE_CONNECTING,
-    PJSIP_TSX_TRANSPORT_STATE_FINAL,
-} pjsip_tsx_transport_state_e;
-
-
-/**
- * Transaction state.
+ * This structure describes SIP transaction object. The transaction object
+ * is used to handle both UAS and UAC transaction.
  */
 struct pjsip_transaction
 {
@@ -79,18 +63,18 @@
      * Administrivia
      */
     pj_pool_t		       *pool;           /**< Pool owned by the tsx. */
+    pjsip_module	       *tsx_user;	/**< Transaction user.	    */
     pjsip_endpoint	       *endpt;          /**< Endpoint instance.     */
     pj_mutex_t		       *mutex;          /**< Mutex for this tsx.    */
-    char			obj_name[PJ_MAX_OBJ_NAME];  /**< Tsx name.  */
-    int                         tracing;        /**< Tracing enabled?       */
 
     /*
      * Transaction identification.
      */
+    char			obj_name[PJ_MAX_OBJ_NAME];  /**< Log info.  */
     pjsip_role_e		role;           /**< Role (UAS or UAC)      */
     pjsip_method		method;         /**< The method.            */
     int				cseq;           /**< The CSeq               */
-    pj_str_t			transaction_key;/**< hash table key.        */
+    pj_str_t			transaction_key;/**< Hash table key.        */
     pj_str_t			branch;         /**< The branch Id.         */
 
     /*
@@ -98,7 +82,8 @@
      */
     int				status_code;    /**< Last status code seen. */
     pjsip_tsx_state_e		state;          /**< State.                 */
-    int				handle_ack;     /**< Should we handle ACK?  */
+    int				handle_200resp; /**< UAS 200/INVITE  retrsm.*/
+    int                         tracing;        /**< Tracing enabled?       */
 
     /** Handler according to current state. */
     pj_status_t (*state_handler)(struct pjsip_transaction *, pjsip_event *);
@@ -106,123 +91,115 @@
     /*
      * Transport.
      */
-    pjsip_tsx_transport_state_e	transport_state;/**< Transport's state.     */
-    pjsip_host_info		dest_name;      /**< Destination address.   */
-    pjsip_server_addresses	remote_addr;    /**< Addresses resolved.    */
-    int				current_addr;   /**< Address currently used. */
-
     pjsip_transport	       *transport;      /**< Transport to use.      */
+    pj_sockaddr			addr;		/**< Destination address.   */
+    int				addr_len;	/**< Address length.	    */
+    pjsip_response_addr		res_addr;	/**< Response address.	    */
+    unsigned			transport_flag;	/**< Miscelaneous flag.	    */
 
     /*
      * Messages and timer.
      */
     pjsip_tx_data	       *last_tx;        /**< Msg kept for retrans.  */
-    int				has_unsent_msg; /**< Non-zero if tsx need to 
-                                                     transmit msg once resolver
-                                                     completes.             */
     int				retransmit_count;/**< Retransmission count. */
     pj_timer_entry		retransmit_timer;/**< Retransmit timer.     */
     pj_timer_entry		timeout_timer;  /**< Timeout timer.         */
 
     /** Module specific data. */
-    void		       *module_data[PJSIP_MAX_MODULE];
+    void		       *mod_data[PJSIP_MAX_MODULE];
 };
 
 
 /**
- * Create new transaction. Application would normally use 
- * #pjsip_endpt_create_tsx rather than this function.
+ * Create and register transaction layer module to the specified endpoint.
  *
- * @param pool	    Pool to use by the transaction.
- * @param endpt	    Endpoint.
- * @param p_tsx	    Pointer to return the transaction.
+ * @param endpt	    The endpoint instance.
  *
- * @return	    PJ_SUCCESS or the appropriate error code.
- *
- * @see pjsip_endpt_create_tsx
- *
+ * @return	    PJ_SUCCESS on success.
  */
-PJ_DEF(pj_status_t) pjsip_tsx_create( pj_pool_t *pool,
-				      pjsip_endpoint *endpt,
-				      pjsip_transaction **p_tsx);
+PJ_DECL(pj_status_t) pjsip_tsx_layer_init(pjsip_endpoint *endpt);
 
-/** 
- * Init transaction as UAC from the specified transmit data (\c tdata).
- * The transmit data must have a valid \c Request-Line and \c CSeq header.
- * If \c Route headers are present, it will be used to calculate remote
- * destination.
+/**
+ * Get the instance of the transaction layer module.
+ *
+ * @return	    The transaction layer module.
+ */
+PJ_DECL(pjsip_module*) pjsip_tsx_layer_instance(void);
+
+/**
+ * Unregister and destroy transaction layer module.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_tsx_layer_destroy(void);
+
+/**
+ * Find a transaction with the specified key. The transaction key normally
+ * is created by calling #pjsip_tsx_create_key() from an incoming message.
+ *
+ * @param key	    The key string to find the transaction.
+ * @param lock	    If non-zero, transaction will be locked before the
+ *		    function returns, to make sure that it's not deleted
+ *		    by other threads.
+ *
+ * @return	    The matching transaction instance, or NULL if transaction
+ *		    can not be found.
+ */
+PJ_DECL(pjsip_transaction*) pjsip_tsx_layer_find_tsx( const pj_str_t *key,
+						      pj_bool_t lock );
+
+/**
+ * Create, initialize, and register a new transaction as UAC from the 
+ * specified transmit data (\c tdata). The transmit data must have a valid
+ * \c Request-Line and \c CSeq header. 
  *
  * If \c Via header does not exist, it will be created along with a unique
  * \c branch parameter. If it exists and contains branch parameter, then
- * the \c branch parameter will be used as is as the transaction key.
+ * the \c branch parameter will be used as is as the transaction key. If
+ * it exists but branch parameter doesn't exist, a unique branch parameter
+ * will be created.
  *
- * The \c Route headers in the transmit data, if present, are used to 
- * calculate remote destination.
- *
- * At the end of the function, the transaction will start resolving the
- * addresses of remote server to contact. Transport will be acquired as soon
- * as the resolving job completes.
- *
- * @param tsx       The transaction.
- * @param tdata     The transmit data.
+ * @param tsx_user  Module to be registered as transaction user of the new
+ *		    transaction, which will receive notification from the
+ *		    transaction via on_tsx_state() callback.
+ * @param tdata     The outgoing request message.
+ * @param p_tsx	    On return will contain the new transaction instance.
  *
  * @return          PJ_SUCCESS if successfull.
  */
-PJ_DECL(pj_status_t) pjsip_tsx_init_uac( pjsip_transaction *tsx, 
-					 pjsip_tx_data *tdata);
+PJ_DECL(pj_status_t) pjsip_tsx_create_uac( pjsip_module *tsx_user,
+					   pjsip_tx_data *tdata,
+					   pjsip_transaction **p_tsx);
 
 /**
- * Init transaction as UAS.
+ * Create, initialize, and register a new transaction as UAS from the
+ * specified incoming request in \c rdata.
  *
- * @param tsx       The transaction to be initialized.
+ * @param tsx_user  Module to be registered as transaction user of the new
+ *		    transaction, which will receive notification from the
+ *		    transaction via on_tsx_state() callback.
  * @param rdata     The received incoming request.
+ * @param p_tsx	    On return will contain the new transaction instance.
  *
- * @return PJ_SUCCESS if successfull.
+ * @return	    PJ_SUCCESS if successfull.
  */
-PJ_DECL(pj_status_t) pjsip_tsx_init_uas( pjsip_transaction *tsx,
-					 pjsip_rx_data *rdata);
+PJ_DECL(pj_status_t) pjsip_tsx_create_uas( pjsip_module *tsx_user,
+					   pjsip_rx_data *rdata,
+					   pjsip_transaction **p_tsx );
 
 /**
- * Process incoming message for this transaction.
- *
- * @param tsx       The transaction.
- * @param rdata     The incoming message.
- */
-PJ_DECL(void) pjsip_tsx_on_rx_msg( pjsip_transaction *tsx,
-				   pjsip_rx_data *rdata);
-
-/**
- * Transmit message with this transaction.
+ * Transmit message in tdata with this transaction. It is possible to
+ * pass NULL in tdata for UAC transaction, which in this case the request
+ * message which was specified in #pjsip_tsx_create_uac() will be sent.
  *
  * @param tsx       The transaction.
  * @param tdata     The outgoing message.
- */
-PJ_DECL(void) pjsip_tsx_on_tx_msg( pjsip_transaction *tsx,
-				   pjsip_tx_data *tdata);
-
-
-/**
- * Transmit ACK message for 2xx/INVITE with this transaction. The ACK for
- * non-2xx/INVITE is automatically sent by the transaction.
- * This operation is only valid if the transaction is configured to handle ACK
- * (tsx->handle_ack is non-zero). If this attribute is not set, then the
- * transaction will comply with RFC-3261, i.e. it will set itself to 
- * TERMINATED state when it receives 2xx/INVITE.
  *
- * @param tsx       The transaction.
- * @param tdata     The ACK request.
+ * @return	    PJ_SUCCESS if successfull.
  */
-PJ_DECL(void) pjsip_tsx_on_tx_ack( pjsip_transaction *tsx,
-				   pjsip_tx_data *tdata);
+PJ_DECL(pj_status_t) pjsip_tsx_send_msg( pjsip_transaction *tsx,
+					 pjsip_tx_data *tdata);
 
-/**
- * Force terminate transaction.
- *
- * @param tsx       The transaction.
- * @param code      The status code to report.
- */
-PJ_DECL(void) pjsip_tsx_terminate( pjsip_transaction *tsx,
-				   int code );
 
 /**
  * Create transaction key, which is used to match incoming requests 
@@ -244,6 +221,29 @@
 
 
 /**
+ * Force terminate transaction.
+ *
+ * @param tsx       The transaction.
+ * @param code      The status code to report.
+ */
+PJ_DECL(pj_status_t) pjsip_tsx_terminate( pjsip_transaction *tsx,
+					  int st_code );
+
+
+/**
+ * Get the transaction instance in the incoming message. If the message
+ * has a corresponding transaction, this function will return non NULL
+ * value.
+ *
+ * @param rdata	    The incoming message buffer.
+ *
+ * @return	    The transaction instance associated with this message,
+ *		    or NULL if the message doesn't match any transactions.
+ */
+PJ_DECL(pjsip_transaction*) pjsip_rdata_get_tsx( pjsip_rx_data *rdata );
+
+
+/**
  * @}
  */
 
@@ -262,9 +262,6 @@
 PJ_DECL(const char *) pjsip_role_name(pjsip_role_e role);
 
 
-/* Thread Local Storage ID for transaction lock (initialized by endpoint) */
-extern long pjsip_tsx_lock_tls_id;
-
 PJ_END_DECL
 
 #endif	/* __PJSIP_TRANSACT_H__ */
diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h
index 6e6ba42..c296054 100644
--- a/pjsip/include/pjsip/sip_transport.h
+++ b/pjsip/include/pjsip/sip_transport.h
@@ -171,7 +171,7 @@
 	pj_uint32_t		 zero;
 
 	/** The length of the packet received. */
-	int			 len;
+	pj_ssize_t		 len;
 
 	/** The source address from which the packet was received. */
 	pj_sockaddr		 src_addr;
@@ -251,9 +251,9 @@
     struct
     {
 	/** 
-	 * This the transaction key generated for the message. 
+	 * Data attached by modules to this message. 
 	 */
-	pj_str_t		 key;
+	void	*mod_data[PJSIP_MAX_MODULE];
 
     } endpt_info;
 
diff --git a/pjsip/include/pjsip/sip_transport_loop.h b/pjsip/include/pjsip/sip_transport_loop.h
new file mode 100644
index 0000000..bfcb93f
--- /dev/null
+++ b/pjsip/include/pjsip/sip_transport_loop.h
@@ -0,0 +1,94 @@
+/* $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 
+ */
+#ifndef __PJSIP_TRANSPORT_LOOP_H__
+#define __PJSIP_TRANSPORT_LOOP_H__
+
+#include <pjsip/sip_transport.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * Create and start datagram loop transport.
+ *
+ * @param endpt		The endpoint instance.
+ * @param transport	Pointer to receive the transport instance.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_loop_start( pjsip_endpoint *endpt,
+				       pjsip_transport **transport);
+
+
+/**
+ * Enable/disable flag to discard any packets sent using the specified
+ * loop transport.
+ *
+ * @param tp		The loop transport.
+ * @param discard	If non-zero, any outgoing packets will be discarded.
+ * @param prev_value	Optional argument to receive previous value of
+ *			the discard flag.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_loop_set_discard( pjsip_transport *tp,
+					     pj_bool_t discard,
+					     pj_bool_t *prev_value );
+
+
+/**
+ * Enable/disable flag to simulate network error. When this flag is set,
+ * outgoing transmission will return either immediate error or error via
+ * callback. If error is to be notified via callback, then the notification
+ * will occur after some delay, which is controlled by #pjsip_loop_set_delay().
+ *
+ * @param tp		The loop transport.
+ * @param fail_flag	If set to 1, the transport will return immediate error.
+ *			If set to 2, the transport will return error via
+ *			callback. If zero, the transport will deliver
+ *			the packet.
+ * @param prev_value	Optional argument to receive previous value of
+ *			the failure flag.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_loop_set_failure( pjsip_transport *tp,
+					     int fail_flag,
+					     int *prev_value );
+
+
+/**
+ * Set delay (in miliseconds) before packet is delivered. This will also 
+ * control the delay for error notification callback.
+ *
+ * @param tp		The loop transport.
+ * @param delay		Delay, in miliseconds.
+ * @param prev_value	Optional argument to receive previous value of the
+ *			delay.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_loop_set_delay( pjsip_transport *tp,
+					   unsigned delay,
+					   unsigned *prev_value);
+
+PJ_END_DECL
+
+
+#endif	/* __PJSIP_TRANSPORT_LOOP_H__ */
+
diff --git a/pjsip/include/pjsip/sip_types.h b/pjsip/include/pjsip/sip_types.h
index 516d50d..72fe3af 100644
--- a/pjsip/include/pjsip/sip_types.h
+++ b/pjsip/include/pjsip/sip_types.h
@@ -52,6 +52,12 @@
     /** SCTP. */
     PJSIP_TRANSPORT_SCTP,
 
+    /** Loopback (stream, reliable) */
+    PJSIP_TRANSPORT_LOOP,
+
+    /** Loopback (datagram, unreliable) */
+    PJSIP_TRANSPORT_LOOP_DGRAM,
+
 } pjsip_transport_type_e;
 
 
diff --git a/pjsip/include/pjsip/sip_util.h b/pjsip/include/pjsip/sip_util.h
index ea0fcf3..f0d55bd 100644
--- a/pjsip/include/pjsip/sip_util.h
+++ b/pjsip/include/pjsip/sip_util.h
@@ -328,6 +328,29 @@
 							   pj_bool_t *cont));
 
 /**
+ * This composite function sends response message statelessly to an incoming
+ * request message. Internally it calls #pjsip_endpt_create_response() and
+ * #pjsip_endpt_send_response().
+ *
+ * @param endpt	    The endpoint instance.
+ * @param rdata	    The incoming request message.
+ * @param st_code   Status code of the response.
+ * @param st_text   Optional status text of the response.
+ * @param hdr_list  Optional header list to be added to the response.
+ * @param body	    Optional message body to be added to the response.
+ *
+ * @return	    PJ_SUCCESS if response message has successfully been
+ *		    created.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_respond_stateless(pjsip_endpoint *endpt,
+						   pjsip_rx_data *rdata,
+						   int st_code,
+						   const pj_str_t *st_text,
+						   const pjsip_hdr *hdr_list,
+						   const pjsip_msg_body *body);
+						    
+
+/**
  * Send outgoing request and initiate UAC transaction for the request.
  * This is an auxiliary function to be used by application to send arbitrary
  * requests outside a dialog. To send a request within a dialog, application
@@ -352,6 +375,65 @@
 					       void *token,
 					       void (*cb)(void*,pjsip_event*));
 
+/**
+ * Create new request message to be forwarded upstream to new destination URI 
+ * in uri. The new request is a full/deep clone of the request received in 
+ * rdata, unless if other copy mechanism is specified in the options. 
+ * The branch parameter, if not NULL, will be used as the branch-param in 
+ * the Via header. If it is NULL, then a unique branch parameter will be used.
+ *
+ * @param endpt	    The endpoint instance.
+ * @param rdata	    The incoming request message.
+ * @param uri	    The URI where the request will be forwarded to.
+ * @param branch    Optional branch parameter.
+ * @param options   Optional option flags when duplicating the message.
+ * @param tdata	    The result.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_create_request_fwd( pjsip_endpoint *endpt,
+						     pjsip_rx_data *rdata, 
+						     const pjsip_uri *uri,
+						     const pj_str_t *branch,
+						     unsigned options,
+						     pjsip_tx_data **tdata);
+
+/**
+ * Create new response message to be forwarded downstream by the proxy from 
+ * the response message found in rdata. Note that this function practically 
+ * will clone the response as is, i.e. without checking the validity of the 
+ * response or removing top most Via header. This function will perform 
+ * full/deep clone of the response, unless other copy mechanism is used in 
+ * the options.
+ *
+ * @param endpt	    The endpoint instance.
+ * @param rdata	    The incoming response message.
+ * @param options   Optional option flags when duplicate the message.
+ * @param tdata	    The result
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_create_response_fwd( pjsip_endpoint *endpt,
+						      pjsip_rx_data *rdata, 
+						      unsigned options,
+						      pjsip_tx_data **tdata);
+
+
+/**
+ * Create a globally unique branch parameter based on the information in 
+ * the incoming request message. This function guarantees that subsequent 
+ * retransmissions of the same request will generate the same branch id.
+ * This function can also be used in the loop detection process. 
+ * If the same request arrives back in the proxy with the same URL, it will
+ * calculate into the same branch id.
+ * Note that the returned string was allocated from rdata's pool.
+ *
+ * @param rdata	    The incoming request message.
+ *
+ * @return	    Unique branch-ID string.
+ */
+PJ_DECL(pj_str_t) pjsip_calculate_branch_id( pjsip_rx_data *rdata );
+
 
 /**
  * @}
diff --git a/pjsip/include/pjsip_core.h b/pjsip/include/pjsip_core.h
index 9f3c7e9..5947d4b 100644
--- a/pjsip/include/pjsip_core.h
+++ b/pjsip/include/pjsip_core.h
@@ -32,6 +32,7 @@
 #include <pjsip/sip_transaction.h>
 #include <pjsip/sip_transport.h>
 #include <pjsip/sip_transport_udp.h>
+#include <pjsip/sip_transport_loop.h>
 #include <pjsip/sip_uri.h>
 #include <pjsip/sip_util.h>
 
diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c
index 61741cb..d299cff 100644
--- a/pjsip/src/pjsip/sip_endpoint.c
+++ b/pjsip/src/pjsip/sip_endpoint.c
@@ -490,9 +490,9 @@
  * Return back pool to endpoint's pool manager to be either destroyed or
  * recycled.
  */
-PJ_DEF(void) pjsip_endpt_destroy_pool( pjsip_endpoint *endpt, pj_pool_t *pool )
+PJ_DEF(void) pjsip_endpt_release_pool( pjsip_endpoint *endpt, pj_pool_t *pool )
 {
-    PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy_pool(%s)", pj_pool_getobjname(pool)));
+    PJ_LOG(5, (THIS_FILE, "pjsip_endpt_release_pool(%s)", pj_pool_getobjname(pool)));
 
     pj_mutex_lock(endpt->mutex);
     pj_pool_release( pool );
diff --git a/pjsip/src/pjsip/sip_errno.c b/pjsip/src/pjsip/sip_errno.c
index f3c7319..f7d2ddd 100644
--- a/pjsip/src/pjsip/sip_errno.c
+++ b/pjsip/src/pjsip/sip_errno.c
@@ -1,4 +1,4 @@
-/* $Id: $ */
+/* $Id$ */
 /* 
  * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
  *
@@ -47,6 +47,7 @@
     { PJSIP_EINVALIDREQURI,	"Invalid Request URI" },
     { PJSIP_ENOTREQUESTMSG,	"Expecting request message"},
     { PJSIP_ENOTRESPONSEMSG,	"Expecting response message"},
+    { PJSIP_EINVALIDHDR,	"Invalid header field"},
 
     /* Transport errors */
     { PJSIP_EUNSUPTRANSPORT,	"Unsupported transport"},
diff --git a/pjsip/src/pjsip/sip_msg.c b/pjsip/src/pjsip/sip_msg.c
index 1d5ffe0..cee850e 100644
--- a/pjsip/src/pjsip/sip_msg.c
+++ b/pjsip/src/pjsip/sip_msg.c
@@ -19,6 +19,7 @@
 #include <pjsip/sip_msg.h>
 #include <pjsip/sip_parser.h>
 #include <pjsip/print_util.h>
+#include <pjsip/sip_errno.h>
 #include <pj/string.h>
 #include <pj/pool.h>
 #include <pj/assert.h>
@@ -365,37 +366,44 @@
 
     /* Process message body. */
     if (msg->body) {
-	pj_str_t ctype_hdr = { "Content-Type: ", 14};
-	int len;
-	const pjsip_media_type *media = &msg->body->content_type;
-	char *clen_pos;
+	char *clen_pos = NULL;
 
-	/* Add Content-Type header. */
-	if ( (end-p) < 24+media->type.slen+media->subtype.slen+media->param.slen) {
-	    return -1;
-	}
-	pj_memcpy(p, ctype_hdr.ptr, ctype_hdr.slen);
-	p += ctype_hdr.slen;
-	p += print_media_type(p, media);
-	*p++ = '\r';
-	*p++ = '\n';
-
-	/* Add Content-Length header. */
-	if ((end-p) < clen_hdr.slen+12+2) {
-	    return -1;
-	}
-	pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen);
-	p += clen_hdr.slen;
-	
-	/* Print blanks after "Content-Type:", this is where we'll put
-	 * the content length value after we know the length of the
-	 * body.
+	/* Automaticly adds Content-Type and Content-Length headers, only
+	 * if content_type is set in the message body.
 	 */
-	pj_memset(p, ' ', 12);
-	clen_pos = p;
-	p += 12;
-	*p++ = '\r';
-	*p++ = '\n';
+	if (msg->body->content_type.type.slen) {
+	    pj_str_t ctype_hdr = { "Content-Type: ", 14};
+	    const pjsip_media_type *media = &msg->body->content_type;
+
+	    /* Add Content-Type header. */
+	    if ( (end-p) < 24 + media->type.slen + media->subtype.slen + 
+			   media->param.slen) 
+	    {
+		return -1;
+	    }
+	    pj_memcpy(p, ctype_hdr.ptr, ctype_hdr.slen);
+	    p += ctype_hdr.slen;
+	    p += print_media_type(p, media);
+	    *p++ = '\r';
+	    *p++ = '\n';
+
+	    /* Add Content-Length header. */
+	    if ((end-p) < clen_hdr.slen + 12 + 2) {
+		return -1;
+	    }
+	    pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen);
+	    p += clen_hdr.slen;
+	    
+	    /* Print blanks after "Content-Type:", this is where we'll put
+	     * the content length value after we know the length of the
+	     * body.
+	     */
+	    pj_memset(p, ' ', 12);
+	    clen_pos = p;
+	    p += 12;
+	    *p++ = '\r';
+	    *p++ = '\n';
+	}
 	
 	/* Add blank newline. */
 	*p++ = '\r';
@@ -411,8 +419,10 @@
 	/* Now that we have the length of the body, print this to the
 	 * Content-Length header.
 	 */
-	len = pj_utoa(len, clen_pos);
-	clen_pos[len] = ' ';
+	if (clen_pos) {
+	    len = pj_utoa(len, clen_pos);
+	    clen_pos[len] = ' ';
+	}
 
     } else {
 	/* There's no message body.
@@ -1464,7 +1474,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 /*
- * General purpose function to textual data in a SIP body. 
+ * Message body manipulations.
  */
 PJ_DEF(int) pjsip_print_text_body(pjsip_msg_body *msg_body, char *buf, pj_size_t size)
 {
@@ -1473,3 +1483,33 @@
     pj_memcpy(buf, msg_body->data, msg_body->len);
     return msg_body->len;
 }
+
+PJ_DEF(pj_status_t) pjsip_msg_body_clone( pj_pool_t *pool,
+					  pjsip_msg_body *dst_body,
+					  const pjsip_msg_body *src_body )
+{
+    /* First check if clone_data field is initialized. */
+    PJ_ASSERT_RETURN( src_body->clone_data!=NULL, PJ_EINVAL );
+
+    /* Duplicate content-type */
+    pj_strdup(pool, &dst_body->content_type.type, 
+		    &src_body->content_type.type);
+    pj_strdup(pool, &dst_body->content_type.subtype, 
+		    &src_body->content_type.subtype);
+    pj_strdup(pool, &dst_body->content_type.param,
+		    &src_body->content_type.param);
+
+    /* Duplicate data. */
+    dst_body->data = (*src_body->clone_data)(pool, src_body->data, 
+					     src_body->len );
+
+    /* Length. */
+    dst_body->len = src_body->len;
+
+    /* Function pointers. */
+    dst_body->print_body = src_body->print_body;
+    dst_body->clone_data = src_body->clone_data;
+
+    return PJ_SUCCESS;
+}
+
diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c
index 1497fbd..cd67333 100644
--- a/pjsip/src/pjsip/sip_transaction.c
+++ b/pjsip/src/pjsip/sip_transaction.c
@@ -17,413 +17,64 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
  */
 #include <pjsip/sip_transaction.h>
-#include <pjsip/sip_transport.h>
-#include <pjsip/sip_config.h>
 #include <pjsip/sip_util.h>
-#include <pjsip/sip_event.h>
+#include <pjsip/sip_module.h>
 #include <pjsip/sip_endpoint.h>
 #include <pjsip/sip_errno.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/os.h>
-#include <pj/guid.h>
+#include <pjsip/sip_event.h>
+#include <pj/hash.h>
 #include <pj/pool.h>
+#include <pj/os.h>
+#include <pj/string.h>
 #include <pj/assert.h>
+#include <pj/guid.h>
+#include <pj/log.h>
 
-#if 0	// XXX JUNK
-    /* Initialize TLS ID for transaction lock. */
-    status = pj_thread_local_alloc(&pjsip_tsx_lock_tls_id);
-    if (status != PJ_SUCCESS) {
-	goto on_error;
-    }
-    pj_thread_local_set(pjsip_tsx_lock_tls_id, NULL);
+/*****************************************************************************
+ **
+ ** Declarations and static variable definitions section.
+ **
+ *****************************************************************************
 
+/* Prototypes. */
+static pj_status_t mod_tsx_layer_load(pjsip_endpoint *endpt);
+static pj_status_t mod_tsx_layer_start(void);
+static pj_status_t mod_tsx_layer_stop(void);
+static pj_status_t mod_tsx_layer_unload(void);
+static pj_bool_t   mod_tsx_layer_on_rx_request(pjsip_rx_data *rdata);
+static pj_bool_t   mod_tsx_layer_on_rx_response(pjsip_rx_data *rdata);
 
-    /* Create hash table for transaction. */
-    endpt->tsx_table = pj_hash_create( endpt->pool, PJSIP_MAX_TSX_COUNT );
-    if (!endpt->tsx_table) {
-	status = PJ_ENOMEM;
-	goto on_error;
-    }
-
-
-/*
- * Create a new transaction.
- * Endpoint must then initialize the new transaction as either UAS or UAC, and
- * register it to the hash table.
- */
-PJ_DEF(pj_status_t) pjsip_endpt_create_tsx(pjsip_endpoint *endpt,
-					   pjsip_transaction **p_tsx)
+/* Transaction layer module definition. */
+static struct mod_tsx_layer
 {
-    pj_pool_t *pool;
-
-    PJ_ASSERT_RETURN(endpt && p_tsx, PJ_EINVAL);
-
-    PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_tsx()"));
-
-    /* Request one pool for the transaction. Mutex is locked there. */
-    pool = pjsip_endpt_create_pool(endpt, "ptsx%p", 
-				      PJSIP_POOL_LEN_TSX, PJSIP_POOL_INC_TSX);
-    if (pool == NULL) {
-	return PJ_ENOMEM;
+    struct pjsip_module  mod;
+    pj_pool_t		*pool;
+    pjsip_endpoint	*endpt;
+    pj_mutex_t		*mutex;
+    pj_hash_table_t	*htable;
+} mod_tsx_layer = 
+{   {
+	NULL, NULL,			/* List's prev and next.    */
+	{ "mod-tsx-layer", 13 },	/* Module name.		    */
+	-1,				/* Module ID		    */
+	PJSIP_MOD_PRIORITY_TSX_LAYER,	/* Priority.		    */
+	NULL,				/* User_data.		    */
+	0,				/* Methods count.	    */
+	{ NULL },			/* Array of methods.	    */
+	mod_tsx_layer_load,		/* load().		    */
+	mod_tsx_layer_start,		/* start()		    */
+	mod_tsx_layer_stop,		/* stop()		    */
+	mod_tsx_layer_unload,		/* unload()		    */
+	mod_tsx_layer_on_rx_request,	/* on_rx_request()	    */
+	mod_tsx_layer_on_rx_response,	/* on_rx_response()	    */
+	NULL
     }
+};
 
-    /* Create the transaction. */
-    return pjsip_tsx_create(pool, endpt, p_tsx);
-}
+/* Thread Local Storage ID for transaction lock */
+static long pjsip_tsx_lock_tls_id;
 
-
-/*
- * Register the transaction to the endpoint.
- * This will put the transaction to the transaction hash table. Before calling
- * this function, the transaction must be INITIALIZED as either UAS or UAC, so
- * that the transaction key is built.
- */
-PJ_DEF(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt,
-				       pjsip_transaction *tsx)
-{
-    PJ_LOG(5, (THIS_FILE, "pjsip_endpt_register_tsx(%s)", tsx->obj_name));
-
-    pj_assert(tsx->transaction_key.slen != 0);
-    //pj_assert(tsx->state != PJSIP_TSX_STATE_NULL);
-
-    /* Lock hash table mutex. */
-    pj_mutex_lock(endpt->tsx_table_mutex);
-
-    /* Register the transaction to the hash table. */
-    pj_hash_set( tsx->pool, endpt->tsx_table, tsx->transaction_key.ptr,
-		 tsx->transaction_key.slen, tsx);
-
-    /* Unlock mutex. */
-    pj_mutex_unlock(endpt->tsx_table_mutex);
-}
-
-/*
- * Find transaction by the key.
- */
-PJ_DEF(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt,
-					          const pj_str_t *key )
-{
-    pjsip_transaction *tsx;
-
-    PJ_LOG(5, (THIS_FILE, "pjsip_endpt_find_tsx()"));
-
-    /* Start lock mutex in the endpoint. */
-    pj_mutex_lock(endpt->tsx_table_mutex);
-
-    /* Find the transaction in the hash table. */
-    tsx = pj_hash_get( endpt->tsx_table, key->ptr, key->slen );
-
-    /* Unlock mutex. */
-    pj_mutex_unlock(endpt->tsx_table_mutex);
-
-    return tsx;
-}
-
-/*
- * Create key.
- */
-static void rdata_create_key( pjsip_rx_data *rdata)
-{
-    pjsip_role_e role;
-    if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
-	role = PJSIP_ROLE_UAS;
-    } else {
-	role = PJSIP_ROLE_UAC;
-    }
-    pjsip_tsx_create_key(rdata->tp_info.pool, &rdata->endpt_info.key, role,
-			 &rdata->msg_info.cseq->method, rdata);
-}
-
-
-/*
- * This is the callback that is called by the transport manager when it 
- * receives a message from the network.
- */
-static void endpt_transport_callback( pjsip_endpoint *endpt,
-				      pj_status_t status,
-				      pjsip_rx_data *rdata )
-{
-    pjsip_msg *msg = rdata->msg_info.msg;
-    pjsip_transaction *tsx;
-    pj_bool_t a_new_transaction_just_been_created = PJ_FALSE;
-
-    PJ_LOG(5, (THIS_FILE, "endpt_transport_callback(rdata=%p)", rdata));
-
-    if (status != PJ_SUCCESS) {
-	const char *src_addr = rdata->pkt_info.src_name;
-	int port = rdata->pkt_info.src_port;
-	PJSIP_ENDPT_LOG_ERROR((endpt, "transport", status,
-			       "Src.addr=%s:%d, packet:--\n"
-			       "%s\n"
-			       "-- end of packet. Error",
-			       src_addr, port, rdata->msg_info.msg_buf));
-	return;
-    }
-
-    /* For response, check that the value in Via sent-by match the transport.
-     * If not matched, silently drop the response.
-     * Ref: RFC3261 Section 18.1.2 Receiving Response
-     */
-    if (msg->type == PJSIP_RESPONSE_MSG) {
-	const pj_str_t *addr_addr;
-	int port = rdata->msg_info.via->sent_by.port;
-	pj_bool_t mismatch = PJ_FALSE;
-	if (port == 0) {
-	    int type;
-	    type = rdata->tp_info.transport->key.type;
-	    port = pjsip_transport_get_default_port_for_type(type);
-	}
-	addr_addr = &rdata->tp_info.transport->local_name.host;
-	if (pj_strcmp(&rdata->msg_info.via->sent_by.host, addr_addr) != 0)
-	    mismatch = PJ_TRUE;
-	else if (port != rdata->tp_info.transport->local_name.port) {
-	    /* Port or address mismatch, we should discard response */
-	    /* But we saw one implementation (we don't want to name it to 
-	     * protect the innocence) which put wrong sent-by port although
-	     * the "rport" parameter is correct.
-	     * So we discard the response only if the port doesn't match
-	     * both the port in sent-by and rport. We try to be lenient here!
-	     */
-	    if (rdata->msg_info.via->rport_param != rdata->tp_info.transport->local_name.port)
-		mismatch = PJ_TRUE;
-	    else {
-		PJ_LOG(4,(THIS_FILE, "Response %p has mismatch port in sent-by"
-				    " but the rport parameter is correct",
-				    rdata));
-	    }
-	}
-
-	if (mismatch) {
-	    pjsip_event e;
-
-	    PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata, PJSIP_EINVALIDVIA);
-	    endpt_do_event( endpt, &e );
-	    return;
-	}
-    }
-
-    /* Create key for transaction lookup. */
-    rdata_create_key( rdata);
-
-    /* Find the transaction for the received message. */
-    PJ_LOG(5, (THIS_FILE, "finding tsx with key=%.*s", 
-			 rdata->endpt_info.key.slen, rdata->endpt_info.key.ptr));
-
-    /* Start lock mutex in the endpoint. */
-    pj_mutex_lock(endpt->tsx_table_mutex);
-
-    /* Find the transaction in the hash table. */
-    tsx = pj_hash_get( endpt->tsx_table, rdata->endpt_info.key.ptr, rdata->endpt_info.key.slen );
-
-    /* Unlock mutex. */
-    pj_mutex_unlock(endpt->tsx_table_mutex);
-
-    /* If the transaction is not found... */
-    if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) {
-
-	/* 
-	 * For response message, discard the message, except if the response is
-	 * an 2xx class response to INVITE, which in this case it must be
-	 * passed to TU to be acked.
-	 */
-	if (msg->type == PJSIP_RESPONSE_MSG) {
-
-	    /* Inform TU about the 200 message, only if it's INVITE. */
-	    if (PJSIP_IS_STATUS_IN_CLASS(msg->line.status.code, 200) &&
-		rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD) 
-	    {
-		pjsip_event e;
-
-		/* Should not happen for UA. Tsx theoritically lives until
-		 * all responses are absorbed.
-		 */
-		pj_assert(0);
-
-		PJSIP_EVENT_INIT_RX_200_MSG(e, rdata);
-		endpt_do_event( endpt, &e );
-
-	    } else {
-		/* Just discard the response, inform TU. */
-		pjsip_event e;
-
-		PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata, 
-		    PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_CALL_TSX_DOES_NOT_EXIST));
-		endpt_do_event( endpt, &e );
-	    }
-
-	/*
-	 * For non-ACK request message, create a new transaction.
-	 */
-	} else if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) {
-
-	    pj_status_t status;
-
-	    /* Create transaction, mutex is locked there. */
-	    status = pjsip_endpt_create_tsx(endpt, &tsx);
-	    if (status != PJ_SUCCESS) {
-		PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
-				       "Unable to create transaction"));
-		return;
-	    }
-
-	    /* Initialize transaction as UAS. */
-	    pjsip_tsx_init_uas( tsx, rdata );
-
-	    /* Register transaction, mutex is locked there. */
-	    pjsip_endpt_register_tsx( endpt, tsx );
-
-	    a_new_transaction_just_been_created = PJ_TRUE;
-	}
-    }
-
-    /* If transaction is found (or newly created), pass the message.
-     * Otherwise if it's an ACK request, pass directly to TU.
-     */
-    if (tsx && tsx->state != PJSIP_TSX_STATE_TERMINATED) {
-	/* Dispatch message to transaction. */
-	pjsip_tsx_on_rx_msg( tsx, rdata );
-
-    } else if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD) {
-	/*
-	 * This is an ACK message, but the INVITE transaction could not
-	 * be found (possibly because the branch parameter in Via in ACK msg
-	 * is different than the branch in original INVITE). This happens with
-	 * SER!
-	 */
-	pjsip_event event;
-
-	PJSIP_EVENT_INIT_RX_ACK_MSG(event,rdata);
-	endpt_do_event( endpt, &event );
-    }
-
-    /*
-     * If a new request message has just been receieved, but no modules
-     * seem to be able to handle the request message, then terminate the
-     * transaction.
-     *
-     * Ideally for cases like "unsupported method", we should be able to
-     * answer the request statelessly. But we can not do that since the
-     * endpoint shoule be able to be used as both user agent and proxy stack,
-     * and a proxy stack should be able to handle arbitrary methods.
-     */
-    if (a_new_transaction_just_been_created && tsx->status_code < 100) {
-	/* Certainly no modules has sent any response message.
-	 * Check that any modules has attached a module data.
-	 */
-	int i;
-	for (i=0; i<PJSIP_MAX_MODULE; ++i) {
-	    if (tsx->module_data[i] != NULL) {
-		break;
-	    }
-	}
-	if (i == PJSIP_MAX_MODULE) {
-	    /* No modules have attached itself to the transaction. 
-	     * Terminate the transaction with 501/Not Implemented.
-	     */
-	    pjsip_tx_data *tdata;
-	    pj_status_t status;
-	    
-	    if (tsx->method.id == PJSIP_OPTIONS_METHOD) {
-		status = pjsip_endpt_create_response(endpt, rdata, 200, 
-						     &tdata);
-	    } else {
-		status = pjsip_endpt_create_response(endpt, rdata, 
-						     PJSIP_SC_METHOD_NOT_ALLOWED,
-						     &tdata);
-	    }
-
-	    if (status != PJ_SUCCESS) {
-		PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
-				       "Unable to create response"));
-		return;
-	    }
-
-	    if (endpt->allow_hdr) {
-		pjsip_msg_add_hdr( tdata->msg, 
-				   pjsip_hdr_shallow_clone(tdata->pool, endpt->allow_hdr));
-	    }
-	    pjsip_tsx_on_tx_msg( tsx, tdata );
-
-	} else {
-	    /*
-	     * If a module has registered itself in the transaction but it
-	     * hasn't responded the request, chances are the module wouldn't
-	     * respond to the request at all. We terminate the request here
-	     * with 500/Internal Server Error, to be safe.
-	     */
-	    pjsip_tx_data *tdata;
-	    pj_status_t status;
-
-	    status = pjsip_endpt_create_response(endpt, rdata, 500, &tdata);
-	    if (status != PJ_SUCCESS) {
-		PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
-				       "Unable to create response"));
-		return;
-	    }
-
-	    pjsip_tsx_on_tx_msg(tsx, tdata);
-	}
-    }
-}
-
-
-
-    /* Transaction tables. */
-    count = pj_hash_count(endpt->tsx_table);
-    PJ_LOG(3, (THIS_FILE, " Number of transactions: %u", count));
-
-    if (count && detail) {
-	pj_hash_iterator_t it_val;
-	pj_hash_iterator_t *it;
-	pj_time_val now;
-
-	PJ_LOG(3, (THIS_FILE, " Dumping transaction tables:"));
-
-	pj_gettimeofday(&now);
-	it = pj_hash_first(endpt->tsx_table, &it_val);
-
-	while (it != NULL) {
-	    int timeout_diff;
-
-	    /* Get the transaction. No need to lock transaction's mutex
-	     * since we already hold endpoint mutex, so that no transactions
-	     * will be deleted.
-	     */
-	    pjsip_transaction *tsx = pj_hash_this(endpt->tsx_table, it);
-
-	    const char *role = (tsx->role == PJSIP_ROLE_UAS ? "UAS" : "UAC");
-	
-	    if (tsx->timeout_timer._timer_id != -1) {
-		if (tsx->timeout_timer._timer_value.sec > now.sec) {
-		    timeout_diff = tsx->timeout_timer._timer_value.sec - now.sec;
-		} else {
-		    timeout_diff = now.sec - tsx->timeout_timer._timer_value.sec;
-		    timeout_diff = 0 - timeout_diff;
-		}
-	    } else {
-		timeout_diff = -1;
-	    }
-
-	    PJ_LOG(3, (THIS_FILE, "  %s %s %10.*s %.9u %s t=%ds", 
-		       tsx->obj_name, role, 
-		       tsx->method.name.slen, tsx->method.name.ptr,
-		       tsx->cseq,
-		       pjsip_tsx_state_str(tsx->state),
-		       timeout_diff));
-
-	    it = pj_hash_next(endpt->tsx_table, it);
-	}
-    }
-
-
-
-#endif	// XXX JUNK
-
-/* Thread Local Storage ID for transaction lock (initialized by endpoint) */
-long pjsip_tsx_lock_tls_id;
-
-/* State names */
+/* Transaction state names */
 static const char *state_str[] = 
 {
     "Null",
@@ -439,8 +90,17 @@
 /* Role names */
 static const char *role_name[] = 
 {
-    "Client",
-    "Server"
+    "UAC",
+    "UAS"
+};
+
+/* Transport flag. */
+enum
+{
+    TSX_HAS_PENDING_TRANSPORT	= 1,
+    TSX_HAS_PENDING_RESCHED	= 2,
+    TSX_HAS_PENDING_SEND	= 4,
+    TSX_HAS_PENDING_DESTROY	= 8,
 };
 
 /* Transaction lock. */
@@ -468,65 +128,78 @@
     TSX_TIMER_TIMEOUT,
 };
 
-/* Function Prototypes */
-static pj_status_t pjsip_tsx_on_state_null(     pjsip_transaction *tsx, 
-				                pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_calling(  pjsip_transaction *tsx, 
-				                pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_trying(   pjsip_transaction *tsx, 
-				                pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_proceeding_uas( pjsip_transaction *tsx, 
-					        pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_proceeding_uac( pjsip_transaction *tsx,
-					        pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_completed_uas( pjsip_transaction *tsx, 
-					        pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_completed_uac( pjsip_transaction *tsx,
-					        pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_confirmed(pjsip_transaction *tsx, 
-					        pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_terminated(pjsip_transaction *tsx, 
-					        pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_destroyed(pjsip_transaction *tsx, 
-					        pjsip_event *event);
 
-static void         tsx_timer_callback( pj_timer_heap_t *theap, 
-			                pj_timer_entry *entry);
-static int          tsx_send_msg( pjsip_transaction *tsx, 
-                                  pjsip_tx_data *tdata);
-static void         lock_tsx( pjsip_transaction *tsx, struct 
-                               tsx_lock_data *lck );
-static pj_status_t  unlock_tsx( pjsip_transaction *tsx, 
-                               struct tsx_lock_data *lck );
+/* Prototypes. */
+static void	   lock_tsx(pjsip_transaction *tsx, struct tsx_lock_data *lck);
+static pj_status_t unlock_tsx( pjsip_transaction *tsx, 
+                               struct tsx_lock_data *lck);
+static pj_status_t tsx_on_state_null(		pjsip_transaction *tsx, 
+				                pjsip_event *event);
+static pj_status_t tsx_on_state_calling(	pjsip_transaction *tsx, 
+				                pjsip_event *event);
+static pj_status_t tsx_on_state_trying(		pjsip_transaction *tsx, 
+				                pjsip_event *event);
+static pj_status_t tsx_on_state_proceeding_uas( pjsip_transaction *tsx, 
+					        pjsip_event *event);
+static pj_status_t tsx_on_state_proceeding_uac( pjsip_transaction *tsx,
+					        pjsip_event *event);
+static pj_status_t tsx_on_state_completed_uas(	pjsip_transaction *tsx, 
+					        pjsip_event *event);
+static pj_status_t tsx_on_state_completed_uac(	pjsip_transaction *tsx,
+					        pjsip_event *event);
+static pj_status_t tsx_on_state_confirmed(	pjsip_transaction *tsx, 
+					        pjsip_event *event);
+static pj_status_t tsx_on_state_terminated(	pjsip_transaction *tsx, 
+					        pjsip_event *event);
+static pj_status_t tsx_on_state_destroyed(	pjsip_transaction *tsx, 
+					        pjsip_event *event);
+static void        tsx_timer_callback( pj_timer_heap_t *theap, 
+			               pj_timer_entry *entry);
+static pj_status_t tsx_create( pjsip_module *tsx_user,
+			       pjsip_transaction **p_tsx);
+static void	   tsx_destroy( pjsip_transaction *tsx );
+static void	   tsx_resched_retransmission( pjsip_transaction *tsx );
+static pj_status_t tsx_retransmit( pjsip_transaction *tsx, int resched);
+static int         tsx_send_msg( pjsip_transaction *tsx, 
+                                 pjsip_tx_data *tdata);
+static void	   tsx_on_rx_msg( pjsip_transaction *tsx,
+				  pjsip_rx_data *rdata );
+
 
 /* State handlers for UAC, indexed by state */
 static int  (*tsx_state_handler_uac[PJSIP_TSX_STATE_MAX])(pjsip_transaction *,
 							  pjsip_event *) = 
 {
-    &pjsip_tsx_on_state_null,
-    &pjsip_tsx_on_state_calling,
-    &pjsip_tsx_on_state_trying,
-    &pjsip_tsx_on_state_proceeding_uac,
-    &pjsip_tsx_on_state_completed_uac,
-    &pjsip_tsx_on_state_confirmed,
-    &pjsip_tsx_on_state_terminated,
-    &pjsip_tsx_on_state_destroyed,
+    &tsx_on_state_null,
+    &tsx_on_state_calling,
+    NULL,
+    &tsx_on_state_proceeding_uac,
+    &tsx_on_state_completed_uac,
+    &tsx_on_state_confirmed,
+    &tsx_on_state_terminated,
+    &tsx_on_state_destroyed,
 };
 
 /* State handlers for UAS */
 static int  (*tsx_state_handler_uas[PJSIP_TSX_STATE_MAX])(pjsip_transaction *, 
 							  pjsip_event *) = 
 {
-    &pjsip_tsx_on_state_null,
-    &pjsip_tsx_on_state_calling,
-    &pjsip_tsx_on_state_trying,
-    &pjsip_tsx_on_state_proceeding_uas,
-    &pjsip_tsx_on_state_completed_uas,
-    &pjsip_tsx_on_state_confirmed,
-    &pjsip_tsx_on_state_terminated,
-    &pjsip_tsx_on_state_destroyed,
+    &tsx_on_state_null,
+    NULL,
+    &tsx_on_state_trying,
+    &tsx_on_state_proceeding_uas,
+    &tsx_on_state_completed_uas,
+    &tsx_on_state_confirmed,
+    &tsx_on_state_terminated,
+    &tsx_on_state_destroyed,
 };
 
+/*****************************************************************************
+ **
+ ** Utilities
+ **
+ *****************************************************************************
+ */
 /*
  * Get transaction state name.
  */
@@ -544,45 +217,6 @@
 }
 
 
-
-/*
- * Unregister the transaction from the hash table, and destroy the resources
- * from the transaction.
- */
-PJ_DEF(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt,
-				      pjsip_transaction *tsx)
-{
-    PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy_tsx(%s)", tsx->obj_name));
-
-    pj_assert(tsx->state == PJSIP_TSX_STATE_DESTROYED);
-
-    /* No need to lock transaction. 
-     * This function typically is called from the transaction callback, which
-     * means that transaction mutex is being held.
-     */
-    pj_assert( pj_mutex_is_locked(tsx->mutex) );
-
-    /* Lock endpoint. */
-    pj_mutex_lock( endpt->tsx_table_mutex );
-
-    /* Unregister from the hash table. */
-    pj_hash_set( NULL, endpt->tsx_table, tsx->transaction_key.ptr, 
-		 tsx->transaction_key.slen, NULL);
-
-    /* Unlock endpoint mutex. */
-    pj_mutex_unlock( endpt->tsx_table_mutex );
-
-    /* Destroy transaction mutex. */
-    pj_mutex_destroy( tsx->mutex );
-
-    /* Release the pool for the transaction. */
-    pj_pool_release(tsx->pool);
-
-    PJ_LOG(4, (THIS_FILE, "tsx%p destroyed", tsx));
-}
-
-
-
 /*
  * Create transaction key for RFC2543 compliant messages, which don't have
  * unique branch parameter in the top most Via header.
@@ -643,15 +277,6 @@
     *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's');
     *p++ = SEPARATOR;
 
-    /* Add Request-URI */
-    /* This is BUG!
-     * Response doesn't have Request-URI!
-     *
-    len = req_uri->vptr->print( PJSIP_URI_IN_REQ_URI, req_uri, p, end-p );
-    p += len;
-    *p++ = SEPARATOR;
-     */
-
     /* Add method, except when method is INVITE or ACK. */
     if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) {
 	pj_memcpy(p, method->name.ptr, method->name.slen);
@@ -770,39 +395,318 @@
     }
 }
 
+/*****************************************************************************
+ **
+ ** Transaction layer module
+ **
+ *****************************************************************************
 
 /*
- * Create new transaction.
+ * Create transaction layer module and registers it to the endpoint.
  */
-PJ_DEF(pj_status_t) pjsip_tsx_create( pj_pool_t *pool,
-				      pjsip_endpoint *endpt,
-				      pjsip_transaction **p_tsx)
+PJ_DEF(pj_status_t) pjsip_tsx_layer_init(pjsip_endpoint *endpt)
 {
-    pjsip_transaction *tsx;
+    pj_pool_t *pool;
     pj_status_t status;
 
-    tsx = pj_pool_calloc(pool, 1, sizeof(pjsip_transaction));
 
-    tsx->pool = pool;
-    tsx->endpt = endpt;
-    tsx->retransmit_timer.id = TSX_TIMER_RETRANSMISSION;
-    tsx->retransmit_timer._timer_id = -1;
-    tsx->retransmit_timer.user_data = tsx;
-    tsx->retransmit_timer.cb = &tsx_timer_callback;
-    tsx->timeout_timer.id = TSX_TIMER_TIMEOUT;
-    tsx->timeout_timer._timer_id = -1;
-    tsx->timeout_timer.user_data = tsx;
-    tsx->timeout_timer.cb = &tsx_timer_callback;
-    pj_sprintf(tsx->obj_name, "tsx%p", tsx);
-    status = pj_mutex_create_recursive(pool, "mtsx%p", &tsx->mutex);
+    PJ_ASSERT_RETURN(mod_tsx_layer.endpt==NULL, PJ_EINVALIDOP);
+
+
+    /* Initialize TLS ID for transaction lock. */
+    status = pj_thread_local_alloc(&pjsip_tsx_lock_tls_id);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    pj_thread_local_set(pjsip_tsx_lock_tls_id, NULL);
+
+    /*
+     * Initialize transaction layer structure.
+     */
+
+    /* Create pool for the module. */
+    pool = pjsip_endpt_create_pool(endpt, "tsxlayer", 
+				   PJSIP_POOL_TSX_LAYER_LEN,
+				   PJSIP_POOL_TSX_LAYER_INC );
+    if (!pool)
+	return PJ_ENOMEM;
+
+    
+    /* Initialize some attributes. */
+    mod_tsx_layer.pool = pool;
+    mod_tsx_layer.endpt = endpt;
+
+
+    /* Create hash table. */
+    mod_tsx_layer.htable = pj_hash_create( pool, PJSIP_MAX_TSX_COUNT );
+    if (!mod_tsx_layer.htable) {
+	pjsip_endpt_release_pool(endpt, pool);
+	return PJ_ENOMEM;
+    }
+
+    /* Create mutex. */
+    status = pj_mutex_create_recursive(pool, "tsxlayer", &mod_tsx_layer.mutex);
     if (status != PJ_SUCCESS) {
+	pjsip_endpt_release_pool(endpt, pool);
 	return status;
     }
 
-    *p_tsx = tsx;
+    /*
+     * Register transaction layer module to endpoint.
+     */
+    status = pjsip_endpt_register_module( endpt, &mod_tsx_layer.mod );
+    if (status != PJ_SUCCESS) {
+	pj_mutex_destroy(mod_tsx_layer.mutex);
+	pjsip_endpt_release_pool(endpt, pool);
+	return status;
+    }
+
     return PJ_SUCCESS;
 }
 
+
+/*
+ * Get the instance of transaction layer module.
+ */
+PJ_DEF(pjsip_module*) pjsip_tsx_layer_instance(void)
+{
+    return &mod_tsx_layer.mod;
+}
+
+
+/*
+ * Unregister and destroy transaction layer module.
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_layer_destroy(void)
+{
+    /* Are we registered? */
+    PJ_ASSERT_RETURN(mod_tsx_layer.endpt!=NULL, PJ_EINVALIDOP);
+
+    /* Unregister from endpoint. 
+     * Clean-ups will be done in the unload() module callback.
+     */
+    return pjsip_endpt_unregister_module( mod_tsx_layer.endpt, 
+					  &mod_tsx_layer.mod);
+}
+
+
+/*
+ * Register the transaction to the hash table.
+ */
+static void mod_tsx_layer_register_tsx( pjsip_transaction *tsx)
+{
+    pj_assert(tsx->transaction_key.slen != 0);
+    //pj_assert(tsx->state != PJSIP_TSX_STATE_NULL);
+
+    /* Lock hash table mutex. */
+    pj_mutex_lock(mod_tsx_layer.mutex);
+
+    /* Register the transaction to the hash table. */
+    pj_hash_set( tsx->pool, mod_tsx_layer.htable, tsx->transaction_key.ptr,
+		 tsx->transaction_key.slen, tsx);
+
+    /* Unlock mutex. */
+    pj_mutex_unlock(mod_tsx_layer.mutex);
+}
+
+
+/*
+ * Unregister the transaction from the hash table.
+ */
+static void mod_tsx_layer_unregister_tsx( pjsip_transaction *tsx)
+{
+    pj_assert(tsx->transaction_key.slen != 0);
+    //pj_assert(tsx->state != PJSIP_TSX_STATE_NULL);
+
+    /* Lock hash table mutex. */
+    pj_mutex_lock(mod_tsx_layer.mutex);
+
+    /* Register the transaction to the hash table. */
+    pj_hash_set( NULL, mod_tsx_layer.htable, tsx->transaction_key.ptr,
+		 tsx->transaction_key.slen, NULL);
+
+    /* Unlock mutex. */
+    pj_mutex_unlock(mod_tsx_layer.mutex);
+}
+
+
+/*
+ * Find a transaction.
+ */
+PJ_DEF(pjsip_transaction*) pjsip_tsx_layer_find_tsx( const pj_str_t *key,
+						     pj_bool_t lock )
+{
+    pjsip_transaction *tsx;
+
+    pj_mutex_lock(mod_tsx_layer.mutex);
+    tsx = pj_hash_get( mod_tsx_layer.htable, key->ptr, key->slen );
+    pj_mutex_unlock(mod_tsx_layer.mutex);
+
+
+    /* Race condition!
+     * Transaction may gets deleted before we have chance to lock it.
+     */
+    PJ_TODO(FIX_RACE_CONDITION_HERE);
+    if (tsx && lock)
+	pj_mutex_lock(tsx->mutex);
+
+    return tsx;
+}
+
+
+/* This module callback is called when module is being loaded by
+ * endpoint. It does nothing for this module.
+ */
+static pj_status_t mod_tsx_layer_load(pjsip_endpoint *endpt)
+{
+    PJ_UNUSED_ARG(endpt);
+    return PJ_SUCCESS;
+}
+
+
+/* This module callback is called when module is being started by
+ * endpoint. It does nothing for this module.
+ */
+static pj_status_t mod_tsx_layer_start(void)
+{
+    return PJ_SUCCESS;
+}
+
+
+/* This module callback is called when module is being stopped by
+ * endpoint. 
+ */
+static pj_status_t mod_tsx_layer_stop(void)
+{
+    pj_hash_iterator_t it_buf, *it;
+
+    pj_mutex_lock(mod_tsx_layer.mutex);
+
+    /* Destroy all transactions. */
+    it = pj_hash_first(mod_tsx_layer.htable, &it_buf);
+    while (it) {
+	pjsip_transaction *tsx = pj_hash_this(mod_tsx_layer.htable, it);
+	if (tsx)
+	    tsx_destroy(tsx);
+	it = pj_hash_next(mod_tsx_layer.htable, it);
+    }
+
+    pj_mutex_unlock(mod_tsx_layer.mutex);
+    return PJ_SUCCESS;
+}
+
+
+/* This module callback is called when module is being unloaded by
+ * endpoint.
+ */
+static pj_status_t mod_tsx_layer_unload(void)
+{
+    /* Destroy mutex. */
+    pj_mutex_destroy(mod_tsx_layer.mutex);
+
+    /* Release pool. */
+    pjsip_endpt_release_pool(mod_tsx_layer.endpt, mod_tsx_layer.pool);
+
+    /* Mark as unregistered. */
+    mod_tsx_layer.endpt = NULL;
+
+    return PJ_SUCCESS;
+}
+
+
+/* This module callback is called when endpoint has received an
+ * incoming request message.
+ */
+static pj_bool_t mod_tsx_layer_on_rx_request(pjsip_rx_data *rdata)
+{
+    pj_str_t key;
+    pjsip_transaction *tsx;
+
+    pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_ROLE_UAS,
+			 &rdata->msg_info.cseq->method, rdata);
+
+    /* Find transaction. */
+    pj_mutex_lock( mod_tsx_layer.mutex );
+    tsx = pj_hash_get( mod_tsx_layer.htable, key.ptr, key.slen );
+    if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+	/* Transaction not found.
+	 * Reject the request so that endpoint passes the request to
+	 * upper layer modules.
+	 */
+	pj_mutex_unlock( mod_tsx_layer.mutex);
+	return PJ_FALSE;
+    }
+
+    /* Unlock hash table. */
+    pj_mutex_unlock( mod_tsx_layer.mutex );
+
+    /* Race condition!
+     * Transaction may gets deleted before we have chance to lock it
+     * in tsx_on_rx_msg().
+     */
+    PJ_TODO(FIX_RACE_CONDITION_HERE);
+
+    /* Pass the message to the transaction. */
+    tsx_on_rx_msg(tsx, rdata );
+
+    return PJ_TRUE;
+}
+
+
+/* This module callback is called when endpoint has received an
+ * incoming response message.
+ */
+static pj_bool_t mod_tsx_layer_on_rx_response(pjsip_rx_data *rdata)
+{
+    pj_str_t key;
+    pjsip_transaction *tsx;
+
+    pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_ROLE_UAC,
+			 &rdata->msg_info.cseq->method, rdata);
+
+    /* Find transaction. */
+    pj_mutex_lock( mod_tsx_layer.mutex );
+    tsx = pj_hash_get( mod_tsx_layer.htable, key.ptr, key.slen );
+    if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+	/* Transaction not found.
+	 * Reject the request so that endpoint passes the request to
+	 * upper layer modules.
+	 */
+	pj_mutex_unlock( mod_tsx_layer.mutex);
+	return PJ_FALSE;
+    }
+
+    /* Unlock hash table. */
+    pj_mutex_unlock( mod_tsx_layer.mutex );
+
+    /* Race condition!
+     * Transaction may gets deleted before we have chance to lock it
+     * in tsx_on_rx_msg().
+     */
+    PJ_TODO(FIX_RACE_CONDITION_HERE);
+
+    /* Pass the message to the transaction. */
+    tsx_on_rx_msg(tsx, rdata );
+
+    return PJ_TRUE;
+}
+
+
+/*
+ * Get transaction instance in the rdata.
+ */
+PJ_DEF(pjsip_transaction*) pjsip_rdata_get_tsx( pjsip_rx_data *rdata )
+{
+    return rdata->endpt_info.mod_data[mod_tsx_layer.mod.id];
+}
+
+
+/*****************************************************************************
+ **
+ ** Transaction
+ **
+ *****************************************************************************
+
 /*
  * Lock transaction and set the value of Thread Local Storage.
  */
@@ -839,6 +743,86 @@
     return lck->is_alive ? PJ_SUCCESS : PJSIP_ETSXDESTROYED;
 }
 
+
+/* Create and initialize basic transaction structure.
+ * This function is called by both UAC and UAS creation.
+ */
+static pj_status_t tsx_create( pjsip_module *tsx_user,
+			       pjsip_transaction **p_tsx)
+{
+    pj_pool_t *pool;
+    pjsip_transaction *tsx;
+    pj_status_t status;
+
+    pool = pjsip_endpt_create_pool( mod_tsx_layer.endpt, "tsx", 
+				    PJSIP_POOL_TSX_LEN, PJSIP_POOL_TSX_INC );
+    if (!pool)
+	return PJ_ENOMEM;
+
+    tsx = pj_pool_zalloc(pool, sizeof(pjsip_transaction));
+    tsx->pool = pool;
+    tsx->tsx_user = tsx_user;
+    tsx->endpt = mod_tsx_layer.endpt;
+
+    pj_sprintf(tsx->obj_name, "tsx%p", tsx);
+
+    tsx->handle_200resp = 1;
+    tsx->retransmit_timer.id = TSX_TIMER_RETRANSMISSION;
+    tsx->retransmit_timer._timer_id = -1;
+    tsx->retransmit_timer.user_data = tsx;
+    tsx->retransmit_timer.cb = &tsx_timer_callback;
+    tsx->timeout_timer.id = TSX_TIMER_TIMEOUT;
+    tsx->timeout_timer._timer_id = -1;
+    tsx->timeout_timer.user_data = tsx;
+    tsx->timeout_timer.cb = &tsx_timer_callback;
+    
+    status = pj_mutex_create_recursive(pool, "tsx%p", &tsx->mutex);
+    if (status != PJ_SUCCESS) {
+	pjsip_endpt_release_pool(mod_tsx_layer.endpt, pool);
+	return status;
+    }
+
+    *p_tsx = tsx;
+    return PJ_SUCCESS;
+}
+
+
+/* Destroy transaction. */
+static void tsx_destroy( pjsip_transaction *tsx )
+{
+    pj_mutex_destroy(tsx->mutex);
+    pjsip_endpt_release_pool(tsx->endpt, tsx->pool);
+}
+
+
+/*
+ * Callback when timer expires.
+ */
+static void tsx_timer_callback( pj_timer_heap_t *theap, pj_timer_entry *entry)
+{
+    pjsip_event event;
+    pjsip_transaction *tsx = entry->user_data;
+    struct tsx_lock_data lck;
+
+    PJ_UNUSED_ARG(theap);
+
+    PJ_LOG(5,(tsx->obj_name, "got timer event (%s timer)", 
+	     (entry->id==TSX_TIMER_RETRANSMISSION ? "Retransmit":"Timeout")));
+
+
+    if (entry->id == TSX_TIMER_RETRANSMISSION) {
+        PJSIP_EVENT_INIT_TIMER(event, &tsx->retransmit_timer);
+    } else {
+        PJSIP_EVENT_INIT_TIMER(event, &tsx->timeout_timer);
+    }
+
+    /* Dispatch event to transaction. */
+    lock_tsx(tsx, &lck);
+    (*tsx->state_handler)(tsx, &event);
+    unlock_tsx(tsx, &lck);
+}
+
+
 /*
  * Set transaction state, and inform TU about the transaction state change.
  */
@@ -847,8 +831,6 @@
 			   pjsip_event_id_e event_src_type,
                            void *event_src )
 {
-    pjsip_event e;
-
     PJ_LOG(4, (tsx->obj_name, "STATE %s-->%s, cause = %s",
 	       state_str[tsx->state], state_str[state], 
                pjsip_event_str(event_src_type)));
@@ -864,18 +846,21 @@
     }
 
     /* Inform TU */
-    PJSIP_EVENT_INIT_TSX_STATE(e, tsx, event_src_type, event_src);
-    pjsip_endpt_send_tsx_event( tsx->endpt, &e  );
+    if (tsx->tsx_user && tsx->tsx_user->on_tsx_state) {
+	pjsip_event e;
+	PJSIP_EVENT_INIT_TSX_STATE(e, tsx, event_src_type, event_src);
+	(*tsx->tsx_user->on_tsx_state)(tsx, &e);
+    }
+    
 
     /* When the transaction is terminated, release transport, and free the
      * saved last transmitted message.
      */
     if (state == PJSIP_TSX_STATE_TERMINATED) {
+	pj_time_val timeout = {0, 0};
 
 	/* Decrement transport reference counter. */
-	if (tsx->transport && 
-            tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) 
-        {
+	if (tsx->transport) {
 	    pjsip_transport_dec_ref( tsx->transport );
 	    tsx->transport = NULL;
 	}
@@ -895,15 +880,15 @@
 	    tsx->retransmit_timer._timer_id = -1;
 	}
 
-	/* If transport is not pending, reschedule timeout timer to
-	 * destroy this transaction.
-	 */
-	if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) {
-	    pj_time_val timeout = {0, 0};
+	/* Reschedule timeout timer to destroy this transaction. */
+	if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
+	    tsx->transport_flag |= TSX_HAS_PENDING_DESTROY;
+	} else {
 	    pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, 
 					&timeout);
 	}
 
+
     } else if (state == PJSIP_TSX_STATE_DESTROYED) {
 
 	/* Clear TLS, so that mutex will not be unlocked */
@@ -914,188 +899,61 @@
 	    }
 	    lck = lck->prev;
 	}
+
+	/* Unregister transaction. */
+	mod_tsx_layer_unregister_tsx(tsx);
+
+	/* Destroy transaction. */
+	tsx_destroy(tsx);
     }
 }
 
-/*
- * Look-up destination address and select which transport to be used to send
- * the request message. The procedure used here follows the guidelines on 
- * sending the request in RFC3261 chapter 8.1.2.
- *
- * This function also modifies the message (request line and Route headers)
- * accordingly.
- */
-static pj_status_t tsx_process_route( pjsip_transaction *tsx,
-				      pjsip_tx_data *tdata,
-				      pjsip_host_info *send_addr )
-{
-    pjsip_route_hdr *route_hdr;
-    
-    pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG);
-
-    /* Get the first "Route" header from the message. If the message doesn't
-     * have any "Route" headers but the endpoint has, then copy the "Route"
-     * headers from the endpoint first.
-     */
-    route_hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL);
-    if (!route_hdr) {
-	const pjsip_route_hdr *hdr_list;
-	const pjsip_route_hdr *hdr;
-	hdr_list = (const pjsip_route_hdr*)pjsip_endpt_get_routing(tsx->endpt);
-	hdr = hdr_list->next;
-	while (hdr != hdr_list {
-	    route_hdr = pjsip_hdr_shallow_clone(tdata->pool, hdr);
-	    pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)route_hdr);
-	    hdr = hdr->next;
-	}
-    }
-
-    return pjsip_get_request_addr(tdata, send_addr);  
-}
-
 
 /*
- * Callback from the transport job.
- * This callback is called when asychronous transport connect() operation
- * has completed, with or without error.
+ * Create, initialize, and register UAC transaction.
  */
-static void tsx_transport_callback(pjsip_transport *tr, 
-				   void *token, 
-				   pj_status_t status)
+PJ_DEF(pj_status_t) pjsip_tsx_create_uac( pjsip_module *tsx_user,
+					  pjsip_tx_data *tdata,
+					  pjsip_transaction **p_tsx)
 {
-    char addr[PJ_MAX_HOSTNAME];
-    pjsip_transaction *tsx = token;
-    struct tsx_lock_data lck;
-
-    pj_memcpy(addr, tsx->dest_name.addr.host.ptr, tsx->dest_name.addr.host.slen);
-    addr[tsx->dest_name.addr.host.slen] = '\0';
-
-
-    if (status == PJ_SUCCESS) {
-	PJ_LOG(4, (tsx->obj_name, "%s connected to %s:%d",
-				  tr->type_name,
-				  addr, tsx->dest_name.addr.port));
-    } else {
-	PJ_LOG(4, (tsx->obj_name, "%s unable to connect to %s:%d, status=%d", 
-				  tr->type_name,
-				  addr, tsx->dest_name.addr.port, status));
-    }
-
-    /* Lock transaction. */
-    lock_tsx(tsx, &lck);
-
-    if (status != PJ_SUCCESS) {
-	tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
-	tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
-
-	tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED,
-                      PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
-
-	/* Unlock transaction. */
-	unlock_tsx(tsx, &lck);
-	return;
-    }
-
-    /* See if transaction has already been terminated. 
-     * If so, schedule to destroy the transaction.
-     */
-    if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
-	pj_time_val timeout = {0, 0};
-	pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, 
-				    &timeout);
-
-	/* Unlock transaction. */
-	unlock_tsx(tsx, &lck);
-	return;
-    }
-
-    /* Add reference counter to the transport. */
-    pjsip_transport_add_ref(tr);
-
-    /* Mark transport as ready. */
-    tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
-    tsx->transport = tr;
-
-    /* If there's a pending message to send, send it now. */
-    if (tsx->has_unsent_msg) {
-	tsx_send_msg( tsx, tsx->last_tx );
-    }
-
-    /* Unlock transaction. */
-    unlock_tsx(tsx, &lck);
-}
-
-/*
- * Callback from the resolver job.
- */
-static void tsx_resolver_callback(pj_status_t status,
-				  void *token,
-				  const struct pjsip_server_addresses *addr)
-{
-    pjsip_transaction *tsx = token;
-    struct tsx_lock_data lck;
-    pjsip_transport *tp;
-
-    PJ_LOG(4, (tsx->obj_name, "resolver job complete, status=%d", status));
-
-    if (status != PJ_SUCCESS || addr->count == 0) {
-	lock_tsx(tsx, &lck);
-	tsx->status_code = PJSIP_SC_TSX_RESOLVE_ERROR;
-	tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, 
-                      PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
-	unlock_tsx(tsx, &lck);
-	return;
-    }
-
-    /* Lock transaction. */
-    lock_tsx(tsx, &lck);
-
-    /* Copy server addresses. */
-    pj_memcpy(&tsx->remote_addr, addr, sizeof(*addr));
-
-    /* Create/find the transport for the remote address. */
-    tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_CONNECTING;
-    status = pjsip_endpt_alloc_transport( tsx->endpt, addr->entry[0].type,
-					  &addr->entry[0].addr,
-					  addr->entry[0].addr_len,
-					  &tp);
-    tsx_transport_callback(tp, tsx, status);
-
-    /* Unlock transaction */
-    unlock_tsx(tsx, &lck);
-
-    /* There should be nothing to do after this point.
-     * Execution for the transaction will resume when the callback for the 
-     * transport is called.
-     */
-}
-
-/*
- * Initialize the transaction as UAC transaction.
- */
-PJ_DEF(pj_status_t) pjsip_tsx_init_uac( pjsip_transaction *tsx, 
-					pjsip_tx_data *tdata)
-{
+    pjsip_transaction *tsx;
     pjsip_msg *msg;
     pjsip_cseq_hdr *cseq;
     pjsip_via_hdr *via;
-    pj_status_t status;
     struct tsx_lock_data lck;
+    pj_status_t status;
 
-    PJ_LOG(4,(tsx->obj_name, "initializing tsx as UAC (tdata=%p)", tdata));
-
-    /* Lock transaction. */
-    lock_tsx(tsx, &lck);
+    PJ_ASSERT_RETURN(tdata!=NULL && p_tsx!=NULL, PJ_EINVAL);
 
     /* Keep shortcut */
     msg = tdata->msg;
 
+    /* Make sure CSeq header is present. */
+    cseq = pjsip_msg_find_hdr(msg, PJSIP_H_CSEQ, NULL);
+    if (!cseq) {
+	pj_assert(!"CSeq header not present in outgoing message!");
+	return PJSIP_EMISSINGHDR;
+    }
+
+
+    /* Create transaction instance. */
+    status = tsx_create( tsx_user, &tsx);
+    if (status != PJ_SUCCESS)
+	return status;
+
+
+    /* Lock transaction. */
+    lock_tsx(tsx, &lck);
+
     /* Role is UAC. */
     tsx->role = PJSIP_ROLE_UAC;
 
     /* Save method. */
     pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method);
 
+    /* Save CSeq. */
+    tsx->cseq = cseq->cseq;
+
     /* Generate Via header if it doesn't exist. */
     via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL);
     if (via == NULL) {
@@ -1103,6 +961,7 @@
 	pjsip_msg_insert_first_hdr(msg, (pjsip_hdr*) via);
     }
 
+    /* Generate branch parameter if it doesn't exist. */
     if (via->branch_param.slen == 0) {
 	pj_str_t tmp;
 	via->branch_param.ptr = pj_pool_alloc(tsx->pool, PJSIP_MAX_BRANCH_LEN);
@@ -1115,96 +974,85 @@
 
         /* Save branch parameter. */
         tsx->branch = via->branch_param;
+
     } else {
         /* Copy branch parameter. */
         pj_strdup(tsx->pool, &tsx->branch, &via->branch_param);
     }
 
-
-    /* Generate transaction key. */
-    status = create_tsx_key_3261( tsx->pool, &tsx->transaction_key,
-			          PJSIP_ROLE_UAC, &tsx->method, 
-			          &via->branch_param);
-    if (status != PJ_SUCCESS) {
-        unlock_tsx(tsx, &lck);
-        return status;
-    }
+   /* Generate transaction key. */
+    create_tsx_key_3261( tsx->pool, &tsx->transaction_key,
+			 PJSIP_ROLE_UAC, &tsx->method, 
+			 &via->branch_param);
 
     PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen,
 	       tsx->transaction_key.ptr));
 
-    /* Save CSeq. */
-    cseq = pjsip_msg_find_hdr(msg, PJSIP_H_CSEQ, NULL);
-    if (!cseq) {
-	pj_assert(!"CSeq header not present in outgoing message!");
-        unlock_tsx(tsx, &lck);
-	return PJSIP_EMISSINGHDR;
-    }
-    tsx->cseq = cseq->cseq;
-
-
     /* Begin with State_Null.
      * Manually set-up the state becase we don't want to call the callback.
      */
     tsx->state = PJSIP_TSX_STATE_NULL;
-    tsx->state_handler = &pjsip_tsx_on_state_null;
+    tsx->state_handler = &tsx_on_state_null;
 
-    /* Get destination name from the message. */
-    status = tsx_process_route(tsx, tdata, &tsx->dest_name);
-    if (status != PJ_SUCCESS) {
-	tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
-	tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
-	tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, 
-                      PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
-	unlock_tsx(tsx, &lck);
-	return status;
-    }
+    /* Save the message. */
+    tsx->last_tx = tdata;
+    pjsip_tx_data_add_ref(tsx->last_tx);
 
-    /* Resolve destination.
-     * This will start asynchronous resolver job, and when it finishes, 
-     * the callback will be called.
-     */
-    PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d",
-			     tsx->dest_name.addr.host.slen, 
-			     tsx->dest_name.addr.host.ptr,
-			     tsx->dest_name.addr.port));
 
-    tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
-    pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name, 
-			 tsx, &tsx_resolver_callback);
+    /* Register transaction to hash table. */
+    mod_tsx_layer_register_tsx(tsx);
 
-    /* There should be nothing to do after this point. 
-     * Execution for the transaction will resume when the resolver callback is
-     * called.
-     */
 
-    /* Unlock transaction and return.
-     * If transaction has been destroyed WITHIN the current thread, the 
-     * unlock_tsx() function will return -1.
-     */
-    return unlock_tsx(tsx, &lck);
+    /* Unlock transaction and return. */
+    unlock_tsx(tsx, &lck);
+
+    *p_tsx = tsx;
+    return PJ_SUCCESS;
 }
 
 
 /*
- * Initialize the transaction as UAS transaction.
+ * Create, initialize, and register UAS transaction.
  */
-PJ_DEF(pj_status_t) pjsip_tsx_init_uas( pjsip_transaction *tsx, 
-					pjsip_rx_data *rdata)
+PJ_DEF(pj_status_t) pjsip_tsx_create_uas( pjsip_module *tsx_user,
+					  pjsip_rx_data *rdata,
+					  pjsip_transaction **p_tsx)
 {
-    pjsip_msg *msg = rdata->msg_info.msg;
+    pjsip_transaction *tsx;
+    pjsip_msg *msg;
     pj_str_t *branch;
     pjsip_cseq_hdr *cseq;
     pj_status_t status;
     struct tsx_lock_data lck;
 
-    PJ_LOG(4,(tsx->obj_name, "initializing tsx as UAS (rdata=%p)", rdata));
-
-    /* Lock transaction. */
-    lock_tsx(tsx, &lck);
+    PJ_ASSERT_RETURN(rdata!=NULL && p_tsx!=NULL, PJ_EINVAL);
 
     /* Keep shortcut to message */
     msg = rdata->msg_info.msg;
+    
+    /* Make sure this is a request message. */
+    PJ_ASSERT_RETURN(msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG);
+
+    /* Make sure CSeq header is present. */
+    cseq = rdata->msg_info.cseq;
+    if (!cseq)
+	return PJSIP_EMISSINGHDR;
+
+    /* Make sure Via header is present. */
+    if (rdata->msg_info.via == NULL)
+	return PJSIP_EMISSINGHDR;
+
+
+    /* 
+     * Create transaction instance. 
+     */
+    status = tsx_create( tsx_user, &tsx);
+    if (status != PJ_SUCCESS)
+	return status;
+
+
+    /* Lock transaction. */
+    lock_tsx(tsx, &lck);
 
     /* Role is UAS */
     tsx->role = PJSIP_ROLE_UAS;
@@ -1212,13 +1060,16 @@
     /* Save method. */
     pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method);
 
+    /* Save CSeq */
+    tsx->cseq = cseq->cseq;
+
     /* Get transaction key either from branch for RFC3261 message, or
      * create transaction key.
      */
     status = pjsip_tsx_create_key(tsx->pool, &tsx->transaction_key, 
                                   PJSIP_ROLE_UAS, &tsx->method, rdata);
     if (status != PJ_SUCCESS) {
-        unlock_tsx(tsx, &lck);
+        tsx_destroy(tsx);
         return status;
     }
 
@@ -1229,223 +1080,77 @@
     PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen,
 	       tsx->transaction_key.ptr));
 
-    /* Save CSeq */
-    cseq = rdata->msg_info.cseq;
-    tsx->cseq = cseq->cseq;
 
-    /* Begin with state NULL
+    /* Begin with state TRYING.
      * Manually set-up the state becase we don't want to call the callback.
      */
-    tsx->state = PJSIP_TSX_STATE_NULL; 
-    tsx->state_handler = &pjsip_tsx_on_state_null;
+    tsx->state = PJSIP_TSX_STATE_TRYING; 
+    tsx->state_handler = &tsx_on_state_trying;
 
-    /* Get the transport to send the response. 
-     * According to section 18.2.2 of RFC3261, if the transport is reliable
-     * then the response must be sent using that transport.
+    /* Get response address. */
+    status = pjsip_get_response_addr( tsx->pool, rdata, &tsx->res_addr );
+    if (status != PJ_SUCCESS) {
+	tsx_destroy(tsx);
+	return status;
+    }
+
+    /* If it's decided that we should use current transport, keep the
+     * transport.
      */
-    /* In addition, RFC 3581 says, if Via has "rport" parameter specified,
-     * then return the response using the same transport.
-     */
-    if (PJSIP_TRANSPORT_IS_RELIABLE(rdata->tp_info.transport) || 
-	rdata->msg_info.via->rport_param >= 0) 
-    {
-	tsx->transport = rdata->tp_info.transport;
+    if (tsx->res_addr.transport) {
+	tsx->transport = tsx->res_addr.transport;
 	pjsip_transport_add_ref(tsx->transport);
-	tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
-
-	tsx->current_addr = 0;
-	tsx->remote_addr.count = 1;
-	tsx->remote_addr.entry[0].type = tsx->transport->key.type;
-	pj_memcpy(&tsx->remote_addr.entry[0].addr, 
-		  &rdata->pkt_info.src_addr, rdata->pkt_info.src_addr_len);
-	
-    } else {
-	pj_status_t status;
-
-	status = pjsip_get_response_addr(tsx->pool, rdata->tp_info.transport,
-					 rdata->msg_info.via, &tsx->dest_name);
-	if (status != PJ_SUCCESS) {
-	    tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
-	    tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
-	    tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, 
-                          PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
-	    unlock_tsx(tsx, &lck);
-	    return status;
-	}
-
-	/* Resolve destination.
-	 * This will start asynchronous resolver job, and when it finishes, 
-	 * the callback will be called.
-	 */
-	PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d",
-				 tsx->dest_name.addr.host.slen, 
-				 tsx->dest_name.addr.host.ptr,
-				 tsx->dest_name.addr.port));
-
-	tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
-	pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name, 
-			     tsx, &tsx_resolver_callback);
+	pj_memcpy(&tsx->addr, &tsx->res_addr.addr, tsx->res_addr.addr_len);
+	tsx->addr_len = tsx->res_addr.addr_len;
     }
-    
-    /* There should be nothing to do after this point. 
-     * Execution for the transaction will resume when the resolver callback is
-     * called.
-     */
 
-    /* Unlock transaction and return.
-     * If transaction has been destroyed WITHIN the current thread, the 
-     * unlock_tsx() function will return -1.
-     */
-    return unlock_tsx(tsx, &lck);
+
+    /* Register the transaction. */
+    mod_tsx_layer_register_tsx(tsx);
+
+
+    /* Unlock transaction and return. */
+    unlock_tsx(tsx, &lck);
+
+    *p_tsx = tsx;
+    return PJ_SUCCESS;
 }
 
-/*
- * Callback when timer expires.
- */
-static void tsx_timer_callback( pj_timer_heap_t *theap, pj_timer_entry *entry)
-{
-    pjsip_event event;
-    pjsip_transaction *tsx = entry->user_data;
-    struct tsx_lock_data lck;
-
-    PJ_UNUSED_ARG(theap);
-
-    PJ_LOG(5,(tsx->obj_name, "got timer event (%s timer)", 
-	     (entry->id==TSX_TIMER_RETRANSMISSION ? "Retransmit" : "Timeout")));
-
-
-    if (entry->id == TSX_TIMER_RETRANSMISSION) {
-        PJSIP_EVENT_INIT_TIMER(event, &tsx->retransmit_timer);
-    } else {
-        PJSIP_EVENT_INIT_TIMER(event, &tsx->timeout_timer);
-    }
-
-    /* Dispatch event to transaction. */
-    lock_tsx(tsx, &lck);
-    (*tsx->state_handler)(tsx, &event);
-    unlock_tsx(tsx, &lck);
-}
 
 /*
- * Transmit ACK message for 2xx/INVITE with this transaction. The ACK for
- * non-2xx/INVITE is automatically sent by the transaction.
- * This operation is only valid if the transaction is configured to handle ACK
- * (tsx->handle_ack is non-zero). If this attribute is not set, then the
- * transaction will comply with RFC-3261, i.e. it will set itself to 
- * TERMINATED state when it receives 2xx/INVITE.
+ * Forcely terminate transaction.
  */
-PJ_DEF(void) pjsip_tsx_on_tx_ack( pjsip_transaction *tsx, pjsip_tx_data *tdata)
+PJ_DEF(pj_status_t) pjsip_tsx_terminate( pjsip_transaction *tsx, int code )
 {
-    pjsip_msg *msg;
-    pjsip_host_info dest_addr;
-    pjsip_via_hdr *via;
     struct tsx_lock_data lck;
-    pj_status_t status = PJ_SUCCESS;
 
-    /* Lock tsx. */
+    PJ_ASSERT_RETURN(tsx != NULL, PJ_EINVAL);
+    PJ_ASSERT_RETURN(code >= 200, PJ_EINVAL);
+
     lock_tsx(tsx, &lck);
-
-    pj_assert(tsx->handle_ack != 0);
-    
-    msg = tdata->msg;
-
-    /* Generate branch parameter if it doesn't exist. */
-    via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL);
-    if (via == NULL) {
-	via = pjsip_via_hdr_create(tdata->pool);
-	pjsip_msg_add_hdr(msg, (pjsip_hdr*) via);
-    }
-
-    if (via->branch_param.slen == 0) {
-	via->branch_param = tsx->branch;
-    } else {
-	pj_assert( pj_strcmp(&via->branch_param, &tsx->branch) == 0 );
-    }
-
-    /* Get destination name from the message. */
-    status = tsx_process_route(tsx, tdata, &dest_addr);
-    if (status != 0){
-	goto on_error;
-    }
-
-    /* Compare message's destination name with transaction's destination name.
-     * If NOT equal, then we'll have to resolve the destination.
-     */
-    if (dest_addr.type == tsx->dest_name.type &&
-	dest_addr.flag == tsx->dest_name.flag &&
-	dest_addr.addr.port == tsx->dest_name.addr.port &&
-	pj_stricmp(&dest_addr.addr.host, &tsx->dest_name.addr.host) == 0)
-    {
-	/* Equal destination. We can use current transport. */
-	pjsip_tsx_on_tx_msg(tsx, tdata);
-	unlock_tsx(tsx, &lck);
-	return;
-
-    }
-
-    /* New destination; we'll have to resolve host and create new transport. */
-    pj_memcpy(&tsx->dest_name, &dest_addr, sizeof(dest_addr));
-    pj_strdup(tsx->pool, &tsx->dest_name.addr.host, &dest_addr.addr.host);
-
-    PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d",
-			     tsx->dest_name.addr.host.slen, 
-			     tsx->dest_name.addr.host.ptr,
-			     tsx->dest_name.addr.port));
-
-    tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
-    pjsip_transport_dec_ref(tsx->transport);
-    tsx->transport = NULL;
-
-    /* Put the message in queue. */
-    pjsip_tsx_on_tx_msg(tsx, tdata);
-
-    /* This is a bug!
-     * We shouldn't change transaction's state before actually sending the
-     * message. Otherwise transaction will terminate before message is sent,
-     * and timeout timer will be scheduled.
-     */
-    PJ_TODO(TSX_DONT_CHANGE_STATE_BEFORE_SENDING_ACK)
-
-    /* 
-     * This will start asynchronous resolver job, and when it finishes, 
-     * the callback will be called.
-     */
-
-    tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
-    pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name, 
-			 tsx, &tsx_resolver_callback);
-
+    tsx->status_code = code;
+    tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, PJSIP_EVENT_USER, NULL);
     unlock_tsx(tsx, &lck);
 
-    /* There should be nothing to do after this point. 
-     * Execution for the transaction will resume when the resolver callback is
-     * called.
-     */
-    return;
-
-on_error:
-    /* Failure condition. 
-     * Send TERMINATED event.
-     */
-    tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
-
-    tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
-                   PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
-
-    unlock_tsx(tsx, &lck);
+    return PJ_SUCCESS;
 }
 
 
 /*
  * This function is called by TU to send a message.
  */
-PJ_DEF(void) pjsip_tsx_on_tx_msg( pjsip_transaction *tsx,
-				  pjsip_tx_data *tdata )
+PJ_DEF(pj_status_t) pjsip_tsx_send_msg( pjsip_transaction *tsx, 
+				        pjsip_tx_data *tdata )
 {
     pjsip_event event;
     struct tsx_lock_data lck;
     pj_status_t status;
 
+    if (tdata == NULL)
+	tdata = tsx->last_tx;
+
+    PJ_ASSERT_RETURN(tdata != NULL, PJ_EINVALIDOP);
+
     PJ_LOG(5,(tsx->obj_name, "Request to transmit msg on state %s (tdata=%p)",
                              state_str[tsx->state], tdata));
 
@@ -1455,14 +1160,16 @@
     lock_tsx(tsx, &lck);
     status = (*tsx->state_handler)(tsx, &event);
     unlock_tsx(tsx, &lck);
+
+    return status;
 }
 
+
 /*
  * This function is called by endpoint when incoming message for the 
  * transaction is received.
  */
-PJ_DEF(void) pjsip_tsx_on_rx_msg( pjsip_transaction *tsx,
-				  pjsip_rx_data *rdata)
+static void tsx_on_rx_msg( pjsip_transaction *tsx, pjsip_rx_data *rdata)
 {
     pjsip_event event;
     struct tsx_lock_data lck;
@@ -1471,6 +1178,10 @@
     PJ_LOG(5,(tsx->obj_name, "Incoming msg on state %s (rdata=%p)", 
 	      state_str[tsx->state], rdata));
 
+    /* Put the transaction in the rdata's mod_data. */
+    rdata->endpt_info.mod_data[mod_tsx_layer.mod.id] = tsx;
+
+    /* Init event. */
     PJSIP_EVENT_INIT_RX_MSG(event, tsx, rdata);
 
     /* Dispatch to transaction. */
@@ -1479,162 +1190,282 @@
     unlock_tsx(tsx, &lck);
 }
 
-/*
- * Forcely terminate transaction.
- */
-PJ_DEF(void) pjsip_tsx_terminate( pjsip_transaction *tsx, int code )
+
+/* Callback called by send message framework */
+static void send_msg_callback( pjsip_send_state *send_state,
+			       pj_ssize_t sent, pj_bool_t *cont )
 {
+    pjsip_transaction *tsx = send_state->token;
     struct tsx_lock_data lck;
 
     lock_tsx(tsx, &lck);
-    tsx->status_code = code;
-    tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
-                   PJSIP_EVENT_USER, NULL);
+
+    if (sent > 0) {
+	/* Successfully sent! */
+	pj_assert(send_state->cur_transport != NULL);
+
+	if (tsx->transport != send_state->cur_transport) {
+	    if (tsx->transport) {
+		pjsip_transport_dec_ref(tsx->transport);
+		tsx->transport = NULL;
+	    }
+	    tsx->transport = send_state->cur_transport;
+	    pjsip_transport_add_ref(tsx->transport);
+	}
+
+	/* Clear pending transport flag. */
+	tsx->transport_flag &= ~(TSX_HAS_PENDING_TRANSPORT);
+
+	/* Pending destroy? */
+	if (tsx->transport_flag & TSX_HAS_PENDING_DESTROY) {
+	    tsx_set_state( tsx, PJSIP_TSX_STATE_DESTROYED, 
+			   PJSIP_EVENT_UNKNOWN, NULL );
+	    unlock_tsx(tsx, &lck);
+	    return;
+	}
+
+	/* Need to transmit a message? */
+	if (tsx->transport_flag & TSX_HAS_PENDING_SEND) {
+	    tsx->transport_flag &= ~(TSX_HAS_PENDING_SEND);
+	    tsx_send_msg(tsx, tsx->last_tx);
+	}
+
+	/* Need to reschedule retransmission? */
+	if (tsx->transport_flag & TSX_HAS_PENDING_RESCHED) {
+	    tsx->transport_flag &= ~(TSX_HAS_PENDING_RESCHED);
+	    tsx_resched_retransmission(tsx);
+	}
+
+    } else {
+	/* Failed to send! */
+	pj_assert(sent != 0);
+
+	/* If transaction is using the same transport as the failed one, 
+	 * release the transport.
+	 */
+	if (send_state->cur_transport==tsx->transport &&
+	    tsx->transport != NULL)
+	{
+	    pjsip_transport_dec_ref(tsx->transport);
+	    tsx->transport = NULL;
+	}
+
+	if (!*cont) {
+	    PJ_LOG(4,(tsx->obj_name, "Failed to send message! status=%d",
+		      -sent));
+
+	    /* Clear pending transport flag. */
+	    tsx->transport_flag &= ~(TSX_HAS_PENDING_TRANSPORT);
+
+	    /* Terminate transaction. */
+	    tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
+	    tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
+			   PJSIP_EVENT_TRANSPORT_ERROR, send_state->tdata );
+	}
+    }
 
     unlock_tsx(tsx, &lck);
 }
 
-/*
- * Transport send completion callback.
- */
-static void tsx_on_send_complete(void *token, pjsip_tx_data *tdata,
-				 pj_ssize_t bytes_sent)
-{
-    PJ_UNUSED_ARG(token);
-    PJ_UNUSED_ARG(tdata);
 
-    if (bytes_sent <= 0) {
-	PJ_TODO(HANDLE_TRANSPORT_ERROR);
-    }
+/* Transport callback. */
+static void transport_callback(void *token, pjsip_tx_data *tdata,
+			       pj_ssize_t sent)
+{
+    if (sent < 0) {
+	pjsip_transaction *tsx = token;
+	struct tsx_lock_data lck;
+
+	PJ_LOG(4,(tsx->obj_name, "Failed to send message! status=%d",
+		  -sent));
+
+	lock_tsx(tsx, &lck);
+
+	/* Dereference transport. */
+	pjsip_transport_dec_ref(tsx->transport);
+	tsx->transport = NULL;
+
+	/* Terminate transaction. */
+	tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
+	tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
+		       PJSIP_EVENT_TRANSPORT_ERROR, tdata );
+
+	unlock_tsx(tsx, &lck);
+   }
 }
 
 /*
  * Send message to the transport.
- * If transport is not yet available, then do nothing. The message will be
- * transmitted when transport connection completion callback is called.
  */
 static pj_status_t tsx_send_msg( pjsip_transaction *tsx, 
                                  pjsip_tx_data *tdata)
 {
-    pj_status_t status = PJ_SUCCESS;
+    pj_status_t status = PJ_EBUG;
 
-    PJ_LOG(5,(tsx->obj_name, "sending msg (tdata=%p)", tdata));
+    PJ_ASSERT_RETURN(tsx && tdata, PJ_EINVAL);
 
-    if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) {
-	pjsip_event before_tx_event;
-
-	pj_assert(tsx->transport != NULL);
-
-	/* Make sure Via transport info is filled up properly for
-	 * requests. 
-	 */
-	if (tdata->msg->type == PJSIP_REQUEST_MSG) {
-	    pjsip_via_hdr *via = (pjsip_via_hdr*) 
-		pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
-
-	    /* For request message, set "rport" parameter by default. */
-	    if (tdata->msg->type == PJSIP_REQUEST_MSG)
-		via->rport_param = 0;
-
-	    /* Don't update Via sent-by on retransmission. */
-	    if (via->sent_by.host.slen == 0) {
-		pj_strdup2(tdata->pool, &via->transport, 
-			   tsx->transport->type_name);
-		pj_strdup(tdata->pool, &via->sent_by.host, 
-			  &tsx->transport->local_name.host);
-		via->sent_by.port = tsx->transport->local_name.port;
-	    }
-	}
-
-	/* Notify everybody we're about to send message. */
-        PJSIP_EVENT_INIT_PRE_TX_MSG(before_tx_event, tsx, tdata, 
-                                    tsx->retransmit_count);
-	pjsip_endpt_send_tsx_event( tsx->endpt, &before_tx_event );
-
-	tsx->has_unsent_msg = 0;
-	status = pjsip_transport_send(tsx->transport, tdata,
-			&tsx->remote_addr.entry[tsx->current_addr].addr,
-			tsx->remote_addr.entry[tsx->current_addr].addr_len,
-			tsx, &tsx_on_send_complete);
-	if (status != PJ_SUCCESS && status != PJ_EPENDING) {
-	    PJ_TODO(HANDLE_TRANSPORT_ERROR);
-	    goto on_error;
-	}
-    } else {
-	tsx->has_unsent_msg = 1;
+    /* Send later if transport is still pending. */
+    if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
+	tsx->transport_flag |= TSX_HAS_PENDING_SEND;
+	return PJ_SUCCESS;
     }
 
-    return 0;
+    if (tdata->msg->type == PJSIP_REQUEST_MSG) {
+	/* If we have the transport, send the message using that transport.
+	 * Otherwise perform full transport resolution.
+	 */
+	if (tsx->transport) {
+	    status = pjsip_transport_send( tsx->transport, tdata, &tsx->addr,
+					   tsx->addr_len, tsx, 
+					   &transport_callback);
+	    if (status == PJ_EPENDING)
+		status = PJ_SUCCESS;
 
-on_error:
-    tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
-    tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
-                   PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
+	    if (status != PJ_SUCCESS) {
+		/* On error, release transport to force using full transport
+		 * resolution procedure.
+		 */
+		if (tsx->transport) {
+		    pjsip_transport_dec_ref(tsx->transport);
+		    tsx->transport = NULL;
+		}
+		tsx->addr_len = 0;
+	    }
+	}
+	
+	if (!tsx->transport) {
+	    tsx->transport_flag |= TSX_HAS_PENDING_TRANSPORT;
+	    status = pjsip_endpt_send_request_stateless(tsx->endpt, tdata, tsx,
+							&send_msg_callback);
+	    if (status == PJ_EPENDING)
+		status = PJ_SUCCESS;
+	}
+
+    } else {
+	/* If we have the transport, send the message using that transport.
+	 * Otherwise perform full transport resolution.
+	 */
+	if (tsx->transport) {
+	    status = pjsip_transport_send( tsx->transport, tdata, 
+					   &tsx->addr, tsx->addr_len,
+					   tsx, &transport_callback);
+	    if (status == PJ_EPENDING)
+		status = PJ_SUCCESS;
+
+	    if (status != PJ_SUCCESS) {
+		if (tsx->transport) {
+		    pjsip_transport_dec_ref(tsx->transport);
+		    tsx->transport = NULL;
+		}
+		tsx->addr_len = 0;
+		tsx->res_addr.transport = NULL;
+		tsx->res_addr.addr_len = 0;
+	    }
+
+	} 
+
+	if (!tsx->transport) {
+	    tsx->transport_flag |= TSX_HAS_PENDING_TRANSPORT;
+	    status = pjsip_endpt_send_response( tsx->endpt, &tsx->res_addr, 
+						tdata, tsx, 
+						&send_msg_callback);
+	    if (status == PJ_EPENDING)
+		status = PJ_SUCCESS;
+	}
+    }
+
     return status;
 }
 
+
+/*
+ * Retransmit last message sent.
+ */
+static void tsx_resched_retransmission( pjsip_transaction *tsx )
+{
+    pj_time_val timeout;
+    int msec_time;
+
+    pj_assert((tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) == 0);
+
+    msec_time = (1 << (tsx->retransmit_count)) * PJSIP_T1_TIMEOUT;
+
+    if (msec_time>PJSIP_T2_TIMEOUT && tsx->method.id!=PJSIP_INVITE_METHOD)
+	msec_time = PJSIP_T2_TIMEOUT;
+
+    timeout.sec = msec_time / 1000;
+    timeout.msec = msec_time % 1000;
+    pjsip_endpt_schedule_timer( tsx->endpt, &tsx->retransmit_timer, 
+				&timeout);
+}
+
 /*
  * Retransmit last message sent.
  */
-static pj_status_t pjsip_tsx_retransmit( pjsip_transaction *tsx,
-					 int should_restart_timer)
+static pj_status_t tsx_retransmit( pjsip_transaction *tsx, int resched)
 {
     pj_status_t status;
 
-    PJ_LOG(4,(tsx->obj_name, "retransmiting (tdata=%p, count=%d, restart?=%d)", 
-	      tsx->last_tx, tsx->retransmit_count, should_restart_timer));
+    PJ_ASSERT_RETURN(tsx->last_tx!=NULL, PJ_EBUG);
 
-    pj_assert(tsx->last_tx != NULL);
+    PJ_LOG(4,(tsx->obj_name, "retransmiting (tdata=%p, count=%d, restart?=%d)", 
+	      tsx->last_tx, tsx->retransmit_count, resched));
 
     ++tsx->retransmit_count;
 
     status = tsx_send_msg( tsx, tsx->last_tx);
-    if (status != PJ_SUCCESS) {
+    if (status != PJ_SUCCESS)
 	return status;
-    }
     
     /* Restart timer T1. */
-    if (should_restart_timer) {
-	pj_time_val timeout;
-	int msec_time = (1 << (tsx->retransmit_count)) * PJSIP_T1_TIMEOUT;
-
-	if (tsx->method.id!=PJSIP_INVITE_METHOD && msec_time>PJSIP_T2_TIMEOUT) 
-	    msec_time = PJSIP_T2_TIMEOUT;
-
-	timeout.sec = msec_time / 1000;
-	timeout.msec = msec_time % 1000;
-	pjsip_endpt_schedule_timer( tsx->endpt, &tsx->retransmit_timer, 
-				    &timeout);
+    if (resched) {
+	if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
+	    tsx->transport_flag |= TSX_HAS_PENDING_RESCHED;
+	} else {
+	    tsx_resched_retransmission(tsx);
+	}
     }
 
     return PJ_SUCCESS;
 }
 
+
 /*
  * Handler for events in state Null.
  */
-static pj_status_t pjsip_tsx_on_state_null( pjsip_transaction *tsx, 
-                                            pjsip_event *event )
+static pj_status_t tsx_on_state_null( pjsip_transaction *tsx, 
+                                      pjsip_event *event )
 {
     pj_status_t status;
 
-    pj_assert( tsx->state == PJSIP_TSX_STATE_NULL);
-    pj_assert( tsx->last_tx == NULL );
-    pj_assert( tsx->has_unsent_msg == 0);
+    pj_assert(tsx->state == PJSIP_TSX_STATE_NULL);
 
     if (tsx->role == PJSIP_ROLE_UAS) {
 
-	/* Set state to Trying. */
-	pj_assert(event->type == PJSIP_EVENT_RX_MSG);
-	tsx_set_state( tsx, PJSIP_TSX_STATE_TRYING, 
-                       PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+	/* UAS doesn't have STATE_NULL.
+	 * State has moved from NULL after transaction is initialized.
+	 */
+	pj_assert(!"Bug bug bug!!");
+	return PJ_EBUG;
 
     } else {
-	pjsip_tx_data *tdata = event->body.tx_msg.tdata;
+	pjsip_tx_data *tdata;
+
+	/* Must be transmit event. */
+	PJ_ASSERT_RETURN(event->type == PJSIP_EVENT_TX_MSG, PJ_EBUG);
+
+	/* Get the txdata */
+	tdata = event->body.tx_msg.tdata;
 
 	/* Save the message for retransmission. */
-	tsx->last_tx = tdata;
-	pjsip_tx_data_add_ref(tdata);
+	if (tsx->last_tx && tsx->last_tx != tdata) {
+	    pjsip_tx_data_dec_ref(tsx->last_tx);
+	    tsx->last_tx = NULL;
+	}
+	if (tsx->last_tx != tdata) {
+	    tsx->last_tx = tdata;
+	    pjsip_tx_data_add_ref(tdata);
+	}
 
 	/* Send the message. */
         status = tsx_send_msg( tsx, tdata);
@@ -1651,12 +1482,14 @@
 	/* Start Timer A (or timer E) for retransmission only if unreliable 
 	 * transport is being used.
 	 */
-	if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL &&
-	    PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) 
-	{
-	    pjsip_endpt_schedule_timer(tsx->endpt, &tsx->retransmit_timer, 
-                                       &t1_timer_val);
+	if (!PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport))  {
 	    tsx->retransmit_count = 0;
+	    if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
+		tsx->transport_flag |= TSX_HAS_PENDING_RESCHED;
+	    } else {
+		pjsip_endpt_schedule_timer(tsx->endpt, &tsx->retransmit_timer,
+					   &t1_timer_val);
+	    }
 	}
 
 	/* Move state. */
@@ -1667,12 +1500,13 @@
     return PJ_SUCCESS;
 }
 
+
 /*
  * State Calling is for UAC after it sends request but before any responses
  * is received.
  */
-static pj_status_t pjsip_tsx_on_state_calling( pjsip_transaction *tsx, 
-				               pjsip_event *event )
+static pj_status_t tsx_on_state_calling( pjsip_transaction *tsx, 
+				         pjsip_event *event )
 {
     pj_assert(tsx->state == PJSIP_TSX_STATE_CALLING);
     pj_assert(tsx->role == PJSIP_ROLE_UAC);
@@ -1683,7 +1517,7 @@
         pj_status_t status;
 
 	/* Retransmit the request. */
-        status = pjsip_tsx_retransmit( tsx, 1 );
+        status = tsx_retransmit( tsx, 1 );
 	if (status != PJ_SUCCESS) {
 	    return status;
 	}
@@ -1705,10 +1539,18 @@
                        PJSIP_EVENT_TIMER, &tsx->timeout_timer);
 
 	/* Transaction is destroyed */
-	return PJSIP_ETSXDESTROYED;
+	//return PJSIP_ETSXDESTROYED;
 
     } else if (event->type == PJSIP_EVENT_RX_MSG) {
-	int code;
+	pjsip_msg *msg;
+	//int code;
+
+	/* Get message instance */
+	msg = event->body.rx_msg.rdata->msg_info.msg;
+
+	/* Better be a response message. */
+	if (msg->type != PJSIP_RESPONSE_MSG)
+	    return PJSIP_ENOTRESPONSEMSG;
 
 	/* Cancel retransmission timer A. */
 	if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0)
@@ -1722,31 +1564,33 @@
 	 * the final response.
 	 */
 	/* Keep last_tx for authorization. */
-	code = event->body.rx_msg.rdata->msg_info.msg->line.status.code;
-	if (tsx->method.id != PJSIP_INVITE_METHOD && code!=401 && code!=407) {
-	    pjsip_tx_data_dec_ref(tsx->last_tx);
-	    tsx->last_tx = NULL;
-	}
+	//blp: always keep last_tx until transaction is destroyed
+	//code = msg->line.status.code;
+	//if (tsx->method.id != PJSIP_INVITE_METHOD && code!=401 && code!=407) {
+	//    pjsip_tx_data_dec_ref(tsx->last_tx);
+	//    tsx->last_tx = NULL;
+	//}
 
 	/* Processing is similar to state Proceeding. */
-	pjsip_tsx_on_state_proceeding_uac( tsx, event);
+	tsx_on_state_proceeding_uac( tsx, event);
 
     } else {
-	pj_assert(0);
+	pj_assert(!"Unexpected event");
         return PJ_EBUG;
     }
 
     return PJ_SUCCESS;
 }
 
+
 /*
  * State Trying is for UAS after it received request but before any responses
  * is sent.
  * Note: this is different than RFC3261, which can use Trying state for
  *	 non-INVITE client transaction (bug in RFC?).
  */
-static pj_status_t pjsip_tsx_on_state_trying( pjsip_transaction *tsx, 
-                                              pjsip_event *event)
+static pj_status_t tsx_on_state_trying( pjsip_transaction *tsx, 
+                                        pjsip_event *event)
 {
     pj_status_t status;
 
@@ -1761,7 +1605,6 @@
      * this happens, just ignore the event (we couldn't retransmit last
      * response because we haven't sent any!).
      */
-    //pj_assert(event->type == PJSIP_EVENT_TX_MSG);
     if (event->type != PJSIP_EVENT_TX_MSG) {
 	return PJ_SUCCESS;
     }
@@ -1769,23 +1612,26 @@
     /* The rest of the processing of the event is exactly the same as in
      * "Proceeding" state.
      */
-    status = pjsip_tsx_on_state_proceeding_uas( tsx, event);
+    status = tsx_on_state_proceeding_uas( tsx, event);
 
     /* Inform the TU of the state transision if state is still State_Trying */
     if (status==PJ_SUCCESS && tsx->state == PJSIP_TSX_STATE_TRYING) {
+
 	tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, 
                        PJSIP_EVENT_TX_MSG, event->body.tx_msg.tdata);
+
     }
 
     return status;
 }
 
+
 /*
  * Handler for events in Proceeding for UAS
  * This state happens after the TU sends provisional response.
  */
-static pj_status_t pjsip_tsx_on_state_proceeding_uas( pjsip_transaction *tsx,
-                                                      pjsip_event *event)
+static pj_status_t tsx_on_state_proceeding_uas( pjsip_transaction *tsx,
+                                                pjsip_event *event)
 {
     pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING || 
 	      tsx->state == PJSIP_TSX_STATE_TRYING);
@@ -1798,10 +1644,16 @@
 
         pj_status_t status;
 
-	/* Send last response. */
-        status = pjsip_tsx_retransmit( tsx, 0 );
-	if (status != PJ_SUCCESS) {
-	    return status;
+	/* Must have last response sent. */
+	PJ_ASSERT_RETURN(tsx->last_tx != NULL, PJ_EBUG);
+
+	/* Send last response */
+	if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
+	    tsx->transport_flag |= TSX_HAS_PENDING_SEND;
+	} else {
+	    status = tsx_send_msg(tsx, tsx->last_tx);
+	    if (status != PJ_SUCCESS)
+		return status;
 	}
 	
     } else if (event->type == PJSIP_EVENT_TX_MSG ) {
@@ -1815,10 +1667,7 @@
 	pjsip_msg *msg = tdata->msg;
 
 	/* This can only be a response message. */
-	pj_assert(msg->type == PJSIP_RESPONSE_MSG);
-
-	/* Status code must be higher than last sent. */
-	pj_assert(msg->line.status.code >= tsx->status_code);
+	PJ_ASSERT_RETURN(msg->type==PJSIP_RESPONSE_MSG, PJSIP_ENOTRESPONSEMSG);
 
 	/* Update last status */
 	tsx->status_code = msg->line.status.code;
@@ -1847,12 +1696,13 @@
 		tsx->last_tx = tdata;
 		pjsip_tx_data_add_ref( tdata );
 	    }
+
 	    tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, 
                            PJSIP_EVENT_TX_MSG, tdata );
 
 	} else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
 
-	    if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_ack==0) {
+	    if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_200resp==0) {
 
 		/* 2xx class message is not saved, because retransmission 
                  * is handled by TU.
@@ -1861,16 +1711,20 @@
                                PJSIP_EVENT_TX_MSG, tdata );
 
 		/* Transaction is destroyed. */
-		return PJSIP_ETSXDESTROYED;
+		//return PJSIP_ETSXDESTROYED;
 
 	    } else {
 		pj_time_val timeout;
 
 		if (tsx->method.id == PJSIP_INVITE_METHOD) {
 		    tsx->retransmit_count = 0;
-		    pjsip_endpt_schedule_timer( tsx->endpt, 
-                                                &tsx->retransmit_timer, 
-						&t1_timer_val);
+		    if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
+			tsx->transport_flag |= TSX_HAS_PENDING_RESCHED;
+		    } else {
+			pjsip_endpt_schedule_timer( tsx->endpt, 
+						    &tsx->retransmit_timer,
+						    &t1_timer_val);
+		    }
 		}
 
 		/* Save last response sent for retransmission when request 
@@ -1884,7 +1738,7 @@
 		/* Start timer J at 64*T1 for unreliable transport or zero for
 		 * reliable transport.
 		 */
-		if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) {
+		if (!PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)) {
 		    timeout = timeout_timer_val;
 		} else {
 		    timeout.sec = timeout.msec = 0;
@@ -1915,14 +1769,18 @@
 	    /* For INVITE, if unreliable transport is used, retransmission 
 	     * timer G will be scheduled (retransmission).
 	     */
-	    if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) {
+	    if (!PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)) {
 		pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ,
                                                            NULL);
 		if (cseq->method.id == PJSIP_INVITE_METHOD) {
 		    tsx->retransmit_count = 0;
-		    pjsip_endpt_schedule_timer(tsx->endpt, 
-                                               &tsx->retransmit_timer, 
-					       &t1_timer_val);
+		    if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
+			tsx->transport_flag |= TSX_HAS_PENDING_RESCHED;
+		    } else {
+			pjsip_endpt_schedule_timer(tsx->endpt, 
+						   &tsx->retransmit_timer, 
+						   &t1_timer_val);
+		    }
 		}
 	    }
 
@@ -1937,14 +1795,18 @@
 
     } else if (event->type == PJSIP_EVENT_TIMER && 
 	       event->body.timer.entry == &tsx->retransmit_timer) {
+
 	/* Retransmission timer elapsed. */
         pj_status_t status;
 
+	/* Must not be triggered while transport is pending. */
+	pj_assert((tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) == 0);
+
 	/* Must have last response to retransmit. */
 	pj_assert(tsx->last_tx != NULL);
 
 	/* Retransmit the last response. */
-        status = pjsip_tsx_retransmit( tsx, 1 );
+        status = tsx_retransmit( tsx, 1 );
 	if (status != PJ_SUCCESS) {
 	    return status;
 	}
@@ -1953,7 +1815,7 @@
 	       event->body.timer.entry == &tsx->timeout_timer) {
 
 	/* Timeout timer. should not happen? */
-	pj_assert(0);
+	pj_assert(!"Should not happen(?)");
 
 	tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
 
@@ -1963,26 +1825,29 @@
 	return PJ_EBUG;
 
     } else {
-	pj_assert(0);
+	pj_assert(!"Unexpected event");
         return PJ_EBUG;
     }
 
     return PJ_SUCCESS;
 }
 
+
 /*
  * Handler for events in Proceeding for UAC
  * This state happens after provisional response(s) has been received from
  * UAS.
  */
-static pj_status_t pjsip_tsx_on_state_proceeding_uac(pjsip_transaction *tsx, 
-                                                     pjsip_event *event)
+static pj_status_t tsx_on_state_proceeding_uac(pjsip_transaction *tsx, 
+                                               pjsip_event *event)
 {
 
     pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING || 
 	      tsx->state == PJSIP_TSX_STATE_CALLING);
 
     if (event->type != PJSIP_EVENT_TIMER) {
+	pjsip_msg *msg;
+
 	/* Must be incoming response, because we should not retransmit
 	 * request once response has been received.
 	 */
@@ -1991,7 +1856,15 @@
 	    return PJ_EINVALIDOP;
 	}
 
-	tsx->status_code = event->body.rx_msg.rdata->msg_info.msg->line.status.code;
+	msg = event->body.rx_msg.rdata->msg_info.msg;
+
+	/* Must be a response message. */
+	if (msg->type != PJSIP_RESPONSE_MSG) {
+	    pj_assert(!"Expecting response message!");
+	    return PJSIP_ENOTRESPONSEMSG;
+	}
+
+	tsx->status_code = msg->line.status.code;
     } else {
 	tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
     }
@@ -2010,10 +1883,10 @@
 	/* For INVITE, the state moves to Terminated state (because ACK is
 	 * handled in TU). For non-INVITE, state moves to Completed.
 	 */
-	if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_ack == 0) {
+	if (tsx->method.id == PJSIP_INVITE_METHOD) {
 	    tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
                            PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
-	    return PJSIP_ETSXDESTROYED;
+	    //return PJSIP_ETSXDESTROYED;
 
 	} else {
 	    pj_time_val timeout;
@@ -2046,8 +1919,19 @@
 
 	/* Generate and send ACK for INVITE. */
 	if (tsx->method.id == PJSIP_INVITE_METHOD) {
-	    pjsip_endpt_create_ack( tsx->endpt, tsx->last_tx, 
-                                    event->body.rx_msg.rdata );
+	    pjsip_tx_data *ack;
+
+	    status = pjsip_endpt_create_ack( tsx->endpt, tsx->last_tx, 
+					     event->body.rx_msg.rdata,
+					     &ack);
+	    if (status != PJ_SUCCESS)
+		return status;
+
+	    if (ack != tsx->last_tx) {
+		pjsip_tx_data_dec_ref(tsx->last_tx);
+		tsx->last_tx = ack;
+	    }
+
             status = tsx_send_msg( tsx, tsx->last_tx);
 	    if (status != PJ_SUCCESS) {
 		return status;
@@ -2055,7 +1939,7 @@
 	}
 
 	/* Start Timer D with TD/T4 timer if unreliable transport is used. */
-	if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport) == 0) {
+	if (!PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)) {
 	    if (tsx->method.id == PJSIP_INVITE_METHOD) {
 		timeout = td_timer_val;
 	    } else {
@@ -2072,30 +1956,34 @@
 
     } else {
 	// Shouldn't happen because there's no timer for this state.
-	pj_assert(0);
+	pj_assert(!"Unexpected event");
         return PJ_EBUG;
     }
 
     return PJ_SUCCESS;
 }
 
+
 /*
  * Handler for events in Completed state for UAS
  */
-static pj_status_t pjsip_tsx_on_state_completed_uas( pjsip_transaction *tsx, 
-                                                     pjsip_event *event)
+static pj_status_t tsx_on_state_completed_uas( pjsip_transaction *tsx, 
+                                               pjsip_event *event)
 {
     pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED);
 
     if (event->type == PJSIP_EVENT_RX_MSG) {
 	pjsip_msg *msg = event->body.rx_msg.rdata->msg_info.msg;
-	pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ, NULL );
+
+	/* This must be a request message retransmission. */
+	if (msg->type != PJSIP_REQUEST_MSG)
+	    return PJSIP_ENOTREQUESTMSG;
 
 	/* On receive request retransmission, retransmit last response. */
-	if (cseq->method.id != PJSIP_ACK_METHOD) {
+	if (msg->line.req.method.id != PJSIP_ACK_METHOD) {
             pj_status_t status;
 
-            status = pjsip_tsx_retransmit( tsx, 0 );
+            status = tsx_retransmit( tsx, 0 );
 	    if (status != PJ_SUCCESS) {
 		return status;
 	    }
@@ -2122,7 +2010,7 @@
 	    /* Retransmit message. */
             pj_status_t status;
 
-            status = pjsip_tsx_retransmit( tsx, 1 );
+            status = tsx_retransmit( tsx, 1 );
 	    if (status != PJ_SUCCESS) {
 		return status;
 	    }
@@ -2139,29 +2027,32 @@
 		tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
                                PJSIP_EVENT_TIMER, &tsx->timeout_timer );
 
-		return PJSIP_ETSXDESTROYED;
+		//return PJSIP_ETSXDESTROYED;
 
 	    } else {
 		/* Transaction terminated, it can now be deleted. */
 		tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
                                PJSIP_EVENT_TIMER, &tsx->timeout_timer );
-		return PJSIP_ETSXDESTROYED;
+		//return PJSIP_ETSXDESTROYED;
 	    }
 	}
 
     } else {
 	/* Ignore request to transmit. */
-	pj_assert(event->body.tx_msg.tdata == tsx->last_tx);
+	PJ_ASSERT_RETURN(event->type == PJSIP_EVENT_TX_MSG && 
+			 event->body.tx_msg.tdata == tsx->last_tx, 
+			 PJ_EINVALIDOP);
     }
 
     return PJ_SUCCESS;
 }
 
+
 /*
  * Handler for events in Completed state for UAC transaction.
  */
-static pj_status_t pjsip_tsx_on_state_completed_uac( pjsip_transaction *tsx,
-                                                     pjsip_event *event)
+static pj_status_t tsx_on_state_completed_uac( pjsip_transaction *tsx,
+                                               pjsip_event *event)
 {
     pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED);
 
@@ -2174,7 +2065,7 @@
                        PJSIP_EVENT_TIMER, event->body.timer.entry );
 
 	/* Transaction has been destroyed. */
-	return PJSIP_ETSXDESTROYED;
+	//return PJSIP_ETSXDESTROYED;
 
     } else if (event->type == PJSIP_EVENT_RX_MSG) {
 	if (tsx->method.id == PJSIP_INVITE_METHOD) {
@@ -2188,7 +2079,7 @@
 	    {
                 pj_status_t status;
 
-                status = pjsip_tsx_retransmit( tsx, 0 );
+                status = tsx_retransmit( tsx, 0 );
 		if (status != PJ_SUCCESS) {
 		    return status;
 		}
@@ -2199,43 +2090,21 @@
 	} else {
 	    /* Just drop the response. */
 	}
-    } else if (tsx->method.id == PJSIP_INVITE_METHOD &&
-	       event->type == PJSIP_EVENT_TX_MSG &&
-	       event->body.tx_msg.tdata->msg->line.req.method.id==PJSIP_ACK_METHOD) {
-
-        pj_status_t status;
-
-	/* Set last transmitted message. */
-	if (tsx->last_tx != event->body.tx_msg.tdata) {
-	    pjsip_tx_data_dec_ref( tsx->last_tx );
-	    tsx->last_tx = event->body.tx_msg.tdata;
-	    pjsip_tx_data_add_ref( tsx->last_tx );
-	}
-
-	/* No state changed, but notify app. 
-	 * Must notify now, so app has chance to put SDP in outgoing ACK msg.
-	 */
-	tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, 
-                       PJSIP_EVENT_TX_MSG, event->body.tx_msg.tdata );
-
-	/* Send msg */
-	status = tsx_send_msg(tsx, event->body.tx_msg.tdata);
-        if (status != PJ_SUCCESS)
-            return status;
 
     } else {
-	pj_assert(0);
-        return PJ_EBUG;
+	pj_assert(!"Unexpected event");
+        return PJ_EINVALIDOP;
     }
 
     return PJ_SUCCESS;
 }
 
+
 /*
  * Handler for events in state Confirmed.
  */
-static pj_status_t pjsip_tsx_on_state_confirmed( pjsip_transaction *tsx,
-                                                 pjsip_event *event)
+static pj_status_t tsx_on_state_confirmed( pjsip_transaction *tsx,
+                                           pjsip_event *event)
 {
     pj_assert(tsx->state == PJSIP_TSX_STATE_CONFIRMED);
 
@@ -2246,20 +2115,15 @@
     /* Absorb any ACK received. */
     if (event->type == PJSIP_EVENT_RX_MSG) {
 
-        pjsip_method_e method_id = 
-            event->body.rx_msg.rdata->msg_info.msg->line.req.method.id;
+	pjsip_msg *msg = event->body.rx_msg.rdata->msg_info.msg;
 
-	/* Must be a request message. */
-	pj_assert(event->body.rx_msg.rdata->msg_info.msg->type == PJSIP_REQUEST_MSG);
+	/* Only expecting request message. */
+	if (msg->type != PJSIP_REQUEST_MSG)
+	    return PJSIP_ENOTREQUESTMSG;
 
 	/* Must be an ACK request or a late INVITE retransmission. */
-	pj_assert(method_id == PJSIP_ACK_METHOD ||
-		  method_id == PJSIP_INVITE_METHOD);
-
-        /* Just so that compiler won't complain about unused vars when
-         * building release code.
-         */
-        PJ_UNUSED_ARG(method_id);
+	pj_assert(msg->line.req.method.id == PJSIP_ACK_METHOD || 
+		  msg->line.req.method.id == PJSIP_INVITE_METHOD);
 
     } else if (event->type == PJSIP_EVENT_TIMER) {
 	/* Must be from timeout_timer_. */
@@ -2270,25 +2134,25 @@
                        PJSIP_EVENT_TIMER, &tsx->timeout_timer );
 
 	/* Transaction has been destroyed. */
-	return PJSIP_ETSXDESTROYED;
+	//return PJSIP_ETSXDESTROYED;
 
     } else {
-	pj_assert(0);
+	pj_assert(!"Unexpected event");
         return PJ_EBUG;
     }
 
     return PJ_SUCCESS;
 }
 
+
 /*
  * Handler for events in state Terminated.
  */
-static pj_status_t pjsip_tsx_on_state_terminated( pjsip_transaction *tsx,
-                                                  pjsip_event *event)
+static pj_status_t tsx_on_state_terminated( pjsip_transaction *tsx,
+                                            pjsip_event *event)
 {
     pj_assert(tsx->state == PJSIP_TSX_STATE_TERMINATED);
-
-    PJ_UNUSED_ARG(event);
+    pj_assert(event->type == PJSIP_EVENT_TIMER);
 
     /* Destroy this transaction */
     tsx_set_state(tsx, PJSIP_TSX_STATE_DESTROYED, 
@@ -2298,11 +2162,16 @@
 }
 
 
-static pj_status_t pjsip_tsx_on_state_destroyed(pjsip_transaction *tsx,
-                                                pjsip_event *event)
+/*
+ * Handler for events in state Destroyed.
+ * Shouldn't happen!
+ */
+static pj_status_t tsx_on_state_destroyed(pjsip_transaction *tsx,
+                                          pjsip_event *event)
 {
     PJ_UNUSED_ARG(tsx);
     PJ_UNUSED_ARG(event);
-    return PJ_SUCCESS;
+    pj_assert(!"Not expecting any events!!");
+    return PJ_EBUG;
 }
 
diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c
index 50ff6be..4823feb 100644
--- a/pjsip/src/pjsip/sip_transport.c
+++ b/pjsip/src/pjsip/sip_transport.c
@@ -70,7 +70,9 @@
     { PJSIP_TRANSPORT_UDP, 5060, {"UDP", 3}, PJSIP_TRANSPORT_DATAGRAM},
     { PJSIP_TRANSPORT_TCP, 5060, {"TCP", 3}, PJSIP_TRANSPORT_RELIABLE},
     { PJSIP_TRANSPORT_TLS, 5061, {"TLS", 3}, PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE},
-    { PJSIP_TRANSPORT_SCTP, 5060, {"SCTP", 4}, PJSIP_TRANSPORT_RELIABLE}
+    { PJSIP_TRANSPORT_SCTP, 5060, {"SCTP", 4}, PJSIP_TRANSPORT_RELIABLE},
+    { PJSIP_TRANSPORT_LOOP, 15060, {"LOOP", 4}, PJSIP_TRANSPORT_RELIABLE}, 
+    { PJSIP_TRANSPORT_LOOP_DGRAM, 15060, {"LOOP-DGRAM", 10}, PJSIP_TRANSPORT_DATAGRAM},
 };
 
 
@@ -194,14 +196,14 @@
 
     status = pj_atomic_create(tdata->pool, 0, &tdata->ref_cnt);
     if (status != PJ_SUCCESS) {
-	pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool );
+	pjsip_endpt_release_pool( mgr->endpt, tdata->pool );
 	return status;
     }
     
     //status = pj_lock_create_simple_mutex(pool, "tdta%p", &tdata->lock);
     status = pj_lock_create_null_mutex(pool, "tdta%p", &tdata->lock);
     if (status != PJ_SUCCESS) {
-	pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool );
+	pjsip_endpt_release_pool( mgr->endpt, tdata->pool );
 	return status;
     }
 
@@ -238,7 +240,7 @@
 #endif
 	pj_atomic_destroy( tdata->ref_cnt );
 	pj_lock_destroy( tdata->lock );
-	pjsip_endpt_destroy_pool( tdata->mgr->endpt, tdata->pool );
+	pjsip_endpt_release_pool( tdata->mgr->endpt, tdata->pool );
 	return PJSIP_EBUFDESTROYED;
     } else {
 	return PJ_SUCCESS;
@@ -715,16 +717,10 @@
 	    goto finish_process_fragment;
 	}
 
-	/* If message is received from address that's different from sent-by,
-  	 * MUST add received parameter to the via.
-	 */
-	if (pj_strcmp2(&rdata->msg_info.via->sent_by.host, 
-		       rdata->pkt_info.src_name) != 0) 
-	{
-	    pj_strdup2(rdata->tp_info.pool, 
-		       &rdata->msg_info.via->recvd_param, 
-		       rdata->pkt_info.src_name);
-	}
+	/* Always add received parameter to the via. */
+	pj_strdup2(rdata->tp_info.pool, 
+		   &rdata->msg_info.via->recvd_param, 
+		   rdata->pkt_info.src_name);
 
 	/* RFC 3581:
 	 * If message contains "rport" param, put the received port there.
diff --git a/pjsip/src/pjsip/sip_transport_loop.c b/pjsip/src/pjsip/sip_transport_loop.c
new file mode 100644
index 0000000..38f44ce
--- /dev/null
+++ b/pjsip/src/pjsip/sip_transport_loop.c
@@ -0,0 +1,423 @@
+/* $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 <pjsip/sip_transport_loop.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_errno.h>
+#include <pj/pool.h>
+#include <pj/os.h>
+#include <pj/string.h>
+#include <pj/lock.h>
+#include <pj/assert.h>
+#include <pj/compat/socket.h>
+
+
+#define FAIL_IMMEDIATE	1
+#define FAIL_CALLBACK	2
+
+#define ADDR_LOOP	"128.0.0.1"
+#define ADDR_LOOP_DGRAM	"129.0.0.1"
+
+
+/** This structure describes incoming packet. */
+struct recv_list
+{
+    PJ_DECL_LIST_MEMBER(struct recv_list);
+    pjsip_rx_data  rdata;
+};
+
+/** This structure is used to keep delayed send failure. */
+struct send_list
+{
+    PJ_DECL_LIST_MEMBER(struct send_list);
+    pj_time_val    sent_time;
+    pj_ssize_t	   sent;
+    pjsip_tx_data *tdata;
+    void	  *token;
+    void	 (*callback)(pjsip_transport*, void*, pj_ssize_t);
+};
+
+/** This structure describes the loop transport. */
+struct loop_transport
+{
+    pjsip_transport	     base;
+    pj_pool_t		    *pool;
+    pj_thread_t		    *thread;
+    pj_bool_t		     thread_quit_flag;
+    pj_bool_t		     discard;
+    int			     fail_mode;
+    unsigned		     delay;
+    struct recv_list	     recv_list;
+    struct send_list	     send_list;
+};
+
+
+/* Helper function to create "incoming" packet */
+struct recv_list *create_incoming_packet( struct loop_transport *loop,
+					  pjsip_tx_data *tdata )
+{
+    pj_pool_t *pool;
+    struct recv_list *pkt;
+
+    pool = pjsip_endpt_create_pool(loop->base.endpt, "rdata", 
+				   PJSIP_POOL_RDATA_LEN, PJSIP_POOL_RDATA_INC);
+    if (!pool)
+	return NULL;
+
+    pkt = pj_pool_zalloc(pool, sizeof(struct recv_list));
+
+    /* Initialize rdata. */
+    pkt->rdata.tp_info.pool = pool;
+    pkt->rdata.tp_info.transport = &loop->base;
+    
+    /* Copy the packet. */
+    pj_memcpy(pkt->rdata.pkt_info.packet, tdata->buf.start,
+	      tdata->buf.cur - tdata->buf.start);
+    pkt->rdata.pkt_info.len = tdata->buf.cur - tdata->buf.start;
+
+    /* "Source address" info. */
+    pkt->rdata.pkt_info.src_addr_len = sizeof(pj_sockaddr_in);
+    if (loop->base.key.type == PJSIP_TRANSPORT_LOOP)
+	pj_native_strcpy(pkt->rdata.pkt_info.src_name, ADDR_LOOP);
+    else
+	pj_native_strcpy(pkt->rdata.pkt_info.src_name, ADDR_LOOP_DGRAM);
+    pkt->rdata.pkt_info.src_port = loop->base.local_name.port;
+
+    /* When do we need to "deliver" this packet. */
+    pj_gettimeofday(&pkt->rdata.pkt_info.timestamp);
+    pkt->rdata.pkt_info.timestamp.msec += loop->delay;
+    pj_time_val_normalize(&pkt->rdata.pkt_info.timestamp);
+
+    /* Done. */
+
+    return pkt;
+}
+
+
+/* Helper function to add pending notification callback. */
+static pj_status_t add_notification( struct loop_transport *loop,
+				     pjsip_tx_data *tdata,
+				     pj_ssize_t sent,
+				     void *token,
+				     void (*callback)(pjsip_transport*, 
+						      void*, pj_ssize_t))
+{
+    struct send_list *sent_status;
+
+    pjsip_tx_data_add_ref(tdata);
+    pj_lock_acquire(tdata->lock);
+    sent_status = pj_pool_alloc(tdata->pool, sizeof(struct send_list));
+    pj_lock_release(tdata->lock);
+
+    sent_status->sent = sent;
+    sent_status->tdata = tdata;
+    sent_status->token = token;
+    sent_status->callback = callback;
+
+    pj_gettimeofday(&sent_status->sent_time);
+    sent_status->sent_time.msec += loop->delay;
+    pj_time_val_normalize(&sent_status->sent_time);
+
+    pj_lock_acquire(loop->base.lock);
+    pj_list_push_back(&loop->send_list, sent_status);
+    pj_lock_release(loop->base.lock);
+
+    return PJ_SUCCESS;
+}
+
+/* Handler for sending outgoing message; called by transport manager. */
+static pj_status_t loop_send_msg( pjsip_transport *tp, 
+				  pjsip_tx_data *tdata,
+				  const pj_sockaddr_t *rem_addr,
+				  int addr_len,
+				  void *token,
+				  void (*cb)(pjsip_transport *transport,
+					     void *token, 
+					     pj_ssize_t sent_bytes))
+{
+    struct loop_transport *loop = (struct loop_transport*)tp;
+    struct recv_list *recv_pkt;
+    
+    PJ_ASSERT_RETURN(tp && (tp->key.type == PJSIP_TRANSPORT_LOOP ||
+	             tp->key.type == PJSIP_TRANSPORT_LOOP_DGRAM), PJ_EINVAL);
+
+    PJ_UNUSED_ARG(rem_addr);
+    PJ_UNUSED_ARG(addr_len);
+
+
+    /* Need to send failure immediately? */
+    if (loop->fail_mode == FAIL_IMMEDIATE) {
+	return PJ_STATUS_FROM_OS(OSERR_ECONNRESET);
+
+    /* Need to send failure later? */
+    } else if (loop->fail_mode == FAIL_CALLBACK) {
+
+	add_notification(loop, tdata, -PJ_STATUS_FROM_OS(OSERR_ECONNRESET), 
+			 token, cb);
+
+	return PJ_EPENDING;
+    }
+
+    /* Discard any packets? */
+    if (loop->discard)
+	return PJ_SUCCESS;
+
+    /* Create rdata for the "incoming" packet. */
+    recv_pkt = create_incoming_packet(loop, tdata);
+    if (!recv_pkt)
+	return PJ_ENOMEM;
+
+    /* If delay is not configured, deliver this packet now! */
+    if (loop->delay == 0) {
+	pj_ssize_t size_eaten;
+
+	size_eaten = pjsip_tpmgr_receive_packet( loop->base.tpmgr, 
+						 &recv_pkt->rdata);
+	pj_assert(size_eaten == recv_pkt->rdata.pkt_info.len);
+
+	pjsip_endpt_release_pool(loop->base.endpt, 
+				 recv_pkt->rdata.tp_info.pool);
+	return PJ_SUCCESS;
+
+    } else {
+	/* Otherwise if delay is configured, add the "packet" to the 
+	 * receive list to be processed by worker thread, and add
+	 * pending notification for calling the callback.
+	 */
+	add_notification(loop, tdata, tdata->buf.cur - tdata->buf.start, 
+			 token, cb);
+
+	pj_lock_acquire(loop->base.lock);
+	pj_list_push_back(&loop->recv_list, recv_pkt);
+	pj_lock_release(loop->base.lock);
+	return PJ_EPENDING;
+    }
+}
+
+/* Handler to destroy the transport; called by transport manager */
+static pj_status_t loop_destroy(pjsip_transport *tp)
+{
+    struct loop_transport *loop = (struct loop_transport*)tp;
+    
+    PJ_ASSERT_RETURN(tp && (tp->key.type == PJSIP_TRANSPORT_LOOP ||
+	             tp->key.type == PJSIP_TRANSPORT_LOOP_DGRAM), PJ_EINVAL);
+    
+    loop->thread_quit_flag = 1;
+    /* Unlock transport mutex before joining thread. */
+    pj_lock_release(tp->lock);
+    pj_thread_join(loop->thread);
+    pj_thread_destroy(loop->thread);
+
+    pj_lock_destroy(loop->base.lock);
+    pj_atomic_destroy(loop->base.ref_cnt);
+    pjsip_endpt_release_pool(loop->base.endpt, loop->base.pool);
+
+    return PJ_SUCCESS;
+}
+
+/* Worker thread for loop transport. */
+static int loop_thread(void *arg)
+{
+    struct loop_transport *loop = arg;
+
+    while (!loop->thread_quit_flag) {
+	pj_time_val now;
+
+	pj_thread_sleep(10);
+	pj_gettimeofday(&now);
+
+	pj_lock_acquire(loop->base.lock);
+
+	/* Process pending send notification. */
+	while (!pj_list_empty(&loop->send_list)) {
+	    struct send_list *node = loop->send_list.next;
+
+	    /* Break when next node time is greater than now. */
+	    if (PJ_TIME_VAL_GTE(node->sent_time, now))
+		break;
+
+	    /* Notify callback. */
+	    if (node->callback) {
+		(*node->callback)(&loop->base, node->token, node->sent);
+	    }
+
+	    /* Delete this from the list. */
+	    pj_list_erase(node);
+
+	    /* Decrement tdata reference counter. */
+	    pjsip_tx_data_dec_ref(node->tdata);
+	}
+
+	/* Process "incoming" packets. */
+	while (!pj_list_empty(&loop->recv_list)) {
+	    struct recv_list *node = loop->recv_list.next;
+	    pj_ssize_t size_eaten;
+
+	    /* Break when next node time is greater than now. */
+	    if (PJ_TIME_VAL_GTE(node->rdata.pkt_info.timestamp, now))
+		break;
+
+	    /* Notify transport manager about the "incoming packet" */
+	    size_eaten = pjsip_tpmgr_receive_packet(loop->base.tpmgr,
+						    &node->rdata);
+
+	    /* Must "eat" all the packets. */
+	    pj_assert(size_eaten == node->rdata.pkt_info.len);
+
+	    /* Delete this from the list. */
+	    pj_list_erase(node);
+
+	    /* Done. */
+	    pjsip_endpt_release_pool(loop->base.endpt,
+				     node->rdata.tp_info.pool);
+	}
+
+	pj_lock_release(loop->base.lock);
+    }
+
+    return 0;
+}
+
+
+/* Start loop transport. */
+PJ_DEF(pj_status_t) pjsip_loop_start( pjsip_endpoint *endpt,
+				      pjsip_transport **transport)
+{
+    pj_pool_t *pool;
+    struct loop_transport *loop;
+    pj_status_t status;
+
+    /* Create pool. */
+    pool = pjsip_endpt_create_pool(endpt, "loop", 4000, 4000);
+    if (!pool)
+	return PJ_ENOMEM;
+
+    /* Create the loop structure. */
+    loop = pj_pool_zalloc(pool, sizeof(struct loop_transport));
+    
+    /* Initialize transport properties. */
+    pj_sprintf(loop->base.obj_name, "loop%p", loop);
+    loop->base.pool = pool;
+    status = pj_atomic_create(pool, 0, &loop->base.ref_cnt);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+    status = pj_lock_create_recursive_mutex(pool, "loop", &loop->base.lock);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+    loop->base.key.type = PJSIP_TRANSPORT_LOOP_DGRAM;
+    loop->base.key.rem_addr.sa_family = PJ_AF_INET;
+    loop->base.type_name = "LOOP-DGRAM";
+    loop->base.info = "LOOP-DGRAM";
+    loop->base.flag = PJSIP_TRANSPORT_DATAGRAM;
+    loop->base.local_name.host = pj_str(ADDR_LOOP_DGRAM);
+    loop->base.local_name.port = 
+	pjsip_transport_get_default_port_for_type(loop->base.key.type);
+    loop->base.addr_len = sizeof(pj_sockaddr_in);
+    loop->base.endpt = endpt;
+    loop->base.tpmgr = pjsip_endpt_get_tpmgr(endpt);
+    loop->base.send_msg = &loop_send_msg;
+    loop->base.destroy = &loop_destroy;
+
+    pj_list_init(&loop->recv_list);
+    pj_list_init(&loop->send_list);
+
+    /* Create worker thread. */
+    status = pj_thread_create(pool, "loop", &loop_thread, loop, 0, 
+			      PJ_THREAD_SUSPENDED, &loop->thread);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Register to transport manager. */
+    status = pjsip_transport_register( loop->base.tpmgr, &loop->base);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Start the thread. */
+    status = pj_thread_resume(loop->thread);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /*
+     * Done.
+     */
+
+    *transport = &loop->base;
+    return PJ_SUCCESS;
+
+on_error:
+    if (loop->base.lock)
+	pj_lock_destroy(loop->base.lock);
+    if (loop->thread)
+	pj_thread_destroy(loop->thread);
+    if (loop->base.ref_cnt)
+	pj_atomic_destroy(loop->base.ref_cnt);
+    pjsip_endpt_release_pool(endpt, loop->pool);
+    return status;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_loop_set_discard( pjsip_transport *tp,
+					    pj_bool_t discard,
+					    pj_bool_t *prev_value )
+{
+    struct loop_transport *loop = (struct loop_transport*)tp;
+
+    PJ_ASSERT_RETURN(tp && (tp->key.type == PJSIP_TRANSPORT_LOOP ||
+	             tp->key.type == PJSIP_TRANSPORT_LOOP_DGRAM), PJ_EINVAL);
+
+    if (prev_value)
+	*prev_value = loop->discard;
+    loop->discard = discard;
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_loop_set_failure( pjsip_transport *tp,
+					    int fail_flag,
+					    int *prev_value )
+{
+    struct loop_transport *loop = (struct loop_transport*)tp;
+
+    PJ_ASSERT_RETURN(tp && (tp->key.type == PJSIP_TRANSPORT_LOOP ||
+	             tp->key.type == PJSIP_TRANSPORT_LOOP_DGRAM), PJ_EINVAL);
+
+    if (prev_value)
+	*prev_value = loop->fail_mode;
+    loop->fail_mode = fail_flag;
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_loop_set_delay( pjsip_transport *tp,
+					  unsigned delay,
+					  unsigned *prev_value)
+{
+    struct loop_transport *loop = (struct loop_transport*)tp;
+
+    PJ_ASSERT_RETURN(tp && (tp->key.type == PJSIP_TRANSPORT_LOOP ||
+	             tp->key.type == PJSIP_TRANSPORT_LOOP_DGRAM), PJ_EINVAL);
+
+    if (prev_value)
+	*prev_value = loop->delay;
+    loop->delay = delay;
+
+    return PJ_SUCCESS;
+}
+
diff --git a/pjsip/src/pjsip/sip_transport_udp.c b/pjsip/src/pjsip/sip_transport_udp.c
index f61b58c..f925d8c 100644
--- a/pjsip/src/pjsip/sip_transport_udp.c
+++ b/pjsip/src/pjsip/sip_transport_udp.c
@@ -1,4 +1,4 @@
-/* $Id: $ */
+/* $Id$ */
 /* 
  * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
  *
@@ -192,6 +192,7 @@
 {
     struct udp_transport *tp = (struct udp_transport*)transport;
     pj_ssize_t size;
+    pj_status_t status;
 
     PJ_ASSERT_RETURN(transport && tdata, PJ_EINVAL);
     PJ_ASSERT_RETURN(tdata->op_key.tdata == NULL, PJSIP_EPENDINGTX);
@@ -203,9 +204,14 @@
 
     /* Send to ioqueue! */
     size = tdata->buf.cur - tdata->buf.start;
-    return pj_ioqueue_sendto(tp->key, (pj_ioqueue_op_key_t*)&tdata->op_key,
-			     tdata->buf.start, &size, 0,
-			     rem_addr, addr_len);
+    status = pj_ioqueue_sendto(tp->key, (pj_ioqueue_op_key_t*)&tdata->op_key,
+			       tdata->buf.start, &size, 0,
+			       rem_addr, addr_len);
+
+    if (status != PJ_EPENDING)
+	tdata->op_key.tdata = NULL;
+
+    return status;
 }
 
 /*
@@ -244,7 +250,7 @@
 	pj_lock_destroy(tp->base.lock);
 
     /* Destroy pool. */
-    pjsip_endpt_destroy_pool(tp->base.endpt, tp->base.pool);
+    pjsip_endpt_release_pool(tp->base.endpt, tp->base.pool);
 
     return PJ_SUCCESS;
 }
@@ -419,7 +425,8 @@
     }
 
     /* Done. */
-    *p_transport = &tp->base;
+    if (p_transport)
+	*p_transport = &tp->base;
     return PJ_SUCCESS;
 
 on_error:
diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c
index 532d6fb..0760e32 100644
--- a/pjsip/src/pjsip/sip_util.c
+++ b/pjsip/src/pjsip/sip_util.c
@@ -176,8 +176,7 @@
 	pj_strdup_with_null(tdata->pool, &tmp, param_target);
 	target = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, 0);
 	if (target == NULL) {
-	    PJ_LOG(4,(THIS_FILE, "Error creating request: invalid target %s", 
-		      tmp.ptr));
+	    status = PJSIP_EINVALIDREQURI;
 	    goto on_error;
 	}
 
@@ -187,8 +186,7 @@
 	from->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, 
 				     PJSIP_PARSE_URI_AS_NAMEADDR);
 	if (from->uri == NULL) {
-	    PJ_LOG(4,(THIS_FILE, "Error creating request: invalid 'From' URI '%s'",
-				tmp.ptr));
+	    status = PJSIP_EINVALIDHDR;
 	    goto on_error;
 	}
 	pj_create_unique_string(tdata->pool, &from->tag);
@@ -199,8 +197,7 @@
 	to->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, 
 				   PJSIP_PARSE_URI_AS_NAMEADDR);
 	if (to->uri == NULL) {
-	    PJ_LOG(4,(THIS_FILE, "Error creating request: invalid 'To' URI '%s'",
-				tmp.ptr));
+	    status = PJSIP_EINVALIDHDR;
 	    goto on_error;
 	}
 
@@ -211,9 +208,7 @@
 	    contact->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen,
 					    PJSIP_PARSE_URI_AS_NAMEADDR);
 	    if (contact->uri == NULL) {
-		PJ_LOG(4,(THIS_FILE, 
-			  "Error creating request: invalid 'Contact' URI '%s'",
-			  tmp.ptr));
+		status = PJSIP_EINVALIDHDR;
 		goto on_error;
 	    }
 	} else {
@@ -1169,7 +1164,7 @@
 	    /* Callback will be called later. */
 	    return PJ_SUCCESS;
 	} else {
-	    send_response_transport_cb(send_state, tdata, -status);
+	    pjsip_transport_dec_ref(send_state->cur_transport);
 	    return status;
 	}
     } else {
@@ -1179,6 +1174,57 @@
     }
 }
 
+/*
+ * Send response
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_respond_stateless( pjsip_endpoint *endpt,
+						   pjsip_rx_data *rdata,
+						   int st_code,
+						   const pj_str_t *st_text,
+						   const pjsip_hdr *hdr_list,
+						   const pjsip_msg_body *body)
+{
+    pj_status_t status;
+    pjsip_response_addr res_addr;
+    pjsip_tx_data *tdata;
+
+    /* Create response message */
+    status = pjsip_endpt_create_response( endpt, rdata, st_code, st_text, 
+					  &tdata);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Add the message headers, if any */
+    if (hdr_list) {
+	const pjsip_hdr *hdr = hdr_list->next;
+	while (hdr != hdr_list) {
+	    pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_clone(tdata->pool, hdr) );
+	    hdr = hdr->next;
+	}
+    }
+
+    /* Add the message body, if any. */
+    if (body) {
+	tdata->msg->body = pj_pool_alloc(tdata->pool, sizeof(pjsip_msg_body));
+	status = pjsip_msg_body_clone( tdata->pool, tdata->msg->body, body );
+	if (status != PJ_SUCCESS) {
+	    pjsip_tx_data_dec_ref(tdata);
+	    return status;
+	}
+    }
+
+    /* Get where to send request. */
+    status = pjsip_get_response_addr( tdata->pool, rdata, &res_addr );
+    if (status != PJ_SUCCESS) {
+	pjsip_tx_data_dec_ref(tdata);
+	return status;
+    }
+
+    /* Send! */
+    status = pjsip_endpt_send_response( endpt, &res_addr, tdata, NULL, NULL );
+
+    return status;
+}
 
 /*
  * Get the event string from the event ID.
diff --git a/pjsip/src/pjsip/sip_util_proxy.c b/pjsip/src/pjsip/sip_util_proxy.c
new file mode 100644
index 0000000..043877b
--- /dev/null
+++ b/pjsip/src/pjsip/sip_util_proxy.c
@@ -0,0 +1,66 @@
+/* $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 <pjsip/sip_util.h>
+#include <pjsip/sip_errno.h>
+#include <pj/assert.h>
+
+PJ_DEF(pj_status_t) pjsip_endpt_create_request_fwd(  pjsip_endpoint *endpt,
+						     pjsip_rx_data *rdata, 
+						     const pjsip_uri *uri,
+						     const pj_str_t *branch,
+						     unsigned options,
+						     pjsip_tx_data **tdata)
+{
+    PJ_UNUSED_ARG(endpt);
+    PJ_UNUSED_ARG(rdata);
+    PJ_UNUSED_ARG(uri);
+    PJ_UNUSED_ARG(branch);
+    PJ_UNUSED_ARG(options);
+    PJ_UNUSED_ARG(tdata);
+
+    pj_assert(!"Not implemented yet");
+    return PJ_EBUG;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_endpt_create_response_fwd( pjsip_endpoint *endpt,
+						     pjsip_rx_data *rdata, 
+						     unsigned options,
+						     pjsip_tx_data **tdata)
+{
+    PJ_UNUSED_ARG(endpt);
+    PJ_UNUSED_ARG(rdata);
+    PJ_UNUSED_ARG(options);
+    PJ_UNUSED_ARG(tdata);
+
+    pj_assert(!"Not implemented yet");
+    return PJ_EBUG;
+}
+
+
+PJ_DEF(pj_str_t) pjsip_calculate_branch_id( pjsip_rx_data *rdata )
+{
+    pj_str_t empty_str = { NULL, 0 };
+
+    PJ_UNUSED_ARG(rdata);
+    pj_assert(!"Not implemented yet");
+    return empty_str;
+}
+
+
diff --git a/pjsip/src/test-pjsip/msg_test.c b/pjsip/src/test-pjsip/msg_test.c
index e370271..11a69b4 100644
--- a/pjsip/src/test-pjsip/msg_test.c
+++ b/pjsip/src/test-pjsip/msg_test.c
@@ -47,7 +47,7 @@
 {
     /* 'Normal' message with all headers. */
     "INVITE sip:user@foo SIP/2.0\n"
-    "From: Hi I'm Joe <sip:joe.user@bar.otherdomain.com>;tag=1234578901234567890\r"
+    "From: Hi I'm Joe <sip:joe.user@bar.otherdomain.com>;tag=123457890123456\r"
     "To: Fellow User <sip:user@foo.bar.domain.com>\r\n"
     "Call-ID: 12345678901234567890@bar\r\n"
     "Content-Length: 0\r\n"
@@ -57,15 +57,16 @@
     "Content-Type: text/html ; charset=ISO-8859-4\r"
     "Route: <sip:bigbox3.site3.atlanta.com;lr>,\r\n"
     "  <sip:server10.biloxi.com;lr>\r"
-    "Record-Route: <sip:server10.biloxi.com>,\r\n"
+    "Record-Route: <sip:server10.biloxi.com>,\r\n" /* multiple routes+folding*/
     "  <sip:bigbox3.site3.atlanta.com;lr>\n"
-    "Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1\n"
-    "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\n"
+    "Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c230\n"
+    "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\n" /* folding. */
     " ;received=192.0.2.1\r\n"
     "Via: SIP/2.0/UDP 10.2.1.1, SIP/2.0/TCP 192.168.1.1\n"
     "Organization: \r"
     "Max-Forwards: 70\n"
-    "X-Header: \r\n"
+    "X-Header: \r\n"	    /* empty header */
+    "P-Associated-URI:\r\n" /* empty header without space */
     "\r\n",
     &create_msg0,
     PJ_SUCCESS
@@ -351,10 +352,10 @@
     pj_strdup2(pool, &url->user, "user");
     pj_strdup2(pool, &url->host, "foo");
 
-    /* "From: Hi I'm Joe <sip:joe.user@bar.otherdomain.com>;tag=1234578901234567890\r" */
+    /* "From: Hi I'm Joe <sip:joe.user@bar.otherdomain.com>;tag=123457890123456\r" */
     fromto = pjsip_from_hdr_create(pool);
     pjsip_msg_add_hdr(msg, (pjsip_hdr*)fromto);
-    pj_strdup2(pool, &fromto->tag, "1234578901234567890");
+    pj_strdup2(pool, &fromto->tag, "123457890123456");
     name_addr = pjsip_name_addr_create(pool);
     fromto->uri = (pjsip_uri*)name_addr;
     pj_strdup2(pool, &name_addr->display, "Hi I'm Joe");
@@ -462,12 +463,12 @@
     pj_strdup2(pool, &url->host, "bigbox3.site3.atlanta.com");
     url->lr_param = 1;
 
-    /* "Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1\n" */
+    /* "Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c230\n" */
     via = pjsip_via_hdr_create(pool);
     pjsip_msg_add_hdr(msg, (pjsip_hdr*)via);
     pj_strdup2(pool, &via->transport, "SCTP");
     pj_strdup2(pool, &via->sent_by.host, "bigbox3.site3.atlanta.com");
-    pj_strdup2(pool, &via->branch_param, "z9hG4bK77ef4c2312983.1");
+    pj_strdup2(pool, &via->branch_param, "z9hG4bK77ef4c230");
 
     /* "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\n"
 	" ;received=192.0.2.1\r\n" */
@@ -518,6 +519,15 @@
     str.slen = 0;
     generic->hvalue = str;
 
+    /* P-Associated-URI:\r\n */
+    str.ptr = "P-Associated-URI";
+    str.slen = 16;
+    generic = pjsip_generic_string_hdr_create(pool, &str);
+    pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic);
+    str.ptr = NULL;
+    str.slen = 0;
+    generic->hvalue = str;
+
     return msg;
 }
 
@@ -677,7 +687,7 @@
     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_destroy_pool(endpt, pool);
+	pjsip_endpt_release_pool(endpt, pool);
 
 	if (status != PJ_SUCCESS)
 	    return status;
@@ -691,7 +701,7 @@
 	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_destroy_pool(endpt, pool);
+	    pjsip_endpt_release_pool(endpt, pool);
 
 	    if (status != PJ_SUCCESS)
 		return status;
diff --git a/pjsip/src/test-pjsip/test.c b/pjsip/src/test-pjsip/test.c
index a8f339c..fbb7291 100644
--- a/pjsip/src/test-pjsip/test.c
+++ b/pjsip/src/test-pjsip/test.c
@@ -81,10 +81,12 @@
 
     PJ_LOG(3,("",""));
 
-    DO_TEST(uri_test());
-    DO_TEST(msg_test());
-    DO_TEST(txdata_test());
-    DO_TEST(transport_udp_test());
+    //DO_TEST(uri_test());
+    //DO_TEST(msg_test());
+    //DO_TEST(txdata_test());
+    //DO_TEST(transport_udp_test());
+    DO_TEST(transport_loop_test());
+    //DO_TEST(tsx_uac_test());
 
 on_return:
 
diff --git a/pjsip/src/test-pjsip/test.h b/pjsip/src/test-pjsip/test.h
index 686dea0..db12e39 100644
--- a/pjsip/src/test-pjsip/test.h
+++ b/pjsip/src/test-pjsip/test.h
@@ -23,22 +23,25 @@
 
 extern pjsip_endpoint *endpt;
 
-#define TEST_UDP_PORT	15060
+#define TEST_UDP_PORT	    15060
+#define TEST_UDP_PORT_STR   "15060"
 
 /* The tests */
 int uri_test(void);
 int msg_test(void);
 int txdata_test(void);
 int transport_udp_test(void);
+int transport_loop_test(void);
+int tsx_uac_test(void);
 
 /* Transport test helpers (transport_test.c). */
 int generic_transport_test(pjsip_transport *tp);
 int transport_send_recv_test( pjsip_transport_type_e tp_type,
 			      pjsip_transport *ref_tp,
-			      const pj_sockaddr_in *rem_addr );
+			      char *target_url );
 int transport_rt_test( pjsip_transport_type_e tp_type,
 		       pjsip_transport *ref_tp,
-		       const pj_sockaddr_in *rem_addr );
+		       char *target_url );
 
 /* Test main entry */
 int  test_main(void);
diff --git a/pjsip/src/test-pjsip/transport_loop_test.c b/pjsip/src/test-pjsip/transport_loop_test.c
new file mode 100644
index 0000000..467f327
--- /dev/null
+++ b/pjsip/src/test-pjsip/transport_loop_test.c
@@ -0,0 +1,127 @@
+/* $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_core.h>
+#include <pjlib.h>
+
+static int datagram_loop_test()
+{
+    pjsip_transport *loop, *tp;
+    pj_str_t s;
+    int i, log_level;
+    pj_sockaddr_in addr;
+    pj_status_t status;
+
+    PJ_LOG(3,("", "testing datagram loop transport"));
+
+    /* Create loop transport. */
+    status = pjsip_loop_start(endpt, &loop);
+    if (status != PJ_SUCCESS) {
+	app_perror("   error: unable to create datagram loop transport", 
+		   status);
+	return -10;
+    }
+
+    /* Create dummy address. */
+    pj_sockaddr_in_init(&addr, pj_cstr(&s, "130.0.0.1"), TEST_UDP_PORT);
+
+    /* Test acquire transport. */
+    status = pjsip_endpt_acquire_transport( endpt, PJSIP_TRANSPORT_LOOP_DGRAM,
+					    &addr, sizeof(addr), &tp);
+    if (status != PJ_SUCCESS) {
+	app_perror("   error: unable to acquire transport", status);
+	return -20;
+    }
+
+    /* Check that this is the right transport. */
+    if (tp != loop) {
+	return -30;
+    }
+
+    /* Test basic transport attributes */
+    status = generic_transport_test(loop);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Basic transport's send/receive loopback test. */
+    for (i=0; i<2; ++i) {
+	status = transport_send_recv_test(PJSIP_TRANSPORT_LOOP_DGRAM, loop, 
+					  "sip:bob@130.0.0.1;transport=loop-dgram");
+	if (status != 0)
+	    return status;
+    }
+
+    /* For multithreaded round-trip test to work, delay must be set
+     * (otherwise functions will be called recursively until no memory is
+     * left in the system)
+     */
+
+    /* Put delay. */
+    pjsip_loop_set_delay(loop, 1, NULL);
+
+    /* Multi-threaded round-trip test. */
+    status = transport_rt_test(PJSIP_TRANSPORT_LOOP_DGRAM, tp, 
+			       "sip:bob@130.0.0.1;transport=loop-dgram");
+    if (status != 0)
+	return status;
+
+
+    /* Next test will test without delay.
+     * This will stress-test the system.
+     */
+    PJ_LOG(3,("","  performing another multithreaded round-trip test..."));
+
+    /* Remove delay. */
+    pjsip_loop_set_delay(loop, 0, NULL);
+
+    /* Ignore errors. */
+    log_level = pj_log_get_level();
+    pj_log_set_level(2);
+
+    /* Multi-threaded round-trip test. */
+    status = transport_rt_test(PJSIP_TRANSPORT_LOOP_DGRAM, tp, 
+			       "sip:bob@130.0.0.1;transport=loop-dgram");
+    if (status != 0)
+	return status;
+
+    /* Restore log level. */
+    pj_log_set_level(log_level);
+
+    /* Check that reference counter is one. */
+    if (pj_atomic_get(loop->ref_cnt) != 1) {
+	return -30;
+    }
+
+    /* Decrement reference. */
+    pjsip_transport_dec_ref(loop);
+
+    return 0;
+}
+
+int transport_loop_test(void)
+{
+    int status;
+
+    status = datagram_loop_test();
+    if (status != 0)
+	return status;
+
+    return 0;
+}
diff --git a/pjsip/src/test-pjsip/transport_test.c b/pjsip/src/test-pjsip/transport_test.c
index 0d54d42..76b6bb9 100644
--- a/pjsip/src/test-pjsip/transport_test.c
+++ b/pjsip/src/test-pjsip/transport_test.c
@@ -75,7 +75,6 @@
  * before we continue with more complicated tests.
  */
 #define FROM_HDR    "Bob <sip:bob@example.com>"
-#define TO_HDR	    "Alice <sip:alice@example.com>"
 #define CONTACT_HDR "Bob <sip:bob@127.0.0.1>"
 #define CALL_ID_HDR "SendRecv-Test"
 #define CSEQ_VALUE  100
@@ -108,7 +107,7 @@
     NULL,				/* unload()		*/
     &my_on_rx_request,			/* on_rx_request()	*/
     &my_on_rx_response,			/* on_rx_response()	*/
-    NULL,				/* tsx_handler()	*/
+    NULL,				/* on_tsx_state()	*/
 };
 
 
@@ -188,10 +187,9 @@
 /* Test that we receive loopback message. */
 int transport_send_recv_test( pjsip_transport_type_e tp_type,
 			      pjsip_transport *ref_tp,
-			      const pj_sockaddr_in *rem_addr )
+			      char *target_url )
 {
     pj_status_t status;
-    char target_buf[80];
     pj_str_t target, from, to, contact, call_id, body;
     pjsip_method method;
     pjsip_tx_data *tdata;
@@ -209,11 +207,9 @@
     }
 
     /* Create a request message. */
-    pj_sprintf(target_buf, "sip:%s:%d", pj_inet_ntoa(rem_addr->sin_addr),
-					pj_ntohs(rem_addr->sin_port));
-    target = pj_str(target_buf);
+    target = pj_str(target_url);
     from = pj_str(FROM_HDR);
-    to = pj_str(TO_HDR);
+    to = pj_str(target_url);
     contact = pj_str(CONTACT_HDR);
     call_id = pj_str(CALL_ID_HDR);
     body = pj_str(BODY);
@@ -333,7 +329,7 @@
     pj_str_t call_id;
 } rt_test_data[16];
 
-static char	 rt_target_uri[32];
+static char	 rt_target_uri[64];
 static pj_bool_t rt_stop;
 static pj_str_t  rt_call_id;
 
@@ -349,15 +345,18 @@
 
 	status = pjsip_endpt_create_response( endpt, rdata, 200, NULL, &tdata);
 	if (status != PJ_SUCCESS) {
+	    app_perror("    error creating response", status);
 	    return PJ_TRUE;
 	}
 	status = pjsip_get_response_addr( tdata->pool, rdata, &res_addr);
 	if (status != PJ_SUCCESS) {
+	    app_perror("    error in get response address", status);
 	    pjsip_tx_data_dec_ref(tdata);
 	    return PJ_TRUE;
 	}
 	status = pjsip_endpt_send_response( endpt, &res_addr, tdata, NULL, NULL);
 	if (status != PJ_SUCCESS) {
+	    app_perror("    error sending response", status);
 	    pjsip_tx_data_dec_ref(tdata);
 	    return PJ_TRUE;
 	}
@@ -376,7 +375,7 @@
     /* Create a request message. */
     target = pj_str(rt_target_uri);
     from = pj_str(FROM_HDR);
-    to = pj_str(TO_HDR);
+    to = pj_str(rt_target_uri);
     contact = pj_str(CONTACT_HDR);
     call_id = rt_test_data[thread_id].call_id;
 
@@ -430,7 +429,7 @@
 
 static int rt_thread(void *arg)
 {
-    int thread_id = (int)arg;
+    int i, thread_id = (int)arg;
     pj_time_val poll_delay = { 0, 10 };
 
     /* Sleep to allow main threads to run. */
@@ -443,12 +442,17 @@
     while (!rt_stop) {
 	pjsip_endpt_handle_events(endpt, &poll_delay);
     }
+
+    /* Exhaust responses. */
+    for (i=0; i<100; ++i)
+	pjsip_endpt_handle_events(endpt, &poll_delay);
+
     return 0;
 }
 
 int transport_rt_test( pjsip_transport_type_e tp_type,
 		       pjsip_transport *ref_tp,
-		       const pj_sockaddr_in *rem_addr )
+		       char *target_url )
 {
     enum { THREADS = 4, INTERVAL = 10 };
     int i;
@@ -483,8 +487,7 @@
 	return -610;
 
     /* Initialize static test data. */
-    pj_sprintf(rt_target_uri, "sip:%s:%d", pj_inet_ntoa(rem_addr->sin_addr),
-					   pj_ntohs(rem_addr->sin_port));
+    pj_native_strcpy(rt_target_uri, target_url);
     rt_call_id = pj_str("RT-Call-Id/");
     rt_stop = PJ_FALSE;
 
@@ -551,7 +554,7 @@
 	PJ_LOG(3,("", "    no message was lost"));
     PJ_LOG(3,("", "    average round-trip=%d usec", usec_rt));
 
-    pjsip_endpt_destroy_pool(endpt, pool);
+    pjsip_endpt_release_pool(endpt, pool);
 
     if (is_reliable && (total_sent != total_recv)) {
 	PJ_LOG(3,("", "   error: %d messages lost", total_sent-total_recv));
diff --git a/pjsip/src/test-pjsip/transport_udp_test.c b/pjsip/src/test-pjsip/transport_udp_test.c
index 1af74ed..0fdbdda 100644
--- a/pjsip/src/test-pjsip/transport_udp_test.c
+++ b/pjsip/src/test-pjsip/transport_udp_test.c
@@ -76,13 +76,15 @@
     /* Basic transport's send/receive loopback test. */
     pj_sockaddr_in_init(&rem_addr, pj_cstr(&s, "127.0.0.1"), TEST_UDP_PORT);
     for (i=0; i<SEND_RECV_LOOP; ++i) {
-	status = transport_send_recv_test(PJSIP_TRANSPORT_UDP, tp, &rem_addr);
+	status = transport_send_recv_test(PJSIP_TRANSPORT_UDP, tp, 
+					  "sip:alice@127.0.0.1:"TEST_UDP_PORT_STR);
 	if (status != 0)
 	    return status;
     }
 
     /* Multi-threaded round-trip test. */
-    status = transport_rt_test(PJSIP_TRANSPORT_UDP, tp, &rem_addr);
+    status = transport_rt_test(PJSIP_TRANSPORT_UDP, tp, 
+			       "sip:alice@127.0.0.1:"TEST_UDP_PORT_STR);
     if (status != 0)
 	return status;
 
@@ -93,6 +95,12 @@
     /* Destroy this transport. */
     pjsip_transport_dec_ref(udp_tp);
 
+    /* Force destroy this transport. */
+    status = pjsip_transport_unregister( pjsip_endpt_get_tpmgr(endpt), udp_tp);
+    if (status != PJ_SUCCESS)
+	return -90;
+
+
     /* Done */
     return 0;
 }
diff --git a/pjsip/src/test-pjsip/tsx_uac_test.c b/pjsip/src/test-pjsip/tsx_uac_test.c
new file mode 100644
index 0000000..24af41e
--- /dev/null
+++ b/pjsip/src/test-pjsip/tsx_uac_test.c
@@ -0,0 +1,306 @@
+/* $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_core.h>
+#include <pjlib.h>
+
+/*****************************************************************************
+ **
+ ** UAC basic retransmission and timeout test.
+ **
+ ** This will test the retransmission of the UAC transaction. Remote will not
+ ** answer the transaction, so the transaction should fail.
+ **
+ *****************************************************************************
+ */
+
+static char *CALL_ID1 = "UAC-Tsx-Basic-Test1";
+static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e);
+static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata);
+
+/* UAC transaction user module. */
+static pjsip_module tsx_user = 
+{
+    NULL, NULL,				/* prev and next	*/
+    { "Tsx-User", 8},			/* Name.		*/
+    -1,					/* Id			*/
+    PJSIP_MOD_PRIORITY_APPLICATION-1,	/* Priority		*/
+    NULL,				/* User data.		*/
+    0,					/* Number of methods supported (=0). */
+    { 0 },				/* Array of methods (none) */
+    NULL,				/* load()		*/
+    NULL,				/* start()		*/
+    NULL,				/* stop()		*/
+    NULL,				/* unload()		*/
+    NULL,				/* on_rx_request()	*/
+    NULL,				/* on_rx_response()	*/
+    &tsx_user_on_tsx_state,		/* on_tsx_state()	*/
+};
+
+/* Module to receive the loop-backed request. */
+static pjsip_module msg_receiver = 
+{
+    NULL, NULL,				/* prev and next	*/
+    { "Test", 4},			/* Name.		*/
+    -1,					/* Id			*/
+    PJSIP_MOD_PRIORITY_APPLICATION-1,	/* Priority		*/
+    NULL,				/* User data.		*/
+    0,					/* Number of methods supported (=0). */
+    { 0 },				/* Array of methods (none) */
+    NULL,				/* load()		*/
+    NULL,				/* start()		*/
+    NULL,				/* stop()		*/
+    NULL,				/* unload()		*/
+    &msg_receiver_on_rx_request,	/* on_rx_request()	*/
+    NULL,				/* on_rx_response()	*/
+    NULL,				/* on_tsx_state()	*/
+};
+
+/* Static vars. */
+static int recv_count;
+static pj_time_val recv_last;
+static pj_bool_t test_complete;
+
+static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
+{
+    if (tsx->state == PJSIP_TSX_STATE_TERMINATED && test_complete==0)
+	test_complete = 1;
+}
+
+#define DIFF(a,b)   ((a<b) ? (b-a) : (a-b))
+
+static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata)
+{
+    if (pj_strcmp2(&rdata->msg_info.call_id, CALL_ID1) == 0) {
+	/*
+	 * The CALL_ID1 test performs the verifications for transaction
+	 * retransmission mechanism. It will not answer the incoming request
+	 * with any response.
+	 */
+	pjsip_msg *msg = rdata->msg_info.msg;
+
+	PJ_LOG(4,("", "   received request"));
+
+	/* Only wants to take INVITE or OPTIONS method. */
+	if (msg->line.req.method.id != PJSIP_INVITE_METHOD &&
+	    msg->line.req.method.id != PJSIP_OPTIONS_METHOD)
+	{
+	    PJ_LOG(3,("", "   error: received unexpected method %.*s",
+			  msg->line.req.method.name.slen,
+			  msg->line.req.method.name.ptr));
+	    test_complete = -600;
+	    return PJ_TRUE;
+	}
+
+	if (recv_count == 0) {
+	    recv_count++;
+	    pj_gettimeofday(&recv_last);
+	} else {
+	    pj_time_val now;
+	    unsigned msec_expected, msec_elapsed;
+
+	    pj_gettimeofday(&now);
+	    PJ_TIME_VAL_SUB(now, recv_last);
+	    msec_elapsed = now.sec*1000 + now.msec;
+
+	    ++recv_count;
+    	    msec_expected = (1<<(recv_count-2))*PJSIP_T1_TIMEOUT;
+
+	    if (msg->line.req.method.id != PJSIP_INVITE_METHOD) {
+		if (msec_expected > PJSIP_T2_TIMEOUT)
+		    msec_expected = PJSIP_T2_TIMEOUT;
+	    }
+
+	    if (DIFF(msec_expected, msec_elapsed) > 100) {
+		PJ_LOG(3,("","   error: expecting %d-th retransmission in %d "
+			     "ms, received in %d ms",
+			     recv_count-1, msec_expected, msec_elapsed));
+		test_complete = -610;
+	    }
+
+	    if (recv_count > 7) {
+		PJ_LOG(3,("", "   error: too many messages (%d) received",
+			      recv_count));
+		test_complete = -620;
+	    }
+
+	    pj_gettimeofday(&recv_last);
+	}
+	return PJ_TRUE;
+    }
+    return PJ_FALSE;
+}
+
+/*****************************************************************************
+ **
+ ** UAC basic retransmission and timeout test.
+ **
+ ** This will test the retransmission of the UAC transaction. Remote will not
+ ** answer the transaction, so the transaction should fail. The Call-ID
+ ** CALL_ID1 will be used for this test.
+ **
+ *****************************************************************************
+ */
+static int tsx_uac_retransmit_test(const pjsip_method *method)
+{
+    pjsip_tx_data *tdata;
+    pjsip_transaction *tsx;
+    char buf[80];
+    pj_str_t target, from, call_id, tsx_key;
+    pj_time_val timeout;
+    pj_status_t status;
+
+    PJ_LOG(3,("", "  basic uac retransmission and timeout test"));
+
+    pj_sprintf(buf, "sip:alice@127.0.0.1:%d", TEST_UDP_PORT);
+    target = pj_str(buf);
+    from = pj_str("sip:bob@127.0.0.1");
+    call_id = pj_str(CALL_ID1);
+
+    /* Create request. */
+    status = pjsip_endpt_create_request( endpt, method, &target,
+					 &from, &target, NULL, &call_id, -1, 
+					 NULL, &tdata);
+    if (status != PJ_SUCCESS) {
+	app_perror("   Error: unable to create request", status);
+	return -500;
+    }
+
+    /* Add additional reference to tdata to prevent transaction from
+     * deleting it.
+     */
+    pjsip_tx_data_add_ref(tdata);
+
+    /* Create transaction. */
+    status = pjsip_tsx_create_uac( &tsx_user, tdata, &tsx);
+    if (status != PJ_SUCCESS) {
+	app_perror("   Error: unable to create UAC transaction", status);
+	return -510;
+    }
+
+    /* Get transaction key. */
+    pj_strdup(tdata->pool, &tsx_key, &tsx->transaction_key);
+
+    /* Send the message. */
+    status = pjsip_tsx_send_msg(tsx, NULL);
+    if (status != PJ_SUCCESS) {
+	app_perror("   Error: unable to send request", status);
+	return -520;
+    }
+
+    /* Set test completion time. */
+    pj_gettimeofday(&timeout);
+    timeout.sec += 33;
+
+    /* Wait until test complete. */
+    while (!test_complete) {
+	pj_time_val now;
+
+	pjsip_endpt_handle_events(endpt, NULL);
+
+	pj_gettimeofday(&now);
+	if (now.sec > timeout.sec) {
+	    PJ_LOG(3,("", "   Error: test has timed out"));
+	    return -530;
+	}
+    }
+
+    if (status < 0)
+	return status;
+
+    /* Make sure transaction has been destroyed. */
+    if (pjsip_tsx_layer_find_tsx(&tsx_key, PJ_FALSE) != NULL) {
+	PJ_LOG(3,("", "   Error: transaction has not been destroyed"));
+	return -540;
+    }
+
+    /* Check tdata reference counter. */
+    if (pj_atomic_get(tdata->ref_cnt) != 1) {
+	PJ_LOG(3,("", "   Error: tdata reference counter is %d",
+		      pj_atomic_get(tdata->ref_cnt)));
+	return -550;
+    }
+
+    /* Destroy txdata */
+    pjsip_tx_data_dec_ref(tdata);
+
+    return PJ_SUCCESS;
+}
+
+/*****************************************************************************
+ **
+ ** UAC Transaction Test.
+ **
+ *****************************************************************************
+ */
+int tsx_uac_test(void)
+{
+    pj_sockaddr_in addr;
+    pj_str_t tmp;
+    pjsip_transport *tp;
+    pj_status_t status;
+
+    pj_sockaddr_in_init(&addr, pj_cstr(&tmp, "127.0.0.1"), TEST_UDP_PORT);
+
+    /* Start UDP transport if necessary. */
+    if (pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_UDP, &addr,
+				      sizeof(addr), &tp) != PJ_SUCCESS)
+    {
+	addr.sin_addr.s_addr = 0;
+	status = pjsip_udp_transport_start( endpt, &addr, NULL, 1, NULL);
+	if (status != PJ_SUCCESS) {
+	    app_perror("   Error: unable to start UDP transport", status);
+	    return -10;
+	}
+    } else {
+	pjsip_transport_dec_ref(tp);
+    }
+
+    /* Start transaction layer module. */
+    status = pjsip_tsx_layer_init(endpt);
+    if (status != PJ_SUCCESS) {
+	app_perror("   Error initializing transaction module", status);
+	return -20;
+    }
+
+    /* Register modules. */
+    status = pjsip_endpt_register_module(endpt, &tsx_user);
+    if (status != PJ_SUCCESS) {
+	app_perror("   Error: unable to register module", status);
+	return -30;
+    }
+    status = pjsip_endpt_register_module(endpt, &msg_receiver);
+    if (status != PJ_SUCCESS) {
+	app_perror("   Error: unable to register module", status);
+	return -30;
+    }
+
+    /* Basic retransmit and timeout test for INVITE. */
+    status = tsx_uac_retransmit_test(&pjsip_invite_method);
+    if (status != 0)
+	return status;
+
+    /* Basic retransmit and timeout test for non-INVITE. */
+    status = tsx_uac_retransmit_test(&pjsip_options_method);
+    if (status != 0)
+	return status;
+
+    return 0;
+}
diff --git a/pjsip/src/test-pjsip/uri_test.c b/pjsip/src/test-pjsip/uri_test.c
index 647397a..de9604b 100644
--- a/pjsip/src/test-pjsip/uri_test.c
+++ b/pjsip/src/test-pjsip/uri_test.c
@@ -804,7 +804,7 @@
 	    goto on_return;
 	}
     }
-    pjsip_endpt_destroy_pool(endpt, pool);
+    pjsip_endpt_release_pool(endpt, pool);
 
     PJ_LOG(3,("", "  benchmarking..."));
     parse_len = print_len = cmp_len = 0;
@@ -818,11 +818,11 @@
 	    if (status != PJ_SUCCESS) {
 		PJ_LOG(3,("uri_test", "  error %d when testing entry %d",
 			  status, i));
-		pjsip_endpt_destroy_pool(endpt, pool);
+		pjsip_endpt_release_pool(endpt, pool);
 		goto on_return;
 	    }
 	}
-	pjsip_endpt_destroy_pool(endpt, pool);
+	pjsip_endpt_release_pool(endpt, pool);
     }
 
     kbytes = parse_len;