예제 #1
0
    def forwardRequestToTarget(self, connectedSIPMessageToSend, targetURI=None, transportIDForHeader=None):
        '''
        https://tools.ietf.org/html/rfc3261#section-16.6
        Special consideration for stateless proxies explained in section 16.11
        - The unique branch id must be invariant for requests with identical headers.
        - Item 10 will send the forwarded message directly to a transportConnection, not transaction.
        '''
        sipMessage = connectedSIPMessageToSend.sipMessage
        # TODO: in progress
        # 1. Copy request
        #   (already copied it)
        # 2.  Update the Request-URI
        # TODO: need to remove any URI parameters that are not allowed in a Request URI.
        # TODO: When we set an attribute on the start line, we may need to manually mark the sip message as dirty.
        if targetURI:
            sipMessage.startLine.requestURI = targetURI.rawString
        # 3.  Update the Max-Forwards header field
        if sipMessage.maxForwards is not None:
            # TODO:  When you use -= 1 on an integer sip header field's integerValue parameter, does that work?  Need to write a test.
            sipMessage.header.maxForwardsHeaderField.integerValue -= 1
        else:
            # TODO: inserting a header field - we should make a method that inserts it using an aesthetically-pleasing order
            # relative to other header fields.  Each header field concrete subclass would have an integer sort attribute for that.
            sipMessage.header.addHeaderField(MaxForwardsSIPHeaderField.newForIntegerValue(70))
        # 4.  Optionally add a Record-route header field value
        # TODO: is this the best way to get the URI host?
        # Are record-route header fields only used for INVITE?  Should we only do this if it's an INVITE?
        #    Answer:  No.  See RFC3261 16.6  point 4
        # TODO: sip or sips scheme?  Should that be derived from the transportConnection?  For now, hard-code to 'sip'
        recordRouteURI = SIPURI.newForAttributes(host=self.transports[0].bindAddress, port=self.transports[0].bindPort, scheme='sip', parameterNamesAndValueStrings={'lr': None})
        recordRouteHeaderField = RecordRouteSIPHeaderField.newForAttributes(recordRouteURI)
        if transportIDForHeader:
            # Do we want to put that state into this header or the Via?
            #    Answer:  we want it here.  See RFC3261 16.6  point 4
            recordRouteHeaderField.parameterNamedPut('bobstackTransportID', transportIDForHeader)
        sipMessage.header.addHeaderFieldBeforeHeaderFieldsOfSameClass(recordRouteHeaderField)

        # 5.  Optionally add additional header fields
        # TODO: make the server header field a user-settable parameter.
        sipMessage.header.addHeaderField(ServerSIPHeaderField.newForValueString('BobStack'))

        # 6.  Postprocess routing information
        routeURIs = sipMessage.header.routeURIs
        if routeURIs:
            if 'lr' not in routeURIs[0].parameterNames:
                # TODO: When we set an attribute on the start line, we may need to manually mark the sip message as dirty.
                sipMessage.header.addHeaderFieldAfterHeaderFieldsOfSameClass(RouteSIPHeaderField.newForAttributes(SIPURI.newParsedFrom(sipMessage.startLine.requestURI)))
                sipMessage.startLine.requestURI = routeURIs[0].rawString
                # TODO: Is the class actually the same object, considering that we're accessing it from a different directory?  Trap for young players.
                # No, as a matter of fact, it is not the same object, and that's a problem.  We've worked around it over there,
                # but we need to do better.
                sipMessage.header.removeFirstHeaderFieldOfClass(RouteSIPHeaderField)
                uriToDetermineNextHop = sipMessage.requestURI
            else:
                uriToDetermineNextHop = routeURIs[0]
        else:
            # TODO: seriously, we need to make start lines use first-class SIPURIs.
            uriToDetermineNextHop = SIPURI.newParsedFrom(sipMessage.requestURI)

        # 7.  Determine the next-hop address, port, and transport
        nextHopConnectedTransportConnection = self.connectedTransportConnectionForSIPURI(uriToDetermineNextHop)
        if not nextHopConnectedTransportConnection:
            # TODO:  we could not connect to the next-hop.  Return some error code.  Which error code and reason phrase?  400 and could not connect are NOT correct.
            raise SendResponseSIPEntityException(statusCodeInteger=400, reasonPhraseString='Could not connect')

        # 8.  Add a Via header field value
        # TODO:  For now, don't do the loop / spiral detection stuff.
        sipMessage.header.addHeaderFieldBeforeHeaderFieldsOfSameClass(self.newViaHeaderFieldForSIPMessage(sipMessage))

        # 9.  Add a Content-Length header field if necessary
        # TODO: if the target transport is stream-oriented, e.g. TLS or TCP, and the message has no Content-Length: header field, add one.
        # TODO: So we'll need to wait until step 7 is done, to know the transport.
        if not sipMessage.header.contentLengthHeaderField:
            if nextHopConnectedTransportConnection.isStateful:
                # TODO: if the target transport is stream-oriented, e.g. TLS or TCP, and the message has no Content-Length: header field, add one.
                sipMessage.header.addHeaderField(self.newContentLengthHeaderFieldForSIPMessage(sipMessage))

        # TODO:  This line is a work-around for a problem that we really
        # need to address: when you hack a header or header field value, that
        # does not clear the SIPMessage's rawString.  Fixing that is not trivial,
        # considering that we observe good layering practices in the message - header - hf
        # object complex.  We will probably need to use events for that.  Don't blow this
        # off, but for now, just manually clear the rawString.
        sipMessage.clearRawString()

        # 10. Forward the new request
        # TODO:  exception handling?
        nextHopConnectedTransportConnection.sendMessage(sipMessage)