Index: /pjproject/trunk/pjlib/include/pj/assert.h
===================================================================
--- /pjproject/trunk/pjlib/include/pj/assert.h	(revision 105)
+++ /pjproject/trunk/pjlib/include/pj/assert.h	(revision 106)
@@ -68,4 +68,24 @@
 #endif
 
+/**
+ * @hideinitializer
+ * If #PJ_ENABLE_EXTRA_CHECK is declared and non-zero, then 
+ * #PJ_ASSERT_ON_FAIL macro will evaluate the expression in @a expr during
+ * run-time. If the expression yields false, assertion will be triggered
+ * and @a exec_on_fail will be executed.
+ *
+ * If #PJ_ENABLE_EXTRA_CHECK is not declared or is zero, then no run-time
+ * checking will be performed. The macro simply evaluates to pj_assert(expr).
+ */
+#if defined(PJ_ENABLE_EXTRA_CHECK) && PJ_ENABLE_EXTRA_CHECK != 0
+#   define PJ_ASSERT_ON_FAIL(expr,exec_on_fail)    \
+	    do { \
+		pj_assert(expr); \
+		if (!(expr)) exec_on_fail; \
+	    } while (0)
+#else
+#   define PJ_ASSERT_ON_FAIL(expr,exec_on_fail)    pj_assert(expr)
+#endif
+
 /** @} */
 
Index: /pjproject/trunk/pjlib/include/pj/os.h
===================================================================
--- /pjproject/trunk/pjlib/include/pj/os.h	(revision 105)
+++ /pjproject/trunk/pjlib/include/pj/os.h	(revision 106)
@@ -505,4 +505,73 @@
  * @}
  */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJ_RW_MUTEX Reader/Writer Mutex
+ * @ingroup PJ_OS
+ * @{
+ * Reader/writer mutex is a classic synchronization object where multiple
+ * readers can acquire the mutex, but only a single writer can acquire the 
+ * mutex.
+ */
+typedef struct pj_rwmutex_t pj_rwmutex_t;
+
+/**
+ * Create reader/writer mutex.
+ *
+ * @param pool	    Pool to allocate memory for the mutex.
+ * @param name	    Name to be assigned to the mutex.
+ * @param mutex	    Pointer to receive the newly created mutex.
+ *
+ * @return	    PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_rwmutex_create(pj_pool_t *pool, const char *name,
+				       pj_rwmutex_t **mutex);
+
+/**
+ * Lock the mutex for reading.
+ *
+ * @param mutex	    The mutex.
+ * @return	    PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_rwmutex_lock_read(pj_rwmutex_t *mutex);
+
+/**
+ * Lock the mutex for writing.
+ *
+ * @param mutex	    The mutex.
+ * @return	    PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_rwmutex_lock_write(pj_rwmutex_t *mutex);
+
+/**
+ * Release read lock.
+ *
+ * @param mutex	    The mutex.
+ * @return	    PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_rwmutex_unlock_read(pj_rwmutex_t *mutex);
+
+/**
+ * Release write lock.
+ *
+ * @param mutex	    The mutex.
+ * @return	    PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_rwmutex_unlock_write(pj_rwmutex_t *mutex);
+
+/**
+ * Destroy reader/writer mutex.
+ *
+ * @param mutex	    The mutex.
+ * @return	    PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_rwmutex_destroy(pj_rwmutex_t *mutex);
+
+
+/**
+ * @}
+ */
+
 
 ///////////////////////////////////////////////////////////////////////////////
Index: /pjproject/trunk/pjlib/include/pj/sock.h
===================================================================
--- /pjproject/trunk/pjlib/include/pj/sock.h	(revision 105)
+++ /pjproject/trunk/pjlib/include/pj/sock.h	(revision 106)
@@ -177,5 +177,5 @@
  * This structure describes Internet socket address.
  */
-typedef struct pj_sockaddr_in
+struct pj_sockaddr_in
 {
     pj_uint16_t	sin_family;	/**< Address family.		    */
@@ -183,5 +183,5 @@
     pj_in_addr	sin_addr;	/**< IP address.		    */
     char	sin_zero[8];	/**< Padding.			    */
-} pj_sockaddr_in;
+};
 
 
Index: /pjproject/trunk/pjlib/include/pj/types.h
===================================================================
--- /pjproject/trunk/pjlib/include/pj/types.h	(revision 105)
+++ /pjproject/trunk/pjlib/include/pj/types.h	(revision 106)
@@ -218,4 +218,7 @@
 /** Generic socket address. */
 typedef void pj_sockaddr_t;
+
+/** Forward declaration. */
+typedef struct pj_sockaddr_in pj_sockaddr_in;
 
 /** Color type. */
Index: /pjproject/trunk/pjlib/src/pj/os_core_win32.c
===================================================================
--- /pjproject/trunk/pjlib/src/pj/os_core_win32.c	(revision 105)
+++ /pjproject/trunk/pjlib/src/pj/os_core_win32.c	(revision 106)
@@ -860,4 +860,121 @@
 
 ///////////////////////////////////////////////////////////////////////////////
+
+struct pj_rwmutex_t
+{
+    pj_mutex_t *read_lock, *write_lock;
+    int reader_count;
+};
+
+/*
+ * Create reader/writer mutex.
+ *
+ */
+PJ_DEF(pj_status_t) pj_rwmutex_create(pj_pool_t *pool, const char *name,
+				      pj_rwmutex_t **p_mutex)
+{
+    pj_status_t status;
+    pj_rwmutex_t *rwmutex;
+
+    PJ_ASSERT_RETURN(pool && p_mutex, PJ_EINVAL);
+
+    *p_mutex = NULL;
+    rwmutex = pj_pool_alloc(pool, sizeof(struct pj_rwmutex_t));
+
+    status = pj_mutex_create_simple(pool, name, &rwmutex ->read_lock);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    status = pj_mutex_create_recursive(pool, name, &rwmutex->write_lock);
+    if (status != PJ_SUCCESS) {
+	pj_mutex_destroy(rwmutex->read_lock);
+	return status;
+    }
+
+    rwmutex->reader_count = 0;
+    *p_mutex = rwmutex;
+    return PJ_SUCCESS;
+}
+
+/*
+ * Lock the mutex for reading.
+ *
+ */
+PJ_DEF(pj_status_t) pj_rwmutex_lock_read(pj_rwmutex_t *mutex)
+{
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+    status = pj_mutex_lock(mutex->read_lock);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    mutex->reader_count++;
+    if (mutex->reader_count == 1)
+	pj_mutex_lock(mutex->write_lock);
+
+    status = pj_mutex_unlock(mutex->read_lock);
+    return status;
+}
+
+/*
+ * Lock the mutex for writing.
+ *
+ */
+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);
+}
+
+/*
+ * Release read lock.
+ *
+ */
+PJ_DEF(pj_status_t) pj_rwmutex_unlock_read(pj_rwmutex_t *mutex)
+{
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+    status = pj_mutex_lock(mutex->read_lock);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    pj_assert(mutex->reader_count >= 1);
+
+    --mutex->reader_count;
+    if (mutex->reader_count == 0)
+	pj_mutex_unlock(mutex->write_lock);
+
+    status = pj_mutex_unlock(mutex->read_lock);
+    return status;
+}
+
+/*
+ * Release write lock.
+ *
+ */
+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);
+}
+
+
+/*
+ * Destroy reader/writer mutex.
+ *
+ */
+PJ_DEF(pj_status_t) pj_rwmutex_destroy(pj_rwmutex_t *mutex)
+{
+    PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+    pj_mutex_destroy(mutex->read_lock);
+    pj_mutex_destroy(mutex->write_lock);
+    return PJ_SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
 /*
  * pj_enter_critical_section()
Index: /pjproject/trunk/pjlib/src/pj/sock_bsd.c
===================================================================
--- /pjproject/trunk/pjlib/src/pj/sock_bsd.c	(revision 105)
+++ /pjproject/trunk/pjlib/src/pj/sock_bsd.c	(revision 106)
@@ -188,5 +188,5 @@
     PJ_CHECK_STACK();
 
-    PJ_ASSERT_RETURN(str_addr && str_addr->slen < PJ_MAX_HOSTNAME, 
+    PJ_ASSERT_RETURN(!str_addr || str_addr->slen < PJ_MAX_HOSTNAME, 
                      (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL));
 
@@ -225,6 +225,5 @@
 					 pj_uint16_t port)
 {
-    PJ_ASSERT_RETURN(addr && str_addr, 
-                     (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL));
+    PJ_ASSERT_RETURN(addr, (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL));
 
     addr->sin_family = PJ_AF_INET;
Index: /pjproject/trunk/pjsip/build/pjsip_core.dsp
===================================================================
--- /pjproject/trunk/pjsip/build/pjsip_core.dsp	(revision 105)
+++ /pjproject/trunk/pjsip/build/pjsip_core.dsp	(revision 106)
@@ -125,4 +125,5 @@
 
 SOURCE=..\src\pjsip\sip_transaction.c
+# PROP Exclude_From_Build 1
 # End Source File
 # Begin Source File
@@ -141,4 +142,9 @@
 
 SOURCE=..\src\pjsip\sip_util.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsip\sip_util_statefull.c
+# PROP Exclude_From_Build 1
 # End Source File
 # End Group
Index: /pjproject/trunk/pjsip/build/test_pjsip.dsp
===================================================================
--- /pjproject/trunk/pjsip/build/test_pjsip.dsp	(revision 105)
+++ /pjproject/trunk/pjsip/build/test_pjsip.dsp	(revision 106)
@@ -102,4 +102,16 @@
 # Begin Source File
 
+SOURCE="..\src\test-pjsip\transport_test.c"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\test-pjsip\transport_udp_test.c"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\test-pjsip\txdata_test.c"
+# End Source File
+# Begin Source File
+
 SOURCE="..\src\test-pjsip\uri_test.c"
 # End Source File
Index: /pjproject/trunk/pjsip/include/pjsip/sip_endpoint.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_endpoint.h	(revision 105)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_endpoint.h	(revision 106)
@@ -116,4 +116,32 @@
 					 const pj_time_val *max_timeout);
 
+
+/**
+ * Schedule timer to endpoint's timer heap. Application must poll the endpoint
+ * periodically (by calling #pjsip_endpt_handle_events) to ensure that the
+ * timer events are handled in timely manner. When the timeout for the timer
+ * has elapsed, the callback specified in the entry argument will be called.
+ * This function, like all other endpoint functions, is thread safe.
+ *
+ * @param endpt	    The endpoint.
+ * @param entry	    The timer entry.
+ * @param delay	    The relative delay of the timer.
+ * @return	    PJ_OK (zero) if successfull.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt,
+						 pj_timer_entry *entry,
+						 const pj_time_val *delay );
+
+/**
+ * Cancel the previously registered timer.
+ * This function, like all other endpoint functions, is thread safe.
+ *
+ * @param endpt	    The endpoint.
+ * @param entry	    The timer entry previously registered.
+ */
+PJ_DECL(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt, 
+					pj_timer_entry *entry );
+
+
 /**
  * Dump endpoint status to the log. This will print the status to the log
@@ -126,4 +154,33 @@
  */
 PJ_DECL(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail );
+
+
+/**
+ * Register new module to the endpoint.
+ * The endpoint will then call the load and start function in the module to 
+ * properly initialize the module, and assign a unique module ID for the 
+ * module.
+ *
+ * @param endpt		The endpoint.
+ * @param module	The module to be registered.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_register_module( pjsip_endpoint *endpt,
+						  pjsip_module *module );
+
+/**
+ * Unregister a module from the endpoint.
+ * The endpoint will then call the stop and unload function in the module to 
+ * properly shutdown the module.
+ *
+ * @param endpt		The endpoint.
+ * @param module	The module to be registered.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_unregister_module( pjsip_endpoint *endpt,
+						    pjsip_module *module );
+
 
 /**
@@ -157,30 +214,4 @@
 
 /**
- * Schedule timer to endpoint's timer heap. Application must poll the endpoint
- * periodically (by calling #pjsip_endpt_handle_events) to ensure that the
- * timer events are handled in timely manner. When the timeout for the timer
- * has elapsed, the callback specified in the entry argument will be called.
- * This function, like all other endpoint functions, is thread safe.
- *
- * @param endpt	    The endpoint.
- * @param entry	    The timer entry.
- * @param delay	    The relative delay of the timer.
- * @return	    PJ_OK (zero) if successfull.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt,
-						 pj_timer_entry *entry,
-						 const pj_time_val *delay );
-
-/**
- * Cancel the previously registered timer.
- * This function, like all other endpoint functions, is thread safe.
- *
- * @param endpt	    The endpoint.
- * @param entry	    The timer entry previously registered.
- */
-PJ_DECL(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt, 
-					pj_timer_entry *entry );
-
-/**
  * Create a new transaction. After creating the transaction, application MUST
  * initialize the transaction as either UAC or UAS (by calling
@@ -279,9 +310,10 @@
  * @see pjsip_transport_get
  */
-PJ_DECL(pj_status_t) pjsip_endpt_alloc_transport( pjsip_endpoint *endpt,
-						  pjsip_transport_type_e type,
-						  const pj_sockaddr *remote,
-						  int addr_len,
-						  pjsip_transport **p_transport);
+PJ_DECL(pj_status_t) 
+pjsip_endpt_acquire_transport( pjsip_endpoint *endpt,
+			       pjsip_transport_type_e type,
+			       const pj_sockaddr_t *remote,
+			       int addr_len,
+			       pjsip_transport **p_transport);
 
 /**
Index: /pjproject/trunk/pjsip/include/pjsip/sip_errno.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_errno.h	(revision 105)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_errno.h	(revision 106)
@@ -94,5 +94,5 @@
 /**
  * @hideinitializer
- * Invalid message (syntax error)
+ * General invalid message error (e.g. syntax error)
  */
 #define PJSIP_EINVALIDMSG       (PJSIP_ERRNO_START_PJSIP + 20)	/* 171020 */
@@ -114,17 +114,43 @@
 /**
  * @hideinitializer
+ * Missing Request-URI.
+ */
+#define PJSIP_EMISSINGREQURI    (PJSIP_ERRNO_START_PJSIP + 24)	/* 171024 */
+/**
+ * @hideinitializer
  * Missing required header(s).
  */
-#define PJSIP_EMISSINGHDR       (PJSIP_ERRNO_START_PJSIP + 24)	/* 171024 */
+#define PJSIP_EMISSINGHDR       (PJSIP_ERRNO_START_PJSIP + 25)	/* 171025 */
+/**
+ * @hideinitializer
+ * Missing message body.
+ */
+#define PJSIP_EMISSINGBODY	(PJSIP_ERRNO_START_PJSIP + 26)	/* 171026 */
 /**
  * @hideinitializer
  * Invalid Via header in response (sent-by, etc).
  */
-#define PJSIP_EINVALIDVIA	(PJSIP_ERRNO_START_PJSIP + 25)	/* 171025 */
+#define PJSIP_EINVALIDVIA	(PJSIP_ERRNO_START_PJSIP + 27)	/* 171027 */
 /**
  * @hideinitializer
  * Multiple Via headers in response.
  */
-#define PJSIP_EMULTIPLEVIA	(PJSIP_ERRNO_START_PJSIP + 26)	/* 171026 */
+#define PJSIP_EMULTIPLEVIA	(PJSIP_ERRNO_START_PJSIP + 28)	/* 171028 */
+/**
+ * @hideinitializer
+ * Invalid request URI.
+ */
+#define PJSIP_EINVALIDREQURI	(PJSIP_ERRNO_START_PJSIP + 29)	/* 171029 */
+/**
+ * @hideinitializer
+ * Expecting request message.
+ */
+#define PJSIP_ENOTREQUESTMSG	(PJSIP_ERRNO_START_PJSIP + 30)	/* 171030 */
+/**
+ * @hideinitializer
+ * Expecting response message.
+ */
+#define PJSIP_ENOTRESPONSEMSG	(PJSIP_ERRNO_START_PJSIP + 31)	/* 171031 */
+
 
 /************************************************************
@@ -145,5 +171,11 @@
  * Rx buffer overflow. See also PJSIP_EMSGTOOLONG.
  */
-#define PJSIP_ERXOVERFLOW         (PJSIP_ERRNO_START_PJSIP + 42)/* 171042 */
+#define PJSIP_ERXOVERFLOW       (PJSIP_ERRNO_START_PJSIP + 42)	/* 171042 */
+/**
+ * @hideinitializer
+ * This is not really an error, it just informs application that
+ * transmit data has been deleted on return of pjsip_tx_data_dec_ref().
+ */
+#define PJSIP_EBUFDESTROYED     (PJSIP_ERRNO_START_PJSIP + 43)	/* 171043 */
 
 
Index: /pjproject/trunk/pjsip/include/pjsip/sip_module.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_module.h	(revision 105)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_module.h	(revision 106)
@@ -42,4 +42,7 @@
 struct pjsip_module
 {
+    /** To allow chaining of modules in the endpoint. */
+    PJ_DECL_LIST_MEMBER(struct pjsip_module);
+
     /**
      * Module name.
@@ -48,7 +51,7 @@
 
     /**
-     * Flag to indicate the type of interfaces supported by the module.
+     * Module ID.
      */
-    pj_uint32_t flag;
+    int id;
 
     /**
@@ -57,5 +60,5 @@
      * initialized later.
      */
-    pj_uint32_t priority;
+    int priority;
 
     /**
@@ -63,5 +66,5 @@
      * the module itself.
      */
-    void *mod_data;
+    void *user_data;
 
     /**
@@ -79,20 +82,22 @@
      *
      * @param endpt	The endpoint instance.
-     * @param mod	The module.
-     * @param id	The unique module ID assigned to this module.
-     *
-     * @return		Module should return zero when initialization succeed.
+     * @return		Module should return PJ_SUCCESS to indicate success.
      */
-    pj_status_t (*init_module)(pjsip_endpoint *endpt,
-			       struct pjsip_module *mod, pj_uint32_t id);
+    pj_status_t (*load)(pjsip_endpoint *endpt);
 
     /**
      * Pointer to function to be called to start the module.
      *
-     * @param mod	The module.
-     *
      * @return		Module should return zero to indicate success.
      */
-    pj_status_t (*start_module)(struct pjsip_module *mod);
+    pj_status_t (*start)(void);
+
+    /**
+     * Pointer to function to be called to deinitialize the module before
+     * it is unloaded.
+     *
+     * @return		Module should return PJ_SUCCESS to indicate success.
+     */
+    pj_status_t (*stop)(void);
 
     /**
@@ -102,32 +107,53 @@
      * @param mod	The module.
      *
-     * @return		Module should return zero to indicate success.
+     * @return		Module should return PJ_SUCCESS to indicate success.
      */
-    pj_status_t (*deinit_module)(struct pjsip_module *mod);
+    pj_status_t (*unload)(void);
 
     /**
-     * Pointer to function to receive transaction related events.
-     * If the module doesn't wish to receive such notification, this member
-     * must be set to NULL.
+     * Called to process incoming request.
      *
-     * @param mod	The module.
-     * @param event	The transaction event.
+     * @param rdata	The incoming message.
+     *
+     * @return		Module should return PJ_TRUE if it handles the request,
+     *			or otherwise it should return PJ_FALSE to allow other
+     *			modules to handle the request.
      */
-    void (*tsx_handler)(struct pjsip_module *mod, pjsip_event *event);
+    pj_bool_t (*on_rx_request)(pjsip_rx_data *rdata);
+
+    /**
+     * Called to processed incoming response.
+     *
+     * @param rdata	The incoming message.
+     *
+     * @return		Module should return PJ_TRUE if it handles the 
+     *			response, or otherwise it should return PJ_FALSE to 
+     *			allow other modules to handle the response.
+     */
+    pj_bool_t (*on_rx_response)(pjsip_rx_data *rdata);
+
+    /**
+     * Called when this module is acting as transaction user for the specified
+     * transaction, when the transaction's state has changed.
+     *
+     * @param tsx	The transaction.
+     * @param event	The event which has caused the transaction state
+     *			to change.
+     */
+    void (*on_tsx_state)(pjsip_transaction *tsx, pjsip_event *event);
+
 };
 
 
 /**
- * Prototype of function to register static modules (eg modules that are
- * linked staticly with the application). This function must be implemented 
- * by any applications that use PJSIP library.
- *
- * @param count	    [input/output] On input, it contains the maximum number of
- *		    elements in the array. On output, the function fills with 
- *		    the number of modules to be registered.
- * @param modules   [output] array of pointer to modules to be registered.
+ * Module priority guidelines.
  */
-pj_status_t register_static_modules( pj_size_t *count,
-				     pjsip_module **modules );
+enum pjsip_module_priority
+{
+    PJSIP_MOD_PRIORITY_TSX_LAYER = 4,
+    PJSIP_MOD_PRIORITY_UA_PROXY_LAYER = 16,
+    PJSIP_MOD_PRIORITY_APPLICATION = 32,
+};
+
 
 /**
Index: /pjproject/trunk/pjsip/include/pjsip/sip_msg.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_msg.h	(revision 105)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_msg.h	(revision 106)
@@ -53,25 +53,12 @@
 typedef enum pjsip_method_e
 {
-    /** INVITE method, for establishing dialogs. */
-    PJSIP_INVITE_METHOD,
-
-    /** CANCEL method, for cancelling request. */
-    PJSIP_CANCEL_METHOD,
-
-    /** ACK method, for acknowledging final response to INVITE. */
-    PJSIP_ACK_METHOD,
-
-    /** BYE method, for terminating dialog. */
-    PJSIP_BYE_METHOD,
-
-    /** REGISTER method. */
-    PJSIP_REGISTER_METHOD,
-
-    /** OPTIONS method, for querying remote capabilities. */
-    PJSIP_OPTIONS_METHOD,
-
-    /** Other method, which means that the method name itself will be stored
-        elsewhere. */
-    PJSIP_OTHER_METHOD,
+    PJSIP_INVITE_METHOD,    /**< INVITE method, for establishing dialogs.   */
+    PJSIP_CANCEL_METHOD,    /**< CANCEL method, for cancelling request.	    */
+    PJSIP_ACK_METHOD,	    /**< ACK method.				    */
+    PJSIP_BYE_METHOD,	    /**< BYE method, for terminating dialog.	    */
+    PJSIP_REGISTER_METHOD,  /**< REGISTER method.			    */
+    PJSIP_OPTIONS_METHOD,   /**< OPTIONS method.			    */
+
+    PJSIP_OTHER_METHOD,	    /**< Other method.				    */
 
 } pjsip_method_e;
