public static void forwardResponse(final ActorSystem system, final SipServletResponse response, final boolean patchForNat) throws IOException {
         if (logger.isInfoEnabled()) {
             logger.info(String.format("B2BUA: Got response: \n %s", response));
         }
         if (response.getSession() == null || !response.getSession().isValid()) {
             if (logger.isDebugEnabled()) {
                 logger.debug("Response session is either null or invalidated");
             }
             return;
         }
         if (getLinkedSession(response) == null || !getLinkedSession(response).isValid()) {
             if (logger.isDebugEnabled()) {
                 logger.debug("Linked session of response is either null or invalidated");
             }
             return;
         }
         CallDetailRecordsDao records = daoManager.getCallDetailRecordsDao();

         if (response.getStatus() > 200)
             response.getSession().setAttribute(B2BUA_LAST_FINAL_RESPONSE, response);

         // container handles CANCEL related responses no need to forward them
         if (response.getStatus() == 487 || (response.getStatus() == 200 && response.getMethod().equalsIgnoreCase("CANCEL"))) {
             if (logger.isDebugEnabled()) {
                 logger.debug("response to CANCEL not forwarding");
             }
             // Update CallDetailRecord
             SipServletRequest request = (SipServletRequest) getLinkedSession(response).getAttribute(B2BUA_LAST_REQUEST);
             CallDetailRecord callRecord = records.getCallDetailRecord((Sid) request.getSession().getAttribute(CDR_SID));

             if (callRecord != null) {
                 if(logger.isInfoEnabled()) {
                     logger.info("CDR found! Updating");
                 }
                 callRecord = callRecord.setStatus(CallStateChanged.State.CANCELED.name());
                 final DateTime now = DateTime.now();
                 callRecord = callRecord.setEndTime(now);
                 int seconds;
                 if (callRecord.getStartTime() != null) {
                     seconds = (int) (DateTime.now().getMillis() - callRecord.getStartTime().getMillis()) / 1000;
                 } else {
                     seconds = 0;
                 }
                 callRecord = callRecord.setDuration(seconds);
                 records.updateCallDetailRecord(callRecord);

                 sendCallInfoStreamEvent(system, request, CallStateChanged.State.CANCELED);
             }

             return;
         }

         // We don't forward 200 OK Response to BYE.
         if (response.getStatus() == 200 && response.getMethod().equalsIgnoreCase("BYE")) {
             if (logger.isDebugEnabled()) {
                 logger.debug("response to BYE not forwarding");
             }
             return;
         }
         // forward the response
         if (response.getSession() != null &&
                 (!response.getSession().getState().equals(SipSession.State.TERMINATED))) {
             response.getSession().setAttribute(B2BUA_LAST_RESPONSE, response);
         }
         SipServletRequest linkedRequest = (SipServletRequest) getLinkedSession(response).getAttribute(B2BUA_LAST_REQUEST);
         SipServletResponse clonedResponse = linkedRequest.createResponse(response.getStatus());
         SipURI originalURI = null;
         try {
             //only fix Contact if necessary, some requests like MESSAGE
             //doesn't need Contact header
             if(clonedResponse.getAddressHeader("Contact") != null &&
                     response.getAddressHeader("Contact") != null &&
                     response.getAddressHeader("Contact").getURI() != null) {
                 originalURI = (SipURI) response.getAddressHeader("Contact").getURI();
                 if (originalURI != null && originalURI.getUser() != null && !originalURI.getUser().isEmpty()) {
                     ((SipURI) clonedResponse.getAddressHeader("Contact").getURI()).setUser(originalURI.getUser());
                 }
             }
         } catch (ServletParseException | NullPointerException e) {
            logger.error("Problem while trying to set User part on a clones response for a P2P call, "+e);
         }

         CallDetailRecord callRecord = records.getCallDetailRecord((Sid) linkedRequest.getSession().getAttribute(CDR_SID));
         Sid organizationSid = daoManager.getAccountsDao().getAccount(callRecord.getAccountSid()).getOrganizationSid();

         if (response.getRawContent() != null && response.getRawContent().length > 0 ) {
             final byte[] sdp = response.getRawContent();
             String offer = null;
             if (response.getContentType().equalsIgnoreCase("application/sdp") && patchForNat) {
                 // Issue 306: https://telestax.atlassian.net/browse/RESTCOMM-306
                 Registration registration = daoManager.getRegistrationsDao().getRegistration(callRecord.getTo(), organizationSid);
                 String externalIp;
                 if (registration != null) {
                     externalIp = registration.getLocation().split(":")[1].split("@")[1];
                 } else {
                     externalIp = callRecord.getTo().split(":")[1].split("@")[1];
                 }
                 try {
                     if(logger.isDebugEnabled()) {
                         logger.debug("Got original address from Registration :" + externalIp);
                     }
                     offer = patch(sdp, externalIp);
                 } catch (SdpException e) {
                     logger.error("Unexpected exception while patching sdp ", e);
                 }
             }
             if (offer != null) {
                 clonedResponse.setContent(offer, response.getContentType());
             } else {
                 clonedResponse.setContent(sdp, response.getContentType());
             }
         }

         addHeadersToMessage(clonedResponse, getCustomHeaders(response, "X-"));

         clonedResponse.send();

         // CallDetailRecord callRecord = records.getCallDetailRecord((Sid) request.getSession().getAttribute(CDR_SID));
         if (callRecord != null) {
             if(logger.isInfoEnabled()) {
                 logger.info("CDR found! Updating");
             }
             if (!linkedRequest.getMethod().equalsIgnoreCase("BYE")) {
                 if (response.getStatus() == 100 || response.getStatus() == 180 || response.getStatus() == 183) {
                     callRecord = callRecord.setStatus(CallStateChanged.State.RINGING.name());
                 } else if (response.getStatus() == 200 || response.getStatus() == 202) {
                     callRecord = callRecord.setStatus(CallStateChanged.State.IN_PROGRESS.name());
                     callRecord = callRecord.setAnsweredBy(((SipURI) response.getTo().getURI()).getUser());
                     final DateTime now = DateTime.now();
                     callRecord = callRecord.setStartTime(now);

                 } else if (response.getStatus() == 486 || response.getStatus() == 600) {
                     callRecord = callRecord.setStatus(CallStateChanged.State.BUSY.name());
                 } else if (response.getStatus() > 400) {
                     callRecord = callRecord.setStatus(CallStateChanged.State.FAILED.name());
                 }
             } else {
                 callRecord = callRecord.setStatus(CallStateChanged.State.COMPLETED.name());
                 final DateTime now = DateTime.now();
                 callRecord = callRecord.setEndTime(now);
                 final int seconds = (int) ((DateTime.now().getMillis() - callRecord.getStartTime().getMillis()) / 1000);
                 callRecord = callRecord.setDuration(seconds);
             }

             records.updateCallDetailRecord(callRecord);

             sendCallInfoStreamEvent(system, linkedRequest, CallStateChanged.State.valueOf(callRecord.getStatus()));
         }

     }
