diff --git a/src/main/java/org/cafesip/sipunit/HeaderConfiguration.java b/src/main/java/org/cafesip/sipunit/HeaderConfiguration.java new file mode 100644 index 0000000000..be405bdb1a --- /dev/null +++ b/src/main/java/org/cafesip/sipunit/HeaderConfiguration.java @@ -0,0 +1,116 @@ +package org.cafesip.sipunit; + +import javax.sip.header.*; +import java.util.List; + +/** + * Helper class for {@link SipPhone} registration process. Used to override the headers which will be generated + * by the REGISTER request. + *
+ * The purpose of this class is to provide an access to the underlying REGISTER request to test out different REGISTRATION + * message forms. This way a separate SIP registrar component may be forced to produce an error, allowing the external + * component to be integration tested with the desired REGISTER request header data. + *
+ * Created by TELES AG on 12/06/2017.
+ */
+public class HeaderConfiguration {
+ private ToHeader toHeader;
+ private FromHeader fromHeader;
+
+ private CallIdHeader callIdHeader;
+
+ private CSeqHeader cSeqHeader;
+ private MaxForwardsHeader maxForwardsHeader;
+
+ private List
* A SipPhone object is created by calling SipStack.createSipPhone().
- *
+ *
*
* Many of the methods in this class return an object or true return value if successful. In case of
* an error or caller-specified timeout, a null object or a false is returned. The
@@ -82,9 +56,9 @@
* indicating the cause of the problem. If an exception was involved, this string will contain the
* name of the Exception class and the exception message. This class has a method, format(), which
* can be called to obtain a human-readable string containing all of this error information.
- *
+ *
* @author Amit Chatterjee, Becky McElroy
- *
+ *
*/
public class SipPhone extends SipSession implements SipActionObject, RequestListener {
@@ -95,6 +69,10 @@ public class SipPhone extends SipSession implements SipActionObject, RequestList
private CSeqHeader cseq;
private Request lastRegistrationRequest;
+ private Response lastRegistrationResponse;
+
+ private String registrarHost;
+ private int registrarPort;
private Hashtable
* Initially, a REGISTER message is sent without any user name and password. If the server returns
* an OK, this method returns a true value.
- *
+ *
*
* If any challenge is received in response to sending the REGISTER message (response code
* UNAUTHORIZED or PROXY_AUTHENTICATION_REQUIRED), the SipPhone's credentials list is checked
@@ -167,31 +159,31 @@ protected SipPhone(SipStack stack, String host, String me)
* user, password). Also, the authorization created for this registration is not saved for re-use
* on a later registration. IE, the user/password parameters are for a one-time, single-shot use
* only.
- *
+ *
*
* After responding to the challenge(s) by resending the REGISTER message, this method returns a
* true or false value depending on the outcome as indicated by the server.
- *
+ *
*
* If the contact parameter is null, user@hostname is used where hostname is the SipStack's IP
* address property which defaults to InetAddress.getLocalHost().getHostAddress(), and other
* SipStack properties also apply. Otherwise, the contact parameter given is used in the
* Registration message sent to the server.
- *
+ *
*
* If the expiry parameter is 0, the registration request never expires. Otherwise, the duration,
* given in seconds, is sent to server.
- *
+ *
*
* This method can be called repeatedly to update the expiry or to add new contacts.
- *
+ *
*
* This method determines the contact information for this user agent, whether the registration
* was successful or not. If successful, the contact information may have been updated by the
* server (such as the expiry time, if not specified to this method by the caller). Once this
* method has been called, the test program can get information about the contact for this agent
* by calling the *MyContact*() getter methods.
- *
+ *
* @param user Optional - user name for authenticating with the server. Required if the server
* issues an authentication challenge.
* @param password Optional - used only if the server issues an authentication challenge.
@@ -205,17 +197,40 @@ protected SipPhone(SipStack stack, String host, String me)
* out why.
*/
public boolean register(String user, String password, String contact, int expiry, long timeout) {
+ return register(user, password, contact, expiry, timeout, null);
+ }
+
+ /**
+ * This method behaves in the same way as {@link SipPhone#register(String, String, String, int, long)}, but provides
+ * the option to override specific headers in the REGISTER request sent to the target registrar.
+ *
+ * @param user Optional - user name for authenticating with the server. Required if the server
+ * issues an authentication challenge.
+ * @param password Optional - used only if the server issues an authentication challenge.
+ * @param contact An URI string (ex: sip:bob@192.0.2.4), or null to use the default contact for
+ * this user agent.
+ * @param expiry Expiry time in seconds, or 0 if no registration expiry.
+ * @param timeout The maximum amount of time to wait for a response, in milliseconds. Use a value
+ * of 0 to wait indefinitely.
+ * @param headerConfiguration Header builder for overrides of specific headers in the REGISTER request
+ * @return false if registration fails or an error is encountered, true otherwise. In case of
+ * false, call getErrorMessage(), getReturnCode() and/or getException() methods to find
+ * out why.
+ *
+ * @see SipPhone#register(String, String, String, int, long)
+ */
+ public boolean register(String user, String password, String contact, int expiry, long timeout, HeaderConfiguration headerConfiguration) {
AddressFactory addrFactory = parent.getAddressFactory();
SipURI requestUri = null;
try {
- if (proxyHost != null) {
- requestUri = addrFactory.createSipURI(null, proxyHost);
- requestUri.setPort(proxyPort);
+ if (registrarHost != null) {
+ requestUri = addrFactory.createSipURI(null, registrarHost);
+ requestUri.setPort(registrarPort);
requestUri.setTransportParam(proxyProto);
}
- return register(requestUri, user, password, contact, expiry, timeout);
+ return register(requestUri, user, password, contact, expiry, timeout, headerConfiguration);
} catch (Exception ex) {
setReturnCode(EXCEPTION_ENCOUNTERED);
setException(ex);
@@ -231,131 +246,196 @@ public boolean register(String user, String password, String contact, int expiry
* is derived from this SipPhone's URI, or address of record (for example, if this SipPhone's
* address of record is "sip:amit@cafesip.org", the REGISTER Request-URI will be sip:cafesip.org).
* Otherwise, the requestUri passed in is used for the REGISTER Request-URI.
- *
*/
public boolean register(SipURI requestUri, String user, String password, String contact,
- int expiry, long timeout) {
- initErrorInfo();
-
- try {
- AddressFactory addr_factory = parent.getAddressFactory();
- HeaderFactory hdr_factory = parent.getHeaderFactory();
-
- if (requestUri == null) {
- requestUri = addr_factory.createSipURI(null, ((SipURI) (myAddress.getURI())).getHost());
- requestUri.setPort(((SipURI) (myAddress.getURI())).getPort());
- if (((SipURI) (myAddress.getURI())).getTransportParam() != null) {
- requestUri.setTransportParam(((SipURI) (myAddress.getURI())).getTransportParam());
- }
- }
-
- String method = Request.REGISTER;
-
- ToHeader to_header = hdr_factory.createToHeader(myAddress, null);
- FromHeader from_header = hdr_factory.createFromHeader(myAddress, generateNewTag());
-
- CallIdHeader callid_header = hdr_factory.createCallIdHeader(myRegistrationId);
-
- cseq = hdr_factory.createCSeqHeader(cseq == null ? 1 : (cseq.getSeqNumber() + 1), method);
-
- MaxForwardsHeader max_forwards = hdr_factory.createMaxForwardsHeader(MAX_FORWARDS_DEFAULT);
-
- if (contact != null) {
- URI uri = addr_factory.createURI(contact);
- if (uri.isSipURI() == false) {
- setReturnCode(INVALID_ARGUMENT);
- setErrorMessage("URI " + contact + " is not a Sip URI");
- return false;
- }
-
- Address contact_address = addr_factory.createAddress(uri);
- ContactHeader hdr = hdr_factory.createContactHeader(contact_address);
- hdr.setExpires(expiry);
-
- synchronized (contactLock) {
- contactInfo = new SipContact();
- contactInfo.setContactHeader(hdr);
- }
- }
-
- List
+ * Supports overriding of headers using the {@link HeaderConfiguration} object. HeaderConfiguration may be null in
+ * case no headers need to be overridden, providing normalized behavior described in {@link SipPhone#register(
+ * SipURI, String, String, String, int, long)}.
+ */
+ public boolean register(SipURI requestUri, String user, String password, String contact,
+ int expiry, long timeout, HeaderConfiguration headerConfiguration) {
+ initErrorInfo();
+
+ if (headerConfiguration == null) {
+ headerConfiguration = new HeaderConfiguration();
+ }
+
+ try {
+ AddressFactory addr_factory = parent.getAddressFactory();
+ HeaderFactory hdr_factory = parent.getHeaderFactory();
+
+ if (requestUri == null) {
+ requestUri = addr_factory.createSipURI(null, ((SipURI) (myAddress.getURI())).getHost());
+ requestUri.setPort(((SipURI) (myAddress.getURI())).getPort());
+ if (((SipURI) (myAddress.getURI())).getTransportParam() != null) {
+ requestUri.setTransportParam(((SipURI) (myAddress.getURI())).getTransportParam());
+ }
+ }
+
+ String method = Request.REGISTER;
+
+ if (headerConfiguration.getToHeader() == null) {
+ headerConfiguration.setToHeader(hdr_factory.createToHeader(myAddress, null));
+ }
+ if (headerConfiguration.getFromHeader() == null) {
+ headerConfiguration.setFromHeader(hdr_factory.createFromHeader(myAddress, generateNewTag()));
+ }
+
+
+ if (headerConfiguration.getCallIdHeader() == null) {
+ headerConfiguration.setCallIdHeader(hdr_factory.createCallIdHeader(myRegistrationId));
+ }
+
+ if (headerConfiguration.getCSeqHeader() == null) {
+ cseq = hdr_factory.createCSeqHeader(cseq == null ? 1 : (cseq.getSeqNumber() + 1), method);
+ headerConfiguration.setCSeqHeader(cseq);
+ }
+
+ if (headerConfiguration.getMaxForwardsHeader() == null) {
+ headerConfiguration.setMaxForwardsHeader(hdr_factory.createMaxForwardsHeader(MAX_FORWARDS_DEFAULT));
+ }
+
+ if (contact != null) {
+ URI uri = addr_factory.createURI(contact);
+ if (uri.isSipURI() == false) {
+ setReturnCode(INVALID_ARGUMENT);
+ setErrorMessage("URI " + contact + " is not a Sip URI");
+ return false;
+ }
+
+ Address contact_address = addr_factory.createAddress(uri);
+ ContactHeader hdr = hdr_factory.createContactHeader(contact_address);
+ hdr.setExpires(expiry);
+
+ synchronized (contactLock) {
+ SipContact newContactInfo = new SipContact();
+ newContactInfo.setContactHeader(hdr);
+
+ contactInfo = newContactInfo;
+ }
+ }
+
+ if (headerConfiguration.getViaHeaders() == null) {
+ headerConfiguration.setViaHeaders(getViaHeaders());
+ }
+
+ Request msg = parent.getMessageFactory().createRequest(requestUri, method, headerConfiguration.getCallIdHeader(),
+ headerConfiguration.getCSeqHeader(), headerConfiguration.getFromHeader(), headerConfiguration.getToHeader(),
+ headerConfiguration.getViaHeaders(), headerConfiguration.getMaxForwardsHeader());
+
+ if (!headerConfiguration.isIgnoreContact()) {
+ if (headerConfiguration.getContactHeader() == null) {
+ msg.addHeader(contactInfo.getContactHeader()); // use
+ // setHeader()?
+ } else {
+ msg.addHeader(headerConfiguration.getContactHeader());
+ }
+ }
+
+ if (headerConfiguration.getExpiresHeader() == null) {
+ if (expiry > 0) {
+ ExpiresHeader expires = hdr_factory.createExpiresHeader(expiry);
+ msg.setExpires(expires);
+ }
+ } else {
+ msg.setExpires(headerConfiguration.getExpiresHeader());
+ }
+
+ if (headerConfiguration.getUserAgentHeader() != null) {
+ msg.addHeader(headerConfiguration.getUserAgentHeader());
+ }
+
+ // include any auth information for this User Agent's registration
+ // if any exists
+
+ Map
*
* If the contact parameter is null, user@hostname is unregistered where hostname is obtained by
* calling InetAddr.getLocalHost(). Otherwise, the contact parameter value is used in the
* unregistration message sent to the server.
- *
+ *
* @param contact The contact URI (ex: sip:bob@192.0.2.4) to unregister or "*".
* @param timeout The maximum amount of time to wait for a response, in milliseconds. Use a value
- * of 0 to wait indefinitely.
- *
+ * of 0 to wait indefinitely.
* @return true if the unregistration succeeded or no unregistration was needed, false otherwise.
*/
public boolean unregister(String contact, long timeout) {
@@ -437,7 +516,7 @@ public boolean unregister(String contact, long timeout) {
}
private Response sendRegistrationMessage(Request msg, String user, String password,
- long timeout) {
+ long timeout) {
SipTransaction trans = sendRequestWithTransaction(msg, false, null);
// get the response
@@ -456,6 +535,7 @@ private Response sendRegistrationMessage(Request msg, String user, String passwo
Response response = ((ResponseEvent) response_event).getResponse();
int status_code = response.getStatusCode();
+ lastRegistrationResponse = response;
while (status_code != Response.OK) {
if (status_code == Response.TRYING) {
@@ -474,9 +554,10 @@ private Response sendRegistrationMessage(Request msg, String user, String passwo
response = ((ResponseEvent) response_event).getResponse();
status_code = response.getStatusCode();
+ lastRegistrationResponse = response;
continue;
} else if ((status_code == Response.UNAUTHORIZED)
- || (status_code == Response.PROXY_AUTHENTICATION_REQUIRED)) {
+ || (status_code == Response.PROXY_AUTHENTICATION_REQUIRED)) {
// modify the request to include user authorization info
msg = processAuthChallenge(response, msg, user, password);
@@ -516,11 +597,12 @@ private Response sendRegistrationMessage(Request msg, String user, String passwo
response = ((ResponseEvent) response_event).getResponse();
status_code = response.getStatusCode();
+ lastRegistrationResponse = response;
continue;
} else {
setReturnCode(status_code);
setErrorMessage(
- "An unsuccessful or error status code was received from the server: " + status_code);
+ "An unsuccessful or error status code was received from the server: " + status_code);
return null;
}
}
@@ -534,13 +616,13 @@ private Response sendRegistrationMessage(Request msg, String user, String passwo
* This method is public for test purposes and for use when using low level SipSession methods for
* sending/receiving messages. A test program using high level SipUnit doesn't need to call this
* method.
- *
+ *
*
* This method modifies the given request to include the authorization header(s) required by the
* given response. It may cache in SipPhone's authorizations list the AuthorizationHeader(s)
* created here for use later. The modified Request object is returned, or null in case of error
* or unresolved challenge.
- *
+ *
*
* For each received challenge present in the response message: SipPhone's credentials list is
* checked first, for the realm entry. If it is not found there, the username parameter passed
@@ -550,17 +632,17 @@ private Response sendRegistrationMessage(Request msg, String user, String passwo
* and the authorization created here is NOT saved for later re-use. If the credentials list
* contains an entry for the challenging realm, then the authorization created here is saved in
* the authorizations list for later re-use.
- *
+ *
* @param response the challenge that was received
- * @param req_msg the request originally sent (that was challenged)
+ * @param req_msg the request originally sent (that was challenged)
* @param username see above
* @param password see above
* @return the original request with the authorization header(s) added, or null if username and
- * password weren't passed in and the SipPhone's credentials list doesn't have an entry
- * for the realm.
+ * password weren't passed in and the SipPhone's credentials list doesn't have an entry
+ * for the realm.
*/
public Request processAuthChallenge(Response response, Request req_msg, String username,
- String password) {
+ String password) {
initErrorInfo();
ListIterator> challenges = null;
@@ -573,14 +655,14 @@ public Request processAuthChallenge(Response response, Request req_msg, String u
if (challenges == null) {
setReturnCode(ERROR_OF_UNKNOWN_ORIGIN);
setErrorMessage(
- "Improper use of processAuthChallenge() or response auth challenge header is missing");
+ "Improper use of processAuthChallenge() or response auth challenge header is missing");
return null;
}
// find the list of cached AuthorizationHeaders for this call (Call-ID)
String call_id = ((CallIdHeader) req_msg.getHeader(CallIdHeader.NAME)).getCallId();
Map
* Use this method when (1) you want to establish a call without worrying about the details and
* (2) your test program doesn't need to do anything else (ie, it can be blocked) until the
* response code parameter passed to this method is received from the network.
- *
+ *
*
* In case the first condition above is false: If you need to see the (intermediate/provisional)
* response messages as they come in, then use SipPhone.createSipCall() and
* SipCall.initiateOutgoingCall() instead of this method. If your test program can tolerate being
* blocked until the desired response is received, you can still use this method and later look
* back at all the received responses by calling SipCall.getAllReceivedResponses().
- *
+ *
*
* In case the second condition above is false: If your test code is handling both sides of the
* call, or it has to do other things while this call establishment is in progress, then this
@@ -755,8 +837,8 @@ protected void dropCall(SipCall call) {
* returns a SipCall object after the INVITE has been successfully sent. Then, later on you can
* check back with the SipCall object to see the call progress or block on the call establishment,
* at a more convenient time.
- *
- *
+ *
+ *
* @param to The URI string (ex: sip:bob@nist.gov) to which the call should be directed
* @param response The SipResponse status code to look for after sending the INVITE. This method
* returns when that status code is received.
@@ -777,10 +859,10 @@ public SipCall makeCall(String to, int response, long timeout, String viaNonProx
* This method is the same as the basic blocking makeCall() method except that it allows the
* caller to specify a message body and/or additional message headers to add to or replace in the
* outbound message without requiring knowledge of the JAIN-SIP API.
- *
+ *
*
* The extra parameters supported by this method are:
- *
+ *
* @param body A String to be used as the body of the message. Parameters contentType,
* contentSubType must both be non-null to get the body included in the message. Use null
* for no body bytes.
@@ -806,7 +888,7 @@ public SipCall makeCall(String to, int response, long timeout, String viaNonProx
* occur if your headers are not syntactically correct or contain nonsensical values (the
* message may not pass through the local SIP stack). Use null for no replacement of
* message headers.
- *
+ *
*/
public SipCall makeCall(String to, int response, long timeout, String viaNonProxyRoute,
String body, String contentType, String contentSubType, ArrayList
* The extra parameters supported by this method are:
- *
+ *
* @param additionalHeaders ArrayList of javax.sip.header.Header, each element a SIP header to add
* to the outbound message. These headers are added to the message after a correct message
* has been constructed. Note that if you try to add a header that there is only supposed
@@ -924,7 +1006,7 @@ public SipCall makeCall(String to, int response, long timeout, String viaNonProx
* etc.) are automatically collected and any received authentication challenges are automatically
* handled as well. The object returned by this method is a SipCall object representing the
* outgoing call leg; that is, the UAC originating a call to the network.
- *
+ *
*
* After calling this method, you can later call one or more of the following methods on the
* returned SipCall object to see what happened (each is nonblocking unless otherwise noted):
@@ -937,20 +1019,20 @@ public SipCall makeCall(String to, int response, long timeout, String viaNonProx
* waitOutgoingCallResponse() - BLOCKING - when your test program is done with its tasks and can
* be blocked until the next response is received (if you are interested in something other than
* OK) - use this only if you know that the INVITE transaction is still up.
- *
+ *
*
* Call this method when (1) you want to establish a call without worrying about the details and
* (2) your test program needs to do other tasks after the INVITE is sent but before a
* final/expected response is received (ie, the calling program cannot be blocked during call
* establishment).
- *
+ *
*
* Otherwise: If you need to see or act on any of the (intermediate/provisional) response messages
* as they come in, use SipPhone.createSipCall() and SipCall.initiateOutgoingCall() instead of
* this method. If your test program doesn't need to do anything else until the call is
* established: use the other SipPhone.makeCall() method which conveniently blocks until the
* response code you specify is received from the network.
- *
+ *
* @param to The URI string (ex: sip:bob@nist.gov) to which the call should be directed
* @param viaNonProxyRoute Indicates whether to route the INVITE via Proxy or some other route. If
* null, route the call to the Proxy that was specified when the SipPhone object was
@@ -958,7 +1040,7 @@ public SipCall makeCall(String to, int response, long timeout, String viaNonProx
* as "hostaddress:port/transport" i.e. 129.1.22.333:5060/UDP.
* @return A SipCall object representing the outgoing call leg, or null if an error was
* encountered.
- *
+ *
*/
public SipCall makeCall(String to, String viaNonProxyRoute) {
return makeCall(to, viaNonProxyRoute, null, null, null);
@@ -969,10 +1051,10 @@ public SipCall makeCall(String to, String viaNonProxyRoute) {
* caller to specify a message body and/or additional JAIN-SIP API message headers to add to or
* replace in the outbound INVITE message. Use of this method requires knowledge of the JAIN-SIP
* API.
- *
+ *
*
* The extra parameters supported by this method are:
- *
+ *
* @param additionalHeaders ArrayList of javax.sip.header.Header, each element a SIP header to add
* to the outbound message. These headers are added to the message after a correct message
* has been constructed. Note that if you try to add a header that there is only supposed
@@ -1009,9 +1091,9 @@ public SipCall makeCall(String to, String viaNonProxyRoute, ArrayList
* This method creates a SUBSCRIBE request message, sends it out, and waits for a response to be
* received. It saves the received response and checks for a "proceedable" (positive) status code
* value. Positive response status codes include any of the following: provisional (status / 100
* == 1), UNAUTHORIZED, PROXY_AUTHENTICATION_REQUIRED, OK and ACCEPTED. Any other status code, or
* a response timeout or any other error, is considered fatal to the subscription.
- *
+ *
*
* This method blocks until one of the above outcomes is reached.
- *
+ *
*
* In the case of a positive response status code, this method returns a PresenceSubscriber object
* that will represent the buddy for the life of the subscription and puts the PresenceSubscriber
@@ -1348,14 +1439,14 @@ private void distributeEventError(String err)
* details at any given time such as the subscription state, amount of time left on the
* subscription, termination reason, presence information, details of received responses and
* requests, etc.
- *
+ *
*
* In the case of a positive response status code (a non-null object is returned), you may find
* out more about the response that was just received by calling the PresenceSubscriber methods
* getReturnCode() and getCurrentResponse()/getLastReceivedResponse(). Your next step at this
* point will be to call the PresenceSubscriber's processResponse() method to proceed with the
* SUBSCRIBE processing.
- *
+ *
*
* In the case of a fatal outcome, no subscription object is created and null is returned. In this
* case, call the usual SipUnit failed-operation methods to find out what happened (ie, call this
@@ -1363,7 +1454,7 @@ private void distributeEventError(String err)
* getReturnCode() method will tell you the response status code that was received from the
* network (unless it is an internal SipUnit error code, see the SipSession javadoc for more on
* that).
- *
+ *
* @param uri the URI (ie, sip:bob@nist.gov) of the buddy to be added to the list.
* @param duration the duration in seconds to put in the SUBSCRIBE message. If 0, this is
* equivalent to a fetch except that the buddy stays in the buddy list even though the
@@ -1425,7 +1516,7 @@ public PresenceSubscriber addBuddy(String uri, int duration, String eventId, lon
* This method is the same as addBuddy(uri, duration, eventId, timeout) except that the duration
* is defaulted to the default period defined in the event package RFC (3600 seconds) and no event
* "id" parameter will be included.
- *
+ *
* @param uri the URI (ie, sip:bob@nist.gov) of the buddy to be added to the list.
* @param timeout The maximum amount of time to wait for a SUBSCRIBE response, in milliseconds.
* Use a value of 0 to wait indefinitely.
@@ -1439,7 +1530,7 @@ public PresenceSubscriber addBuddy(String uri, long timeout) {
/**
* This method is the same as addBuddy(uri, duration, eventId, timeout) except that no event "id"
* parameter will be included.
- *
+ *
* @param uri the URI (ie, sip:bob@nist.gov) of the buddy to be added to the list.
* @param duration the duration in seconds to put in the SUBSCRIBE message. If 0, this is
* equivalent to a fetch except that the buddy stays in the buddy list even though the
@@ -1456,7 +1547,7 @@ public PresenceSubscriber addBuddy(String uri, int duration, long timeout) {
/**
* This method is the same as addBuddy(uri, duration, eventId, timeout) except that the duration
* is defaulted to the default period defined in the event package RFC (3600 seconds).
- *
+ *
* @param uri the URI (ie, sip:bob@nist.gov) of the buddy to be added to the list.
* @param eventId the event "id" to use in the SUBSCRIBE message, or null for no event "id"
* parameter. See addBuddy(uri, duration, eventId, timeout) javadoc for details on event
@@ -1475,7 +1566,7 @@ public PresenceSubscriber addBuddy(String uri, String eventId, long timeout) {
* user's presence status and information. Please read the SipUnit User Guide webpage Event
* Subscription (at least the operation overview part) for information on how to use SipUnit
* presence capabilities.
- *
+ *
*
* This method creates a SUBSCRIBE request message with expiry time of 0, sends it out, and waits
* for a response to be received. It saves the received response and checks for a "proceedable"
@@ -1483,10 +1574,10 @@ public PresenceSubscriber addBuddy(String uri, String eventId, long timeout) {
* provisional (status / 100 == 1), UNAUTHORIZED, PROXY_AUTHENTICATION_REQUIRED, OK and ACCEPTED.
* Any other status code, or a response timeout or any other error, is considered fatal to the
* operation.
- *
+ *
*
* This method blocks until one of the above outcomes is reached.
- *
+ *
*
* In the case of a positive response status code, this method returns a PresenceSubscriber object
* representing the user and puts the object in this SipPhone's retired buddy list. The retired
@@ -1496,14 +1587,14 @@ public PresenceSubscriber addBuddy(String uri, String eventId, long timeout) {
* PresenceSubscriber object to proceed through the remainder of the SUBSCRIBE-NOTIFY sequence and
* to find out details such as the subscription state, termination reason, presence information,
* details of received responses and requests, etc.
- *
+ *
*
* In the case of a positive response status code (a non-null object is returned), you may find
* out more about the response that was just received by calling the PresenceSubscriber methods
* getReturnCode() and getCurrentResponse()/getLastReceivedResponse(). Your next step at this
* point will be to call the PresenceSubscriber's processResponse() method to proceed with the
* SUBSCRIBE processing.
- *
+ *
*
* In the case of a fatal outcome, no Subscription object is created and null is returned. In this
* case, call the usual SipUnit failed-operation methods to find out what happened (ie, call this
@@ -1511,7 +1602,7 @@ public PresenceSubscriber addBuddy(String uri, String eventId, long timeout) {
* getReturnCode() method will tell you the response status code that was received from the
* network (unless it is an internal SipUnit error code, see the SipSession javadoc for more on
* that).
- *
+ *
* @param uri the URI (ie, sip:bob@nist.gov) of the user whose presence info is to be fetched.
* @param eventId the event "id" to use in the SUBSCRIBE message, or null for no event "id"
* parameter. Whatever is indicated here will be used subsequently, for error checking the
@@ -1573,7 +1664,7 @@ public PresenceSubscriber fetchPresenceInfo(String uri, String eventId, long tim
* This method is the same as fetchPresenceInfo(uri, eventId, timeout) except that no event "id"
* parameter will be included in the SUBSCRIBE message. When error checking the SUBSCRIBE response
* and NOTIFY from the server, no event "id" parameter will be expected.
- *
+ *
* @param uri the URI (ie, sip:bob@nist.gov) of the user whose presence info is to be fetched.
* @param timeout The maximum amount of time to wait for a SUBSCRIBE response, in milliseconds.
* Use a value of 0 to wait indefinitely.
@@ -1592,13 +1683,13 @@ public PresenceSubscriber fetchPresenceInfo(String uri, long timeout) {
* from the returned object. The user may have been a buddy in the buddy list (but was removed
* from the list by the test program), or fetchPresenceInfo() was previously called for the user
* to get a one-time status report, or the user may still be in the buddy list.
- *
+ *
* @param uri the URI (ie, sip:bob@nist.gov) of the user whose subscription object is to be
* returned.
* @return A PresenceSubscriber object that contains information about the user's last obtained
* presence status and other info, or null if there was never any status fetch done for
* this user and this user was never in the buddy list.
- *
+ *
*/
public PresenceSubscriber getBuddyInfo(String uri) {
synchronized (buddyList) {
@@ -1618,10 +1709,10 @@ public PresenceSubscriber getBuddyInfo(String uri) {
* are still in the buddy list. A given buddy, or subscription, in this list may be active or not
* - ie, subscription termination by the far end does not remove a buddy from this list. Buddies
* are removed from the list only by the test program (by calling removeBuddy()).
- *
+ *
*
* See related methods getBuddyInfo(), getRetiredBuddies().
- *
+ *
* @return a Hashtable of zero or more entries, where the key = URI of the buddy, value =
* PresenceSubscriber object.
*/
@@ -1635,10 +1726,10 @@ public Map
* See related methods getBuddyInfo(), getBuddyList().
- *
+ *
* @return a Hashtable of zero or more entries, where the key = URI of the user, value =
* PresenceSubscriber object.
*/
@@ -1672,12 +1763,12 @@ protected boolean removeRefer(ReferSubscriber ref) {
* Gets the subscription object(s) associated with the given referToUri. The returned object(s)
* contains subscription state, received requests (NOTIFY's) and REFER/SUBSCRIBE responses, etc.
* for outbound REFER subscription(s) associated with the referToUri.
- *
+ *
* @param referToUri the referToUri that was previously passed in to SipPhone.refer(). See javadoc
* there.
* @return ReferSubscriber object(s) associated with the referToUri, or an empty list if there was
* never a call to SipPhone.refer() with that referToUri.
- *
+ *
*/
public List
* See related method getRefererInfo().
- *
+ *
* @return a list of ReferSubscriber objects or an empty list if there are none.
*/
public List
* This method blocks until one of the above outcomes is reached.
- *
+ *
*
* In the case of a positive response status code, this method returns a ReferSubscriber object
* that represents the implicit subscription. You can save the returned object yourself or
@@ -1825,13 +1916,13 @@ public SipURI getUri(String scheme, String userHostPort, String transportUriPara
* REFER-NOTIFY sequence as well as for future SUBSCRIBE-NOTIFY sequences on this subscription and
* also to find out details at any given time such as the subscription state, amount of time left
* on the subscription, termination reason, details of received responses and requests, etc.
- *
+ *
*
* In the case of a positive response status code (a non-null object is returned), you may find
* out more about the response that was just received by calling the ReferSubscriber methods
* getReturnCode() and getCurrentResponse(). Your next step will then be to call the
* ReferSubscriber's processResponse() method to proceed with the REFER processing.
- *
+ *
*
* In the case of a fatal outcome, no subscription object is created and null is returned. In this
* case, call the usual SipUnit failed-operation methods to find out what happened (ie, call this
@@ -1839,7 +1930,7 @@ public SipURI getUri(String scheme, String userHostPort, String transportUriPara
* getReturnCode() method will tell you the response status code that was received from the
* network (unless it is an internal SipUnit error code, see the SipSession javadoc for more on
* that).
- *
+ *
* @param refereeUri The URI (ie, sip:bob@nist.gov) of the far end of the subscription. This is
* the party the REFER request is sent to.
* @param referToUri The URI that the refereeUri is to refer to. You can use SipPhone.getUri() to
@@ -1860,7 +1951,7 @@ public SipURI getUri(String scheme, String userHostPort, String transportUriPara
* viaNonProxyRoute node which is specified as "hostaddress:port/transport" i.e.
* 129.1.22.333:5060/UDP. A route header will be added to the REFER for this, before the
* request is sent.
- *
+ *
* @return ReferSubscriber object representing the implicit subscription if the operation is
* successful so far, null otherwise.
*/
@@ -1873,10 +1964,10 @@ public ReferSubscriber refer(String refereeUri, SipURI referToUri, String eventI
* This method is the same as the basic out-of-dialog refer() method except that it allows the
* caller to specify a message body and/or additional JAIN-SIP API message headers to add to or
* replace in the outbound message. Use of this method requires knowledge of the JAIN-SIP API.
- *
+ *
*
* The extra parameters supported by this method are:
- *
+ *
* @param additionalHeaders ArrayList of javax.sip.header.Header, each element a SIP header to add
* to the outbound message. These headers are added to the message after a correct message
* has been constructed. Note that if you try to add a header that there is only supposed
@@ -1943,10 +2034,10 @@ public ReferSubscriber refer(String refereeUri, SipURI referToUri, String eventI
* This method is the same as the basic out-of-dialog refer() method except that it allows the
* caller to specify a message body and/or additional message headers to add to or replace in the
* outbound message without requiring knowledge of the JAIN-SIP API.
- *
+ *
*
* The extra parameters supported by this method are:
- *
+ *
* @param body A String to be used as the body of the message. Parameters contentType,
* contentSubType must both be non-null to get the body included in the message. Use null
* for no body bytes.
@@ -1972,7 +2063,7 @@ public ReferSubscriber refer(String refereeUri, SipURI referToUri, String eventI
* occur if your headers are not syntactically correct or contain nonsensical values (the
* message may not pass through the local SIP stack). Use null for no replacement of
* message headers.
- *
+ *
*/
public ReferSubscriber refer(String refereeUri, SipURI referToUri, String eventId, long timeout,
String viaNonProxyRoute, String body, String contentType, String contentSubType,
@@ -1995,10 +2086,10 @@ public ReferSubscriber refer(String refereeUri, SipURI referToUri, String eventI
* provisional (status / 100 == 1), UNAUTHORIZED, PROXY_AUTHENTICATION_REQUIRED, OK and ACCEPTED.
* Any other status code, or a response timeout or any other error, is considered fatal to the
* subscription.
- *
+ *
*
* This method blocks until one of the above outcomes is reached.
- *
+ *
*
* In the case of a positive response status code, this method returns a ReferSubscriber object
* that represents the implicit subscription. You can save the returned object yourself or
@@ -2007,13 +2098,13 @@ public ReferSubscriber refer(String refereeUri, SipURI referToUri, String eventI
* REFER-NOTIFY sequence as well as for future SUBSCRIBE-NOTIFY sequences on this subscription and
* also to find out details at any given time such as the subscription state, amount of time left
* on the subscription, termination reason, details of received responses and requests, etc.
- *
+ *
*
* In the case of a positive response status code (a non-null object is returned), you may find
* out more about the response that was just received by calling the ReferSubscriber methods
* getReturnCode() and getCurrentResponse(). Your next step will then be to call the
* ReferSubscriber's processResponse() method to proceed with the REFER processing.
- *
+ *
*
* In the case of a fatal outcome, no subscription object is created and null is returned. In this
* case, call the usual SipUnit failed-operation methods to find out what happened (ie, call this
@@ -2021,7 +2112,7 @@ public ReferSubscriber refer(String refereeUri, SipURI referToUri, String eventI
* getReturnCode() method will tell you the response status code that was received from the
* network (unless it is an internal SipUnit error code, see the SipSession javadoc for more on
* that).
- *
+ *
* @param dialog The existing dialog that this REFER should be associated with. You can get it
* from SipCall.getDialog() or ReferSubscriber.getDialog().
* @param referToUri The URI that the far end of the dialog is to refer to. You can use
@@ -2034,7 +2125,7 @@ public ReferSubscriber refer(String refereeUri, SipURI referToUri, String eventI
* refresh() or unsubscribe().
* @param timeout The maximum amount of time to wait for a response, in milliseconds. Use a value
* of 0 to wait indefinitely.
- *
+ *
* @return ReferSubscriber object representing the implicit subscription if the operation is
* successful so far, null otherwise.
*/
@@ -2046,10 +2137,10 @@ public ReferSubscriber refer(Dialog dialog, SipURI referToUri, String eventId, l
* This method is the same as the basic in-dialog refer() method except that it allows the caller
* to specify a message body and/or additional JAIN-SIP API message headers to add to or replace
* in the outbound message. Use of this method requires knowledge of the JAIN-SIP API.
- *
+ *
*
* The extra parameters supported by this method are:
- *
+ *
* @param additionalHeaders ArrayList of javax.sip.header.Header, each element a SIP header to add
* to the outbound message. These headers are added to the message after a correct message
* has been constructed. Note that if you try to add a header that there is only supposed
@@ -2114,10 +2205,10 @@ public ReferSubscriber refer(Dialog dialog, SipURI referToUri, String eventId, l
* This method is the same as the basic in-dialog refer() method except that it allows the caller
* to specify a message body and/or additional message headers to add to or replace in the
* outbound message without requiring knowledge of the JAIN-SIP API.
- *
+ *
*
* The extra parameters supported by this method are:
- *
+ *
* @param body A String to be used as the body of the message. Parameters contentType,
* contentSubType must both be non-null to get the body included in the message. Use null
* for no body bytes.
@@ -2143,7 +2234,7 @@ public ReferSubscriber refer(Dialog dialog, SipURI referToUri, String eventId, l
* occur if your headers are not syntactically correct or contain nonsensical values (the
* message may not pass through the local SIP stack). Use null for no replacement of
* message headers.
- *
+ *
*/
public ReferSubscriber refer(Dialog dialog, SipURI referToUri, String eventId, long timeout,
String body, String contentType, String contentSubType, ArrayList
+ * Created by TELES AG on 08/01/2018.
+ */
+public class TestExternalRegistrar {
+ private static final Logger LOG = LoggerFactory.getLogger(TestExternalRegistrar.class);
+
+ private static final String LOCALHOST = "127.0.0.1";
+
+ private SipStack sipStack;
+ private SipPhone ua;
+ private final int uaPort = 5081;
+ private final String uaProtocol = "UDP";
+ private final String uaContact = "sip:client@127.0.0.1:5081";
+
+
+ private SipStack sipStackRegistrar;
+ private SipPhone registrar;
+ private final int registrarPort = 5080;
+ private final String registrarProtocol = "UDP";
+ private final String registrarContact = "sip:registrar@127.0.0.1:5080";
+
+ private final int proxyPort = 5090;
+ private final String proxyProtocol = "UDP";
+
+ private Properties uaProperties = new Properties();
+ private final Properties registrarProperties = new Properties();
+
+ private ExecutorService executorService = Executors.newCachedThreadPool();
+
+ @Before
+ public void setup() throws Exception {
+ uaProperties.setProperty("javax.sip.IP_ADDRESS", LOCALHOST);
+ uaProperties.setProperty("javax.sip.STACK_NAME", "testAgent");
+ uaProperties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "16");
+ uaProperties.setProperty("gov.nist.javax.sip.DEBUG_LOG", "testAgent_debug.txt");
+ uaProperties.setProperty("gov.nist.javax.sip.SERVER_LOG", "testAgent_log.txt");
+ uaProperties.setProperty("gov.nist.javax.sip.READ_TIMEOUT", "1000");
+ uaProperties.setProperty("gov.nist.javax.sip.CACHE_SERVER_CONNECTIONS", "false");
+ uaProperties.setProperty("gov.nist.javax.sip.PASS_INVITE_NON_2XX_ACK_TO_LISTENER", "true");
+
+ sipStack = new SipStack(uaProtocol, uaPort, uaProperties);
+ ua = sipStack.createSipPhone(LOCALHOST, registrarPort, LOCALHOST, proxyProtocol, proxyPort, uaContact);
+
+ registrarProperties.setProperty("javax.sip.STACK_NAME", "testRegistrar");
+ registrarProperties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "16");
+ registrarProperties.setProperty("gov.nist.javax.sip.DEBUG_LOG", "testRegistrar_debug.txt");
+ registrarProperties.setProperty("gov.nist.javax.sip.SERVER_LOG", "testRegistrar_log.txt");
+ registrarProperties.setProperty("gov.nist.javax.sip.READ_TIMEOUT", "1000");
+ registrarProperties.setProperty("gov.nist.javax.sip.CACHE_SERVER_CONNECTIONS", "false");
+ registrarProperties.setProperty("gov.nist.javax.sip.PASS_INVITE_NON_2XX_ACK_TO_LISTENER", "true");
+ registrarProperties.setProperty("javax.sip.IP_ADDRESS", LOCALHOST);
+
+ sipStackRegistrar = new SipStack(registrarProtocol, registrarPort, registrarProperties);
+ registrar = sipStackRegistrar.createSipPhone(registrarContact);
+ }
+
+
+ /**
+ * Release the sipStack and a user agent for the test.
+ */
+ @After
+ public void tearDown() {
+ ua.unregister(uaContact, 1000);
+ ua.dispose();
+ awaitStackDispose(sipStack);
+
+ registrar.dispose();
+ awaitStackDispose(sipStackRegistrar);
+ }
+
+ @Test
+ public void testRegisterWithRegistrar() throws Exception {
+ registrar.setSupportRegisterRequests(true);
+ assertTrue(registrar.listenRequestMessage());
+
+ Future registrarResult = executorService.submit(new Runnable() {
+ @Override
+ public void run() {
+ RequestEvent requestEvent = registrar.waitRequest(10000);
+ assertNotNull(requestEvent);
+ Response response = null;
+ try {
+ response = registrar.getParent().getMessageFactory().createResponse(200, requestEvent.getRequest());
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ registrar.sendReply(requestEvent, response);
+ }
+ });
+
+ assertTrue(ua.register("amit", "a1b2c3d4", uaContact, 4890, 1000000));
+ assertLastOperationSuccess("user a registration - " + ua.format(), ua);
+
+ registrarResult.get();
+
+ assertNotNull(ua.getLastRegistrationRequest());
+ assertNotNull(ua.getLastRegistrationResponse());
+ }
+
+ @Test
+ public void testRegisterWithCallIdOverride() throws Exception {
+ HeaderConfiguration headerConfiguration = new HeaderConfiguration();
+
+ final CallIdHeader overrideHeader = ua.getParent().getHeaderFactory().createCallIdHeader("test-call-id");
+ headerConfiguration.setCallIdHeader(overrideHeader);
+
+ testHeaderOverride(headerConfiguration, overrideHeader);
+ }
+
+ @Test
+ public void testRegisterWithToOverride() throws Exception {
+ HeaderConfiguration headerConfiguration = new HeaderConfiguration();
+
+ final Address expectedAddress = ua.getParent().getAddressFactory().createAddress("sip:testaddr@test.com");
+ final ToHeader overrideHeader = ua.getParent().getHeaderFactory().createToHeader(expectedAddress, "test-tag");
+ headerConfiguration.setToHeader(overrideHeader);
+
+ testHeaderOverride(headerConfiguration, overrideHeader);
+ }
+
+ @Test
+ public void testRegisterWithFromOverride() throws Exception {
+ final HeaderConfiguration headerConfiguration = new HeaderConfiguration();
+
+ final Address expectedAddress = ua.getParent().getAddressFactory().createAddress("sip:testaddr@test.com");
+ final FromHeader overrideHeader = ua.getParent().getHeaderFactory().createFromHeader(expectedAddress, "test-tag");
+ headerConfiguration.setFromHeader(overrideHeader);
+
+ testHeaderOverride(headerConfiguration, overrideHeader);
+ }
+
+ @Test
+ public void testRegisterWithCSeqOverride() throws Exception {
+ HeaderConfiguration headerConfiguration = new HeaderConfiguration();
+
+ final CSeqHeader overrideHeader = ua.getParent().getHeaderFactory().createCSeqHeader(555l, Request.REGISTER);
+ headerConfiguration.setCSeqHeader(overrideHeader);
+
+ testHeaderOverride(headerConfiguration, overrideHeader);
+ }
+
+ @Test
+ public void testRegisterWithContactOverride() throws Exception {
+ HeaderConfiguration headerConfiguration = new HeaderConfiguration();
+
+ final Address expectedAddress = ua.getParent().getAddressFactory().createAddress("sip:testaddr@test.com");
+ final ContactHeader overrideHeader = ua.getParent().getHeaderFactory().createContactHeader(expectedAddress);
+ headerConfiguration.setContactHeader(overrideHeader);
+
+ testHeaderOverride(headerConfiguration, overrideHeader);
+ }
+
+ @Test
+ public void testRegisterWithExpiresOverride() throws Exception {
+ HeaderConfiguration headerConfiguration = new HeaderConfiguration();
+
+ final ExpiresHeader overrideHeader = ua.getParent().getHeaderFactory().createExpiresHeader(3600);
+ headerConfiguration.setExpiresHeader(overrideHeader);
+
+ testHeaderOverride(headerConfiguration, overrideHeader);
+ }
+
+ @Test
+ public void testRegisterUserAgentOverride() throws Exception {
+ HeaderConfiguration headerConfiguration = new HeaderConfiguration();
+
+ final UserAgentHeader overrideHeader = ua.getParent().getHeaderFactory()
+ .createUserAgentHeader(Arrays.asList("test-agent"));
+ headerConfiguration.setUserAgentHeader(overrideHeader);
+
+ testHeaderOverride(headerConfiguration, overrideHeader);
+ }
+
+ @Test
+ public void testRegisterWithOmittedContactOverride() throws Exception {
+ HeaderConfiguration headerConfiguration = new HeaderConfiguration();
+ headerConfiguration.setIgnoreContact(true);
+
+ testHeaderOverride(headerConfiguration, ContactHeader.NAME, null);
+ }
+
+ private void testHeaderOverride(final HeaderConfiguration headerConfiguration, final Header overrideHeader) throws InterruptedException, java.util.concurrent.ExecutionException {
+ testHeaderOverride(headerConfiguration, overrideHeader.getName(), overrideHeader);
+ }
+
+ private void testHeaderOverride(final HeaderConfiguration headerConfiguration, final String headerName, final Header expectedHeaderValue) throws InterruptedException, java.util.concurrent.ExecutionException {
+ registrar.setSupportRegisterRequests(true);
+ assertTrue(registrar.listenRequestMessage());
+
+ Future registrarResult = executorService.submit(new Runnable() {
+ @Override
+ public void run() {
+ RequestEvent requestEvent = registrar.waitRequest(10000);
+ assertNotNull(requestEvent);
+
+ Header receivedHeader = requestEvent.getRequest().getHeader(headerName);
+ assertEquals(expectedHeaderValue, receivedHeader);
+
+ Response response = null;
+ try {
+ response = registrar.getParent().getMessageFactory().createResponse(200, requestEvent.getRequest());
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ registrar.sendReply(requestEvent, response);
+ }
+ });
+
+ assertTrue(ua.register("amit", "a1b2c3d4", uaContact, 4890, 1000000, headerConfiguration));
+ assertLastOperationSuccess("user a registration - " + ua.format(), ua);
+
+ registrarResult.get();
+ }
+}