@@ -92,4 +79,15 @@
 			         method string. */
 } pjsip_method;
+
+
+/*
+ * For convenience, standard method structures are defined in the library.
+ */
+extern const pjsip_method pjsip_invite_method;	    /**< INVITE structure.  */
+extern const pjsip_method pjsip_cancel_method;	    /**< CANCEL structure.  */
+extern const pjsip_method pjsip_ack_method;	    /**< ACK structure.     */
+extern const pjsip_method pjsip_bye_method;	    /**< BYE structure.     */
+extern const pjsip_method pjsip_register_method;    /**< REGISTER structure.*/
+extern const pjsip_method pjsip_options_method;	    /**< OPTIONS structure. */
 
 
@@ -639,6 +637,6 @@
  *		    type is found.
  */
-PJ_DECL(void*)  pjsip_msg_find_hdr( pjsip_msg *msg, 
-				    pjsip_hdr_e type, void *start);
+PJ_DECL(void*)  pjsip_msg_find_hdr( const pjsip_msg *msg, 
+				    pjsip_hdr_e type, const void *start);
 
 /** 
@@ -655,6 +653,7 @@
  *		    type is found.
  */
-PJ_DECL(void*)  pjsip_msg_find_hdr_by_name( pjsip_msg *msg, 
-					    const pj_str_t *name, void *start);
+PJ_DECL(void*)  pjsip_msg_find_hdr_by_name( const pjsip_msg *msg, 
+					    const pj_str_t *name, 
+					    const void *start);
 
 /** 
@@ -703,5 +702,6 @@
  *		value if the message is too large for the specified buffer.
  */
-PJ_DECL(pj_ssize_t) pjsip_msg_print(pjsip_msg *msg, char *buf, pj_size_t size);
+PJ_DECL(pj_ssize_t) pjsip_msg_print(const pjsip_msg *msg, 
+				    char *buf, pj_size_t size);
 
 /**
Index: /pjproject/trunk/pjsip/include/pjsip/sip_transport.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_transport.h	(revision 105)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_transport.h	(revision 106)
@@ -368,9 +368,13 @@
 /**
  * Decrement reference counter of the transmit buffer.
- * When the transmit buffer is no longer used, it will be destroyed.
+ * When the transmit buffer is no longer used, it will be destroyed and
+ * caller is informed with PJSIP_EBUFDESTROYED return status.
  *
  * @param tdata	    The transmit buffer data.
- */
-PJ_DECL(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata );
+ * @return	    This function will always succeeded eventhough the return
+ *		    status is non-zero. A status PJSIP_EBUFDESTROYED will be
+ *		    returned to inform that buffer is destroyed.
+ */
+PJ_DECL(pj_status_t) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata );
 
 /**
@@ -378,4 +382,5 @@
  *
  * @param tdata	    The transmit buffer.
+ * @return	    Non-zero (PJ_TRUE) if buffer contains a valid message.
  */
 PJ_DECL(pj_bool_t) pjsip_tx_data_is_valid( pjsip_tx_data *tdata );
@@ -543,4 +548,6 @@
     /**
      * Create new outbound connection.
+     * Note that the factory is responsible for both creating the
+     * transport and registering it to the transport manager.
      */
     pj_status_t (*create_transport)(pjsip_tpfactory *factory,
@@ -548,4 +555,5 @@
 				    pjsip_endpoint *endpt,
 				    const pj_sockaddr *rem_addr,
+				    int addr_len,
 				    pjsip_transport **transport);
 
@@ -622,9 +630,9 @@
  * suitable transport is found, a new one will be created.
  */
-PJ_DECL(pj_status_t) pjsip_tpmgr_alloc_transport( pjsip_tpmgr *mgr,
-						  pjsip_transport_type_e type,
-						  const pj_sockaddr_t *remote,
-						  int addr_len,
-						  pjsip_transport **p_transport );
+PJ_DECL(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr,
+						   pjsip_transport_type_e type,
+						   const pj_sockaddr_t *remote,
+						   int addr_len,
+						   pjsip_transport **tp);
 
 
Index: /pjproject/trunk/pjsip/include/pjsip/sip_transport_udp.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_transport_udp.h	(revision 105)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_transport_udp.h	(revision 106)
@@ -22,5 +22,5 @@
 #include <pjsip/sip_transport.h>
 
-PJ_DECL
+PJ_BEGIN_DECL
 
 /**
Index: /pjproject/trunk/pjsip/include/pjsip/sip_util.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_util.h	(revision 105)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_util.h	(revision 106)
@@ -21,4 +21,5 @@
 
 #include <pjsip/sip_msg.h>
+#include <pjsip/sip_resolve.h>
 
 PJ_BEGIN_DECL
@@ -34,4 +35,13 @@
  * request outside a dialog, such as OPTIONS, MESSAGE, etc. To create a request
  * inside a dialog, application should use #pjsip_dlg_create_request.
+ *
+ * This function adds the following headers in the request:
+ *  - From, To, Call-ID, and CSeq,
+ *  - Contact header, if contact is specified.
+ *  - A blank Via header.
+ *  - Additional request headers (such as Max-Forwards) which are copied
+ *    from endpoint configuration.
+ *
+ * In addition, the function adds a unique tag in the From header.
  *
  * Once a transmit data is created, the reference counter is initialized to 1.
@@ -63,5 +73,14 @@
 /**
  * Create an independent request message from the specified headers. This
- * function will shallow clone the headers and put them in the request.
+ * function will clone the headers and put them in the request.
+ *
+ * This function adds the following headers in the request:
+ *  - From, To, Call-ID, and CSeq,
+ *  - Contact header, if contact is specified.
+ *  - A blank Via header.
+ *  - Additional request headers (such as Max-Forwards) which are copied
+ *    from endpoint configuration.
+ *
+ * In addition, the function adds a unique tag in the From header.
  *
  * Once a transmit data is created, the reference counter is initialized to 1.
@@ -93,4 +112,221 @@
 
 /**
+ * Construct a minimal response message for the received request. This function
+ * will construct all the Via, Record-Route, Call-ID, From, To, CSeq, and 
+ * Call-ID headers from the request.
+ *
+ * Note: the txdata reference counter is set to ZERO!.
+ *
+ * @param endpt	    The endpoint.
+ * @param rdata	    The request receive data.
+ * @param st_code   Status code to be put in the response.
+ * @param st_text   Optional status text, or NULL to get the default text.
+ * @param p_tdata   Pointer to receive the transmit data.
+ *
+ * @return	    PJ_SUCCESS, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
+						  const pjsip_rx_data *rdata,
+						  int st_code,
+						  const pj_str_t *st_text,
+						  pjsip_tx_data **p_tdata);
+
+/**
+ * Construct a full ACK request for the received non-2xx final response.
+ * This utility function is normally called by the transaction to construct
+ * an ACK request to 3xx-6xx final response.
+ * The generation of ACK message for 2xx final response is different than
+ * this one.
+ * 
+ * @param endpt	    The endpoint.
+ * @param tdata	    This contains the original INVITE request
+ * @param rdata	    The final response.
+ * @param ack	    The ACK request created.
+ *
+ * @return	    PJ_SUCCESS, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_create_ack( pjsip_endpoint *endpt,
+					     const pjsip_tx_data *tdata,
+					     const pjsip_rx_data *rdata,
+					     pjsip_tx_data **ack);
+
+
+/**
+ * Construct CANCEL request for the previously sent request.
+ *
+ * @param endpt	    The endpoint.
+ * @param tdata	    The transmit buffer for the request being cancelled.
+ * @param p_tdata   Pointer to receive the transmit data.
+ *
+ * @return	    PJ_SUCCESS, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
+						const pjsip_tx_data *tdata,
+						pjsip_tx_data **p_tdata);
+
+
+/**
+ * Find which destination to be used to send the request message, based
+ * on the request URI and Route headers in the message. The procedure
+ * used here follows the guidelines on sending the request in RFC 3261
+ * chapter 8.1.2.
+ *
+ * This function may modify the message (request line and Route headers),
+ * if the Route information specifies strict routing and the request
+ * URI in the message is different than the calculated target URI. In that
+ * case, the target URI will be put as the request URI of the request and
+ * current request URI will be put as the last entry of the Route headers.
+ *
+ * @param tdata	    The transmit data containing the request message.
+ * @param dest_info On return, it contains information about destination
+ *		    host to contact, along with the preferable transport
+ *		    type, if any. Caller will then normally proceed with
+ *		    resolving this host with server resolution procedure
+ *		    described in RFC 3263.
+ *
+ * @return	    PJ_SUCCESS, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_get_request_addr( pjsip_tx_data *tdata,
+					     pjsip_host_info *dest_info );
+
+
+/**
+ * This structure holds the state of outgoing stateless request.
+ */
+typedef struct pjsip_send_state
+{
+    /** Application token, which was specified when the function
+     *  #pjsip_endpt_send_request_stateless() is called.
+     */
+    void *token;
+
+    /** Endpoint instance. 
+     */
+    pjsip_endpoint *endpt;
+
+    /** Transmit data buffer being sent. 
+     */
+    pjsip_tx_data *tdata;
+
+    /** Server addresses resolved. 
+     */
+    pjsip_server_addresses   addr;
+
+    /** Current server address being tried. 
+     */
+    unsigned cur_addr;
+
+    /** Current transport being used. 
+     */
+    pjsip_transport *cur_transport;
+
+    /** The application callback which was specified when the function
+     *  #pjsip_endpt_send_request_stateless() was called.
+     */
+    void (*app_cb)(struct pjsip_send_state*,
+		   pj_ssize_t sent,
+		   pj_bool_t *cont);
+} pjsip_send_state;
+
+/**
+ * Send outgoing request statelessly The function will take care of which 
+ * destination and transport to use based on the information in the message,
+ * taking care of URI in the request line and Route header.
+ *
+ * This function is different than #pjsip_transport_send() in that this 
+ * function adds/modify the Via header as necessary.
+ *
+ * @param endpt	    The endpoint instance.
+ * @param tdata	    The transmit data to be sent.
+ *
+ * @return	    PJ_SUCCESS, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) 
+pjsip_endpt_send_request_stateless( pjsip_endpoint *endpt,
+				    pjsip_tx_data *tdata,
+				    void *token,
+				    void (*cb)(pjsip_send_state*,
+					       pj_ssize_t sent,
+					       pj_bool_t *cont));
+
+/**
+ * This structure describes destination information to send response.
+ * It is initialized by calling #pjsip_get_response_addr().
+ *
+ * If the response message should be sent using transport from which
+ * the request was received, then transport, addr, and addr_len fields
+ * are initialized.
+ *
+ * The dst_host field is also initialized. It should be used when server
+ * fails to send the response using the transport from which the request
+ * was received, or when the transport is NULL, which means server
+ * must send the response to this address (this situation occurs when
+ * maddr parameter is set, or when rport param is not set in the request).
+ */
+typedef struct pjsip_response_addr
+{
+    pjsip_transport *transport;	/**< Immediate transport to be used. */
+    pj_sockaddr	     addr;	/**< Immediate address to send to.   */
+    int		     addr_len;	/**< Address length.		     */
+    pjsip_host_info  dst_host;	/**< Destination host to contact.    */
+} pjsip_response_addr;
+
+/**
+ * Determine which address (and transport) to use to send response message
+ * based on the received request. This function follows the specification
+ * in section 18.2.2 of RFC 3261 and RFC 3581 for calculating the destination
+ * address and transport.
+ *
+ * The information about destination to send the response will be returned
+ * in res_addr argument. Please see #pjsip_response_addr for more info.
+ *
+ * @param pool	    The pool.
+ * @param rdata	    The incoming request received by the server.
+ * @param res_addr  On return, it will be initialized with information about
+ *		    destination address and transport to send the response.
+ *
+ * @return	    zero (PJ_OK) if successfull.
+ */
+PJ_DECL(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool,
+					     pjsip_rx_data *rdata,
+					     pjsip_response_addr *res_addr);
+
+/**
+ * Send response in tdata statelessly. The function will take care of which 
+ * response destination and transport to use based on the information in the 
+ * Via header (such as the presence of rport, symmetric transport, etc.).
+ *
+ * This function will create a new ephemeral transport if no existing 
+ * transports can be used to send the message to the destination. The ephemeral
+ * transport will be destroyed after some period if it is not used to send any 
+ * more messages.
+ *
+ * The behavior of this function complies with section 18.2.2 of RFC 3261
+ * and RFC 3581.
+ *
+ * @param endpt	    The endpoint instance.
+ * @param res_addr  The information about the address and transport to send
+ *		    the response to. Application can get this information
+ *		    by calling #pjsip_get_response_addr().
+ * @param tdata	    The response message to be sent.
+ * @param token	    Token to be passed back when the callback is called.
+ * @param cb	    Optional callback to notify the transmission status
+ *		    to application, and to inform whether next address or
+ *		    transport will be tried.
+ * 
+ * @return	    PJ_SUCCESS if response has been successfully created and
+ *		    sent to transport layer, or a non-zero error code. 
+ *		    However, even when it returns PJ_SUCCESS, there is no 
+ *		    guarantee that the response has been successfully sent.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_send_response( pjsip_endpoint *endpt,
+					        pjsip_response_addr *res_addr,
+					        pjsip_tx_data *tdata,
+						void *token,
+						void (*cb)(pjsip_send_state*,
+							   pj_ssize_t sent,
+							   pj_bool_t *cont));
+
+/**
  * Send outgoing request and initiate UAC transaction for the request.
  * This is an auxiliary function to be used by application to send arbitrary
@@ -117,69 +353,4 @@
 					       void (*cb)(void*,pjsip_event*));
 
-/**
- * Construct a minimal response message for the received request. This function
- * will construct all the Via, Record-Route, Call-ID, From, To, CSeq, and 
- * Call-ID headers from the request.
- *
- * Note: the txdata reference counter is set to ZERO!.
- *
- * @param endpt	    The endpoint.
- * @param rdata	    The request receive data.
- * @param code	    Status code to be put in the response.
- * @param p_tdata   Pointer to receive the transmit data.
- *
- * @return	    PJ_SUCCESS, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
-						  const pjsip_rx_data *rdata,
-						  int code,
-						  pjsip_tx_data **p_tdata);
-
-/**
- * Construct a full ACK request for the received non-2xx final response.
- * This utility function is normally called by the transaction to construct
- * an ACK request to 3xx-6xx final response.
- * The generation of ACK message for 2xx final response is different than
- * this one.
- * 
- * @param endpt	    The endpoint.
- * @param tdata	    On input, this contains the original INVITE request, and on
- *		    output, it contains the ACK message.
- * @param rdata	    The final response message.
- */
-PJ_DECL(void) pjsip_endpt_create_ack( pjsip_endpoint *endpt,
-				      pjsip_tx_data *tdata,
-				      const pjsip_rx_data *rdata );
-
-
-/**
- * Construct CANCEL request for the previously sent request.
- *
- * @param endpt	    The endpoint.
- * @param tdata	    The transmit buffer for the request being cancelled.
- * @param p_tdata   Pointer to receive the transmit data.
- *
- * @return	    PJ_SUCCESS, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
-						pjsip_tx_data *tdata,
-						pjsip_tx_data **p_tdata);
-
-
-/**
- * Get the address parameters (host, port, flag, TTL, etc) to send the
- * response.
- *
- * @param pool	    The pool.
- * @param tr	    The transport where the request was received.
- * @param via	    The top-most Via header of the request.
- * @param addr	    The send address concluded from the calculation.
- *
- * @return	    zero (PJ_OK) if successfull.
- */
-PJ_DECL(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool,
-					     const pjsip_transport *tr,
-					     const pjsip_via_hdr *via,
-					     pjsip_host_info *addr);
 
 /**
Index: /pjproject/trunk/pjsip/include/pjsip_core.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip_core.h	(revision 105)
+++ /pjproject/trunk/pjsip/include/pjsip_core.h	(revision 106)
@@ -32,4 +32,5 @@
 #include <pjsip/sip_transaction.h>
 #include <pjsip/sip_transport.h>
+#include <pjsip/sip_transport_udp.h>
 #include <pjsip/sip_uri.h>
 #include <pjsip/sip_util.h>
Index: /pjproject/trunk/pjsip/src/pjsip/sip_endpoint.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_endpoint.c	(revision 105)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_endpoint.c	(revision 106)
@@ -57,10 +57,4 @@
     pj_str_t		 name;
 
-    /** Transaction table. */
-    pj_hash_table_t	*tsx_table;
-
-    /** Mutex for transaction table. */
-    pj_mutex_t		*tsx_table_mutex;
-
     /** Timer heap. */
     pj_timer_heap_t	*timer_heap;
@@ -75,9 +69,12 @@
     pjsip_resolver_t	*resolver;
 
-    /** Number of modules registered. */
-    pj_uint32_t		 mod_count;
+    /** Modules lock. */
+    pj_rwmutex_t	*mod_mutex;
 
     /** Modules. */
     pjsip_module        *modules[PJSIP_MAX_MODULE];
