Bug 526207: Make IP address and port information available to HTTP activity observers. r=biesi
authorHonza Bambas <honzab.moz@firemni.cz>
Sun, 10 Apr 2011 10:33:08 -0700
changeset 67756 8bf059c601c850671836689773ff951387e838de
parent 67755 f7d8fe104567acce9ab04b1689d5b08fca0474eb
child 67757 90140c158e789994c6615d7090dc7f7031fac504
push id19425
push userzackw@panix.com
push dateSun, 10 Apr 2011 17:33:43 +0000
treeherdermozilla-central@8bf059c601c8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbiesi
bugs526207
milestone2.2a1pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 526207: Make IP address and port information available to HTTP activity observers. r=biesi
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelChild.h
netwerk/protocol/http/nsAHttpTransaction.h
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpChannel.h
netwerk/protocol/http/nsHttpConnection.cpp
netwerk/protocol/http/nsHttpConnectionMgr.cpp
netwerk/protocol/http/nsHttpPipeline.cpp
netwerk/protocol/http/nsHttpTransaction.cpp
netwerk/protocol/http/nsIHttpChannelInternal.idl
netwerk/test/unit/test_traceable_channel.js
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1001,16 +1001,46 @@ HttpChannelChild::SetRequestHeader(const
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpChannelChild::SetupFallbackChannel(const char *aFallbackKey)
 {
   DROP_DEAD();
 }
 
+// The next four _should_ be implemented, but we need to figure out how
+// to transfer the data from the chrome process first.
+
+NS_IMETHODIMP
+HttpChannelChild::GetRemoteAddress(nsACString & _result)
+{
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+HttpChannelChild::GetRemotePort(PRInt32 * _result)
+{
+  NS_ENSURE_ARG_POINTER(_result);
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+HttpChannelChild::GetLocalAddress(nsACString & _result)
+{
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+HttpChannelChild::GetLocalPort(PRInt32 * _result)
+{
+  NS_ENSURE_ARG_POINTER(_result);
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+
 //-----------------------------------------------------------------------------
 // HttpChannelChild::nsICacheInfoChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpChannelChild::GetCacheTokenExpirationTime(PRUint32 *_retval)
 {
   NS_ENSURE_ARG_POINTER(_retval);
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -104,16 +104,20 @@ public:
   NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo);
   NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext);
   // HttpBaseChannel::nsIHttpChannel
   NS_IMETHOD SetRequestHeader(const nsACString& aHeader, 
                               const nsACString& aValue, 
                               PRBool aMerge);
   // nsIHttpChannelInternal
   NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey);
+  NS_IMETHOD GetLocalAddress(nsACString& addr);
+  NS_IMETHOD GetLocalPort(PRInt32* port);
+  NS_IMETHOD GetRemoteAddress(nsACString& addr);
+  NS_IMETHOD GetRemotePort(PRInt32* port);
   // nsISupportsPriority
   NS_IMETHOD SetPriority(PRInt32 value);
   // nsIResumableChannel
   NS_IMETHOD ResumeAt(PRUint64 startPos, const nsACString& entityID);
 
   // IPDL holds a reference while the PHttpChannel protocol is live (starting at
   // AsyncOpen, and ending at either OnStopRequest or any IPDL error, either of
   // which call NeckoChild::DeallocPHttpChannel()).
--- a/netwerk/protocol/http/nsAHttpTransaction.h
+++ b/netwerk/protocol/http/nsAHttpTransaction.h
@@ -40,16 +40,17 @@
 
 #include "nsISupports.h"
 
 class nsAHttpConnection;
 class nsAHttpSegmentReader;
 class nsAHttpSegmentWriter;
 class nsIInterfaceRequestor;
 class nsIEventTarget;
+class nsITransport;
 class nsHttpRequestHead;
 
 //----------------------------------------------------------------------------
 // Abstract base class for a HTTP transaction:
 //
 // A transaction is a "sink" for the response data.  The connection pushes
 // data to the transaction by writing to it.  The transaction supports
 // WriteSegments and may refuse to accept data if its buffers are full (its
@@ -57,23 +58,24 @@ class nsHttpRequestHead;
 //----------------------------------------------------------------------------
 
 class nsAHttpTransaction : public nsISupports
 {
 public:
     // called by the connection when it takes ownership of the transaction.
     virtual void SetConnection(nsAHttpConnection *) = 0;
 
-    // called by the connection to get security callbacks to set on the 
+    // called by the connection to get security callbacks to set on the
     // socket transport.
     virtual void GetSecurityCallbacks(nsIInterfaceRequestor **,
                                       nsIEventTarget **) = 0;
 
     // called to report socket status (see nsITransportEventSink)
-    virtual void OnTransportStatus(nsresult status, PRUint64 progress) = 0;
+    virtual void OnTransportStatus(nsITransport* transport,
+                                   nsresult status, PRUint64 progress) = 0;
 
     // called to check the transaction status.
     virtual PRBool   IsDone() = 0;
     virtual nsresult Status() = 0;
 
     // called to find out how much request data is available for writing.
     virtual PRUint32 Available() = 0;
 
@@ -94,17 +96,18 @@ public:
     // called to retrieve the request headers of the transaction
     virtual nsHttpRequestHead *RequestHead() = 0;
 };
 
 #define NS_DECL_NSAHTTPTRANSACTION \
     void SetConnection(nsAHttpConnection *); \
     void GetSecurityCallbacks(nsIInterfaceRequestor **, \
                               nsIEventTarget **);       \
-    void OnTransportStatus(nsresult status, PRUint64 progress); \
+    void OnTransportStatus(nsITransport* transport, \
+                           nsresult status, PRUint64 progress); \
     PRBool   IsDone(); \
     nsresult Status(); \
     PRUint32 Available(); \
     nsresult ReadSegments(nsAHttpSegmentReader *, PRUint32, PRUint32 *); \
     nsresult WriteSegments(nsAHttpSegmentWriter *, PRUint32, PRUint32 *); \
     void     Close(nsresult reason);                                    \
     void     SetSSLConnectFailed();                                     \
     nsHttpRequestHead *RequestHead();
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -129,41 +129,44 @@ nsHttpChannel::nsHttpChannel()
     , mFallbackChannel(PR_FALSE)
     , mTracingEnabled(PR_TRUE)
     , mCustomConditionalRequest(PR_FALSE)
     , mFallingBack(PR_FALSE)
     , mWaitingForRedirectCallback(PR_FALSE)
     , mRequestTimeInitialized(PR_FALSE)
 {
     LOG(("Creating nsHttpChannel [this=%p]\n", this));
+    // Subfields of unions cannot be targeted in an initializer list
+    mSelfAddr.raw.family = PR_AF_UNSPEC;
+    mPeerAddr.raw.family = PR_AF_UNSPEC;
 }
 
 nsHttpChannel::~nsHttpChannel()
 {
     LOG(("Destroying nsHttpChannel [this=%p]\n", this));
 
-    if (mAuthProvider) 
+    if (mAuthProvider)
         mAuthProvider->Disconnect(NS_ERROR_ABORT);
 }
 
 nsresult
 nsHttpChannel::Init(nsIURI *uri,
                     PRUint8 caps,
                     nsProxyInfo *proxyInfo)
 {
     nsresult rv = HttpBaseChannel::Init(uri, caps, proxyInfo);
-    if (NS_FAILED(rv)) 
+    if (NS_FAILED(rv))
         return rv;
 
     LOG(("nsHttpChannel::Init [this=%p]\n", this));
 
     mAuthProvider =
         do_CreateInstance("@mozilla.org/network/http-channel-auth-provider;1",
                           &rv);
-    if (NS_FAILED(rv)) 
+    if (NS_FAILED(rv))
         return rv;
     rv = mAuthProvider->Init(this);
 
     return rv;
 }
 //-----------------------------------------------------------------------------
 // nsHttpChannel <private>
 //-----------------------------------------------------------------------------
@@ -3706,16 +3709,76 @@ nsHttpChannel::SetupFallbackChannel(cons
     LOG(("nsHttpChannel::SetupFallbackChannel [this=%x, key=%s]",
          this, aFallbackKey));
     mFallbackChannel = PR_TRUE;
     mFallbackKey = aFallbackKey;
 
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsHttpChannel::GetRemoteAddress(nsACString & _result)
+{
+    if (mPeerAddr.raw.family == PR_AF_UNSPEC)
+        return NS_ERROR_NOT_AVAILABLE;
+
+    _result.SetCapacity(64);
+    PR_NetAddrToString(&mPeerAddr, _result.BeginWriting(), 64);
+    _result.SetLength(strlen(_result.BeginReading()));
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpChannel::GetRemotePort(PRInt32 * _result)
+{
+    NS_ENSURE_ARG_POINTER(_result);
+
+    if (mPeerAddr.raw.family == PR_AF_INET) {
+        *_result = (PRInt32)PR_ntohs(mPeerAddr.inet.port);
+    }
+    else if (mPeerAddr.raw.family == PR_AF_INET6) {
+        *_result = (PRInt32)PR_ntohs(mPeerAddr.ipv6.port);
+    }
+    else
+        return NS_ERROR_NOT_AVAILABLE;
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpChannel::GetLocalAddress(nsACString & _result)
+{
+    if (mSelfAddr.raw.family == PR_AF_UNSPEC)
+        return NS_ERROR_NOT_AVAILABLE;
+
+    _result.SetCapacity(64);
+    PR_NetAddrToString(&mSelfAddr, _result.BeginWriting(), 64);
+    _result.SetLength(strlen(_result.BeginReading()));
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpChannel::GetLocalPort(PRInt32 * _result)
+{
+    NS_ENSURE_ARG_POINTER(_result);
+
+    if (mSelfAddr.raw.family == PR_AF_INET) {
+        *_result = (PRInt32)PR_ntohs(mSelfAddr.inet.port);
+    }
+    else if (mSelfAddr.raw.family == PR_AF_INET6) {
+        *_result = (PRInt32)PR_ntohs(mSelfAddr.ipv6.port);
+    }
+    else
+        return NS_ERROR_NOT_AVAILABLE;
+
+    return NS_OK;
+}
+
 //-----------------------------------------------------------------------------
 // nsHttpChannel::nsISupportsPriority
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsHttpChannel::SetPriority(PRInt32 value)
 {
     PRInt16 newValue = NS_CLAMP(value, PR_INT16_MIN, PR_INT16_MAX);
@@ -4148,16 +4211,26 @@ nsHttpChannel::OnDataAvailable(nsIReques
 NS_IMETHODIMP
 nsHttpChannel::OnTransportStatus(nsITransport *trans, nsresult status,
                                  PRUint64 progress, PRUint64 progressMax)
 {
     // cache the progress sink so we don't have to query for it each time.
     if (!mProgressSink)
         GetCallback(mProgressSink);
 
+    if (status == nsISocketTransport::STATUS_CONNECTED_TO ||
+        status == nsISocketTransport::STATUS_WAITING_FOR) {
+        nsCOMPtr<nsISocketTransport> socketTransport =
+            do_QueryInterface(trans);
+        if (socketTransport) {
+            socketTransport->GetSelfAddr(&mSelfAddr);
+            socketTransport->GetPeerAddr(&mPeerAddr);
+        }
+    }
+
     // block socket status event after Cancel or OnStopRequest has been called.
     if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending && !(mLoadFlags & LOAD_BACKGROUND)) {
         LOG(("sending status notification [this=%p status=%x progress=%llu/%llu]\n",
             this, status, progress, progressMax));
 
         nsCAutoString host;
         mURI->GetHost(host);
         mProgressSink->OnStatus(this, nsnull, status,
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -132,16 +132,20 @@ public:
     NS_IMETHOD Cancel(nsresult status);
     NS_IMETHOD Suspend();
     NS_IMETHOD Resume();
     // nsIChannel
     NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo);
     NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext);
     // nsIHttpChannelInternal
     NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey);
+    NS_IMETHOD GetLocalAddress(nsACString& addr);
+    NS_IMETHOD GetLocalPort(PRInt32* port);
+    NS_IMETHOD GetRemoteAddress(nsACString& addr);
+    NS_IMETHOD GetRemotePort(PRInt32* port);
     // nsISupportsPriority
     NS_IMETHOD SetPriority(PRInt32 value);
     // nsIResumableChannel
     NS_IMETHOD ResumeAt(PRUint64 startPos, const nsACString& entityID);
 
 public: /* internal necko use only */ 
     typedef void (nsHttpChannel:: *nsAsyncCallback)(void);
 
@@ -342,16 +346,19 @@ private:
     // and also we want to pass possible 304 code response through.
     PRUint32                          mCustomConditionalRequest : 1;
     PRUint32                          mFallingBack              : 1;
     PRUint32                          mWaitingForRedirectCallback : 1;
     // True if mRequestTime has been set. In such a case it is safe to update
     // the cache entry's expiration time. Otherwise, it is not(see bug 567360).
     PRUint32                          mRequestTimeInitialized : 1;
 
+    PRNetAddr                         mSelfAddr;
+    PRNetAddr                         mPeerAddr;
+
     nsTArray<nsContinueRedirectionFunc> mRedirectFuncStack;
 
     nsCOMPtr<nsICryptoHash>        mHasher;
 
     nsresult WaitForRedirectCallback();
     void PushRedirectAsyncFunc(nsContinueRedirectionFunc func);
     void PopRedirectAsyncFunc(nsContinueRedirectionFunc func);
 };
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -610,17 +610,18 @@ nsHttpConnection::OnSocketWritable()
         }
         else if (n == 0) {
             // 
             // at this point we've written out the entire transaction, and now we
             // must wait for the server's response.  we manufacture a status message
             // here to reflect the fact that we are waiting.  this message will be
             // trumped (overwritten) if the server responds quickly.
             //
-            mTransaction->OnTransportStatus(nsISocketTransport::STATUS_WAITING_FOR,
+            mTransaction->OnTransportStatus(mSocketTransport,
+                                            nsISocketTransport::STATUS_WAITING_FOR,
                                             LL_ZERO);
 
             rv = mSocketIn->AsyncWait(this, 0, 0, nsnull); // start reading
             again = PR_FALSE;
         }
         // write more to the socket until error or end-of-request...
     } while (again);
 
@@ -806,17 +807,17 @@ nsHttpConnection::OnOutputStreamReady(ns
 
 NS_IMETHODIMP
 nsHttpConnection::OnTransportStatus(nsITransport *trans,
                                     nsresult status,
                                     PRUint64 progress,
                                     PRUint64 progressMax)
 {
     if (mTransaction)
-        mTransaction->OnTransportStatus(status, progress);
+        mTransaction->OnTransportStatus(trans, status, progress);
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpConnection::nsIInterfaceRequestor
 //-----------------------------------------------------------------------------
 
 // not called on the socket transport thread
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -1486,17 +1486,17 @@ nsHalfOpenSocket::OnOutputStreamReady(ns
 // method for nsITransportEventSink
 NS_IMETHODIMP
 nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans,
                                                          nsresult status,
                                                          PRUint64 progress,
                                                          PRUint64 progressMax)
 {
     if (mTransaction)
-        mTransaction->OnTransportStatus(status, progress);
+      mTransaction->OnTransportStatus(trans, status, progress);
     return NS_OK;
 }
 
 // method for nsIInterfaceRequestor
 NS_IMETHODIMP
 nsHttpConnectionMgr::nsHalfOpenSocket::GetInterface(const nsIID &iid,
                                                     void **result)
 {
--- a/netwerk/protocol/http/nsHttpPipeline.cpp
+++ b/netwerk/protocol/http/nsHttpPipeline.cpp
@@ -362,38 +362,39 @@ nsHttpPipeline::GetSecurityCallbacks(nsI
     else {
         *result = nsnull;
         if (target)
             *target = nsnull;
     }
 }
 
 void
-nsHttpPipeline::OnTransportStatus(nsresult status, PRUint64 progress)
+nsHttpPipeline::OnTransportStatus(nsITransport* transport,
+                                  nsresult status, PRUint64 progress)
 {
     LOG(("nsHttpPipeline::OnStatus [this=%x status=%x progress=%llu]\n",
         this, status, progress));
 
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
     nsAHttpTransaction *trans;
     switch (status) {
     case NS_NET_STATUS_RECEIVING_FROM:
-        // forward this only to the transaction currently recieving data 
+        // forward this only to the transaction currently recieving data
         trans = Response(0);
         if (trans)
-            trans->OnTransportStatus(status, progress);
+            trans->OnTransportStatus(transport, status, progress);
         break;
     default:
         // forward other notifications to all transactions
         PRInt32 i, count = mRequestQ.Length();
         for (i=0; i<count; ++i) {
             trans = Request(i);
             if (trans)
-                trans->OnTransportStatus(status, progress);
+                trans->OnTransportStatus(transport, status, progress);
         }
         break;
     }
 }
 
 PRBool
 nsHttpPipeline::IsDone()
 {
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -346,24 +346,25 @@ nsHttpTransaction::GetSecurityCallbacks(
                                         nsIEventTarget        **target)
 {
     NS_IF_ADDREF(*cb = mCallbacks);
     if (target)
         NS_IF_ADDREF(*target = mConsumerTarget);
 }
 
 void
-nsHttpTransaction::OnTransportStatus(nsresult status, PRUint64 progress)
+nsHttpTransaction::OnTransportStatus(nsITransport* transport,
+                                     nsresult status, PRUint64 progress)
 {
     LOG(("nsHttpTransaction::OnSocketStatus [this=%x status=%x progress=%llu]\n",
         this, status, progress));
 
     if (!mTransportSink)
         return;
-    
+
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
     // Need to do this before the STATUS_RECEIVING_FROM check below, to make
     // sure that the activity distributor gets told about all status events.
     if (mActivityDistributor) {
         // upon STATUS_WAITING_FOR; report request body sent
         if ((mHasRequestBody) &&
             (status == nsISocketTransport::STATUS_WAITING_FOR))
@@ -405,17 +406,17 @@ nsHttpTransaction::OnTransportStatus(nsr
         // notifications.
         progressMax = mRequestSize; // XXX mRequestSize is 32-bit!
     }
     else {
         progress = LL_ZERO;
         progressMax = 0;
     }
 
-    mTransportSink->OnTransportStatus(nsnull, status, progress, progressMax);
+    mTransportSink->OnTransportStatus(transport, status, progress, progressMax);
 }
 
 PRBool
 nsHttpTransaction::IsDone()
 {
     return mTransactionDone;
 }
 
--- a/netwerk/protocol/http/nsIHttpChannelInternal.idl
+++ b/netwerk/protocol/http/nsIHttpChannelInternal.idl
@@ -41,22 +41,22 @@
 #include "nsTArray.h"
 class nsCString;
 %}
 [ptr] native StringArray(nsTArray<nsCString>);
 
 interface nsIURI;
 interface nsIProxyInfo;
 
-/** 
- * Dumping ground for http.  This interface will never be frozen.  If you are 
- * using any feature exposed by this interface, be aware that this interface 
+/**
+ * Dumping ground for http.  This interface will never be frozen.  If you are
+ * using any feature exposed by this interface, be aware that this interface
  * will change and you will be broken.  You have been warned.
  */
-[scriptable, uuid(44e35ead-6656-4f9e-b51d-ebaf1bee6e2e)]
+[scriptable, uuid(12eb906a-71fe-4b79-b33a-6fe9ab57ea38)]
 interface nsIHttpChannelInternal : nsISupports
 {
     /**
      * An http channel can own a reference to the document URI
      */
     attribute nsIURI documentURI;
 
     /**
@@ -86,22 +86,63 @@ interface nsIHttpChannelInternal : nsISu
 
     /**
      * Force relevant cookies to be sent with this load even if normally they
      * wouldn't be.
      */
     attribute boolean forceAllowThirdPartyCookie;
 
     /**
-     * Returns true iff the channel has been canceled.
+     * True iff the channel has been canceled.
      */
     readonly attribute boolean canceled;
 
     /**
-     * Lets externalhandler tell the channel it is open on behalf of a download
+     * External handlers may set this to true to notify the channel
+     * that it is open on behalf of a download.
      */
     attribute boolean channelIsForDownload;
 
     /**
-     * Transfer chain of redirected cache-keys 
+     * The local IP address to which this channel is bound, in the
+     * format produced by PR_NetAddrToString. May be IPv4 or IPv6.
+     * Note: in the presence of NAT, this may not be the same as the
+     * address that the remote host thinks it's talking to.
+     *
+     * May throw NS_ERROR_NOT_AVAILABLE if accessed when the channel's
+     * endpoints are not yet determined, or in any case when
+     * nsIHttpActivityObserver.isActive is false. See bugs 534698 and 526207.
+     */
+    readonly attribute AUTF8String localAddress;
+
+    /**
+     * The local port number to which this channel is bound.
+     *
+     * May throw NS_ERROR_NOT_AVAILABLE if accessed when the channel's
+     * endpoints are not yet determined, or in any case when
+     * nsIHttpActivityObserver.isActive is false. See bugs 534698 and 526207.
+     */
+    readonly attribute PRInt32 localPort;
+
+    /**
+     * The IP address of the remote host that this channel is
+     * connected to, in the format produced by PR_NetAddrToString.
+     *
+     * May throw NS_ERROR_NOT_AVAILABLE if accessed when the channel's
+     * endpoints are not yet determined, or in any case when
+     * nsIHttpActivityObserver.isActive is false. See bugs 534698 and 526207.
+     */
+    readonly attribute AUTF8String remoteAddress;
+
+    /**
+     * The remote port number that this channel is connected to.
+     *
+     * May throw NS_ERROR_NOT_AVAILABLE if accessed when the channel's
+     * endpoints are not yet determined, or in any case when
+     * nsIHttpActivityObserver.isActive is false. See bugs 534698 and 526207.
+     */
+    readonly attribute PRInt32 remotePort;
+
+    /**
+     * Transfer chain of redirected cache-keys.
      */
     [noscript] void setCacheKeysRedirectChain(in StringArray cacheKeys);
 };
--- a/netwerk/test/unit/test_traceable_channel.js
+++ b/netwerk/test/unit/test_traceable_channel.js
@@ -15,16 +15,23 @@ var gotOnStartRequest = false;
 function TracingListener() {}
 
 TracingListener.prototype = {
   onStartRequest: function(request, context) {
     dump("*** tracing listener onStartRequest\n");
 
     gotOnStartRequest = true;
 
+    request.QueryInterface(Components.interfaces.nsIHttpChannelInternal);
+    do_check_eq(request.localAddress, "127.0.0.1");
+    do_check_eq(request.localPort > 0, true);
+    do_check_neq(request.localPort, 4444);
+    do_check_eq(request.remoteAddress, "127.0.0.1");
+    do_check_eq(request.remotePort, 4444);
+
     // Make sure listener can't be replaced after OnStartRequest was called.
     request.QueryInterface(Components.interfaces.nsITraceableChannel);
     try {
       var newListener = new TracingListener();
       newListener.listener = request.setNewListener(newListener);
     } catch(e) {
       dump("TracingListener.onStartRequest swallowing exception: " + e + "\n");
       return; // OK