Bug 658822 - Fix implementation of DNS timing in nsITimedChannel; r=jduell
authorChristian Biesinger <cbiesinger@gmail.com>
Sun, 22 May 2011 21:29:42 -0700
changeset 70249 a98b112f0ed297a27f0cb1718dc102114f3f1141
parent 70248 b97aef275b5e2342e56a36deff7ba9a5bbd2396d
child 70250 aca490d48c951bdb5403c5a0beeccde1b587864b
push idunknown
push userunknown
push dateunknown
reviewersjduell
bugs658822
milestone6.0a1
Bug 658822 - Fix implementation of DNS timing in nsITimedChannel; r=jduell
netwerk/base/src/nsDNSPrefetch.cpp
netwerk/base/src/nsDNSPrefetch.h
netwerk/protocol/http/TimingStruct.h
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpChannel.h
--- a/netwerk/base/src/nsDNSPrefetch.cpp
+++ b/netwerk/base/src/nsDNSPrefetch.cpp
@@ -61,32 +61,39 @@ nsDNSPrefetch::Initialize(nsIDNSService 
 
 nsresult
 nsDNSPrefetch::Shutdown()
 {
     NS_IF_RELEASE(sDNSService);
     return NS_OK;
 }
 
-nsDNSPrefetch::nsDNSPrefetch(nsIURI *aURI)
+nsDNSPrefetch::nsDNSPrefetch(nsIURI *aURI, PRBool storeTiming)
+    : mStoreTiming(storeTiming)
 {
     aURI->GetAsciiHost(mHostname);
 }
 
 nsresult 
 nsDNSPrefetch::Prefetch(PRUint16 flags)
 {
     if (mHostname.IsEmpty())
         return NS_ERROR_NOT_AVAILABLE;
   
     if (!sDNSService)
         return NS_ERROR_NOT_AVAILABLE;
     
     nsCOMPtr<nsICancelable> tmpOutstanding;  
 
+    if (mStoreTiming)
+        mStartTimestamp = mozilla::TimeStamp::Now();
+    // If AsyncResolve fails, for example because prefetching is disabled,
+    // then our timing will be useless. However, in such a case,
+    // mEndTimestamp will be a null timestamp and callers should check
+    // TimingsValid() before using the timing.
     return sDNSService->AsyncResolve(mHostname, flags | nsIDNSService::RESOLVE_SPECULATE,
                                      this, nsnull, getter_AddRefs(tmpOutstanding));
 }
 
 nsresult
 nsDNSPrefetch::PrefetchLow()
 {
     return Prefetch(nsIDNSService::RESOLVE_PRIORITY_LOW);
@@ -107,10 +114,12 @@ nsDNSPrefetch::PrefetchHigh()
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDNSPrefetch, nsIDNSListener)
 
 NS_IMETHODIMP
 nsDNSPrefetch::OnLookupComplete(nsICancelable *request,
                                 nsIDNSRecord  *rec,
                                 nsresult       status)
 {
+    if (mStoreTiming)
+        mEndTimestamp = mozilla::TimeStamp::Now();
     return NS_OK;
 }
--- a/netwerk/base/src/nsDNSPrefetch.h
+++ b/netwerk/base/src/nsDNSPrefetch.h
@@ -36,37 +36,47 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsDNSPrefetch_h___
 #define nsDNSPrefetch_h___
 
 #include "nsCOMPtr.h"
 #include "nsString.h"
+#include "mozilla/TimeStamp.h"
 
 #include "nsIDNSListener.h"
 
 class nsIURI;
 class nsIDNSService;
 
 class nsDNSPrefetch : public nsIDNSListener
 {
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIDNSLISTENER
   
-    nsDNSPrefetch(nsIURI *aURI);
+    nsDNSPrefetch(nsIURI *aURI, PRBool storeTiming);
+    bool TimingsValid() const {
+        return !mStartTimestamp.IsNull() && !mEndTimestamp.IsNull();
+    }
+    // Only use the two timings if TimingsValid() returns true
+    const mozilla::TimeStamp& StartTimestamp() const { return mStartTimestamp; }
+    const mozilla::TimeStamp& EndTimestamp() const { return mEndTimestamp; }
 
     static nsresult Initialize(nsIDNSService *aDNSService);
     static nsresult Shutdown();
 
     // Call one of the following methods to start the Prefetch.
     nsresult PrefetchHigh();
     nsresult PrefetchMedium();
     nsresult PrefetchLow();
   
 private:
-    nsCString  mHostname;
-    
+    nsCString mHostname;
+    PRBool mStoreTiming;
+    mozilla::TimeStamp mStartTimestamp;
+    mozilla::TimeStamp mEndTimestamp;
+
     nsresult Prefetch(PRUint16 flags);
 };
 
 #endif 
--- a/netwerk/protocol/http/TimingStruct.h
+++ b/netwerk/protocol/http/TimingStruct.h
@@ -37,19 +37,16 @@
 
 
 #ifndef TimingStruct_h_
 #define TimingStruct_h_
 
 #include "mozilla/TimeStamp.h"
 
 struct TimingStruct {
-  mozilla::TimeStamp channelCreation;
-  mozilla::TimeStamp asyncOpen;
-
   mozilla::TimeStamp domainLookupStart;
   mozilla::TimeStamp domainLookupEnd;
   mozilla::TimeStamp connectStart;
   mozilla::TimeStamp connectEnd;
   mozilla::TimeStamp requestStart;
   mozilla::TimeStamp responseStart;
   mozilla::TimeStamp responseEnd;
 };
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -3654,20 +3654,26 @@ nsHttpChannel::AsyncOpen(nsIStreamListen
     rv = NS_CheckPortSafety(mURI);
     if (NS_FAILED(rv))
         return rv;
 
     if (!(mConnectionInfo && mConnectionInfo->UsingHttpProxy())) {
         // Start a DNS lookup very early in case the real open is queued the DNS can 
         // happen in parallel. Do not do so in the presence of an HTTP proxy as 
         // all lookups other than for the proxy itself are done by the proxy.
-        nsRefPtr<nsDNSPrefetch> prefetch = new nsDNSPrefetch(mURI);
-        if (prefetch) {
-            prefetch->PrefetchHigh();
-        }
+        //
+        // We keep the DNS prefetch object around so that we can retrieve
+        // timing information from it. There is no guarantee that we actually
+        // use the DNS prefetch data for the real connection, but as we keep
+        // this data around for 3 minutes by default, this should almost always
+        // be correct, and even when it isn't, the timing still represents _a_
+        // valid DNS lookup timing for the site, even if it is not _the_
+        // timing we used.
+        mDNSPrefetch = new nsDNSPrefetch(mURI, mTimingEnabled);
+        mDNSPrefetch->PrefetchHigh();
     }
     
     // Remember the cookie header that was set, if any
     const char *cookieHeader = mRequestHead.PeekHeader(nsHttp::Cookie);
     if (cookieHeader) {
         mUserSetCookieHeader = cookieHeader;
     }
 
@@ -3825,26 +3831,30 @@ nsHttpChannel::GetChannelCreation(mozill
 NS_IMETHODIMP
 nsHttpChannel::GetAsyncOpen(mozilla::TimeStamp* _retval) {
     *_retval = mAsyncOpenTime;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHttpChannel::GetDomainLookupStart(mozilla::TimeStamp* _retval) {
-    if (mTransaction)
+    if (mDNSPrefetch && mDNSPrefetch->TimingsValid())
+        *_retval = mDNSPrefetch->StartTimestamp();
+    else if (mTransaction)
         *_retval = mTransaction->Timings().domainLookupStart;
     else
         *_retval = mTransactionTimings.domainLookupStart;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHttpChannel::GetDomainLookupEnd(mozilla::TimeStamp* _retval) {
-    if (mTransaction)
+    if (mDNSPrefetch && mDNSPrefetch->TimingsValid())
+        *_retval = mDNSPrefetch->EndTimestamp();
+    else if (mTransaction)
         *_retval = mTransaction->Timings().domainLookupEnd;
     else
         *_retval = mTransactionTimings.domainLookupEnd;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHttpChannel::GetConnectStart(mozilla::TimeStamp* _retval) {
@@ -4186,16 +4196,25 @@ nsHttpChannel::OnStopRequest(nsIRequest 
         if (mCaps & NS_HTTP_STICKY_CONNECTION)
             stickyConn = mTransaction->Connection();
         
         // at this point, we're done with the transaction
         mTransactionTimings = mTransaction->Timings();
         mTransaction = nsnull;
         mTransactionPump = 0;
 
+        // We no longer need the dns prefetch object
+        if (mDNSPrefetch && mDNSPrefetch->TimingsValid()) {
+            mTransactionTimings.domainLookupStart =
+                mDNSPrefetch->StartTimestamp();
+            mTransactionTimings.domainLookupEnd =
+                mDNSPrefetch->EndTimestamp();
+        }
+        mDNSPrefetch = nsnull;
+
         // handle auth retry...
         if (authRetry) {
             mAuthRetryPending = PR_FALSE;
             status = DoAuthRetry(conn);
             if (NS_SUCCEEDED(status))
                 return NS_OK;
         }
 
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -60,16 +60,17 @@
 #include "nsIProtocolProxyCallback.h"
 #include "nsICancelable.h"
 #include "nsIHttpAuthenticableChannel.h"
 #include "nsITraceableChannel.h"
 #include "nsIHttpChannelAuthProvider.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsICryptoHash.h"
 #include "nsITimedChannel.h"
+#include "nsDNSPrefetch.h"
 #include "TimingStruct.h"
 
 class nsAHttpConnection;
 class AutoRedirectVetoNotifier;
 
 using namespace mozilla::net;
 
 //-----------------------------------------------------------------------------
@@ -358,15 +359,17 @@ private:
     PRTime                            mChannelCreationTime;
     mozilla::TimeStamp                mChannelCreationTimestamp;
     mozilla::TimeStamp                mAsyncOpenTime;
     mozilla::TimeStamp                mCacheReadStart;
     mozilla::TimeStamp                mCacheReadEnd;
     // copied from the transaction before we null out mTransaction
     // so that the timing can still be queried from OnStopRequest
     TimingStruct                      mTransactionTimings;
+    // Needed for accurate DNS timing
+    nsRefPtr<nsDNSPrefetch>           mDNSPrefetch;
 
     nsresult WaitForRedirectCallback();
     void PushRedirectAsyncFunc(nsContinueRedirectionFunc func);
     void PopRedirectAsyncFunc(nsContinueRedirectionFunc func);
 };
 
 #endif // nsHttpChannel_h__