+
+    /** Module list, sorted by priority. */
+    pjsip_module	 module_list;
 
     /** Number of supported methods. */
@@ -128,165 +125,117 @@
 
 /*
- * Initialize modules.
- */
-static pj_status_t init_modules( pjsip_endpoint *endpt )
+ * Register new module to the endpoint.
+ * The endpoint will then call the load and start function in the module to 
+ * properly initialize the module, and assign a unique module ID for the 
+ * module.
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_register_module( pjsip_endpoint *endpt,
+						 pjsip_module *mod )
+{
+    pj_status_t status = PJ_SUCCESS;
+    pjsip_module *m;
+    int i;
+
+    pj_rwmutex_lock_write(endpt->mod_mutex);
+
+    /* Make sure that this module has not been registered. */
+    PJ_ASSERT_ON_FAIL(	pj_list_find_node(&endpt->module_list, mod) == NULL,
+			{status = PJ_EEXISTS; goto on_return;});
+
+    /* Find unused ID for this module. */
+    for (i=0; i<PJ_ARRAY_SIZE(endpt->modules); ++i) {
+	if (endpt->modules[i] == NULL)
+	    break;
+    }
+    if (i == PJ_ARRAY_SIZE(endpt->modules)) {
+	pj_assert(!"Too many modules registered!");
+	status = PJ_ETOOMANY;
+	goto on_return;
+    }
+
+    /* Assign the ID. */
+    mod->id = i;
+
+    /* Try to load the module. */
+    if (mod->load) {
+	status = (*mod->load)(endpt);
+	if (status != PJ_SUCCESS)
+	    goto on_return;
+    }
+
+    /* Try to start the module. */
+    if (mod->start) {
+	status = (*mod->start)();
+	if (status != PJ_SUCCESS)
+	    goto on_return;
+    }
+
+    /* Save the module. */
+    endpt->modules[i] = mod;
+
+    /* Put in the module list, sorted by priority. */
+    m = endpt->module_list.next;
+    while (m != &endpt->module_list) {
+	if (m->priority > mod->priority)
+	    break;
+	m = m->next;
+    }
+    pj_list_insert_before(m, mod);
+
+    /* Done. */
+    PJ_TODO(BUILD_ALLOW_HEADER_BASED_ON_MODULES_SUPPORTED_METHODS);
+
+on_return:
+    pj_rwmutex_unlock_write(endpt->mod_mutex);
+    return status;
+}
+
+/*
+ * Unregister a module from the endpoint.
+ * The endpoint will then call the stop and unload function in the module to 
+ * properly shutdown the module.
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_unregister_module( pjsip_endpoint *endpt,
+						   pjsip_module *mod )
 {
     pj_status_t status;
-    unsigned i;
-    //pj_str_t str_COMMA = { ", ", 2 };
-    extern pjsip_module aux_tsx_module;
-
-    PJ_LOG(5, (THIS_FILE, "init_modules()"));
-
-    /* Load static modules. */
-    endpt->mod_count = PJSIP_MAX_MODULE;
-    status = register_static_modules( &endpt->mod_count, endpt->modules );
-    if (status != 0) {
-	return status;
-    }
-
-    /* Add mini aux module. */
-    endpt->modules[endpt->mod_count++] = &aux_tsx_module;
-
-    /* Load dynamic modules. */
-    // Not supported yet!
-
-    /* Sort modules on the priority. */
-    for (i=endpt->mod_count-1; i>0; --i) {
-	pj_uint32_t max = 0;
-	unsigned j;
-	for (j=1; j<=i; ++j) {
-	    if (endpt->modules[j]->priority > endpt->modules[max]->priority)
-		max = j;
-	}
-	if (max != i) {
-	    pjsip_module *temp = endpt->modules[max];
-	    endpt->modules[max] = endpt->modules[i];
-	    endpt->modules[i] = temp;
-	}
-    }
-
-    /* Initialize each module. */
-    for (i=0; i < endpt->mod_count; ++i) {
-	int j;
-
-	pjsip_module *mod = endpt->modules[i];
-	if (mod->init_module) {
-	    status = mod->init_module(endpt, mod, i);
-	    if (status != 0) {
-		return status;
-	    }
-	}
-
-	/* Collect all supported methods from modules. */
-	for (j=0; j<mod->method_cnt; ++j) {
-	    unsigned k;
-	    for (k=0; k<endpt->method_cnt; ++k) {
-		if (pjsip_method_cmp(mod->methods[j], endpt->methods[k]) == 0)
-		    break;
-	    }
-	    if (k == endpt->method_cnt) {
-		if (endpt->method_cnt < MAX_METHODS) {
-		    endpt->methods[endpt->method_cnt++] = mod->methods[j];
-		} else {
-		    PJ_LOG(1,(THIS_FILE, "Too many methods"));
-		    return -1;
-		}
-	    }
-	}
-    }
-
-    /* Create Allow header. */
-    endpt->allow_hdr = pjsip_allow_hdr_create( endpt->pool );
-    endpt->allow_hdr->count = endpt->method_cnt;
-    for (i=0; i<endpt->method_cnt; ++i) {
-	endpt->allow_hdr->values[i] = endpt->methods[i]->name;
-    }
-
-    /* Start each module. */
-    for (i=0; i < endpt->mod_count; ++i) {
-	pjsip_module *mod = endpt->modules[i];
-	if (mod->start_module) {
-	    status = mod->start_module(mod);
-	    if (status != 0) {
-		return status;
-	    }
-	}
-    }
+
+    pj_rwmutex_lock_write(endpt->mod_mutex);
+
+    /* Make sure the module exists in the list. */
+    PJ_ASSERT_ON_FAIL(	pj_list_find_node(&endpt->module_list, mod) == mod,
+			{status = PJ_ENOTFOUND;goto on_return;} );
+
+    /* Make sure the module exists in the array. */
+    PJ_ASSERT_ON_FAIL(	mod->id>=0 && mod->id<PJ_ARRAY_SIZE(endpt->modules) &&
+			endpt->modules[mod->id] == mod,
+			{status = PJ_ENOTFOUND; goto on_return;});
+
+    /* Try to stop the module. */
+    if (mod->stop) {
+	status = (*mod->stop)();
+	if (status != PJ_SUCCESS) goto on_return;
+    }
+
+    /* Try to unload the module. */
+    if (mod->unload) {
+	status = (*mod->unload)();
+	if (status != PJ_SUCCESS) goto on_return;
+    }
+
+    /* Remove module from array. */
+    endpt->modules[mod->id] = NULL;
+
+    /* Remove module from list. */
+    pj_list_erase(mod);
 
     /* Done. */
-    return 0;
-}
-
-/*
- * 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));
-}
-
-
-/*
- * Receive transaction events from transactions and dispatch them to the 
- * modules.
- */
-static void endpt_do_event( pjsip_endpoint *endpt, pjsip_event *evt)
-{
-    unsigned i;
-
-    /* Dispatch event to modules. */
-    for (i=0; i<endpt->mod_count; ++i) {
-	pjsip_module *mod = endpt->modules[i];
-	if (mod && mod->tsx_handler) {
-	    mod->tsx_handler( mod, evt );
-	}
-    }
-
-    /* Destroy transaction if it is terminated. */
-    if (evt->type == PJSIP_EVENT_TSX_STATE && 
-	evt->body.tsx_state.tsx->state == PJSIP_TSX_STATE_DESTROYED) 
-    {
-	/* No need to lock mutex. Mutex is locked inside the destroy function */
-	pjsip_endpt_destroy_tsx( endpt, evt->body.tsx_state.tsx );
-    }
-}
-
-/*
- * Receive transaction events from transactions and put in the event queue
- * to be processed later.
- */
-void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt )
-{
-    // Need to protect this with try/catch?
-    endpt_do_event(endpt, evt);
+    status = PJ_SUCCESS;
+
+    PJ_TODO(REMOVE_METHODS_FROM_ALLOW_HEADER_WHEN_MODULE_IS_UNREGISTERED);
+
+on_return:
+    pj_rwmutex_unlock_write(endpt->mod_mutex);
+    return status;
 }
 
@@ -376,7 +325,15 @@
 
     /* Create endpoint. */
-    endpt = pj_pool_calloc(pool, 1, sizeof(*endpt));
+    endpt = pj_pool_zalloc(pool, sizeof(*endpt));
     endpt->pool = pool;
     endpt->pf = pf;
+
+    /* Init modules list. */
+    pj_list_init(&endpt->module_list);
+
+    /* Create R/W mutex for module manipulation. */
+    status = pj_rwmutex_create(endpt->pool, "ept%p", &endpt->mod_mutex);
+    if (status != PJ_SUCCESS)
+	goto on_error;
 
     /* Init parser. */
@@ -400,18 +357,4 @@
     }
 
-    /* Create mutex for the transaction table. */
-    status = pj_mutex_create_recursive( endpt->pool, "mtbl%p", 
-                                        &endpt->tsx_table_mutex);
-    if (status != PJ_SUCCESS) {
-	goto on_error;
-    }
-
-    /* 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 timer heap to manage all timers within this endpoint. */
     status = pj_timer_heap_create( endpt->pool, PJSIP_MAX_TIMER_COUNT, 
@@ -453,11 +396,4 @@
     }
 
-    /* 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);
-
     /* Initialize request headers. */
     pj_list_init(&endpt->req_hdr);
@@ -471,11 +407,4 @@
     pj_list_insert_before( &endpt->req_hdr, mf_hdr);
 
-    /* Load and init modules. */
-    status = init_modules(endpt);
-    if (status != PJ_SUCCESS) {
-	PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init(): error in init_modules()"));
-	return status;
-    }
-
     /* Done. */
     *p_endpt = endpt;
@@ -491,7 +420,7 @@
 	endpt->mutex = NULL;
     }
-    if (endpt->tsx_table_mutex) {
-	pj_mutex_destroy(endpt->tsx_table_mutex);
-	endpt->tsx_table_mutex = NULL;
+    if (endpt->mod_mutex) {
+	pj_rwmutex_destroy(endpt->mod_mutex);
+	endpt->mod_mutex = NULL;
     }
     pj_pool_release( endpt->pool );
@@ -513,7 +442,4 @@
     /* Delete endpoint mutex. */
     pj_mutex_destroy(endpt->mutex);
-
-    /* Delete transaction table mutex. */
-    pj_mutex_destroy(endpt->tsx_table_mutex);
 
     /* Finally destroy pool. */
@@ -625,91 +551,4 @@
 
 /*
- * 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)
-{
-    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;
-    }
-
-    /* Create the transaction. */
-    return pjsip_tsx_create(pool, endpt, p_tsx);
-}
-
-/*
- * 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.
@@ -720,17 +559,15 @@
 {
     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"
+			       "Error processing packet from %s:%d, packet:--\n"
 			       "%s\n"
-			       "-- end of packet. Error",
-			       src_addr, port, rdata->msg_info.msg_buf));
+			       "-- end of packet.",
+			       rdata->pkt_info.src_name, 
+			       rdata->pkt_info.src_port, 
+			       rdata->msg_info.msg_buf));
 	return;
     }
@@ -741,5 +578,5 @@
      */
     if (msg->type == PJSIP_RESPONSE_MSG) {
-	const pj_str_t *addr_addr;
+	const pj_str_t *local_addr;
 	int port = rdata->msg_info.via->sent_by.port;
 	pj_bool_t mismatch = PJ_FALSE;
@@ -749,6 +586,6 @@
 	    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)
+	local_addr = &rdata->tp_info.transport->local_name.host;
+	if (pj_strcmp(&rdata->msg_info.via->sent_by.host, local_addr) != 0)
 	    mismatch = PJ_TRUE;
 	else if (port != rdata->tp_info.transport->local_name.port) {
@@ -760,184 +597,69 @@
 	     * 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)
+	    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));
+		PJ_LOG(4,(THIS_FILE, "Response %p from %s has mismatch port in "
+				     "sent-by but the rport parameter is "
+				     "correct",
+				     rdata, rdata->pkt_info.src_name));
 	    }
 	}
 
 	if (mismatch) {
-	    pjsip_event e;
-
-	    PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata, PJSIP_EINVALIDVIA);
-	    endpt_do_event( endpt, &e );
+	    PJ_TODO(ENDPT_REPORT_WHEN_DROPPING_MESSAGE);
+	    PJ_LOG(4,(THIS_FILE, "Dropping response from %s:%d because sent-by"
+				 " is mismatch", 
+				 rdata->pkt_info.src_name, 
+				 rdata->pkt_info.src_port));
 	    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;
+    }
+
+
+    /* Distribute to modules. */
+    pj_rwmutex_lock_read(endpt->mod_mutex);
+
+    if (msg->type == PJSIP_REQUEST_MSG) {
+	pjsip_module *mod;
+	pj_bool_t handled = PJ_FALSE;
+
+	mod = endpt->module_list.next;
+	while (mod != &endpt->module_list) {
+	    if (mod->on_rx_request)
+		handled = (*mod->on_rx_request)(rdata);
+	    if (handled)
+		break;
+	    mod = mod->next;
 	}
-    }
-
-    /* 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) {
+
+	/* No module is able to handle the request. */
+	if (!handled) {
+	    PJ_TODO(ENDPT_RESPOND_UNHANDLED_REQUEST);
+	    PJ_LOG(4,(THIS_FILE, "Request from %s:%d was dropped/unhandled by"
+				 " any modules"));
+	}
+
+    } else {
+	pjsip_module *mod;
+	pj_bool_t handled = PJ_FALSE;
+
+	mod = endpt->module_list.next;
+	while (mod != &endpt->module_list) {
+	    if (mod->on_rx_response)
+		handled = (*mod->on_rx_response)(rdata);
+	    if (handled)
 		break;
-	    }
+	    mod = mod->next;
 	}
-	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);
+
+	if (!handled) {
+	    PJ_LOG(4,(THIS_FILE, "Response from %s:%d was dropped/unhandled by"
+				 " any modules"));
 	}
     }
+
+    pj_rwmutex_unlock_read(endpt->mod_mutex);
 }
 
@@ -984,13 +706,13 @@
  * Find/create transport.
  */
-PJ_DEF(pj_status_t) pjsip_endpt_alloc_transport( pjsip_endpoint *endpt,
+PJ_DEF(pj_status_t) pjsip_endpt_acquire_transport(pjsip_endpoint *endpt,
 						  pjsip_transport_type_e type,
-						  const pj_sockaddr *remote,
+						  const pj_sockaddr_t *remote,
 						  int addr_len,
-						  pjsip_transport **p_transport)
-{
-    PJ_LOG(5, (THIS_FILE, "pjsip_endpt_alloc_transport()"));
-    return pjsip_tpmgr_alloc_transport( endpt->transport_mgr, type, 
-					remote, addr_len, p_transport);
+						  pjsip_transport **transport)
+{
+    PJ_LOG(5, (THIS_FILE, "pjsip_endpt_acquire_transport()"));
+    return pjsip_tpmgr_acquire_transport(endpt->transport_mgr, type, 
+					 remote, addr_len, transport);
 }
 
@@ -1050,6 +772,4 @@
 {
 #if PJ_LOG_MAX_LEVEL >= 3
-    unsigned count;
-
     PJ_LOG(5, (THIS_FILE, "pjsip_endpt_dump()"));
 
@@ -1066,51 +786,4 @@
 	       pj_pool_get_capacity(endpt->pool),
 	       pj_pool_get_used_size(endpt->pool)));
-
-    /* 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);
-	}
-    }
 
     /* Transports. 
Index: /pjproject/trunk/pjsip/src/pjsip/sip_errno.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_errno.c	(revision 105)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_errno.c	(revision 106)
@@ -40,7 +40,12 @@
     { PJSIP_EMSGTOOLONG,	"Message too long" },
     { PJSIP_EPARTIALMSG,	"Partial message" },
+    { PJSIP_EMISSINGREQURI,	"Missing Request-URI" },
     { PJSIP_EMISSINGHDR,	"Missing required header(s)" },
+    { PJSIP_EMISSINGBODY,	"Missing message body" },
     { PJSIP_EINVALIDVIA,	"Invalid Via header" },
     { PJSIP_EMULTIPLEVIA,	"Multiple Via headers in response" },
+    { PJSIP_EINVALIDREQURI,	"Invalid Request URI" },
+    { PJSIP_ENOTREQUESTMSG,	"Expecting request message"},
+    { PJSIP_ENOTRESPONSEMSG,	"Expecting response message"},
 
     /* Transport errors */
@@ -48,4 +53,5 @@
     { PJSIP_EPENDINGTX,		"Transmit buffer already pending"},
     { PJSIP_ERXOVERFLOW,	"Rx buffer overflow"},
+    { PJSIP_EBUFDESTROYED,	"Buffer destroyed"},
 
     /* Transaction errors */
Index: /pjproject/trunk/pjsip/src/pjsip/sip_msg.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_msg.c	(revision 105)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_msg.c	(revision 106)
@@ -22,4 +22,5 @@
 #include <pj/string.h>
 #include <pj/pool.h>
