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