public static void forwardResponse ( final ActorSystem system , final SipServletResponse response , final boolean patchForNat ) throws IOException { if ( logger . isInfoEnabled ( ) ) { logger . info ( String . format ( "B2BUA: Got response: \n %s" , response ) ) ; } if ( response . getSession ( ) == null || ! response . getSession ( ) . isValid ( ) ) { if ( logger . isDebugEnabled ( ) ) { logger . debug ( "Response session is either null or invalidated" ) ; } return ; } if ( getLinkedSession ( response ) == null || ! getLinkedSession ( response ) . isValid ( ) ) { if ( logger . isDebugEnabled ( ) ) { logger . debug ( "Linked session of response is either null or invalidated" ) ; } return ; } CallDetailRecordsDao records = daoManager . getCallDetailRecordsDao ( ) ; if ( response . getStatus ( ) > 200 ) response . getSession ( ) . setAttribute ( B2BUA_LAST_FINAL_RESPONSE , response ) ; if ( response . getStatus ( ) == 487 || ( response . getStatus ( ) == 200 && response . getMethod ( ) . equalsIgnoreCase ( "CANCEL" ) ) ) { if ( logger . isDebugEnabled ( ) ) { logger . debug ( "response to CANCEL not forwarding" ) ; } SipServletRequest request = ( SipServletRequest ) getLinkedSession ( response ) . getAttribute ( B2BUA_LAST_REQUEST ) ; CallDetailRecord callRecord = records . getCallDetailRecord ( ( Sid ) request . getSession ( ) . getAttribute ( CDR_SID ) ) ; if ( callRecord != null ) { if ( logger . isInfoEnabled ( ) ) { logger . info ( "CDR found! Updating" ) ; } callRecord = callRecord . setStatus ( CallStateChanged . State . CANCELED . name ( ) ) ; final DateTime now = DateTime . now ( ) ; callRecord = callRecord . setEndTime ( now ) ; int seconds ; if ( callRecord . getStartTime ( ) != null ) { seconds = ( int ) ( DateTime . now ( ) . getMillis ( ) - callRecord . getStartTime ( ) . getMillis ( ) ) / 1000 ; } else { seconds = 0 ; } callRecord = callRecord . setDuration ( seconds ) ; records . updateCallDetailRecord ( callRecord ) ; sendCallInfoStreamEvent ( system , request , CallStateChanged . State . CANCELED ) ; } return ; } if ( response . getStatus ( ) == 200 && response . getMethod ( ) . equalsIgnoreCase ( "BYE" ) ) { if ( logger . isDebugEnabled ( ) ) { logger . debug ( "response to BYE not forwarding" ) ; } return ; } if ( response . getSession ( ) != null && ( ! response . getSession ( ) . getState ( ) . equals ( SipSession . State . TERMINATED ) ) ) { response . getSession ( ) . setAttribute ( B2BUA_LAST_RESPONSE , response ) ; } SipServletRequest linkedRequest = ( SipServletRequest ) getLinkedSession ( response ) . getAttribute ( B2BUA_LAST_REQUEST ) ; SipServletResponse clonedResponse = linkedRequest . createResponse ( response . getStatus ( ) ) ; SipURI originalURI = null ; try { if ( clonedResponse . getAddressHeader ( "Contact" ) != null && response . getAddressHeader ( "Contact" ) != null && response . getAddressHeader ( "Contact" ) . getURI ( ) != null ) { originalURI = ( SipURI ) response . getAddressHeader ( "Contact" ) . getURI ( ) ; if ( originalURI != null && originalURI . getUser ( ) != null && ! originalURI . getUser ( ) . isEmpty ( ) ) { ( ( SipURI ) clonedResponse . getAddressHeader ( "Contact" ) . getURI ( ) ) . setUser ( originalURI . getUser ( ) ) ; } } } catch ( ServletParseException | NullPointerException e ) { logger . error ( "Problem while trying to set User part on a clones response for a P2P call, " + e ) ; } CallDetailRecord callRecord = records . getCallDetailRecord ( ( Sid ) linkedRequest . getSession ( ) . getAttribute ( CDR_SID ) ) ; Sid organizationSid = daoManager . getAccountsDao ( ) . getAccount ( callRecord . getAccountSid ( ) ) . getOrganizationSid ( ) ; if ( response . getRawContent ( ) != null && response . getRawContent ( ) . length > 0 ) { final byte [ ] sdp = response . getRawContent ( ) ; String offer = null ; if ( response . getContentType ( ) . equalsIgnoreCase ( "application/sdp" ) && patchForNat ) { Registration registration = daoManager . getRegistrationsDao ( ) . getRegistration ( callRecord . getTo ( ) , organizationSid ) ; String externalIp ; if ( registration != null ) { externalIp = registration . getLocation ( ) . split ( ":" ) [ 1 ] . split ( "@" ) [ 1 ] ; } else { externalIp = callRecord . getTo ( ) . split ( ":" ) [ 1 ] . split ( "@" ) [ 1 ] ; } try { if ( logger . isDebugEnabled ( ) ) { logger . debug ( "Got original address from Registration :" + externalIp ) ; } offer = patch ( sdp , externalIp ) ; } catch ( SdpException e ) { logger . error ( "Unexpected exception while patching sdp " , e ) ; } } if ( offer != null ) { clonedResponse . setContent ( offer , response . getContentType ( ) ) ; } else { clonedResponse . setContent ( sdp , response . getContentType ( ) ) ; } } addHeadersToMessage ( clonedResponse , getCustomHeaders ( response , "X-" ) ) ; clonedResponse . send ( ) ; if ( callRecord != null ) { if ( logger . isInfoEnabled ( ) ) { logger . info ( "CDR found! Updating" ) ; } if ( ! linkedRequest . getMethod ( ) . equalsIgnoreCase ( "BYE" ) ) { if ( response . getStatus ( ) == 100 || response . getStatus ( ) == 180 || response . getStatus ( ) == 183 ) { callRecord = callRecord . setStatus ( CallStateChanged . State . RINGING . name ( ) ) ; } else if ( response . getStatus ( ) == 200 || response . getStatus ( ) == 202 ) { callRecord = callRecord . setStatus ( CallStateChanged . State . IN_PROGRESS . name ( ) ) ; callRecord = callRecord . setAnsweredBy ( ( ( SipURI ) response . getTo ( ) . getURI ( ) ) . getUser ( ) ) ; final DateTime now = DateTime . now ( ) ; callRecord = callRecord . setStartTime ( now ) ; } else if ( response . getStatus ( ) == 486 || response . getStatus ( ) == 600 ) { callRecord = callRecord . setStatus ( CallStateChanged . State . BUSY . name ( ) ) ; } else if ( response . getStatus ( ) > 400 ) { callRecord = callRecord . setStatus ( CallStateChanged . State . FAILED . name ( ) ) ; } } else { callRecord = callRecord . setStatus ( CallStateChanged . State . COMPLETED . name ( ) ) ; final DateTime now = DateTime . now ( ) ; callRecord = callRecord . setEndTime ( now ) ; final int seconds = ( int ) ( ( DateTime . now ( ) . getMillis ( ) - callRecord . getStartTime ( ) . getMillis ( ) ) / 1000 ) ; callRecord = callRecord . setDuration ( seconds ) ; } records . updateCallDetailRecord ( callRecord ) ; sendCallInfoStreamEvent ( system , linkedRequest , CallStateChanged . State . valueOf ( callRecord . getStatus ( ) ) ) ; } }
@param response
@throws IOException