+#include <pj/assert.h>
 
 /*
@@ -30,12 +31,20 @@
 #endif
 
-static const pj_str_t method_names[] = 
-{
-    { "INVITE",	    6 },
-    { "CANCEL",	    6 },
-    { "ACK",	    3 },
-    { "BYE",	    3 },
-    { "REGISTER",   8 },
-    { "OPTIONS",    7 },
+const pjsip_method 
+    pjsip_invite_method	    = { PJSIP_INVITE_METHOD,	{ "INVITE",6 }	},
+    pjsip_cancel_method	    = { PJSIP_CANCEL_METHOD,	{ "CANCEL",6 }	},
+    pjsip_ack_method	    = { PJSIP_ACK_METHOD,	{ "ACK",3}	},
+    pjsip_bye_method	    = { PJSIP_BYE_METHOD,	{ "BYE",3}	},
+    pjsip_register_method   = { PJSIP_REGISTER_METHOD,	{ "REGISTER",8}	},
+    pjsip_options_method    = { PJSIP_OPTIONS_METHOD,	{ "OPTIONS",7}	};
+
+static const pj_str_t *method_names[] = 
+{
+    &pjsip_invite_method.name,
+    &pjsip_cancel_method.name,
+    &pjsip_ack_method.name,
+    &pjsip_bye_method.name,
+    &pjsip_register_method.name,
+    &pjsip_options_method.name
 };
 
@@ -179,6 +188,7 @@
 PJ_DEF(void) pjsip_method_set( pjsip_method *m, pjsip_method_e me )
 {
+    pj_assert(me < PJSIP_OTHER_METHOD);
     m->id = me;
-    m->name = method_names[me];
+    m->name = *method_names[me];
 }
 
@@ -188,7 +198,7 @@
     int i;
     for (i=0; i<PJ_ARRAY_SIZE(method_names); ++i) {
-	if (pj_stricmp(str, &method_names[i])==0) {
+	if (pj_stricmp(str, method_names[i])==0) {
 	    m->id = (pjsip_method_e)i;
-	    m->name = method_names[i];
+	    m->name = *method_names[i];
 	    return;
 	}
@@ -236,8 +246,8 @@
 }
 
-PJ_DEF(void*)  pjsip_msg_find_hdr( pjsip_msg *msg, 
-				   pjsip_hdr_e hdr_type, void *start)
-{
-    pjsip_hdr *hdr=start, *end=&msg->hdr;
+PJ_DEF(void*)  pjsip_msg_find_hdr( const pjsip_msg *msg, 
+				   pjsip_hdr_e hdr_type, const void *start)
+{
+    const pjsip_hdr *hdr=start, *end=&msg->hdr;
 
     if (hdr == NULL) {
@@ -246,13 +256,14 @@
     for (; hdr!=end; hdr = hdr->next) {
 	if (hdr->type == hdr_type)
-	    return hdr;
+	    return (void*)hdr;
     }
     return NULL;
 }
 
-PJ_DEF(void*)  pjsip_msg_find_hdr_by_name( pjsip_msg *msg, 
-					   const pj_str_t *name, void *start)
-{
-    pjsip_hdr *hdr=start, *end=&msg->hdr;
+PJ_DEF(void*)  pjsip_msg_find_hdr_by_name( const pjsip_msg *msg, 
+					   const pj_str_t *name, 
+					   const void *start)
+{
+    const pjsip_hdr *hdr=start, *end=&msg->hdr;
 
     if (hdr == NULL) {
@@ -262,8 +273,8 @@
 	if (hdr->type < PJSIP_H_OTHER) {
 	    if (pj_stricmp(&pjsip_hdr_names[hdr->type], name) == 0)
-		return hdr;
+		return (void*)hdr;
 	} else {
 	    if (pj_stricmp(&hdr->name, name) == 0)
-		return hdr;
+		return (void*)hdr;
 	}
     }
@@ -281,5 +292,6 @@
 }
 
-PJ_DEF(pj_ssize_t) pjsip_msg_print( pjsip_msg *msg, char *buf, pj_size_t size)
+PJ_DEF(pj_ssize_t) pjsip_msg_print( const pjsip_msg *msg, 
+				    char *buf, pj_size_t size)
 {
     char *p=buf, *end=buf+size;
@@ -1350,5 +1362,5 @@
     pjsip_via_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
     init_hdr(hdr, PJSIP_H_VIA, &via_hdr_vptr);
-    hdr->sent_by.port = 5060;
+    //hdr->sent_by.port = 5060;
     hdr->ttl_param = -1;
     hdr->rport_param = -1;
@@ -1384,7 +1396,9 @@
     pj_memcpy(buf, hdr->sent_by.host.ptr, hdr->sent_by.host.slen);
     buf += hdr->sent_by.host.slen;
-    *buf++ = ':';
-    printed = pj_utoa(hdr->sent_by.port, buf);
-    buf += printed;
+    if (hdr->sent_by.port != 0) {
+	*buf++ = ':';
+	printed = pj_utoa(hdr->sent_by.port, buf);
+	buf += printed;
+    }
 
     if (hdr->ttl_param >= 0) {
Index: /pjproject/trunk/pjsip/src/pjsip/sip_parser.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_parser.c	(revision 105)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_parser.c	(revision 106)
@@ -1783,6 +1783,4 @@
 	    pj_scan_get(scanner, &pjsip_DIGIT_SPEC, &digit);
 	    hdr->sent_by.port = pj_strtoul(&digit);
-	} else {
-	    hdr->sent_by.port = 5060;
 	}
 	
Index: /pjproject/trunk/pjsip/src/pjsip/sip_transaction.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_transaction.c	(revision 105)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_transaction.c	(revision 106)
@@ -30,4 +30,394 @@
 #include <pj/pool.h>
 #include <pj/assert.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);
+
+
+    /* 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)
+{
+    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;
+    }
+
+    /* Create the transaction. */
+    return pjsip_tsx_create(pool, endpt, p_tsx);
+}
+
+
+/*
+ * 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) */
@@ -153,4 +543,43 @@
     return role_name[role];
 }
+
+
+
+/*
+ * 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));
+}
+
 
 
@@ -501,7 +930,5 @@
 				      pjsip_host_info *send_addr )
 {
-    const pjsip_uri *new_request_uri, *target_uri;
-    const pjsip_name_addr *topmost_route_uri;
-    pjsip_route_hdr *first_route_hdr, *last_route_hdr;
+    pjsip_route_hdr *route_hdr;
     
     pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG);
@@ -511,128 +938,18 @@
      * headers from the endpoint first.
      */
-    last_route_hdr = first_route_hdr = 
-	pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL);
-    if (first_route_hdr) {
-	topmost_route_uri = &first_route_hdr->name_addr;
-	while (last_route_hdr->next != (void*)&tdata->msg->hdr) {
-	    pjsip_route_hdr *hdr;
-	    hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, 
-                                     last_route_hdr->next);
-	    if (!hdr)
-		break;
-	    last_route_hdr = hdr;
-	}
-    } else {
+    route_hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL);
+    if (!route_hdr) {
 	const pjsip_route_hdr *hdr_list;
-	hdr_list = (pjsip_route_hdr*)pjsip_endpt_get_routing(tsx->endpt);
-	if (hdr_list->next != hdr_list) {
-	    const pjsip_route_hdr *hdr = (pjsip_route_hdr*)hdr_list->next;
-	    first_route_hdr = NULL;
-	    topmost_route_uri = &hdr->name_addr;
-	    do {
-		last_route_hdr = pjsip_hdr_shallow_clone(tdata->pool, hdr);
-		if (first_route_hdr == NULL)
-		    first_route_hdr = last_route_hdr;
-		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)last_route_hdr);
-		hdr = hdr->next;
-	    } while (hdr != hdr_list);
-	} else {
-	    topmost_route_uri = NULL;
-	}
-    }
-
-    /* If Route headers exist, and the first element indicates loose-route,
-     * the URI is taken from the Request-URI, and we keep all existing Route
-     * headers intact.
-     * If Route headers exist, and the first element DOESN'T indicate loose
-     * route, the URI is taken from the first Route header, and remove the
-     * first Route header from the message.
-     * Otherwise if there's no Route headers, the URI is taken from the
-     * Request-URI.
-     */
-    if (topmost_route_uri) {
-	pj_bool_t has_lr_param;
-
-	if (PJSIP_URI_SCHEME_IS_SIP(topmost_route_uri) ||
-	    PJSIP_URI_SCHEME_IS_SIPS(topmost_route_uri))
-	{
-	    const pjsip_url *url = pjsip_uri_get_uri((void*)topmost_route_uri);
-	    has_lr_param = url->lr_param;
-	} else {
-	    has_lr_param = 0;
-	}
-
-	if (has_lr_param) {
-	    new_request_uri = tdata->msg->line.req.uri;
-	    /* We shouldn't need to delete topmost Route if it has lr param.
-	     * But seems like it breaks some proxy implementation, so we
-	     * delete it anyway.
-	     */
-	    /*
-	    pj_list_erase(first_route_hdr);
-	    if (first_route_hdr == last_route_hdr)
-		last_route_hdr = NULL;
-	    */
-	} else {
-	    new_request_uri = pjsip_uri_get_uri((void*)topmost_route_uri);
-	    pj_list_erase(first_route_hdr);
-	    if (first_route_hdr == last_route_hdr)
-		last_route_hdr = NULL;
-	}
-
-	target_uri = (pjsip_uri*)topmost_route_uri;
-
-    } else {
-	target_uri = new_request_uri = tdata->msg->line.req.uri;
-    }
-
-    /* The target URI must be a SIP/SIPS URL so we can resolve it's address.
-     * Otherwise we're in trouble (i.e. there's no host part in tel: URL).
-     */
-    pj_memset(send_addr, 0, sizeof(*send_addr));
-
-    if (PJSIP_URI_SCHEME_IS_SIPS(target_uri)) {
-	pjsip_uri *uri = (pjsip_uri*) target_uri;
-	const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri);
-	send_addr->flag |= (PJSIP_TRANSPORT_SECURE | PJSIP_TRANSPORT_RELIABLE);
-	pj_strdup(tdata->pool, &send_addr->addr.host, &url->host);
-        send_addr->addr.port = url->port;
-	send_addr->type = 
-            pjsip_transport_get_type_from_name(&url->transport_param);
-
-    } else if (PJSIP_URI_SCHEME_IS_SIP(target_uri)) {
-	pjsip_uri *uri = (pjsip_uri*) target_uri;
-	const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri);
-	pj_strdup(tdata->pool, &send_addr->addr.host, &url->host);
-	send_addr->addr.port = url->port;
-	send_addr->type = 
-            pjsip_transport_get_type_from_name(&url->transport_param);
-#if PJ_HAS_TCP
-	if (send_addr->type == PJSIP_TRANSPORT_TCP || 
-	    send_addr->type == PJSIP_TRANSPORT_SCTP) 
-	{
-	    send_addr->flag |= PJSIP_TRANSPORT_RELIABLE;
-	}
-#endif
-    } else {
-        pj_assert(!"Unsupported URI scheme!");
-	return PJSIP_EINVALIDSCHEME;
-    }
-
-    /* If target URI is different than request URI, replace 
-     * request URI add put the original URI in the last Route header.
-     */
-    if (new_request_uri && new_request_uri!=tdata->msg->line.req.uri) {
-	pjsip_route_hdr *route = pjsip_route_hdr_create(tdata->pool);
-	route->name_addr.uri = tdata->msg->line.req.uri;
-	if (last_route_hdr)
-	    pj_list_insert_after(last_route_hdr, route);
-	else
-	    pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)route);
-	tdata->msg->line.req.uri = (pjsip_uri*)new_request_uri;
-    }
-
-    /* Success. */
-    return PJ_SUCCESS;  
+	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);  
 }
 
Index: /pjproject/trunk/pjsip/src/pjsip/sip_transport.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_transport.c	(revision 105)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_transport.c	(revision 106)
@@ -44,4 +44,7 @@
     pjsip_endpoint  *endpt;
     pjsip_tpfactory  factory_list;
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+    pj_atomic_t	    *tdata_counter;
+#endif
     void           (*msg_cb)(pjsip_endpoint*, pj_status_t, pjsip_rx_data*);
 };
@@ -205,4 +208,8 @@
     pj_ioqueue_op_key_init(&tdata->op_key.key, sizeof(tdata->op_key));
 
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+    pj_atomic_inc( tdata->mgr->tdata_counter );
+#endif
+
     *p_tdata = tdata;
     return PJ_SUCCESS;
@@ -222,12 +229,18 @@
  * reaches zero.
  */
-PJ_DEF(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata )
+PJ_DEF(pj_status_t) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata )
 {
     pj_assert( pj_atomic_get(tdata->ref_cnt) > 0);
     if (pj_atomic_dec_and_get(tdata->ref_cnt) <= 0) {
 	PJ_LOG(5,(tdata->obj_name, "destroying txdata"));
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+	pj_atomic_dec( tdata->mgr->tdata_counter );
+#endif
 	pj_atomic_destroy( tdata->ref_cnt );
 	pj_lock_destroy( tdata->lock );
 	pjsip_endpt_destroy_pool( tdata->mgr->endpt, tdata->pool );
+	return PJSIP_EBUFDESTROYED;
+    } else {
+	return PJ_SUCCESS;
     }
 }
@@ -453,5 +466,5 @@
      * Unregister timer, if any.
      */
-    pj_assert(tp->idle_timer.id == PJ_FALSE);
+    //pj_assert(tp->idle_timer.id == PJ_FALSE);
     if (tp->idle_timer.id != PJ_FALSE) {
 	pjsip_endpt_cancel_timer(mgr->endpt, &tp->idle_timer);
@@ -567,4 +580,10 @@
 	return status;
 
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+    status = pj_atomic_create(pool, 0, &mgr->tdata_counter);
+    if (status != PJ_SUCCESS)
+	return status;
+#endif
+
     *p_mgr = mgr;
     return PJ_SUCCESS;
@@ -582,4 +601,8 @@
     
     PJ_LOG(5, (THIS_FILE, "pjsip_tpmgr_destroy()"));
+
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+    pj_assert(pj_atomic_get(mgr->tdata_counter) == 0);
+#endif
 
     pj_lock_acquire(mgr->lock);
@@ -601,4 +624,5 @@
 
     pj_lock_release(mgr->lock);
+    pj_lock_destroy(mgr->lock);
 
     return PJ_SUCCESS;
@@ -742,14 +766,14 @@
 
 /*
- * pjsip_tpmgr_alloc_transport()
+ * pjsip_tpmgr_acquire_transport()
  *
  * Get transport suitable to communicate to remote. Create a new one
  * if necessary.
  */
-PJ_DEF(pj_status_t) pjsip_tpmgr_alloc_transport( pjsip_tpmgr *mgr,
-						 pjsip_transport_type_e type,
-						 const pj_sockaddr_t *remote,
-						 int addr_len,
-						 pjsip_transport **p_transport)
+PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr,
+						  pjsip_transport_type_e type,
+						  const pj_sockaddr_t *remote,
+						  int addr_len,
+						  pjsip_transport **tp)
 {
     struct transport_key
@@ -799,5 +823,5 @@
 	pjsip_transport_add_ref(transport);
 	pj_lock_release(mgr->lock);
-	*p_transport = transport;
+	*tp = transport;
 	return PJ_SUCCESS;
     }
@@ -822,6 +846,10 @@
     /* Request factory to create transport. */
     status = factory->create_transport(factory, mgr, mgr->endpt,
-				       remote, p_transport);
-
+				       remote, addr_len, tp);
+    if (status == PJ_SUCCESS) {
+	PJ_ASSERT_ON_FAIL(tp!=NULL, 
+	    {pj_lock_release(mgr->lock); return PJ_EBUG;});
+	pjsip_transport_add_ref(*tp);
+    }
     pj_lock_release(mgr->lock);
     return status;
Index: /pjproject/trunk/pjsip/src/pjsip/sip_transport_udp.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_transport_udp.c	(revision 105)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_transport_udp.c	(revision 106)
@@ -22,4 +22,5 @@
 #include <pj/pool.h>
 #include <pj/sock.h>
+#include <pj/addr_resolv.h>
 #include <pj/os.h>
 #include <pj/lock.h>
@@ -268,4 +269,7 @@
     pj_status_t status;
 
+    PJ_ASSERT_RETURN(endpt && sock!=PJ_INVALID_SOCKET && a_name && async_cnt>0,
+		     PJ_EINVAL);
+
     /* Create pool. */
     pool = pjsip_endpt_create_pool(endpt, "udp%p", PJSIP_POOL_LEN_TRANSPORT, 
@@ -328,5 +332,5 @@
 			      pj_inet_ntoa(((pj_sockaddr_in*)&tp->base.local_addr)->sin_addr),
 			      pj_ntohs(((pj_sockaddr_in*)&tp->base.local_addr)->sin_port),
-			      tp->base.local_name.host,
+			      tp->base.local_name.host.ptr,
 			      tp->base.local_name.port);
 
@@ -368,4 +372,6 @@
     /* Create rdata and put it in the array. */
     tp->rdata_cnt = 0;
+    tp->rdata = pj_pool_calloc(tp->base.pool, async_cnt, 
+			       sizeof(pjsip_rx_data*));
     for (i=0; i<async_cnt; ++i) {
 	pj_pool_t *rdata_pool = pjsip_endpt_create_pool(endpt, "rtd%p", 
@@ -383,4 +389,5 @@
 	tp->rdata[i]->tp_info.pool = rdata_pool;
 	tp->rdata[i]->tp_info.transport = &tp->base;
+	tp->rdata[i]->tp_info.op_key.rdata = tp->rdata[i];
 	pj_ioqueue_op_key_init(&tp->rdata[i]->tp_info.op_key.op_key, 
 			       sizeof(pj_ioqueue_op_key_t));
@@ -437,4 +444,6 @@
     pjsip_host_port bound_name;
 
+    PJ_ASSERT_RETURN(local != NULL, PJ_EINVAL);
+
     status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock);
     if (status != PJ_SUCCESS)
@@ -448,8 +457,29 @@
 
     if (a_name == NULL) {
+	/* Address name is not specified. 
+	 * Build a name based on bound address.
+	 */
 	a_name = &bound_name;
 	bound_name.host.ptr = addr_buf;
-	pj_strcpy2(&bound_name.host, pj_inet_ntoa(local->sin_addr));
 	bound_name.port = pj_ntohs(local->sin_port);
+
+	/* If bound address specifies "0.0.0.0", get the IP address
+	 * of local hostname.
+	 */
+	if (local->sin_addr.s_addr == PJ_INADDR_ANY) {
+	    pj_hostent he;
+	    const pj_str_t *hostname = pj_gethostname();
+	    status = pj_gethostbyname(hostname, &he);
+	    if (status != PJ_SUCCESS) {
+		pj_sock_close(sock);
+		return status;
+	    }
+	    pj_strcpy2(&bound_name.host, 
+		       pj_inet_ntoa(*(pj_in_addr*)he.h_addr));
+	} else {
+	    /* Otherwise use bound address. */
+	    pj_strcpy2(&bound_name.host, pj_inet_ntoa(local->sin_addr));
+	}
+	
     }
 
Index: /pjproject/trunk/pjsip/src/pjsip/sip_util.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_util.c	(revision 105)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_util.c	(revision 106)
@@ -24,4 +24,5 @@
 #include <pjsip/sip_transaction.h>
 #include <pjsip/sip_module.h>
+#include <pjsip/sip_errno.h>
 #include <pj/log.h>
 #include <pj/string.h>
@@ -52,99 +53,4 @@
 static pj_str_t str_TEXT = { "text", 4},
 		str_PLAIN = { "plain", 5 };
-static int aux_mod_id;
-
-struct aux_tsx_data
-{
-    void *token;
-    void (*cb)(void*,pjsip_event*);
-};
-
-static pj_status_t aux_tsx_init( pjsip_endpoint *endpt,
-				 struct pjsip_module *mod, pj_uint32_t id )
-{
-    PJ_UNUSED_ARG(endpt);
-    PJ_UNUSED_ARG(mod);
-
-    aux_mod_id = id;
-    return 0;
-}
-
-static void aux_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
-{
-    pjsip_transaction *tsx;
-    struct aux_tsx_data *tsx_data;
-
-    PJ_UNUSED_ARG(mod);
-
-    if (event->type != PJSIP_EVENT_TSX_STATE)
-	return;
-
-    pj_assert(event->body.tsx_state.tsx != NULL);
-    tsx = event->body.tsx_state.tsx;
-    if (tsx == NULL)
-	return;
-    if (tsx->module_data[aux_mod_id] == NULL)
-	return;
-    if (tsx->status_code < 200)
-	return;
-
-    /* Call the callback, if any, and prevent the callback to be called again
-     * by clearing the transaction's module_data.
-     */
-    tsx_data = tsx->module_data[aux_mod_id];
-    tsx->module_data[aux_mod_id] = NULL;
-
-    if (tsx_data->cb) {
-	(*tsx_data->cb)(tsx_data->token, event);
-    }
-}
-
-pjsip_module aux_tsx_module = 
-{
-    { "Aux-Tsx", 7},	    /* Name.		*/
-    0,			    /* Flag		*/
-    128,		    /* Priority		*/
-    NULL,		    /* Arbitrary data.	*/
-    0,			    /* Number of methods supported (none). */
-    { 0 },		    /* Array of methods (none) */
-    &aux_tsx_init,	    /* init_module()	*/
-    NULL,		    /* start_module()	*/
-    NULL,		    /* deinit_module()	*/
-    &aux_tsx_handler,	    /* tsx_handler()	*/
-};
-
-PJ_DEF(pj_status_t) pjsip_endpt_send_request(  pjsip_endpoint *endpt,
-					       pjsip_tx_data *tdata,
-					       int timeout,
-					       void *token,
-					       void (*cb)(void*,pjsip_event*))
-{
-    pjsip_transaction *tsx;
-    struct aux_tsx_data *tsx_data;
-    pj_status_t status;
-
-    status = pjsip_endpt_create_tsx(endpt, &tsx);
-    if (!tsx) {
-	pjsip_tx_data_dec_ref(tdata);
-	return -1;
-    }
-
-    tsx_data = pj_pool_alloc(tsx->pool, sizeof(struct aux_tsx_data));
-    tsx_data->token = token;
-    tsx_data->cb = cb;
-    tsx->module_data[aux_mod_id] = tsx_data;
-
-    if (pjsip_tsx_init_uac(tsx, tdata) != 0) {
-	pjsip_endpt_destroy_tsx(endpt, tsx);
-	pjsip_tx_data_dec_ref(tdata);
-	return -1;
-    }
-
-    pjsip_endpt_register_tsx(endpt, tsx);
-    pjsip_tx_data_invalidate_msg(tdata);
-    pjsip_tsx_on_tx_msg(tsx, tdata);
-    pjsip_tx_data_dec_ref(tdata);
-    return 0;
-}
 
 /*
@@ -170,4 +76,5 @@
     pjsip_msg *msg;
     pjsip_msg_body *body;
+    pjsip_via_hdr *via;
     const pjsip_hdr *endpt_hdr;
 
@@ -205,4 +112,9 @@
     /* Add CSeq header. */
     pjsip_msg_add_hdr(msg, (void*)param_cseq);
+
+    /* Add a blank Via header. */
+    via = pjsip_via_hdr_create(tdata->pool);
+    via->rport_param = 0;
+    pjsip_msg_insert_first_hdr(msg, (void*)via);
 
     /* Create message body. */
@@ -217,4 +129,11 @@
 	msg->body = body;
     }
