| /* |
| * Conditions Of Use |
| * |
| * This software was developed by employees of the National Institute of |
| * Standards and Technology (NIST), an agency of the Federal Government. |
| * Pursuant to title 15 Untied States Code Section 105, works of NIST |
| * employees are not subject to copyright protection in the United States |
| * and are considered to be in the public domain. As a result, a formal |
| * license is not needed to use the software. |
| * |
| * This software is provided by NIST as a service and is expressly |
| * provided "AS IS." NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED |
| * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT |
| * AND DATA ACCURACY. NIST does not warrant or make any representations |
| * regarding the use of the software or the results thereof, including but |
| * not limited to the correctness, accuracy, reliability or usefulness of |
| * the software. |
| * |
| * Permission to use this software is contingent upon your acceptance |
| * of the terms of this agreement |
| * |
| * . |
| * |
| */ |
| /****************************************************************************** |
| * Product of NIST/ITL Advanced Networking Technologies Division (ANTD). * |
| ******************************************************************************/ |
| package gov.nist.javax.sip.stack; |
| |
| import gov.nist.javax.sip.header.*; |
| import gov.nist.javax.sip.message.*; |
| import gov.nist.javax.sip.parser.*; |
| import gov.nist.core.*; |
| import java.net.*; |
| import java.io.*; |
| import java.text.ParseException; |
| import java.util.TimerTask; |
| |
| import javax.sip.address.Hop; |
| |
| /* |
| * Ahmet Uyar <auyar@csit.fsu.edu>sent in a bug report for TCP operation of the JAIN sipStack. |
| * Niklas Uhrberg suggested that a mechanism be added to limit the number of simultaneous open |
| * connections. The TLS Adaptations were contributed by Daniel Martinez. Hagai Sela contributed a |
| * bug fix for symmetric nat. Jeroen van Bemmel added compensation for buggy clients ( Microsoft |
| * RTC clients ). Bug fixes by viswashanti.kadiyala@antepo.com, Joost Yervante Damand |
| */ |
| |
| /** |
| * This is a stack abstraction for TCP connections. This abstracts a stream of parsed messages. |
| * The SIP sipStack starts this from the main SIPStack class for each connection that it accepts. |
| * It starts a message parser in its own thread and talks to the message parser via a pipe. The |
| * message parser calls back via the parseError or processMessage functions that are defined as |
| * part of the SIPMessageListener interface. |
| * |
| * @see gov.nist.javax.sip.parser.PipelinedMsgParser |
| * |
| * |
| * @author M. Ranganathan <br/> |
| * |
| * @version 1.2 $Revision: 1.59 $ $Date: 2009/11/20 04:45:53 $ |
| */ |
| public class TCPMessageChannel extends MessageChannel implements SIPMessageListener, Runnable, |
| RawMessageChannel { |
| |
| private Socket mySock; |
| |
| private PipelinedMsgParser myParser; |
| |
| protected InputStream myClientInputStream; // just to pass to thread. |
| |
| protected OutputStream myClientOutputStream; |
| |
| protected String key; |
| |
| protected boolean isCached; |
| |
| protected boolean isRunning; |
| |
| private Thread mythread; |
| |
| protected SIPTransactionStack sipStack; |
| |
| protected String myAddress; |
| |
| protected int myPort; |
| |
| protected InetAddress peerAddress; |
| |
| protected int peerPort; |
| |
| protected String peerProtocol; |
| |
| // Incremented whenever a transaction gets assigned |
| // to the message channel and decremented when |
| // a transaction gets freed from the message channel. |
| // protected int useCount; |
| |
| private TCPMessageProcessor tcpMessageProcessor; |
| |
| protected TCPMessageChannel(SIPTransactionStack sipStack) { |
| this.sipStack = sipStack; |
| |
| } |
| |
| /** |
| * Constructor - gets called from the SIPStack class with a socket on accepting a new client. |
| * All the processing of the message is done here with the sipStack being freed up to handle |
| * new connections. The sock input is the socket that is returned from the accept. Global data |
| * that is shared by all threads is accessible in the Server structure. |
| * |
| * @param sock Socket from which to read and write messages. The socket is already connected |
| * (was created as a result of an accept). |
| * |
| * @param sipStack Ptr to SIP Stack |
| */ |
| |
| protected TCPMessageChannel(Socket sock, SIPTransactionStack sipStack, |
| TCPMessageProcessor msgProcessor) throws IOException { |
| |
| if (sipStack.isLoggingEnabled()) { |
| sipStack.getStackLogger().logDebug("creating new TCPMessageChannel "); |
| sipStack.getStackLogger().logStackTrace(); |
| } |
| mySock = sock; |
| peerAddress = mySock.getInetAddress(); |
| myAddress = msgProcessor.getIpAddress().getHostAddress(); |
| myClientInputStream = mySock.getInputStream(); |
| myClientOutputStream = mySock.getOutputStream(); |
| mythread = new Thread(this); |
| mythread.setDaemon(true); |
| mythread.setName("TCPMessageChannelThread"); |
| // Stash away a pointer to our sipStack structure. |
| this.sipStack = sipStack; |
| this.peerPort = mySock.getPort(); |
| |
| this.tcpMessageProcessor = msgProcessor; |
| this.myPort = this.tcpMessageProcessor.getPort(); |
| // Bug report by Vishwashanti Raj Kadiayl |
| super.messageProcessor = msgProcessor; |
| // Can drop this after response is sent potentially. |
| mythread.start(); |
| } |
| |
| /** |
| * Constructor - connects to the given inet address. Acknowledgement -- Lamine Brahimi (IBM |
| * Zurich) sent in a bug fix for this method. A thread was being uncessarily created. |
| * |
| * @param inetAddr inet address to connect to. |
| * @param sipStack is the sip sipStack from which we are created. |
| * @throws IOException if we cannot connect. |
| */ |
| protected TCPMessageChannel(InetAddress inetAddr, int port, SIPTransactionStack sipStack, |
| TCPMessageProcessor messageProcessor) throws IOException { |
| if (sipStack.isLoggingEnabled()) { |
| sipStack.getStackLogger().logDebug("creating new TCPMessageChannel "); |
| sipStack.getStackLogger().logStackTrace(); |
| } |
| this.peerAddress = inetAddr; |
| this.peerPort = port; |
| this.myPort = messageProcessor.getPort(); |
| this.peerProtocol = "TCP"; |
| this.sipStack = sipStack; |
| this.tcpMessageProcessor = messageProcessor; |
| this.myAddress = messageProcessor.getIpAddress().getHostAddress(); |
| // Bug report by Vishwashanti Raj Kadiayl |
| this.key = MessageChannel.getKey(peerAddress, peerPort, "TCP"); |
| super.messageProcessor = messageProcessor; |
| |
| } |
| |
| /** |
| * Returns "true" as this is a reliable transport. |
| */ |
| public boolean isReliable() { |
| return true; |
| } |
| |
| /** |
| * Close the message channel. |
| */ |
| public void close() { |
| try { |
| if (mySock != null) { |
| mySock.close(); |
| mySock = null; |
| } |
| if (sipStack.isLoggingEnabled()) |
| sipStack.getStackLogger().logDebug("Closing message Channel " + this); |
| } catch (IOException ex) { |
| if (sipStack.isLoggingEnabled()) |
| sipStack.getStackLogger().logDebug("Error closing socket " + ex); |
| } |
| } |
| |
| /** |
| * Get my SIP Stack. |
| * |
| * @return The SIP Stack for this message channel. |
| */ |
| public SIPTransactionStack getSIPStack() { |
| return sipStack; |
| } |
| |
| /** |
| * get the transport string. |
| * |
| * @return "tcp" in this case. |
| */ |
| public String getTransport() { |
| return "TCP"; |
| } |
| |
| /** |
| * get the address of the client that sent the data to us. |
| * |
| * @return Address of the client that sent us data that resulted in this channel being |
| * created. |
| */ |
| public String getPeerAddress() { |
| if (peerAddress != null) { |
| return peerAddress.getHostAddress(); |
| } else |
| return getHost(); |
| } |
| |
| protected InetAddress getPeerInetAddress() { |
| return peerAddress; |
| } |
| |
| public String getPeerProtocol() { |
| return this.peerProtocol; |
| } |
| |
| /** |
| * Send message to whoever is connected to us. Uses the topmost via address to send to. |
| * |
| * @param msg is the message to send. |
| * @param retry |
| */ |
| private void sendMessage(byte[] msg, boolean retry) throws IOException { |
| |
| /* |
| * Patch from kircuv@dev.java.net (Issue 119 ) This patch avoids the case where two |
| * TCPMessageChannels are now pointing to the same socket.getInputStream(). |
| * |
| * JvB 22/5 removed |
| */ |
| // Socket s = this.sipStack.ioHandler.getSocket(IOHandler.makeKey( |
| // this.peerAddress, this.peerPort)); |
| Socket sock = this.sipStack.ioHandler.sendBytes(this.messageProcessor.getIpAddress(), |
| this.peerAddress, this.peerPort, this.peerProtocol, msg, retry, this); |
| |
| // Created a new socket so close the old one and stick the new |
| // one in its place but dont do this if it is a datagram socket. |
| // (could have replied via udp but received via tcp!). |
| // if (mySock == null && s != null) { |
| // this.uncache(); |
| // } else |
| if (sock != mySock && sock != null) { |
| try { |
| if (mySock != null) |
| mySock.close(); |
| } catch (IOException ex) { |
| } |
| mySock = sock; |
| this.myClientInputStream = mySock.getInputStream(); |
| this.myClientOutputStream = mySock.getOutputStream(); |
| Thread thread = new Thread(this); |
| thread.setDaemon(true); |
| thread.setName("TCPMessageChannelThread"); |
| thread.start(); |
| } |
| |
| } |
| |
| /** |
| * Return a formatted message to the client. We try to re-connect with the peer on the other |
| * end if possible. |
| * |
| * @param sipMessage Message to send. |
| * @throws IOException If there is an error sending the message |
| */ |
| public void sendMessage(SIPMessage sipMessage) throws IOException { |
| byte[] msg = sipMessage.encodeAsBytes(this.getTransport()); |
| |
| long time = System.currentTimeMillis(); |
| |
| // JvB: also retry for responses, if the connection is gone we should |
| // try to reconnect |
| this.sendMessage(msg, /* sipMessage instanceof SIPRequest */true); |
| |
| if (this.sipStack.getStackLogger().isLoggingEnabled(ServerLogger.TRACE_MESSAGES)) |
| logMessage(sipMessage, peerAddress, peerPort, time); |
| } |
| |
| /** |
| * Send a message to a specified address. |
| * |
| * @param message Pre-formatted message to send. |
| * @param receiverAddress Address to send it to. |
| * @param receiverPort Receiver port. |
| * @throws IOException If there is a problem connecting or sending. |
| */ |
| public void sendMessage(byte message[], InetAddress receiverAddress, int receiverPort, |
| boolean retry) throws IOException { |
| if (message == null || receiverAddress == null) |
| throw new IllegalArgumentException("Null argument"); |
| Socket sock = this.sipStack.ioHandler.sendBytes(this.messageProcessor.getIpAddress(), |
| receiverAddress, receiverPort, "TCP", message, retry, this); |
| if (sock != mySock && sock != null) { |
| if (mySock != null) { |
| /* |
| * Delay the close of the socket for some time in case it is being used. |
| */ |
| sipStack.getTimer().schedule(new TimerTask() { |
| @Override |
| public boolean cancel() { |
| try { |
| mySock.close(); |
| super.cancel(); |
| } catch (IOException ex) { |
| |
| } |
| return true; |
| } |
| |
| @Override |
| public void run() { |
| try { |
| mySock.close(); |
| } catch (IOException ex) { |
| |
| } |
| } |
| }, 8000); |
| } |
| |
| mySock = sock; |
| this.myClientInputStream = mySock.getInputStream(); |
| this.myClientOutputStream = mySock.getOutputStream(); |
| // start a new reader on this end of the pipe. |
| Thread mythread = new Thread(this); |
| mythread.setDaemon(true); |
| mythread.setName("TCPMessageChannelThread"); |
| mythread.start(); |
| } |
| |
| } |
| |
| /** |
| * Exception processor for exceptions detected from the parser. (This is invoked by the parser |
| * when an error is detected). |
| * |
| * @param sipMessage -- the message that incurred the error. |
| * @param ex -- parse exception detected by the parser. |
| * @param header -- header that caused the error. |
| * @throws ParseException Thrown if we want to reject the message. |
| */ |
| public void handleException(ParseException ex, SIPMessage sipMessage, Class hdrClass, |
| String header, String message) throws ParseException { |
| if (sipStack.isLoggingEnabled()) |
| sipStack.getStackLogger().logException(ex); |
| // Log the bad message for later reference. |
| if ((hdrClass != null) |
| && (hdrClass.equals(From.class) || hdrClass.equals(To.class) |
| || hdrClass.equals(CSeq.class) || hdrClass.equals(Via.class) |
| || hdrClass.equals(CallID.class) || hdrClass.equals(RequestLine.class) || hdrClass |
| .equals(StatusLine.class))) { |
| if (sipStack.isLoggingEnabled()) { |
| sipStack.getStackLogger().logDebug( |
| "Encountered Bad Message \n" + sipMessage.toString()); |
| } |
| |
| // JvB: send a 400 response for requests (except ACK) |
| // Currently only UDP, @todo also other transports |
| String msgString = sipMessage.toString(); |
| if (!msgString.startsWith("SIP/") && !msgString.startsWith("ACK ")) { |
| |
| String badReqRes = createBadReqRes(msgString, ex); |
| if (badReqRes != null) { |
| if (sipStack.isLoggingEnabled()) { |
| sipStack.getStackLogger().logDebug("Sending automatic 400 Bad Request:"); |
| sipStack.getStackLogger().logDebug(badReqRes); |
| } |
| try { |
| this.sendMessage(badReqRes.getBytes(), this.getPeerInetAddress(), this |
| .getPeerPort(), false); |
| } catch (IOException e) { |
| this.sipStack.getStackLogger().logException(e); |
| } |
| } else { |
| if (sipStack.isLoggingEnabled()) { |
| sipStack.getStackLogger().logDebug( |
| "Could not formulate automatic 400 Bad Request"); |
| } |
| } |
| } |
| |
| throw ex; |
| } else { |
| sipMessage.addUnparsed(header); |
| } |
| } |
| |
| /** |
| * Gets invoked by the parser as a callback on successful message parsing (i.e. no parser |
| * errors). |
| * |
| * @param sipMessage Mesage to process (this calls the application for processing the |
| * message). |
| */ |
| public void processMessage(SIPMessage sipMessage) throws Exception { |
| try { |
| if (sipMessage.getFrom() == null |
| || // sipMessage.getFrom().getTag() |
| // == null || |
| sipMessage.getTo() == null || sipMessage.getCallId() == null |
| || sipMessage.getCSeq() == null || sipMessage.getViaHeaders() == null) { |
| String badmsg = sipMessage.encode(); |
| if (sipStack.isLoggingEnabled()) { |
| sipStack.getStackLogger().logDebug(">>> Dropped Bad Msg"); |
| sipStack.getStackLogger().logDebug(badmsg); |
| } |
| |
| return; |
| } |
| |
| ViaList viaList = sipMessage.getViaHeaders(); |
| // For a request |
| // first via header tells where the message is coming from. |
| // For response, this has already been recorded in the outgoing |
| // message. |
| if (sipMessage instanceof SIPRequest) { |
| Via v = (Via) viaList.getFirst(); |
| Hop hop = sipStack.addressResolver.resolveAddress(v.getHop()); |
| this.peerProtocol = v.getTransport(); |
| try { |
| this.peerAddress = mySock.getInetAddress(); |
| // Check to see if the received parameter matches |
| // the peer address and tag it appropriately. |
| |
| // JvB: dont do this. It is both costly and incorrect |
| // Must set received also when it is a FQDN, regardless |
| // whether |
| // it resolves to the correct IP address |
| // InetAddress sentByAddress = |
| // InetAddress.getByName(hop.getHost()); |
| // JvB: if sender added 'rport', must always set received |
| if (v.hasParameter(Via.RPORT) |
| || !hop.getHost().equals(this.peerAddress.getHostAddress())) { |
| v.setParameter(Via.RECEIVED, this.peerAddress.getHostAddress()); |
| } |
| // @@@ hagai |
| // JvB: technically, may only do this when Via already |
| // contains |
| // rport |
| v.setParameter(Via.RPORT, Integer.toString(this.peerPort)); |
| } catch (java.text.ParseException ex) { |
| InternalErrorHandler.handleException(ex, sipStack.getStackLogger()); |
| } |
| // Use this for outgoing messages as well. |
| if (!this.isCached) { |
| ((TCPMessageProcessor) this.messageProcessor).cacheMessageChannel(this); |
| this.isCached = true; |
| int remotePort = ((java.net.InetSocketAddress) mySock.getRemoteSocketAddress()).getPort(); |
| String key = IOHandler.makeKey(mySock.getInetAddress(), remotePort); |
| sipStack.ioHandler.putSocket(key, mySock); |
| } |
| } |
| |
| |
| // Foreach part of the request header, fetch it and process it |
| |
| long receptionTime = System.currentTimeMillis(); |
| |
| if (sipMessage instanceof SIPRequest) { |
| // This is a request - process the request. |
| SIPRequest sipRequest = (SIPRequest) sipMessage; |
| // Create a new sever side request processor for this |
| // message and let it handle the rest. |
| |
| if (sipStack.isLoggingEnabled()) { |
| sipStack.getStackLogger().logDebug("----Processing Message---"); |
| } |
| |
| // Check for reasonable size - reject message |
| // if it is too long. |
| if (this.sipStack.getStackLogger().isLoggingEnabled(ServerLogger.TRACE_MESSAGES)) { |
| sipStack.serverLogger.logMessage(sipMessage, this.getPeerHostPort().toString(), |
| this.getMessageProcessor().getIpAddress().getHostAddress() + ":" |
| + this.getMessageProcessor().getPort(), false, receptionTime); |
| |
| } |
| |
| if (sipStack.getMaxMessageSize() > 0 |
| && sipRequest.getSize() |
| + (sipRequest.getContentLength() == null ? 0 : sipRequest |
| .getContentLength().getContentLength()) > sipStack |
| .getMaxMessageSize()) { |
| SIPResponse sipResponse = sipRequest |
| .createResponse(SIPResponse.MESSAGE_TOO_LARGE); |
| byte[] resp = sipResponse.encodeAsBytes(this.getTransport()); |
| this.sendMessage(resp, false); |
| throw new Exception("Message size exceeded"); |
| } |
| |
| ServerRequestInterface sipServerRequest = sipStack.newSIPServerRequest( |
| sipRequest, this); |
| |
| if (sipServerRequest != null) { |
| try { |
| sipServerRequest.processRequest(sipRequest, this); |
| } finally { |
| if (sipServerRequest instanceof SIPTransaction) { |
| SIPServerTransaction sipServerTx = (SIPServerTransaction) sipServerRequest; |
| if (!sipServerTx.passToListener()) |
| ((SIPTransaction) sipServerRequest).releaseSem(); |
| } |
| } |
| } else { |
| if (sipStack.isLoggingEnabled()) |
| this.sipStack.getStackLogger() |
| .logWarning("Dropping request -- could not acquire semaphore in 10 sec"); |
| } |
| |
| } else { |
| SIPResponse sipResponse = (SIPResponse) sipMessage; |
| // JvB: dont do this |
| // if (sipResponse.getStatusCode() == 100) |
| // sipResponse.getTo().removeParameter("tag"); |
| try { |
| sipResponse.checkHeaders(); |
| } catch (ParseException ex) { |
| if (sipStack.isLoggingEnabled()) |
| sipStack.getStackLogger() |
| .logError("Dropping Badly formatted response message >>> " |
| + sipResponse); |
| return; |
| } |
| // This is a response message - process it. |
| // Check the size of the response. |
| // If it is too large dump it silently. |
| if (sipStack.getMaxMessageSize() > 0 |
| && sipResponse.getSize() |
| + (sipResponse.getContentLength() == null ? 0 : sipResponse |
| .getContentLength().getContentLength()) > sipStack |
| .getMaxMessageSize()) { |
| if (sipStack.isLoggingEnabled()) |
| this.sipStack.getStackLogger().logDebug("Message size exceeded"); |
| return; |
| |
| } |
| ServerResponseInterface sipServerResponse = sipStack.newSIPServerResponse( |
| sipResponse, this); |
| if (sipServerResponse != null) { |
| try { |
| if (sipServerResponse instanceof SIPClientTransaction |
| && !((SIPClientTransaction) sipServerResponse) |
| .checkFromTag(sipResponse)) { |
| if (sipStack.isLoggingEnabled()) |
| sipStack.getStackLogger() |
| .logError("Dropping response message with invalid tag >>> " |
| + sipResponse); |
| return; |
| } |
| |
| sipServerResponse.processResponse(sipResponse, this); |
| } finally { |
| if (sipServerResponse instanceof SIPTransaction |
| && !((SIPTransaction) sipServerResponse).passToListener()) |
| ((SIPTransaction) sipServerResponse).releaseSem(); |
| } |
| } else { |
| sipStack |
| .getStackLogger() |
| .logWarning( |
| "Application is blocked -- could not acquire semaphore -- dropping response"); |
| } |
| } |
| } finally { |
| } |
| } |
| |
| /** |
| * This gets invoked when thread.start is called from the constructor. Implements a message |
| * loop - reading the tcp connection and processing messages until we are done or the other |
| * end has closed. |
| */ |
| public void run() { |
| Pipeline hispipe = null; |
| // Create a pipeline to connect to our message parser. |
| hispipe = new Pipeline(myClientInputStream, sipStack.readTimeout, |
| ((SIPTransactionStack) sipStack).getTimer()); |
| // Create a pipelined message parser to read and parse |
| // messages that we write out to him. |
| myParser = new PipelinedMsgParser(this, hispipe, this.sipStack.getMaxMessageSize()); |
| // Start running the parser thread. |
| myParser.processInput(); |
| // bug fix by Emmanuel Proulx |
| int bufferSize = 4096; |
| this.tcpMessageProcessor.useCount++; |
| this.isRunning = true; |
| try { |
| while (true) { |
| try { |
| byte[] msg = new byte[bufferSize]; |
| int nbytes = myClientInputStream.read(msg, 0, bufferSize); |
| // no more bytes to read... |
| if (nbytes == -1) { |
| hispipe.write("\r\n\r\n".getBytes("UTF-8")); |
| try { |
| if (sipStack.maxConnections != -1) { |
| synchronized (tcpMessageProcessor) { |
| tcpMessageProcessor.nConnections--; |
| tcpMessageProcessor.notify(); |
| } |
| } |
| hispipe.close(); |
| mySock.close(); |
| } catch (IOException ioex) { |
| } |
| return; |
| } |
| hispipe.write(msg, 0, nbytes); |
| |
| } catch (IOException ex) { |
| // Terminate the message. |
| try { |
| hispipe.write("\r\n\r\n".getBytes("UTF-8")); |
| } catch (Exception e) { |
| // InternalErrorHandler.handleException(e); |
| } |
| |
| try { |
| if (sipStack.isLoggingEnabled()) |
| sipStack.getStackLogger().logDebug("IOException closing sock " + ex); |
| try { |
| if (sipStack.maxConnections != -1) { |
| synchronized (tcpMessageProcessor) { |
| tcpMessageProcessor.nConnections--; |
| // System.out.println("Notifying!"); |
| tcpMessageProcessor.notify(); |
| } |
| } |
| mySock.close(); |
| hispipe.close(); |
| } catch (IOException ioex) { |
| } |
| } catch (Exception ex1) { |
| // Do nothing. |
| } |
| return; |
| } catch (Exception ex) { |
| InternalErrorHandler.handleException(ex, sipStack.getStackLogger()); |
| } |
| } |
| } finally { |
| this.isRunning = false; |
| this.tcpMessageProcessor.remove(this); |
| this.tcpMessageProcessor.useCount--; |
| myParser.close(); |
| } |
| |
| } |
| |
| protected void uncache() { |
| if (isCached && !isRunning) { |
| this.tcpMessageProcessor.remove(this); |
| } |
| } |
| |
| /** |
| * Equals predicate. |
| * |
| * @param other is the other object to compare ourselves to for equals |
| */ |
| |
| public boolean equals(Object other) { |
| |
| if (!this.getClass().equals(other.getClass())) |
| return false; |
| else { |
| TCPMessageChannel that = (TCPMessageChannel) other; |
| if (this.mySock != that.mySock) |
| return false; |
| else |
| return true; |
| } |
| } |
| |
| /** |
| * Get an identifying key. This key is used to cache the connection and re-use it if |
| * necessary. |
| */ |
| public String getKey() { |
| if (this.key != null) { |
| return this.key; |
| } else { |
| this.key = MessageChannel.getKey(this.peerAddress, this.peerPort, "TCP"); |
| return this.key; |
| } |
| } |
| |
| /** |
| * Get the host to assign to outgoing messages. |
| * |
| * @return the host to assign to the via header. |
| */ |
| public String getViaHost() { |
| return myAddress; |
| } |
| |
| /** |
| * Get the port for outgoing messages sent from the channel. |
| * |
| * @return the port to assign to the via header. |
| */ |
| public int getViaPort() { |
| return myPort; |
| } |
| |
| /** |
| * Get the port of the peer to whom we are sending messages. |
| * |
| * @return the peer port. |
| */ |
| public int getPeerPort() { |
| return peerPort; |
| } |
| |
| public int getPeerPacketSourcePort() { |
| return this.peerPort; |
| } |
| |
| public InetAddress getPeerPacketSourceAddress() { |
| return this.peerAddress; |
| } |
| |
| /** |
| * TCP Is not a secure protocol. |
| */ |
| public boolean isSecure() { |
| return false; |
| } |
| } |