+
+    PJ_LOG(4,(THIS_FILE, "Request %s (CSeq=%d/%.*s) created.", 
+			 tdata->obj_name, 
+			 param_cseq->cseq, 
+			 param_cseq->method.name.slen,
+			 param_cseq->method.name.ptr));
+
 }
 
@@ -329,10 +248,4 @@
     PJ_END
 
-    PJ_LOG(4,(THIS_FILE, "Request %s (%d %.*s) created.", 
-		        tdata->obj_name, 
-			cseq->cseq, 
-			cseq->method.name.slen,
-			cseq->method.name.ptr));
-
     *p_tdata = tdata;
     return PJ_SUCCESS;
@@ -367,21 +280,28 @@
     PJ_LOG(5,(THIS_FILE, "Entering pjsip_endpt_create_request_from_hdr()"));
 
+    /* Check arguments. */
+    PJ_ASSERT_RETURN(endpt && method && param_target && param_from &&
+		     param_to && p_tdata, PJ_EINVAL);
+
+    /* Create new transmit data. */
     status = pjsip_endpt_create_tdata(endpt, &tdata);
     if (status != PJ_SUCCESS)
 	return status;
 
+    /* Set initial reference counter to 1. */
     pjsip_tx_data_add_ref(tdata);
 
     PJ_TRY {
+	/* Duplicate target URI and headers. */
 	target = pjsip_uri_clone(tdata->pool, param_target);
-	from = pjsip_hdr_shallow_clone(tdata->pool, param_from);
+	from = pjsip_hdr_clone(tdata->pool, param_from);
 	pjsip_fromto_set_from(from);
-	to = pjsip_hdr_shallow_clone(tdata->pool, param_to);
+	to = pjsip_hdr_clone(tdata->pool, param_to);
 	pjsip_fromto_set_to(to);
 	if (param_contact)
-	    contact = pjsip_hdr_shallow_clone(tdata->pool, param_contact);
+	    contact = pjsip_hdr_clone(tdata->pool, param_contact);
 	else
 	    contact = NULL;
-	call_id = pjsip_hdr_shallow_clone(tdata->pool, param_call_id);
+	call_id = pjsip_hdr_clone(tdata->pool, param_call_id);
 	cseq = pjsip_cseq_hdr_create(tdata->pool);
 	if (param_cseq >= 0)
@@ -391,4 +311,5 @@
 	pjsip_method_copy(tdata->pool, &cseq->method, method);
 
+	/* Copy headers to the request. */
 	init_request_throw(endpt, tdata, &cseq->method, target, from, to, 
                            contact, call_id, cseq, param_text);
@@ -400,10 +321,4 @@
     PJ_END;
 
-    PJ_LOG(4,(THIS_FILE, "Request %s (%d %.*s) created.", 
-			tdata->obj_name, 
-			cseq->cseq, 
-			cseq->method.name.slen,
-			cseq->method.name.ptr));
-
     *p_tdata = tdata;
     return PJ_SUCCESS;
@@ -419,5 +334,6 @@
 PJ_DEF(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
 						 const pjsip_rx_data *rdata,
-						 int code,
+						 int st_code,
+						 const pj_str_t *st_text,
 						 pjsip_tx_data **p_tdata)
 {
@@ -435,5 +351,5 @@
     /* Log this action. */
     PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_response(rdata=%p, code=%d)", 
-		         rdata, code));
+		         rdata, st_code));
 
     /* Create a new transmit buffer. */
@@ -442,10 +358,16 @@
 	return status;
 
+    /* Set initial reference count to 1. */
+    pjsip_tx_data_add_ref(tdata);
+
     /* Create new response message. */
     tdata->msg = msg = pjsip_msg_create(tdata->pool, PJSIP_RESPONSE_MSG);
 
     /* Set status code and reason text. */
-    msg->line.status.code = code;
-    msg->line.status.reason = *pjsip_get_status_text(code);
+    msg->line.status.code = st_code;
+    if (st_text)
+	pj_strdup(tdata->pool, &msg->line.status.reason, st_text);
+    else
+	msg->line.status.reason = *pjsip_get_status_text(st_code);
 
     /* Set TX data attributes. */
@@ -501,16 +423,21 @@
  * and one must not use this function to generate such ACK.
  */
-PJ_DEF(void) pjsip_endpt_create_ack(pjsip_endpoint *endpt,
-				    pjsip_tx_data *tdata,
-				    const pjsip_rx_data *rdata )
-{
-    pjsip_msg *ack_msg, *invite_msg;
+PJ_DEF(pj_status_t) pjsip_endpt_create_ack( pjsip_endpoint *endpt,
+					    const pjsip_tx_data *tdata,
+					    const pjsip_rx_data *rdata,
+					    pjsip_tx_data **ack_tdata)
+{
+    pjsip_tx_data *ack = NULL;
+    const pjsip_msg *invite_msg;
+    const pjsip_from_hdr *from_hdr;
+    const pjsip_to_hdr *to_hdr;
+    const pjsip_cid_hdr *cid_hdr;
+    const pjsip_cseq_hdr *cseq_hdr;
+    const pjsip_hdr *hdr;
     pjsip_to_hdr *to;
-    pjsip_from_hdr *from;
-    pjsip_cseq_hdr *cseq;
-    pjsip_hdr *hdr;
-
-    /* Make compiler happy. */
-    PJ_UNUSED_ARG(endpt);
+    pj_status_t status;
+
+    /* Log this action. */
+    PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_ack(rdata=%p)", rdata));
 
     /* rdata must be a final response. */
@@ -518,62 +445,71 @@
 	      rdata->msg_info.msg->line.status.code >= 300);
 
-    /* Log this action. */
-    PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_ack(rdata=%p)", rdata));
-
-    /* Create new request message. */
-    ack_msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
-    pjsip_method_set( &ack_msg->line.req.method, PJSIP_ACK_METHOD );
+    /* Initialize return value to NULL. */
+    *ack_tdata = NULL;
 
     /* The original INVITE message. */
     invite_msg = tdata->msg;
 
-    /* Copy Request-Uri from the original INVITE. */
-    ack_msg->line.req.uri = invite_msg->line.req.uri;
-    
-    /* Copy Call-ID from the original INVITE */
-    hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_CALL_ID, NULL);
-    pjsip_msg_add_hdr( ack_msg, hdr );
-
-    /* Copy From header from the original INVITE. */
-    from = (pjsip_from_hdr*)pjsip_msg_find_remove_hdr(invite_msg, 
-						      PJSIP_H_FROM, NULL);
-    pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)from );
-
-    /* Copy To header from the original INVITE. */
-    to = (pjsip_to_hdr*)pjsip_msg_find_remove_hdr( invite_msg, 
-						   PJSIP_H_TO, NULL);
-    pj_strdup(tdata->pool, &to->tag, &rdata->msg_info.to->tag);
-    pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)to );
+    /* Get the headers from original INVITE request. */
+#   define FIND_HDR(m,HNAME) pjsip_msg_find_hdr(m, PJSIP_H_##HNAME, NULL)
+
+    from_hdr = (const pjsip_from_hdr*) FIND_HDR(invite_msg, FROM);
+    PJ_ASSERT_ON_FAIL(from_hdr != NULL, goto on_missing_hdr);
+
+    to_hdr = (const pjsip_to_hdr*) FIND_HDR(invite_msg, TO);
+    PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
+
+    cid_hdr = (const pjsip_cid_hdr*) FIND_HDR(invite_msg, CALL_ID);
+    PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
+
+    cseq_hdr = (const pjsip_cseq_hdr*) FIND_HDR(invite_msg, CSEQ);
+    PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
+
+#   undef FIND_HDR
+
+    /* Create new request message from the headers. */
+    status = pjsip_endpt_create_request_from_hdr(endpt, 
+						 &pjsip_ack_method,
+						 tdata->msg->line.req.uri,
+						 from_hdr, to_hdr,
+						 NULL, cid_hdr,
+						 cseq_hdr->cseq, NULL,
+						 &ack);
+
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Update tag in To header with the one from the response (if any). */
+    to = (pjsip_to_hdr*) pjsip_msg_find_hdr(ack->msg, PJSIP_H_TO, NULL);
+    pj_strdup(ack->pool, &to->tag, &rdata->msg_info.to->tag);
 
     /* Must contain single Via, just as the original INVITE. */
-    hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_VIA, NULL);
-    pjsip_msg_insert_first_hdr( ack_msg, hdr );
-
-    /* Must have the same CSeq value as the original INVITE, but method 
-     * changed to ACK 
-     */
-    cseq = (pjsip_cseq_hdr*) pjsip_msg_find_remove_hdr( invite_msg, 
-							PJSIP_H_CSEQ, NULL);
-    pjsip_method_set( &cseq->method, PJSIP_ACK_METHOD );
-    pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*) cseq );
+    hdr = pjsip_msg_find_hdr( invite_msg, PJSIP_H_VIA, NULL);
+    if (hdr) {
+        pjsip_msg_insert_first_hdr( ack->msg, pjsip_hdr_clone(ack->pool,hdr) );
+    }
 
     /* If the original INVITE has Route headers, those header fields MUST 
      * appear in the ACK.
      */
-    hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL);
+    hdr = pjsip_msg_find_hdr( invite_msg, PJSIP_H_ROUTE, NULL);
     while (hdr != NULL) {
-	pjsip_msg_add_hdr( ack_msg, hdr );
-	hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL);
-    }
-
-    /* Set the message in the "tdata" to point to the ACK message. */
-    tdata->msg = ack_msg;
-
-    /* Reset transmit packet buffer, to force 're-printing' of message. */
-    tdata->buf.cur = tdata->buf.start;
+	pjsip_msg_add_hdr( ack->msg, pjsip_hdr_clone(ack->pool, hdr) );
+	hdr = hdr->next;
+	if (hdr == &invite_msg->hdr)
+	    break;
+	hdr = pjsip_msg_find_hdr( invite_msg, PJSIP_H_ROUTE, hdr);
+    }
 
     /* We're done.
      * "tdata" parameter now contains the ACK message.
      */
+    *ack_tdata = ack;
+    return PJ_SUCCESS;
+
+on_missing_hdr:
+    if (ack)
+	pjsip_tx_data_dec_ref(ack);
+    return PJSIP_EMISSINGHDR;
 }
 
@@ -584,13 +520,13 @@
  */
 PJ_DEF(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
-					       pjsip_tx_data *req_tdata,
+					       const pjsip_tx_data *req_tdata,
 					       pjsip_tx_data **p_tdata)
 {
-    pjsip_msg *req_msg;	/* the original request. */
-    pjsip_tx_data *cancel_tdata;
-    pjsip_msg *cancel_msg;
-    pjsip_hdr *hdr;
-    pjsip_cseq_hdr *req_cseq, *cseq;
-    pjsip_uri *req_uri;
+    pjsip_tx_data *cancel_tdata = NULL;
+    const pjsip_from_hdr *from_hdr;
+    const pjsip_to_hdr *to_hdr;
+    const pjsip_cid_hdr *cid_hdr;
+    const pjsip_cseq_hdr *cseq_hdr;
+    const pjsip_hdr *hdr;
     pj_status_t status;
 
@@ -598,55 +534,46 @@
     PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_cancel(tdata=%p)", req_tdata));
 
-    /* Get the original request. */
-    req_msg = req_tdata->msg;
-
     /* The transmit buffer must INVITE request. */
-    PJ_ASSERT_RETURN(req_msg->type == PJSIP_REQUEST_MSG &&
-		     req_msg->line.req.method.id == PJSIP_INVITE_METHOD,
+    PJ_ASSERT_RETURN(req_tdata->msg->type == PJSIP_REQUEST_MSG &&
+		     req_tdata->msg->line.req.method.id == PJSIP_INVITE_METHOD,
 		     PJ_EINVAL);
 
-    /* Create new transmit buffer. */
-    status = pjsip_endpt_create_tdata( endpt, &cancel_tdata);
-    if (status != PJ_SUCCESS) {
+    /* Get the headers from original INVITE request. */
+#   define FIND_HDR(m,HNAME) pjsip_msg_find_hdr(m, PJSIP_H_##HNAME, NULL)
+
+    from_hdr = (const pjsip_from_hdr*) FIND_HDR(req_tdata->msg, FROM);
+    PJ_ASSERT_ON_FAIL(from_hdr != NULL, goto on_missing_hdr);
+
+    to_hdr = (const pjsip_to_hdr*) FIND_HDR(req_tdata->msg, TO);
+    PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
+
+    cid_hdr = (const pjsip_cid_hdr*) FIND_HDR(req_tdata->msg, CALL_ID);
+    PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
+
+    cseq_hdr = (const pjsip_cseq_hdr*) FIND_HDR(req_tdata->msg, CSEQ);
+    PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
+
+#   undef FIND_HDR
+
+    /* Create new request message from the headers. */
+    status = pjsip_endpt_create_request_from_hdr(endpt, 
+						 &pjsip_cancel_method,
+						 req_tdata->msg->line.req.uri,
+						 from_hdr, to_hdr,
+						 NULL, cid_hdr,
+						 cseq_hdr->cseq, NULL,
+						 &cancel_tdata);
+
+    if (status != PJ_SUCCESS)
 	return status;
-    }
-
-    /* Create CANCEL request message. */
-    cancel_msg = pjsip_msg_create(cancel_tdata->pool, PJSIP_REQUEST_MSG);
-    cancel_tdata->msg = cancel_msg;
-
-    /* Request-URI, Call-ID, From, To, and the numeric part of the CSeq are
-     * copied from the original request.
-     */
-    /* Set request line. */
-    pjsip_method_set(&cancel_msg->line.req.method, PJSIP_CANCEL_METHOD);
-    req_uri = req_msg->line.req.uri;
-    cancel_msg->line.req.uri = pjsip_uri_clone(cancel_tdata->pool, req_uri);
-
-    /* Copy Call-ID */
-    hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_CALL_ID, NULL);
-    pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
-
-    /* Copy From header. */
-    hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_FROM, NULL);
-    pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
-
-    /* Copy To header. */
-    hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_TO, NULL);
-    pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
-
-    /* Create new CSeq with equal number, but method set to CANCEL. */
-    req_cseq = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(req_msg, PJSIP_H_CSEQ, NULL);
-    cseq = pjsip_cseq_hdr_create(cancel_tdata->pool);
-    cseq->cseq = req_cseq->cseq;
-    pjsip_method_set(&cseq->method, PJSIP_CANCEL_METHOD);
-    pjsip_msg_add_hdr(cancel_msg, (pjsip_hdr*)cseq);
 
     /* Must only have single Via which matches the top-most Via in the 
      * request being cancelled. 
      */
-    hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, NULL);
-    pjsip_msg_insert_first_hdr(cancel_msg, 
-			       pjsip_hdr_clone(cancel_tdata->pool, hdr));
+    hdr = pjsip_msg_find_hdr(req_tdata->msg, PJSIP_H_VIA, NULL);
+    if (hdr) {
+	pjsip_msg_insert_first_hdr(cancel_tdata->msg, 
+				   pjsip_hdr_clone(cancel_tdata->pool, hdr));
+    }
 
     /* If the original request has Route header, the CANCEL request must also
@@ -654,10 +581,11 @@
      * Copy "Route" header from the request.
      */
-    hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, NULL);
+    hdr = pjsip_msg_find_hdr(req_tdata->msg, PJSIP_H_ROUTE, NULL);
     while (hdr != NULL) {
-	pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
+	pjsip_msg_add_hdr(cancel_tdata->msg, 
+			  pjsip_hdr_clone(cancel_tdata->pool, hdr));
 	hdr = hdr->next;
-	if (hdr != &cancel_msg->hdr)
-	    hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, hdr);
+	if (hdr != &cancel_tdata->msg->hdr)
+	    hdr = pjsip_msg_find_hdr(cancel_tdata->msg, PJSIP_H_ROUTE, hdr);
 	else
 	    break;
@@ -669,47 +597,587 @@
     *p_tdata = cancel_tdata;
     return PJ_SUCCESS;
-}
-
-/* Get the address parameters (host, port, flag, TTL, etc) to send the
- * response.
- */
-PJ_DEF(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool,
-					    const pjsip_transport *req_transport,
-					    const pjsip_via_hdr *via,
-					    pjsip_host_info *send_addr)
-{
-    /* Determine the destination address (section 18.2.2):
-     * - for TCP, SCTP, or TLS, send the response using the transport where
-     *   the request was received.
-     * - if maddr parameter is present, send to this address using the port
-     *   in sent-by or 5060. If multicast is used, the TTL in the Via must
-     *   be used, or 1 if ttl parameter is not present.
-     * - otherwise if received parameter is present, set to this address.
-     * - otherwise send to the address in sent-by.
+
+on_missing_hdr:
+    if (cancel_tdata)
+	pjsip_tx_data_dec_ref(cancel_tdata);
+    return PJSIP_EMISSINGHDR;
+}
+
+
+/*
+ * Find which destination to be used to send the request message, based
+ * on the request URI and Route headers in the message. The procedure
+ * used here follows the guidelines on sending the request in RFC 3261
+ * chapter 8.1.2.
+ */
+PJ_DEF(pj_status_t) pjsip_get_request_addr( pjsip_tx_data *tdata,
+					    pjsip_host_info *dest_info )
+{
+    const pjsip_uri *new_request_uri, *target_uri;
+    const pjsip_name_addr *topmost_route_uri;
+    pjsip_route_hdr *first_route_hdr, *last_route_hdr;
+    
+    PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG, 
+		     PJSIP_ENOTREQUESTMSG);
+    PJ_ASSERT_RETURN(dest_info != NULL, PJ_EINVAL);
+
+    /* 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.
      */
-    send_addr->flag = req_transport->flag;
-    send_addr->type = req_transport->key.type;
-
-    if (PJSIP_TRANSPORT_IS_RELIABLE(req_transport)) {
-	pj_strdup( pool, &send_addr->addr.host, 
-		   &req_transport->remote_name.host);
-	send_addr->addr.port = req_transport->remote_name.port;
-
+    last_route_hdr = first_route_hdr = 
+	pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL);
+    if (first_route_hdr) {
+	topmost_route_uri = &first_route_hdr->name_addr;
+	while (last_route_hdr->next != (void*)&tdata->msg->hdr) {
+	    pjsip_route_hdr *hdr;
+	    hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, 
+                                     last_route_hdr->next);
+	    if (!hdr)
+		break;
+	    last_route_hdr = hdr;
+	}
     } else {
-	/* Set the host part */
-	if (via->maddr_param.slen) {
-	    pj_strdup(pool, &send_addr->addr.host, &via->maddr_param);
-	} else if (via->recvd_param.slen) {
-	    pj_strdup(pool, &send_addr->addr.host, &via->recvd_param);
+	topmost_route_uri = NULL;
+    }
+
+    /* If Route headers exist, and the first element indicates loose-route,
+     * the URI is taken from the Request-URI, and we keep all existing Route
+     * headers intact.
+     * If Route headers exist, and the first element DOESN'T indicate loose
+     * route, the URI is taken from the first Route header, and remove the
+     * first Route header from the message.
+     * Otherwise if there's no Route headers, the URI is taken from the
+     * Request-URI.
+     */
+    if (topmost_route_uri) {
+	pj_bool_t has_lr_param;
+
+	if (PJSIP_URI_SCHEME_IS_SIP(topmost_route_uri) ||
+	    PJSIP_URI_SCHEME_IS_SIPS(topmost_route_uri))
+	{
+	    const pjsip_url *url = pjsip_uri_get_uri((void*)topmost_route_uri);
+	    has_lr_param = url->lr_param;
 	} else {
-	    pj_strdup(pool, &send_addr->addr.host, &via->sent_by.host);
-	}
-
-	/* Set the port */
-	send_addr->addr.port = via->sent_by.port;
-    }
-
+	    has_lr_param = 0;
+	}
+
+	if (has_lr_param) {
+	    new_request_uri = tdata->msg->line.req.uri;
+	    /* We shouldn't need to delete topmost Route if it has lr param.
+	     * But seems like it breaks some proxy implementation, so we
+	     * delete it anyway.
+	     */
+	    /*
+	    pj_list_erase(first_route_hdr);
+	    if (first_route_hdr == last_route_hdr)
+		last_route_hdr = NULL;
+	    */
+	} else {
+	    new_request_uri = pjsip_uri_get_uri((void*)topmost_route_uri);
+	    pj_list_erase(first_route_hdr);
+	    if (first_route_hdr == last_route_hdr)
+		last_route_hdr = NULL;
+	}
+
+	target_uri = (pjsip_uri*)topmost_route_uri;
+
+    } else {
+	target_uri = new_request_uri = tdata->msg->line.req.uri;
+    }
+
+    /* The target URI must be a SIP/SIPS URL so we can resolve it's address.
+     * Otherwise we're in trouble (i.e. there's no host part in tel: URL).
+     */
+    pj_memset(dest_info, 0, sizeof(*dest_info));
+
+    if (PJSIP_URI_SCHEME_IS_SIPS(target_uri)) {
+	pjsip_uri *uri = (pjsip_uri*) target_uri;
+	const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri);
+	dest_info->flag |= (PJSIP_TRANSPORT_SECURE | PJSIP_TRANSPORT_RELIABLE);
+	pj_strdup(tdata->pool, &dest_info->addr.host, &url->host);
+        dest_info->addr.port = url->port;
+	dest_info->type = 
+            pjsip_transport_get_type_from_name(&url->transport_param);
+
+    } else if (PJSIP_URI_SCHEME_IS_SIP(target_uri)) {
+	pjsip_uri *uri = (pjsip_uri*) target_uri;
+	const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri);
+	pj_strdup(tdata->pool, &dest_info->addr.host, &url->host);
+	dest_info->addr.port = url->port;
+	dest_info->type = 
+            pjsip_transport_get_type_from_name(&url->transport_param);
+#if PJ_HAS_TCP
+	if (dest_info->type == PJSIP_TRANSPORT_TCP || 
+	    dest_info->type == PJSIP_TRANSPORT_SCTP) 
+	{
+	    dest_info->flag |= PJSIP_TRANSPORT_RELIABLE;
+	}
+#endif
+    } else {
+        pj_assert(!"Unsupported URI scheme!");
+	PJ_TODO(SUPPORT_REQUEST_ADDR_RESOLUTION_FOR_TEL_URI);
+	return PJSIP_EINVALIDSCHEME;
+    }
+
+    /* If target URI is different than request URI, replace 
+     * request URI add put the original URI in the last Route header.
+     */
+    if (new_request_uri && new_request_uri!=tdata->msg->line.req.uri) {
+	pjsip_route_hdr *route = pjsip_route_hdr_create(tdata->pool);
+	route->name_addr.uri = tdata->msg->line.req.uri;
+	if (last_route_hdr)
+	    pj_list_insert_after(last_route_hdr, route);
+	else
+	    pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)route);
+	tdata->msg->line.req.uri = (pjsip_uri*)new_request_uri;
+    }
+
+    /* Success. */
+    return PJ_SUCCESS;  
+}
+
+
+/* Transport callback for sending stateless request. 
+ * This is one of the most bizzare function in pjsip, so
+ * good luck if you happen to debug this function!!
+ */
+static void stateless_send_transport_cb( void *token,
+					 pjsip_tx_data *tdata,
+					 pj_ssize_t sent )
+{
+    pjsip_send_state *stateless_data = token;
+
+    PJ_UNUSED_ARG(tdata);
+    pj_assert(tdata == stateless_data->tdata);
+
+    for (;;) {
+	pj_status_t status;
+	pj_bool_t cont;
+
+	pj_sockaddr_t *cur_addr;
+	pjsip_transport_type_e cur_addr_type;
+	int cur_addr_len;
+
+	pjsip_via_hdr *via;
+
+	if (sent == -PJ_EPENDING) {
+	    /* This is the initial process.
+	     * When the process started, this function will be called by
+	     * stateless_send_resolver_callback() with sent argument set to
+	     * -PJ_EPENDING.
+	     */
+	    cont = PJ_TRUE;
+	} else {
+	    /* There are two conditions here:
+	     * (1) Message is sent (i.e. sent > 0),
+	     * (2) Failure (i.e. sent <= 0)
+	     */
+	    cont = (sent > 0) ? PJ_FALSE :
+		   (stateless_data->cur_addr<stateless_data->addr.count-1);
+	    if (stateless_data->app_cb) {
+		(*stateless_data->app_cb)(stateless_data, sent, &cont);
+	    } else {
+		/* Doesn't have application callback.
+		 * Terminate the process.
+		 */
+		cont = PJ_FALSE;
+	    }
+	}
+
+	/* Finished with this transport. */
+	if (stateless_data->cur_transport) {
+	    pjsip_transport_dec_ref(stateless_data->cur_transport);
+	    stateless_data->cur_transport = NULL;
+	}
+
+	/* Done if application doesn't want to continue. */
+	if (sent > 0 || !cont) {
+	    pjsip_tx_data_dec_ref(tdata);
+	    return;
+	}
+
+	/* Try next address, if any, and only when this is not the 
+	 * first invocation. 
+	 */
+	if (sent != -PJ_EPENDING) {
+	    stateless_data->cur_addr++;
+	}
+
+	/* Have next address? */
+	if (stateless_data->cur_addr >= stateless_data->addr.count) {
+	    /* This only happens when a rather buggy application has
+	     * sent 'cont' to PJ_TRUE when the initial value was PJ_FALSE.
+	     * In this case just stop the processing; we don't need to
+	     * call the callback again as application has been informed
+	     * before.
+	     */
+	    pjsip_tx_data_dec_ref(tdata);
+	    return;
+	}
+
+	/* Keep current server address information handy. */
+	cur_addr = &stateless_data->addr.entry[stateless_data->cur_addr].addr;
+	cur_addr_type = stateless_data->addr.entry[stateless_data->cur_addr].type;
+	cur_addr_len = stateless_data->addr.entry[stateless_data->cur_addr].addr_len;
+
+	/* Acquire transport. */
+	status = pjsip_endpt_acquire_transport( stateless_data->endpt,
+						cur_addr_type,
+						cur_addr,
+						cur_addr_len,
+						&stateless_data->cur_transport);
+	if (status != PJ_SUCCESS) {
+	    sent = -status;
+	    continue;
+	}
+
+	/* Modify Via header. */
+	via = (pjsip_via_hdr*) pjsip_msg_find_hdr( tdata->msg,
+						   PJSIP_H_VIA, NULL);
+	if (!via) {
+	    /* Shouldn't happen if request was created with PJSIP API! 
+	     * But we handle the case anyway for robustness.
+	     */
+	    pj_assert(!"Via header not found!");
+	    via = pjsip_via_hdr_create(tdata->pool);
+	    pjsip_msg_insert_first_hdr(tdata->msg, (pjsip_hdr*)via);
+	}
+
+	if (via->branch_param.slen == 0) {
+	    pj_str_t tmp;
+	    via->branch_param.ptr = pj_pool_alloc(tdata->pool,
+						  PJSIP_MAX_BRANCH_LEN);
+	    via->branch_param.slen = PJSIP_MAX_BRANCH_LEN;
+	    pj_memcpy(via->branch_param.ptr, PJSIP_RFC3261_BRANCH_ID,
+		      PJSIP_RFC3261_BRANCH_LEN);
+	    tmp.ptr = via->branch_param.ptr + PJSIP_RFC3261_BRANCH_LEN;
+	    pj_generate_unique_string(&tmp);
+	}
+
+	via->transport = pj_str(stateless_data->cur_transport->type_name);
+	via->sent_by = stateless_data->cur_transport->local_name;
+	via->rport_param = 0;
+
+	/* Send message using this transport. */
+	status = pjsip_transport_send( stateless_data->cur_transport,
+				       tdata,
+				       cur_addr,
+				       cur_addr_len,
+				       stateless_data,
+				       &stateless_send_transport_cb);
+	if (status == PJ_SUCCESS) {
+	    /* Recursively call this function. */
+	    sent = tdata->buf.cur - tdata->buf.start;
+	    stateless_send_transport_cb( stateless_data, tdata, sent );
+	    return;
+	} else if (status == PJ_EPENDING) {
+	    /* This callback will be called later. */
+	    return;
+	} else {
+	    /* Recursively call this function. */
+	    sent = -status;
+	    stateless_send_transport_cb( stateless_data, tdata, sent );
+	    return;
+	}
+    }
+
+}
+
+/* Resolver callback for sending stateless request. */
+static void 
+stateless_send_resolver_callback( pj_status_t status,
+				  void *token,
+				  const struct pjsip_server_addresses *addr)
+{
+    pjsip_send_state *stateless_data = token;
+
+    /* Fail on server resolution. */
+    if (status != PJ_SUCCESS) {
+	if (stateless_data->app_cb) {
+	    pj_bool_t cont = PJ_FALSE;
+	    (*stateless_data->app_cb)(stateless_data, -status, &cont);
+	}
+	pjsip_tx_data_dec_ref(stateless_data->tdata);
+	return;
+    }
+
+    /* Copy server addresses */
+    pj_memcpy( &stateless_data->addr, addr, sizeof(pjsip_server_addresses));
+
+    /* Process the addresses. */
+    stateless_send_transport_cb( stateless_data, stateless_data->tdata,
+				 -PJ_EPENDING);
+}
+
+/*
+ * Send stateless request.
+ * The sending process consists of several stages:
+ *  - determine which host to contact (#pjsip_get_request_addr).
+ *  - resolve the host (#pjsip_endpt_resolve)
+ *  - establish transport (#pjsip_endpt_acquire_transport)
+ *  - send the message (#pjsip_transport_send)
+ */
+PJ_DEF(pj_status_t) 
+pjsip_endpt_send_request_stateless(pjsip_endpoint *endpt, 
+				   pjsip_tx_data *tdata,
+				   void *token,
+				   void (*cb)(pjsip_send_state*,
+					      pj_ssize_t sent,
+					      pj_bool_t *cont))
+{
+    pjsip_host_info dest_info;
+    pjsip_send_state *stateless_data;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(endpt && tdata, PJ_EINVAL);
+
+    /* Get destination name to contact. */
+    status = pjsip_get_request_addr(tdata, &dest_info);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Keep stateless data. */
+    stateless_data = pj_pool_zalloc(tdata->pool, sizeof(pjsip_send_state));
+    stateless_data->token = token;
+    stateless_data->endpt = endpt;
+    stateless_data->tdata = tdata;
+    stateless_data->app_cb = cb;
+
+    /* Resolve destination host.
+     * The processing then resumed when the resolving callback is called.
+     */
+    pjsip_endpt_resolve( endpt, tdata->pool, &dest_info, stateless_data,
+			 &stateless_send_resolver_callback);
     return PJ_SUCCESS;
 }
+
+/*
+ * Determine which address (and transport) to use to send response message
+ * based on the received request. This function follows the specification
+ * in section 18.2.2 of RFC 3261 and RFC 3581 for calculating the destination
+ * address and transport.
+ */
+PJ_DEF(pj_status_t) pjsip_get_response_addr( pj_pool_t *pool,
+					     pjsip_rx_data *rdata,
+					     pjsip_response_addr *res_addr )
+{
+    pjsip_transport *src_transport = rdata->tp_info.transport;
+
+    /* Check arguments. */
+    PJ_ASSERT_RETURN(pool && rdata && res_addr, PJ_EINVAL);
+
+    /* All requests must have "received" parameter.
+     * This must always be done in transport layer.
+     */
+    pj_assert(rdata->msg_info.via->recvd_param.slen != 0);
+
+    /* Do the calculation based on RFC 3261 Section 18.2.2 and RFC 3581 */
+
+    if (PJSIP_TRANSPORT_IS_RELIABLE(src_transport)) {
+	/* For reliable protocol such as TCP or SCTP, or TLS over those, the
+	 * response MUST be sent using the existing connection to the source
+	 * of the original request that created the transaction, if that 
+	 * connection is still open. 
+	 * If that connection is no longer open, the server SHOULD open a 
+	 * connection to the IP address in the received parameter, if present,
+	 * using the port in the sent-by value, or the default port for that 
+	 * transport, if no port is specified. 
+	 * If that connection attempt fails, the server SHOULD use the 
+	 * procedures in [4] for servers in order to determine the IP address
+	 * and port to open the connection and send the response to.
+	 */
+	res_addr->transport = rdata->tp_info.transport;
+	pj_memcpy(&res_addr->addr, &rdata->pkt_info.src_addr,
+		  rdata->pkt_info.src_addr_len);
+	res_addr->addr_len = rdata->pkt_info.src_addr_len;
+	res_addr->dst_host.type = src_transport->key.type;
+	res_addr->dst_host.flag = src_transport->flag;
+	pj_strdup( pool, &res_addr->dst_host.addr.host, 
+		   &rdata->msg_info.via->recvd_param);
+	res_addr->dst_host.addr.port = rdata->msg_info.via->sent_by.port;
+	if (res_addr->dst_host.addr.port == 0) {
+	    res_addr->dst_host.addr.port = 
+		pjsip_transport_get_default_port_for_type(res_addr->dst_host.type);
+	}
+
+    } else if (rdata->msg_info.via->maddr_param.slen) {
+	/* Otherwise, if the Via header field value contains a maddr parameter,
+	 * the response MUST be forwarded to the address listed there, using 
+	 * the port indicated in sent-by, or port 5060 if none is present. 
+	 * If the address is a multicast address, the response SHOULD be sent 
+	 * using the TTL indicated in the ttl parameter, or with a TTL of 1 if
+	 * that parameter is not present. 
+	 */
+	res_addr->transport = NULL;
+	res_addr->dst_host.type = src_transport->key.type;
+	res_addr->dst_host.flag = src_transport->flag;
+	pj_strdup( pool, &res_addr->dst_host.addr.host, 
+		   &rdata->msg_info.via->maddr_param);
+	res_addr->dst_host.addr.port = rdata->msg_info.via->sent_by.port;
+	if (res_addr->dst_host.addr.port == 0)
+	    res_addr->dst_host.addr.port = 5060;
+
+    } else if (rdata->msg_info.via->rport_param >= 0) {
+	/* There is both a "received" parameter and an "rport" parameter, 
+	 * the response MUST be sent to the IP address listed in the "received"
+	 * parameter, and the port in the "rport" parameter. 
+	 * The response MUST be sent from the same address and port that the 
+	 * corresponding request was received on.
+	 */
+	res_addr->transport = rdata->tp_info.transport;
+	pj_memcpy(&res_addr->addr, &rdata->pkt_info.src_addr,
+		  rdata->pkt_info.src_addr_len);
+	res_addr->addr_len = rdata->pkt_info.src_addr_len;
+	res_addr->dst_host.type = src_transport->key.type;
+	res_addr->dst_host.flag = src_transport->flag;
+	pj_strdup( pool, &res_addr->dst_host.addr.host, 
+		   &rdata->msg_info.via->recvd_param);
+	res_addr->dst_host.addr.port = rdata->msg_info.via->sent_by.port;
+	if (res_addr->dst_host.addr.port == 0) {
+	    res_addr->dst_host.addr.port = 
+		pjsip_transport_get_default_port_for_type(res_addr->dst_host.type);
+	}
+
+    } else {
+	res_addr->transport = NULL;
+	res_addr->dst_host.type = src_transport->key.type;
+	res_addr->dst_host.flag = src_transport->flag;
+	pj_strdup( pool, &res_addr->dst_host.addr.host, 
+		   &rdata->msg_info.via->recvd_param);
+	res_addr->dst_host.addr.port = rdata->msg_info.via->sent_by.port;
+	if (res_addr->dst_host.addr.port == 0) {
+	    res_addr->dst_host.addr.port = 
+		pjsip_transport_get_default_port_for_type(res_addr->dst_host.type);
+	}
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Callback called by transport during send_response.
+ */
+static void send_response_transport_cb(void *token, pjsip_tx_data *tdata,
+				       pj_ssize_t sent)
+{
+    pjsip_send_state *send_state = token;
+    pj_bool_t cont = PJ_FALSE;
+
+    /* Call callback, if any. */
+    if (send_state->app_cb)
+	(*send_state->app_cb)(send_state, sent, &cont);
+
+    /* Decrement transport reference counter. */
+    pjsip_transport_dec_ref(send_state->cur_transport);
+
+    /* Decrement transmit data ref counter. */
+    pjsip_tx_data_dec_ref(tdata);
+}
+
+/*
+ * Resolver calback during send_response.
+ */
+static void send_response_resolver_cb( pj_status_t status, void *token,
+				       const pjsip_server_addresses *addr )
+{
+    pjsip_send_state *send_state = token;
+
+    if (status != PJ_SUCCESS) {
+	if (send_state->app_cb) {
+	    pj_bool_t cont = PJ_FALSE;
+	    (*send_state->app_cb)(send_state, -status, &cont);
+	}
+	pjsip_tx_data_dec_ref(send_state->tdata);
+	return;
+    }
+
+    /* Only handle the first address resolved. */
+
+    /* Acquire transport. */
+    status = pjsip_endpt_acquire_transport( send_state->endpt, 
+					    addr->entry[0].type,
+					    &addr->entry[0].addr,
+					    addr->entry[0].addr_len,
+					    &send_state->cur_transport);
+    if (status != PJ_SUCCESS) {
+	if (send_state->app_cb) {
+	    pj_bool_t cont = PJ_FALSE;
+	    (*send_state->app_cb)(send_state, -status, &cont);
+	}
+	pjsip_tx_data_dec_ref(send_state->tdata);
+	return;
+    }
+
+    /* Send response using the transoprt. */
+    status = pjsip_transport_send( send_state->cur_transport, 
+				   send_state->tdata,
+				   &addr->entry[0].addr,
+				   addr->entry[0].addr_len,
+				   send_state,
+				   &send_response_transport_cb);
+    if (status == PJ_SUCCESS) {
+	pj_ssize_t sent = send_state->tdata->buf.cur - 
+			  send_state->tdata->buf.start;
+	send_response_transport_cb(send_state, send_state->tdata, sent);
+
+    } else if (status == PJ_EPENDING) {
+	/* Transport callback will be called later. */
+    } else {
+	send_response_transport_cb(send_state, send_state->tdata, -status);
+    }
+}
+
+/*
+ * Send response.
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_send_response( pjsip_endpoint *endpt,
+					       pjsip_response_addr *res_addr,
+					       pjsip_tx_data *tdata,
+					       void *token,
+					       void (*cb)(pjsip_send_state*,
+							  pj_ssize_t sent,
+							  pj_bool_t *cont))
+{
+    /* Determine which transports and addresses to send the response,
+     * based on Section 18.2.2 of RFC 3261.
+     */
+    pjsip_send_state *send_state;
+    pj_status_t status;
+
+    /* Create structure to keep the sending state. */
+    send_state = pj_pool_zalloc(tdata->pool, sizeof(pjsip_send_state));
+    send_state->endpt = endpt;
+    send_state->tdata = tdata;
+    send_state->token = token;
+    send_state->app_cb = cb;
+
+    if (res_addr->transport != NULL) {
+	send_state->cur_transport = res_addr->transport;
+	pjsip_transport_add_ref(send_state->cur_transport);
+
+	status = pjsip_transport_send( send_state->cur_transport, tdata, 
+				       &res_addr->addr,
+				       res_addr->addr_len,
+				       send_state,
+				       &send_response_transport_cb );
+	if (status == PJ_SUCCESS) {
+	    pj_ssize_t sent = tdata->buf.cur - tdata->buf.start;
+	    send_response_transport_cb(send_state, tdata, sent);
+	    return PJ_SUCCESS;
+	} else if (status == PJ_EPENDING) {
+	    /* Callback will be called later. */
+	    return PJ_SUCCESS;
+	} else {
+	    send_response_transport_cb(send_state, tdata, -status);
+	    return status;
+	}
+    } else {
+	pjsip_endpt_resolve(endpt, tdata->pool, &res_addr->dst_host, 
+			    send_state, &send_response_resolver_cb);
+	return PJ_SUCCESS;
+    }
+}
+
 
 /*
Index: /pjproject/trunk/pjsip/src/pjsip/sip_util_statefull.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_util_statefull.c	(revision 106)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_util_statefull.c	(revision 106)
@@ -0,0 +1,108 @@
+/* $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_module.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_event.h>
+#include <pj/pool.h>
+
+struct aux_tsx_data
+{
+    void *token;
+    void (*cb)(void*,pjsip_event*);
+};
+
+static void aux_tsx_handler( pjsip_transaction *tsx, pjsip_event *event );
+
+pjsip_module aux_tsx_module = 
+{
+    NULL, NULL,				/* prev and next	*/
+    { "Aux-Tsx", 7},			/* 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()	*/
+    &aux_tsx_handler,			/* tsx_handler()	*/
+};
+
+static void aux_tsx_handler( pjsip_transaction *tsx, pjsip_event *event )
+{
+    struct aux_tsx_data *tsx_data;
+
+    if (event->type != PJSIP_EVENT_TSX_STATE)
+	return;
+    if (tsx->module_data[aux_tsx_module.id] == NULL)
+	return;
+    if (tsx->status_code < 200)
+	return;
+
+    /* Call the callback, if any, and prevent the callback to be called again
+     * by clearing the transaction's module_data.
+     */
+    tsx_data = tsx->module_data[aux_tsx_module.id];
+    tsx->module_data[aux_tsx_module.id] = NULL;
+
+    if (tsx_data->cb) {
+	(*tsx_data->cb)(tsx_data->token, event);
+    }
+}
+
+
+PJ_DEF(pj_status_t) pjsip_endpt_send_request(  pjsip_endpoint *endpt,
+					       pjsip_tx_data *tdata,
+					       int timeout,
+					       void *token,
+					       void (*cb)(void*,pjsip_event*))
+{
+    pjsip_transaction *tsx;
+    struct aux_tsx_data *tsx_data;
+    pj_status_t status;
+
+    status = pjsip_endpt_create_tsx(endpt, &tsx);
+    if (!tsx) {
+	pjsip_tx_data_dec_ref(tdata);
+	return -1;
+    }
+
+    tsx_data = pj_pool_alloc(tsx->pool, sizeof(struct aux_tsx_data));
+    tsx_data->token = token;
+    tsx_data->cb = cb;
+    tsx->module_data[aux_tsx_module.id] = tsx_data;
+
+    if (pjsip_tsx_init_uac(tsx, tdata) != 0) {
+	pjsip_endpt_destroy_tsx(endpt, tsx);
+	pjsip_tx_data_dec_ref(tdata);
+	return -1;
+    }
+
+    pjsip_endpt_register_tsx(endpt, tsx);
+    pjsip_tx_data_invalidate_msg(tdata);
+    pjsip_tsx_on_tx_msg(tsx, tdata);
+    pjsip_tx_data_dec_ref(tdata);
+    return 0;
+}
+
Index: /pjproject/trunk/pjsip/src/test-pjsip/main.c
===================================================================
--- /pjproject/trunk/pjsip/src/test-pjsip/main.c	(revision 105)
+++ /pjproject/trunk/pjsip/src/test-pjsip/main.c	(revision 106)
@@ -18,7 +18,16 @@
  */
 #include "test.h"
+#include <stdio.h>
 
-int main(void)
+int main(int argc, char *argv[])
 {
-    return test_main();
+    int retval = test_main();
+
+    if (argc != 1) {
+	char s[10];
+	printf("<Press ENTER to quit>\n");
+	fgets(s, sizeof(s), stdin);
+    }
+
+    return retval;
 }
Index: /pjproject/trunk/pjsip/src/test-pjsip/msg_test.c
===================================================================
--- /pjproject/trunk/pjsip/src/test-pjsip/msg_test.c	(revision 105)
+++ /pjproject/trunk/pjsip/src/test-pjsip/msg_test.c	(revision 106)
@@ -665,5 +665,5 @@
 /*****************************************************************************/
 
-pj_status_t msg_test(void)
+int msg_test(void)
 {
     pj_status_t status;
Index: /pjproject/trunk/pjsip/src/test-pjsip/test.c
===================================================================
--- /pjproject/trunk/pjsip/src/test-pjsip/test.c	(revision 105)
+++ /pjproject/trunk/pjsip/src/test-pjsip/test.c	(revision 106)
@@ -84,4 +84,6 @@
     DO_TEST(uri_test());
     DO_TEST(msg_test());
+    DO_TEST(txdata_test());
+    DO_TEST(transport_udp_test());
 
 on_return:
Index: /pjproject/trunk/pjsip/src/test-pjsip/test.h
===================================================================
--- /pjproject/trunk/pjsip/src/test-pjsip/test.h	(revision 105)
+++ /pjproject/trunk/pjsip/src/test-pjsip/test.h	(revision 106)
@@ -24,8 +24,25 @@
 extern pjsip_endpoint *endpt;
 
-pj_status_t uri_test(void);
-pj_status_t msg_test(void);
+#define TEST_UDP_PORT	15060
 
+/* The tests */
+int uri_test(void);
+int msg_test(void);
+int txdata_test(void);
+int transport_udp_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 );
+int transport_rt_test( pjsip_transport_type_e tp_type,
+		       pjsip_transport *ref_tp,
+		       const pj_sockaddr_in *rem_addr );
+
+/* Test main entry */
 int  test_main(void);
+
+/* Test utilities. */
 void app_perror(const char *msg, pj_status_t status);
 
Index: /pjproject/trunk/pjsip/src/test-pjsip/transport_test.c
===================================================================
--- /pjproject/trunk/pjsip/src/test-pjsip/transport_test.c	(revision 106)
+++ /pjproject/trunk/pjsip/src/test-pjsip/transport_test.c	(revision 106)
@@ -0,0 +1,561 @@
+/* $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>
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Generic testing for transport, to make sure that basic
+ * attributes have been initialized properly.
+ */
+int generic_transport_test(pjsip_transport *tp)
+{
+    PJ_LOG(3,("", "  structure test..."));
+
+    /* Check that local address name is valid. */
+    {
+	struct pj_in_addr addr;
+
+	/* Note: inet_aton() returns non-zero if addr is valid! */
+	if (pj_inet_aton(&tp->local_name.host, &addr) != 0) {
+	    if (addr.s_addr==PJ_INADDR_ANY || addr.s_addr==PJ_INADDR_NONE) {
+		PJ_LOG(3,("", "   Error: invalid address name"));
+		return -420;
+	    }
+	} else {
+	    /* It's okay. local_name.host may be a hostname instead of
+	     * IP address.
+	     */
+	}
+    }
+
+    /* Check that port is valid. */
+    if (tp->local_name.port <= 0) {
+	return -430;
+    }
+
+    /* Check length of address (for now we only check against sockaddr_in). */
+    if (tp->addr_len != sizeof(pj_sockaddr_in))
+	return -440;
+
+    /* Check type. */
+    if (tp->key.type == PJSIP_TRANSPORT_UNSPECIFIED)
+	return -450;
+
+    /* That's it. */
+    return PJ_SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/* 
+ * Send/receive test.
+ *
+ * This test sends a request to loopback address; as soon as request is 
+ * received, response will be sent, and time is recorded.
+ *
+ * The main purpose is to test that the basic transport functionalities works,
+ * 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
+#define BODY	    "Hello World!"
+
+static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata);
+static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata);
+
+/* Flag to indicate message has been received
+ * (or failed to send)
+ */
+#define NO_STATUS   -2
+static int send_status = NO_STATUS;
+static int recv_status = NO_STATUS;
+static pj_timestamp my_send_time, my_recv_time;
+
+/* Module to receive messages for this test. */
+static pjsip_module my_module = 
+{
+    NULL, NULL,				/* prev and next	*/
+    { "Transport-Test", 14},		/* Name.		*/
+    -1,					/* Id			*/
+    PJSIP_MOD_PRIORITY_TSX_LAYER-1,	/* Priority		*/
+    NULL,				/* User data.		*/
+    0,					/* Number of methods supported (=0). */
+    { 0 },				/* Array of methods (none) */
+    NULL,				/* load()		*/
+    NULL,				/* start()		*/
+    NULL,				/* stop()		*/
+    NULL,				/* unload()		*/
+    &my_on_rx_request,			/* on_rx_request()	*/
+    &my_on_rx_response,			/* on_rx_response()	*/
+    NULL,				/* tsx_handler()	*/
+};
+
+
+static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata)
+{
+    /* Check that this is our request. */
+    if (pj_strcmp2(&rdata->msg_info.call_id, CALL_ID_HDR) == 0) {
+	/* It is! */
+	/* Send response. */
+	pjsip_tx_data *tdata;
+	pjsip_response_addr res_addr;
+	pj_status_t status;
+
+	PJ_LOG(4,("test", "Received %d bytes request: --begin-\n"
+			  "%s\n"
+			  "--end--",
+			  rdata->msg_info.len,
+			  rdata->msg_info.msg_buf));
+
+
+	status = pjsip_endpt_create_response( endpt, rdata, 200, NULL, &tdata);
+	if (status != PJ_SUCCESS) {
+	    recv_status = status;
+	    return PJ_TRUE;
+	}
+	status = pjsip_get_response_addr( tdata->pool, rdata, &res_addr);
+	if (status != PJ_SUCCESS) {
+	    recv_status = 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) {
+	    recv_status = status;
+	    pjsip_tx_data_dec_ref(tdata);
+	    return PJ_TRUE;
+	}
+	return PJ_TRUE;
+    }
+    
+    /* Not ours. */
+    return PJ_FALSE;
+}
+
+static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata)
+{
+    if (pj_strcmp2(&rdata->msg_info.call_id, CALL_ID_HDR) == 0) {
+	PJ_LOG(4,("test", "Received %d bytes response: --begin-\n"
+			  "%s\n"
+			  "--end--",
+			  rdata->msg_info.len,
+			  rdata->msg_info.msg_buf));
+
+	pj_get_timestamp(&my_recv_time);
+	recv_status = PJ_SUCCESS;
+	return PJ_TRUE;
+    }
+    return PJ_FALSE;
+}
+
+/* Transport callback. */
+static void send_msg_callback(pjsip_send_state *stateless_data,
+			      pj_ssize_t sent, pj_bool_t *cont)
+{
+    if (sent < 1) {
+	/* Obtain the error code. */
+	send_status = -sent;
+    } else {
+	send_status = PJ_SUCCESS;
+    }
+
+    /* Don't want to continue. */
+    *cont = PJ_FALSE;
+}
+
+
+/* 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 )
+{
+    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;
+    pj_time_val timeout;
+
+    PJ_LOG(3,("", "  single message round-trip test..."));
+
+    /* Register out test module to receive the message (if necessary). */
+    if (my_module.id == -1) {
+	status = pjsip_endpt_register_module( endpt, &my_module );
+	if (status != PJ_SUCCESS) {
+	    app_perror("   error: unable to register module", status);
+	    return -500;
+	}
+    }
+
+    /* 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);
+    from = pj_str(FROM_HDR);
+    to = pj_str(TO_HDR);
+    contact = pj_str(CONTACT_HDR);
+    call_id = pj_str(CALL_ID_HDR);
+    body = pj_str(BODY);
+
+    pjsip_method_set(&method, PJSIP_OPTIONS_METHOD);
+    status = pjsip_endpt_create_request( endpt, &method, &target, &from, &to,
+					 &contact, &call_id, CSEQ_VALUE, 
+					 &body, &tdata );
+    if (status != PJ_SUCCESS) {
+	app_perror("   error: unable to create request", status);
+	return -510;
+    }
+
+    /* Reset statuses */
+    send_status = recv_status = NO_STATUS;
+
+    /* Start time. */
+    pj_get_timestamp(&my_send_time);
+
+    /* Send the message (statelessly). */
+    status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL,
+					         &send_msg_callback);
+    if (status != PJ_SUCCESS) {
+	/* Immediate error! */
+	pjsip_tx_data_dec_ref(tdata);
+	send_status = status;
+    }
+
+    /* Set the timeout (1 second from now) */
+    pj_gettimeofday(&timeout);
+    timeout.sec += 1;
+
+    /* Loop handling events until we get status */
+    do {
+	pj_time_val now;
+	pj_time_val poll_interval = { 0, 10 };
+
+	pj_gettimeofday(&now);
+	if (PJ_TIME_VAL_GTE(now, timeout)) {
+	    PJ_LOG(3,("", "   error: timeout in send/recv test"));
+	    status = -540;
+	    goto on_return;
+	}
+
+	if (send_status!=NO_STATUS && send_status!=PJ_SUCCESS) {
+	    app_perror("   error sending message", send_status);
+	    status = -550;
+	    goto on_return;
+	}
+
+	if (recv_status!=NO_STATUS && recv_status!=PJ_SUCCESS) {
+	    app_perror("   error receiving message", recv_status);
+	    status = -560;
+	    goto on_return;
+	}
+
+	if (send_status!=NO_STATUS && recv_status!=NO_STATUS) {
+	    /* Success! */
+	    break;
+	}
+
+	pjsip_endpt_handle_events(endpt, &poll_interval);
+
+    } while (1);
+
+    if (status == PJ_SUCCESS) {
+	unsigned usec_rt;
+	usec_rt = pj_elapsed_usec(&my_send_time, &my_recv_time);
+	PJ_LOG(3,("", "    round-trip = %d usec", usec_rt));
+    }
+
+    status = PJ_SUCCESS;
+
+on_return:
+    return status;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/* 
+ * Multithreaded round-trip test
+ *
+ * This test will spawn multiple threads, each of them send a request. As soon
+ * as request is received, response will be sent, and time is recorded.
+ *
+ * The main purpose of this test is to ensure there's no crash when multiple
+ * threads are sending/receiving messages.
+ *
+ */
+static pj_bool_t rt_on_rx_request(pjsip_rx_data *rdata);
+static pj_bool_t rt_on_rx_response(pjsip_rx_data *rdata);
+
+static pjsip_module rt_module = 
+{
+    NULL, NULL,				/* prev and next	*/
+    { "Transport-RT-Test", 17},		/* Name.		*/
+    -1,					/* Id			*/
+    PJSIP_MOD_PRIORITY_TSX_LAYER-1,	/* Priority		*/
+    NULL,				/* User data.		*/
+    0,					/* Number of methods supported (=0). */
+    { 0 },				/* Array of methods (none) */
+    NULL,				/* load()		*/
+    NULL,				/* start()		*/
+    NULL,				/* stop()		*/
+    NULL,				/* unload()		*/
+    &rt_on_rx_request,			/* on_rx_request()	*/
+    &rt_on_rx_response,			/* on_rx_response()	*/
+    NULL,				/* tsx_handler()	*/
+};
+
+static struct
+{
+    pj_thread_t *thread;
+    pj_timestamp send_time;
+    pj_timestamp total_rt_time;
+    int sent_request_count, recv_response_count;
+    pj_str_t call_id;
+} rt_test_data[16];
+
+static char	 rt_target_uri[32];
+static pj_bool_t rt_stop;
+static pj_str_t  rt_call_id;
+
+static pj_bool_t rt_on_rx_request(pjsip_rx_data *rdata)
+{
+    if (!pj_strncmp(&rdata->msg_info.call_id, &rt_call_id, rt_call_id.slen)) {
+	char *pos = pj_strchr(&rdata->msg_info.call_id, '/');
+	int thread_id = (*pos - '0');
+
+	pjsip_tx_data *tdata;
+	pjsip_response_addr res_addr;
+	pj_status_t status;
+
+	status = pjsip_endpt_create_response( endpt, rdata, 200, NULL, &tdata);
+	if (status != PJ_SUCCESS) {
+	    return PJ_TRUE;
+	}
+	status = pjsip_get_response_addr( tdata->pool, rdata, &res_addr);
+	if (status != PJ_SUCCESS) {
+	    pjsip_tx_data_dec_ref(tdata);
+	    return PJ_TRUE;
+	}
+	status = pjsip_endpt_send_response( endpt, &res_addr, tdata, NULL, NULL);
+	if (status != PJ_SUCCESS) {
+	    pjsip_tx_data_dec_ref(tdata);
+	    return PJ_TRUE;
+	}
+	return PJ_TRUE;
+	
+    }
+    return PJ_FALSE;
+}
+
+static pj_status_t rt_send_request(int thread_id)
+{
+    pj_status_t status;
+    pj_str_t target, from, to, contact, call_id;
+    pjsip_tx_data *tdata;
+
+    /* Create a request message. */
+    target = pj_str(rt_target_uri);
+    from = pj_str(FROM_HDR);
+    to = pj_str(TO_HDR);
+    contact = pj_str(CONTACT_HDR);
+    call_id = rt_test_data[thread_id].call_id;
+
+    status = pjsip_endpt_create_request( endpt, &pjsip_options_method, 
+					 &target, &from, &to,
+					 &contact, &call_id, -1, 
+					 NULL, &tdata );
+    if (status != PJ_SUCCESS) {
+	app_perror("   error: unable to create request", status);
+	return -610;
+    }
+
+    /* Start time. */
+    pj_get_timestamp(&rt_test_data[thread_id].send_time);
+
+    /* Send the message (statelessly). */
+    status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL, NULL);
+    if (status != PJ_SUCCESS) {
+	/* Immediate error! */
+	app_perror("   error: send request", status);
+	pjsip_tx_data_dec_ref(tdata);
+	return -620;
+    }
+
+    /* Update counter. */
+    rt_test_data[thread_id].sent_request_count++;
+
+    return PJ_SUCCESS;
+}
+
+static pj_bool_t rt_on_rx_response(pjsip_rx_data *rdata)
+{
+    if (!pj_strncmp(&rdata->msg_info.call_id, &rt_call_id, rt_call_id.slen)) {
+	char *pos = pj_strchr(&rdata->msg_info.call_id, '/')+1;
+	int thread_id = (*pos - '0');
+	pj_timestamp recv_time;
+
+	/* Update counter and end-time. */
+	rt_test_data[thread_id].recv_response_count++;
+	pj_get_timestamp(&recv_time);
+
+	pj_sub_timestamp(&recv_time, &rt_test_data[thread_id].send_time);
+	pj_add_timestamp(&rt_test_data[thread_id].total_rt_time, &recv_time);
+
+	if (!rt_stop)
+	    rt_send_request(thread_id);
+	return PJ_TRUE;
+    }
+    return PJ_FALSE;
+}
+
+static int rt_thread(void *arg)
+{
+    int thread_id = (int)arg;
+    pj_time_val poll_delay = { 0, 10 };
+
+    /* Sleep to allow main threads to run. */
+    pj_thread_sleep(10);
+
+    /* Send the first request. */
+    if (rt_send_request(thread_id) != PJ_SUCCESS)
+	return -1;
+
+    while (!rt_stop) {
+	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 )
+{
+    enum { THREADS = 4, INTERVAL = 10 };
+    int i;
+    pj_status_t status;
+    pj_pool_t *pool;
+    pj_bool_t is_reliable;
+
+    pj_timestamp zero_time, total_time;
+    unsigned usec_rt;
+    unsigned total_sent;
+    unsigned total_recv;
+
+
+    PJ_LOG(3,("", "  multithreaded round-trip test (%d threads)...",
+		  THREADS));
+    PJ_LOG(3,("", "    this will take approx %d seconds, please wait..", INTERVAL));
+
+    is_reliable = (pjsip_transport_get_flag_from_type(tp_type) & PJSIP_TRANSPORT_RELIABLE);
+
+    /* Register module (if not yet registered) */
+    if (rt_module.id == -1) {
+	status = pjsip_endpt_register_module( endpt, &rt_module );
+	if (status != PJ_SUCCESS) {
+	    app_perror("   error: unable to register module", status);
+	    return -600;
+	}
+    }
+
+    /* Create pool for this test. */
+    pool = pjsip_endpt_create_pool(endpt, NULL, 4000, 4000);
+    if (!pool)
+	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));
+    rt_call_id = pj_str("RT-Call-Id/");
+    rt_stop = PJ_FALSE;
+
+    /* Initialize thread data. */
+    for (i=0; i<THREADS; ++i) {
+	char buf[1];
+	pj_str_t str_id = { buf, 1 };
+
+	pj_memset(&rt_test_data[i], 0, sizeof(rt_test_data[i]));
+
+	/* Generate Call-ID for each thread. */
+	rt_test_data[i].call_id.ptr = pj_pool_alloc(pool, rt_call_id.slen+1);
+	pj_strcpy(&rt_test_data[i].call_id, &rt_call_id);
+	buf[0] = '0' + i;
+	pj_strcat(&rt_test_data[i].call_id, &str_id);
+
+	/* Create thread, suspended. */
+	status = pj_thread_create(pool, "rttest%p", &rt_thread, (void*)i, 0,
+				  PJ_THREAD_SUSPENDED, &rt_test_data[i].thread);
+	if (status != PJ_SUCCESS) {
+	    app_perror("   error: unable to create thread", status);
+	    return -620;
+	}
+    }
+
+    /* Start threads! */
+    for (i=0; i<THREADS; ++i) {
+	pj_thread_resume(rt_test_data[i].thread);
+    }
+
+    /* Sleep for some time. */
+    pj_thread_sleep(INTERVAL * 1000);
+
+    /* Signal thread to stop. */
+    rt_stop = PJ_TRUE;
+
+    /* Wait threads to complete. */
+    for (i=0; i<THREADS; ++i) {
+	pj_thread_join(rt_test_data[i].thread);
+	pj_thread_destroy(rt_test_data[i].thread);
+    }
+
+    /* Gather statistics. */
+    pj_memset(&total_time, 0, sizeof(total_time));
+    pj_memset(&zero_time, 0, sizeof(zero_time));
+    usec_rt = total_sent = total_recv = 0;
+    for (i=0; i<THREADS; ++i) {
+	total_sent += rt_test_data[i].sent_request_count;
+	total_recv +=  rt_test_data[i].recv_response_count;
+	pj_add_timestamp(&total_time, &rt_test_data[i].total_rt_time);
+    }
+
+    /* Display statistics. */
+    if (total_recv)
+	total_time.u64 = total_time.u64/total_recv;
+    else
+	total_time.u64 = 0;
+    usec_rt = pj_elapsed_usec(&zero_time, &total_time);
+    PJ_LOG(3,("", "    done."));
+    PJ_LOG(3,("", "    total %d messages sent", total_sent));
+    if (total_sent-total_recv)
+	PJ_LOG(2,("", "    total %d messages LOST", total_sent-total_recv));
+    else
+	PJ_LOG(3,("", "    no message was lost"));
+    PJ_LOG(3,("", "    average round-trip=%d usec", usec_rt));
+
+    pjsip_endpt_destroy_pool(endpt, pool);
+
+    if (is_reliable && (total_sent != total_recv)) {
+	PJ_LOG(3,("", "   error: %d messages lost", total_sent-total_recv));
+	return -650;
+    }
+    return 0;
+}
Index: /pjproject/trunk/pjsip/src/test-pjsip/transport_udp_test.c
===================================================================
--- /pjproject/trunk/pjsip/src/test-pjsip/transport_udp_test.c	(revision 106)
+++ /pjproject/trunk/pjsip/src/test-pjsip/transport_udp_test.c	(revision 106)
@@ -0,0 +1,98 @@
+/* $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>
+
+
+/*
+ * UDP transport test.
+ */
+int transport_udp_test(void)
+{
+    enum { SEND_RECV_LOOP = 2 };
+    pjsip_transport *udp_tp, *tp;
+    pj_sockaddr_in addr, rem_addr;
+    pj_str_t s;
+    pj_status_t status;
+    int i;
+
+    pj_sockaddr_in_init(&addr, NULL, TEST_UDP_PORT);
+
+    /* Start UDP transport. */
+    status = pjsip_udp_transport_start( endpt, &addr, NULL, 1, &udp_tp);
+    if (status != PJ_SUCCESS) {
+	app_perror("   Error: unable to start UDP transport", status);
+	return -10;
+    }
+
+    /* UDP transport must have initial reference counter set to 1. */
+    if (pj_atomic_get(udp_tp->ref_cnt) != 1)
+	return -20;
+
+    /* Test basic transport attributes */
+    status = generic_transport_test(udp_tp);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Test that transport manager is returning the correct
+     * transport.
+     */
+    pj_sockaddr_in_init(&rem_addr, pj_cstr(&s, "1.1.1.1"), 80);
+    status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_UDP, 
+					   &rem_addr, sizeof(rem_addr),
+					   &tp);
+    if (status != PJ_SUCCESS)
+	return -50;
+    if (tp != udp_tp)
+	return -60;
+
+    /* pjsip_endpt_acquire_transport() adds reference, so we need
+     * to decrement it.
+     */
+    pjsip_transport_dec_ref(tp);
+
+    /* Check again that reference counter is 1. */
+    if (pj_atomic_get(udp_tp->ref_cnt) != 1)
+	return -70;
+
+    /* 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);
+	if (status != 0)
+	    return status;
+    }
+
+    /* Multi-threaded round-trip test. */
+    status = transport_rt_test(PJSIP_TRANSPORT_UDP, tp, &rem_addr);
+    if (status != 0)
+	return status;
+
+    /* Check again that reference counter is 1. */
+    if (pj_atomic_get(udp_tp->ref_cnt) != 1)
+	return -80;
+
+    /* Destroy this transport. */
+    pjsip_transport_dec_ref(udp_tp);
+
+    /* Done */
+    return 0;
+}
Index: /pjproject/trunk/pjsip/src/test-pjsip/txdata_test.c
===================================================================
--- /pjproject/trunk/pjsip/src/test-pjsip/txdata_test.c	(revision 106)
+++ /pjproject/trunk/pjsip/src/test-pjsip/txdata_test.c	(revision 106)
@@ -0,0 +1,320 @@
+/* $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>
+
+#define HFIND(msg,h,H) ((pjsip_##h##_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_##H, NULL))
+
+/*
+ * This tests various core message creation functions. 
+ */
+int txdata_test(void)
+{
+    pj_status_t status;
+    pj_str_t target, from, to, contact, body;
+    pjsip_rx_data dummy_rdata;
+    pjsip_tx_data *invite, *invite2, *cancel, *response, *ack;
+
+    /* Create INVITE request. */
+    target = pj_str("tel:+1");
+    from = pj_str("tel:+0");
+    to = pj_str("tel:+1");
+    contact = pj_str("Bob <sip:+0@example.com;user=phone>");
+    body = pj_str("Hello world!");
+
+    status = pjsip_endpt_create_request( endpt, &pjsip_invite_method, &target,
+					 &from, &to, &contact, NULL, 10, &body,
+					 &invite);
+    if (status != PJ_SUCCESS) {
+	app_perror("   error: unable to create request", status);
+	return -10;
+    }
+
+    /* Buffer must be invalid. */
+    if (pjsip_tx_data_is_valid(invite) != 0) {
+	PJ_LOG(3,("", "   error: buffer must be invalid"));
+	return -14;
+    }
+    /* Reference counter must be set to 1. */
+    if (pj_atomic_get(invite->ref_cnt) != 1) {
+	PJ_LOG(3,("", "   error: invalid reference counter"));
+	return -15;
+    }
+    /* Check message type. */
+    if (invite->msg->type != PJSIP_REQUEST_MSG)
+	return -16;
+    /* Check method. */
+    if (invite->msg->line.req.method.id != PJSIP_INVITE_METHOD)
+	return -17;
+
+    /* Check that mandatory headers are present. */
+    if (HFIND(invite->msg, from, FROM) == 0)
+	return -20;
+    if (HFIND(invite->msg, to, TO) == 0)
+	return -21;
+    if (HFIND(invite->msg, contact, CONTACT) == 0)
+	return -22;
+    if (HFIND(invite->msg, cid, CALL_ID) == 0)
+	return -23;
+    if (HFIND(invite->msg, cseq, CSEQ) == 0)
+	return -24;
+    do {
+	pjsip_via_hdr *via = HFIND(invite->msg, via, VIA);
+	if (via == NULL)
+	    return -25;
+	/* Branch param must be empty. */
+	if (via->branch_param.slen != 0)
+	    return -26;
+    } while (0);
+    if (invite->msg->body == NULL)
+	return -28;
+
+    /* Create another INVITE request from first request. */
+    status = pjsip_endpt_create_request_from_hdr( endpt, &pjsip_invite_method,
+						  invite->msg->line.req.uri,
+						  HFIND(invite->msg,from,FROM),
+						  HFIND(invite->msg,to,TO),
+						  HFIND(invite->msg,contact,CONTACT),
+						  HFIND(invite->msg,cid,CALL_ID),
+						  10, &body, &invite2);
+    if (status != PJ_SUCCESS) {
+	app_perror("   error: create second request failed", status);
+	return -30;
+    }
+    
+    /* Buffer must be invalid. */
+    if (pjsip_tx_data_is_valid(invite2) != 0) {
+	PJ_LOG(3,("", "   error: buffer must be invalid"));
+	return -34;
+    }
+    /* Reference counter must be set to 1. */
+    if (pj_atomic_get(invite2->ref_cnt) != 1) {
+	PJ_LOG(3,("", "   error: invalid reference counter"));
+	return -35;
+    }
+    /* Check message type. */
+    if (invite2->msg->type != PJSIP_REQUEST_MSG)
+	return -36;
+    /* Check method. */
+    if (invite2->msg->line.req.method.id != PJSIP_INVITE_METHOD)
+	return -37;
+
+    /* Check that mandatory headers are again present. */
+    if (HFIND(invite2->msg, from, FROM) == 0)
+	return -40;
+    if (HFIND(invite2->msg, to, TO) == 0)
+	return -41;
+    if (HFIND(invite2->msg, contact, CONTACT) == 0)
+	return -42;
+    if (HFIND(invite2->msg, cid, CALL_ID) == 0)
+	return -43;
+    if (HFIND(invite2->msg, cseq, CSEQ) == 0)
+	return -44;
+    if (HFIND(invite2->msg, via, VIA) == 0)
+	return -45;
+    /*
+    if (HFIND(invite2->msg, ctype, CONTENT_TYPE) == 0)
+	return -46;
+    if (HFIND(invite2->msg, clen, CONTENT_LENGTH) == 0)
+	return -47;
+    */
+    if (invite2->msg->body == NULL)
+	return -48;
+
+    /* Done checking invite2. We can delete this. */
+    if (pjsip_tx_data_dec_ref(invite2) != PJSIP_EBUFDESTROYED) {
+	PJ_LOG(3,("", "   error: request buffer not destroyed!"));
+	return -49;
+    }
+
+    /* Initialize dummy rdata (to simulate receiving a request) 
+     * We should never do this in real application, as there are many
+     * many more fields need to be initialized!!
+     */
+    dummy_rdata.msg_info.call_id = (HFIND(invite->msg, cid, CALL_ID))->id;
+    dummy_rdata.msg_info.clen = NULL;
+    dummy_rdata.msg_info.cseq = HFIND(invite->msg, cseq, CSEQ);
+    dummy_rdata.msg_info.ctype = NULL;
+    dummy_rdata.msg_info.from = HFIND(invite->msg, from, FROM);
+    dummy_rdata.msg_info.max_fwd = NULL;
+    dummy_rdata.msg_info.msg = invite->msg;
+    dummy_rdata.msg_info.record_route = NULL;
+    dummy_rdata.msg_info.require = NULL;
+    dummy_rdata.msg_info.route = NULL;
+    dummy_rdata.msg_info.to = HFIND(invite->msg, to, TO);
+    dummy_rdata.msg_info.via = HFIND(invite->msg, via, VIA);
+
+    /* Create a response message for the request. */
+    status = pjsip_endpt_create_response( endpt, &dummy_rdata, 301, NULL, 
+					  &response);
+    if (status != PJ_SUCCESS) {
+	app_perror("   error: unable to create response", status);
+	return -50;
+    }
+    
+    /* Buffer must be invalid. */
+    if (pjsip_tx_data_is_valid(response) != 0) {
+	PJ_LOG(3,("", "   error: buffer must be invalid"));
+	return -54;
+    }
+    /* Check reference counter. */
+    if (pj_atomic_get(response->ref_cnt) != 1) {
+	PJ_LOG(3,("", "   error: invalid ref count in response"));
+	return -55;
+    }
+    /* Check message type. */
+    if (response->msg->type != PJSIP_RESPONSE_MSG)
+	return -56;
+    /* Check correct status is set. */
+    if (response->msg->line.status.code != 301)
+	return -57;
+
+    /* Check that mandatory headers are again present. */
+    if (HFIND(response->msg, from, FROM) == 0)
+	return -60;
+    if (HFIND(response->msg, to, TO) == 0)
+	return -61;
+    /*
+    if (HFIND(response->msg, contact, CONTACT) == 0)
+	return -62;
+     */
+    if (HFIND(response->msg, cid, CALL_ID) == 0)
+	return -63;
+    if (HFIND(response->msg, cseq, CSEQ) == 0)
+	return -64;
+    if (HFIND(response->msg, via, VIA) == 0)
+	return -65;
+
+    /* This response message will be used later when creating ACK */
+
+    /* Create CANCEL request for the original request. */
+    status = pjsip_endpt_create_cancel( endpt, invite, &cancel);
+    if (status != PJ_SUCCESS) {
+	app_perror("   error: unable to create CANCEL request", status);
+	return -80;
+    }
+
+    /* Buffer must be invalid. */
+    if (pjsip_tx_data_is_valid(cancel) != 0) {
+	PJ_LOG(3,("", "   error: buffer must be invalid"));
+	return -84;
+    }
+    /* Check reference counter. */
+    if (pj_atomic_get(cancel->ref_cnt) != 1) {
+	PJ_LOG(3,("", "   error: invalid ref count in CANCEL request"));
+	return -85;
+    }
+    /* Check message type. */
+    if (cancel->msg->type != PJSIP_REQUEST_MSG)
+	return -86;
+    /* Check method. */
+    if (cancel->msg->line.req.method.id != PJSIP_CANCEL_METHOD)
+	return -87;
+
+    /* Check that mandatory headers are again present. */
+    if (HFIND(cancel->msg, from, FROM) == 0)
+	return -90;
+    if (HFIND(cancel->msg, to, TO) == 0)
+	return -91;
+    /*
+    if (HFIND(cancel->msg, contact, CONTACT) == 0)
+	return -92;
+    */
+    if (HFIND(cancel->msg, cid, CALL_ID) == 0)
+	return -93;
+    if (HFIND(cancel->msg, cseq, CSEQ) == 0)
+	return -94;
+    if (HFIND(cancel->msg, via, VIA) == 0)
+	return -95;
+
+    /* Done checking CANCEL request. */
+    if (pjsip_tx_data_dec_ref(cancel) != PJSIP_EBUFDESTROYED) {
+	PJ_LOG(3,("", "   error: response buffer not destroyed!"));
+	return -99;
+    }
+
+    /* Modify dummy_rdata to simulate receiving response. */
+    pj_memset(&dummy_rdata, 0, sizeof(dummy_rdata));
+    dummy_rdata.msg_info.msg = response->msg;
+    dummy_rdata.msg_info.to = HFIND(response->msg, to, TO);
+
+    /* Create ACK request */
+    status = pjsip_endpt_create_ack( endpt, invite, &dummy_rdata, &ack );
+    if (status != PJ_SUCCESS) {
+	PJ_LOG(3,("", "   error: unable to create ACK"));
+	return -100;
+    }
+    /* Buffer must be invalid. */
+    if (pjsip_tx_data_is_valid(ack) != 0) {
+	PJ_LOG(3,("", "   error: buffer must be invalid"));
+	return -104;
+    }
+    /* Check reference counter. */
+    if (pj_atomic_get(ack->ref_cnt) != 1) {
+	PJ_LOG(3,("", "   error: invalid ref count in ACK request"));
+	return -105;
+    }
+    /* Check message type. */
+    if (ack->msg->type != PJSIP_REQUEST_MSG)
+	return -106;
+    /* Check method. */
+    if (ack->msg->line.req.method.id != PJSIP_ACK_METHOD)
+	return -107;
+    /* Check Request-URI is present. */
+    if (ack->msg->line.req.uri == NULL)
+	return -108;
+
+    /* Check that mandatory headers are again present. */
+    if (HFIND(ack->msg, from, FROM) == 0)
+	return -110;
+    if (HFIND(ack->msg, to, TO) == 0)
+	return -111;
+    if (HFIND(ack->msg, cid, CALL_ID) == 0)
+	return -112;
+    if (HFIND(ack->msg, cseq, CSEQ) == 0)
+	return -113;
+    if (HFIND(ack->msg, via, VIA) == 0)
+	return -114;
+    if (ack->msg->body != NULL)
+	return -115;
+
+    /* Done checking invite message. */
+    if (pjsip_tx_data_dec_ref(invite) != PJSIP_EBUFDESTROYED) {
+	PJ_LOG(3,("", "   error: response buffer not destroyed!"));
+	return -120;
+    }
+
+    /* Done checking response message. */
+    if (pjsip_tx_data_dec_ref(response) != PJSIP_EBUFDESTROYED) {
+	PJ_LOG(3,("", "   error: response buffer not destroyed!"));
+	return -130;
+    }
+
+    /* Done checking ack message. */
+    if (pjsip_tx_data_dec_ref(ack) != PJSIP_EBUFDESTROYED) {
+	PJ_LOG(3,("", "   error: response buffer not destroyed!"));
+	return -140;
+    }
+
+    /* Done. */
+    return 0;
+}
+
Index: /pjproject/trunk/pjsip/src/test-pjsip/uri_test.c
===================================================================
--- /pjproject/trunk/pjsip/src/test-pjsip/uri_test.c	(revision 105)
+++ /pjproject/trunk/pjsip/src/test-pjsip/uri_test.c	(revision 106)
@@ -784,5 +784,5 @@
 }
 
-pj_status_t uri_test()
+int uri_test()
 {
     unsigned i, loop;
