Bug 1200337 - Part 1: Don't expose standard HTTP headers during interception in non-e10s mode, r=mcmanus
authorMichael Layzell <michael@thelayzells.com>
Thu, 03 Sep 2015 18:17:23 -0400
changeset 294774 2cbbc360ec41d01937e41c5f264fea40032535e1
parent 294773 c1cdde5ccb2ce94790fd7f6f7dfd0f21d69cf72d
child 294775 86fd2ab7d9f62ea6a7cb85ac6f49e440d66ffe68
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus
bugs1200337
milestone43.0a1
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 1200337 - Part 1: Don't expose standard HTTP headers during interception in non-e10s mode, r=mcmanus
dom/workers/ServiceWorkerManager.cpp
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/NullHttpChannel.cpp
netwerk/protocol/http/nsHttpHandler.cpp
netwerk/protocol/http/nsHttpHeaderArray.cpp
netwerk/protocol/http/nsHttpHeaderArray.h
netwerk/protocol/http/nsIHttpChannel.idl
netwerk/protocol/viewsource/nsViewSourceChannel.cpp
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -3722,17 +3722,17 @@ public:
       } else {
         bool includeCrossOrigin;
         internalChannel->GetCorsIncludeCredentials(&includeCrossOrigin);
         if (includeCrossOrigin) {
           mRequestCredentials = RequestCredentials::Include;
         }
       }
 
-      rv = httpChannel->VisitRequestHeaders(this);
+      rv = httpChannel->VisitNonDefaultRequestHeaders(this);
       NS_ENSURE_SUCCESS(rv, rv);
 
       nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel);
       if (uploadChannel) {
         MOZ_ASSERT(!mUploadStream);
         rv = uploadChannel->CloneUploadStream(getter_AddRefs(mUploadStream));
         NS_ENSURE_SUCCESS(rv, rv);
       }
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -1351,16 +1351,23 @@ HttpBaseChannel::SetEmptyRequestHeader(c
 
 NS_IMETHODIMP
 HttpBaseChannel::VisitRequestHeaders(nsIHttpHeaderVisitor *visitor)
 {
   return mRequestHead.Headers().VisitHeaders(visitor);
 }
 
 NS_IMETHODIMP
+HttpBaseChannel::VisitNonDefaultRequestHeaders(nsIHttpHeaderVisitor *visitor)
+{
+  return mRequestHead.Headers().VisitHeaders(
+      visitor, nsHttpHeaderArray::eFilterSkipDefault);
+}
+
+NS_IMETHODIMP
 HttpBaseChannel::GetResponseHeader(const nsACString &header, nsACString &value)
 {
   value.Truncate();
 
   if (!mResponseHead)
     return NS_ERROR_NOT_AVAILABLE;
 
   nsHttpAtom atom = nsHttp::ResolveAtom(header);
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -135,16 +135,17 @@ public:
   NS_IMETHOD SetReferrer(nsIURI *referrer) override;
   NS_IMETHOD GetReferrerPolicy(uint32_t *referrerPolicy) override;
   NS_IMETHOD SetReferrerWithPolicy(nsIURI *referrer, uint32_t referrerPolicy) override;
   NS_IMETHOD GetRequestHeader(const nsACString& aHeader, nsACString& aValue) override;
   NS_IMETHOD SetRequestHeader(const nsACString& aHeader,
                               const nsACString& aValue, bool aMerge) override;
   NS_IMETHOD SetEmptyRequestHeader(const nsACString& aHeader) override;
   NS_IMETHOD VisitRequestHeaders(nsIHttpHeaderVisitor *visitor) override;
+  NS_IMETHOD VisitNonDefaultRequestHeaders(nsIHttpHeaderVisitor *visitor) override;
   NS_IMETHOD GetResponseHeader(const nsACString &header, nsACString &value) override;
   NS_IMETHOD SetResponseHeader(const nsACString& header,
                                const nsACString& value, bool merge) override;
   NS_IMETHOD VisitResponseHeaders(nsIHttpHeaderVisitor *visitor) override;
   NS_IMETHOD GetAllowPipelining(bool *value) override;
   NS_IMETHOD SetAllowPipelining(bool value) override;
   NS_IMETHOD GetAllowSTS(bool *value) override;
   NS_IMETHOD SetAllowSTS(bool value) override;
--- a/netwerk/protocol/http/NullHttpChannel.cpp
+++ b/netwerk/protocol/http/NullHttpChannel.cpp
@@ -113,16 +113,22 @@ NullHttpChannel::SetEmptyRequestHeader(c
 
 NS_IMETHODIMP
 NullHttpChannel::VisitRequestHeaders(nsIHttpHeaderVisitor *aVisitor)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
+NullHttpChannel::VisitNonDefaultRequestHeaders(nsIHttpHeaderVisitor *aVisitor)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 NullHttpChannel::GetAllowPipelining(bool *aAllowPipelining)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 NullHttpChannel::SetAllowPipelining(bool aAllowPipelining)
 {
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -417,44 +417,50 @@ nsHttpHandler::InitConnectionMgr()
 }
 
 nsresult
 nsHttpHandler::AddStandardRequestHeaders(nsHttpHeaderArray *request)
 {
     nsresult rv;
 
     // Add the "User-Agent" header
-    rv = request->SetHeader(nsHttp::User_Agent, UserAgent());
+    rv = request->SetHeader(nsHttp::User_Agent, UserAgent(),
+                            false, nsHttpHeaderArray::eVarietyDefault);
     if (NS_FAILED(rv)) return rv;
 
     // MIME based content negotiation lives!
     // Add the "Accept" header
-    rv = request->SetHeader(nsHttp::Accept, mAccept);
+    rv = request->SetHeader(nsHttp::Accept, mAccept,
+                            false, nsHttpHeaderArray::eVarietyDefault);
     if (NS_FAILED(rv)) return rv;
 
     // Add the "Accept-Language" header
     if (!mAcceptLanguages.IsEmpty()) {
         // Add the "Accept-Language" header
-        rv = request->SetHeader(nsHttp::Accept_Language, mAcceptLanguages);
+        rv = request->SetHeader(nsHttp::Accept_Language, mAcceptLanguages,
+                                false, nsHttpHeaderArray::eVarietyDefault);
         if (NS_FAILED(rv)) return rv;
     }
 
     // Add the "Accept-Encoding" header
-    rv = request->SetHeader(nsHttp::Accept_Encoding, mAcceptEncodings);
+    rv = request->SetHeader(nsHttp::Accept_Encoding, mAcceptEncodings,
+                            false, nsHttpHeaderArray::eVarietyDefault);
     if (NS_FAILED(rv)) return rv;
 
     // Add the "Do-Not-Track" header
     if (mDoNotTrackEnabled) {
-      rv = request->SetHeader(nsHttp::DoNotTrack, NS_LITERAL_CSTRING("1"));
+      rv = request->SetHeader(nsHttp::DoNotTrack, NS_LITERAL_CSTRING("1"),
+                              false, nsHttpHeaderArray::eVarietyDefault);
       if (NS_FAILED(rv)) return rv;
     }
 
     // add the "Send Hint" header
     if (mSafeHintEnabled || mParentalControlEnabled) {
-      rv = request->SetHeader(nsHttp::Prefer, NS_LITERAL_CSTRING("safe"));
+      rv = request->SetHeader(nsHttp::Prefer, NS_LITERAL_CSTRING("safe"),
+                              false, nsHttpHeaderArray::eVarietyDefault);
       if (NS_FAILED(rv)) return rv;
     }
     return NS_OK;
 }
 
 nsresult
 nsHttpHandler::AddConnectionHeader(nsHttpHeaderArray *request,
                                    uint32_t caps)
--- a/netwerk/protocol/http/nsHttpHeaderArray.cpp
+++ b/netwerk/protocol/http/nsHttpHeaderArray.cpp
@@ -15,42 +15,47 @@ namespace mozilla {
 namespace net {
 
 //-----------------------------------------------------------------------------
 // nsHttpHeaderArray <public>
 //-----------------------------------------------------------------------------
 nsresult
 nsHttpHeaderArray::SetHeader(nsHttpAtom header,
                              const nsACString &value,
-                             bool merge)
+                             bool merge,
+                             nsHttpHeaderArray::HeaderVariety variety)
 {
     nsEntry *entry = nullptr;
     int32_t index;
 
     index = LookupEntry(header, &entry);
 
     // If an empty value is passed in, then delete the header entry...
     // unless we are merging, in which case this function becomes a NOP.
     if (value.IsEmpty()) {
         if (!merge && entry)
             mHeaders.RemoveElementAt(index);
         return NS_OK;
     }
 
+    MOZ_ASSERT(!entry || variety != eVarietyDefault,
+               "Cannot set default entry which overrides existing entry!");
     if (!entry) {
         entry = mHeaders.AppendElement(); // new nsEntry()
         if (!entry)
             return NS_ERROR_OUT_OF_MEMORY;
         entry->header = header;
         entry->value = value;
+        entry->variety = variety;
     } else if (merge && !IsSingletonHeader(header)) {
         MergeHeader(header, entry, value);
     } else {
         // Replace the existing string with the new value
         entry->value = value;
+        entry->variety = eVarietyOverride;
     }
 
     return NS_OK;
 }
 
 nsresult
 nsHttpHeaderArray::SetEmptyHeader(nsHttpAtom header)
 {
@@ -128,22 +133,25 @@ nsHttpHeaderArray::GetHeader(nsHttpAtom 
     LookupEntry(header, &entry);
     if (!entry)
         return NS_ERROR_NOT_AVAILABLE;
     result = entry->value;
     return NS_OK;
 }
 
 nsresult
-nsHttpHeaderArray::VisitHeaders(nsIHttpHeaderVisitor *visitor)
+nsHttpHeaderArray::VisitHeaders(nsIHttpHeaderVisitor *visitor, nsHttpHeaderArray::VisitorFilter filter)
 {
     NS_ENSURE_ARG_POINTER(visitor);
     uint32_t i, count = mHeaders.Length();
     for (i = 0; i < count; ++i) {
         const nsEntry &entry = mHeaders[i];
+        if (filter == eFilterSkipDefault && entry.variety == eVarietyDefault) {
+            continue;
+        }
         if (NS_FAILED(visitor->VisitHeader(nsDependentCString(entry.header),
                                            entry.value)))
             break;
     }
     return NS_OK;
 }
 
 nsresult
--- a/netwerk/protocol/http/nsHttpHeaderArray.h
+++ b/netwerk/protocol/http/nsHttpHeaderArray.h
@@ -21,19 +21,25 @@ namespace IPC {
 
 namespace mozilla { namespace net {
 
 class nsHttpHeaderArray
 {
 public:
     const char *PeekHeader(nsHttpAtom header) const;
 
+    enum HeaderVariety
+    {
+        eVarietyOverride,
+        eVarietyDefault,
+    };
+
     // Used by internal setters: to set header from network use SetHeaderFromNet
     nsresult SetHeader(nsHttpAtom header, const nsACString &value,
-                       bool merge = false);
+                       bool merge = false, HeaderVariety variety = eVarietyOverride);
 
     // Used by internal setters to set an empty header
     nsresult SetEmptyHeader(nsHttpAtom header);
 
     // Merges supported headers. For other duplicate values, determines if error
     // needs to be thrown or 1st value kept.
     nsresult SetHeaderFromNet(nsHttpAtom header, const nsACString &value);
 
@@ -48,17 +54,23 @@ public:
     }
 
     // Determine if the given header value exists.
     bool HasHeaderValue(nsHttpAtom header, const char *value) const
     {
         return FindHeaderValue(header, value) != nullptr;
     }
 
-    nsresult VisitHeaders(nsIHttpHeaderVisitor *visitor);
+    enum VisitorFilter
+    {
+        eFilterAll,
+        eFilterSkipDefault,
+    };
+
+    nsresult VisitHeaders(nsIHttpHeaderVisitor *visitor, VisitorFilter filter = eFilterAll);
 
     // parse a header line, return the header atom and a pointer to the
     // header value (the substring of the header line -- do not free).
     nsresult ParseHeaderLine(const char *line,
                              nsHttpAtom *header=nullptr,
                              char **value=nullptr);
 
     void Flatten(nsACString &, bool pruneProxyHeaders=false);
@@ -71,16 +83,17 @@ public:
 
     void Clear();
 
     // Must be copy-constructable and assignable
     struct nsEntry
     {
         nsHttpAtom header;
         nsCString value;
+        HeaderVariety variety = eVarietyOverride;
 
         struct MatchHeader {
           bool Equals(const nsEntry &entry, const nsHttpAtom &header) const {
             return entry.header == header;
           }
         };
 
         bool operator==(const nsEntry& aOther) const
@@ -183,16 +196,17 @@ nsHttpHeaderArray::MergeHeader(nsHttpAto
             // in the values of these headers contrary to what the spec says.
             entry->value.Append('\n');
         } else {
             // Delimit each value from the others using a comma (per HTTP spec)
             entry->value.AppendLiteral(", ");
         }
     }
     entry->value.Append(value);
+    entry->variety = eVarietyOverride;
 }
 
 inline bool
 nsHttpHeaderArray::IsSuspectDuplicateHeader(nsHttpAtom header)
 {
     bool retval =  header == nsHttp::Content_Length         ||
                      header == nsHttp::Content_Disposition    ||
                      header == nsHttp::Location;
--- a/netwerk/protocol/http/nsIHttpChannel.idl
+++ b/netwerk/protocol/http/nsIHttpChannel.idl
@@ -9,17 +9,17 @@ interface nsIHttpHeaderVisitor;
 
 /**
  * nsIHttpChannel
  *
  * This interface allows for the modification of HTTP request parameters and
  * the inspection of the resulting HTTP response status and headers when they
  * become available.
  */
-[builtinclass, scriptable, uuid(182ee0b9-e3c1-4426-9db3-70814ea7ed24)]
+[builtinclass, scriptable, uuid(46ca0df4-bb4d-423a-9b3e-2fb0988cd1f3)]
 interface nsIHttpChannel : nsIChannel
 {
     /**************************************************************************
      * REQUEST CONFIGURATION
      *
      * Modifying request parameters after asyncOpen has been called is an error.
      */
 
@@ -149,16 +149,26 @@ interface nsIHttpChannel : nsIChannel
      * while visiting request headers has undefined behavior.  Don't do it!
      *
      * @param aVisitor
      *        the header visitor instance.
      */
     void visitRequestHeaders(in nsIHttpHeaderVisitor aVisitor);
 
     /**
+     * Call this method to visit all non-default (UA-provided) request headers.
+     * Calling setRequestHeader while visiting request headers has undefined
+     * behavior. Don't do it!
+     *
+     * @param aVisitor
+     *        the header visitor instance.
+     */
+    void visitNonDefaultRequestHeaders(in nsIHttpHeaderVisitor aVisitor);
+
+    /**
      * This attribute is a hint to the channel to indicate whether or not
      * the underlying HTTP transaction should be allowed to be pipelined
      * with other transactions.  This should be set to FALSE, for example,
      * if the application knows that the corresponding document is likely
      * to be very large.
      *
      * This attribute is true by default, though other factors may prevent
      * pipelining.
--- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
+++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
@@ -757,16 +757,23 @@ nsViewSourceChannel::SetEmptyRequestHead
 NS_IMETHODIMP
 nsViewSourceChannel::VisitRequestHeaders(nsIHttpHeaderVisitor *aVisitor)
 {
     return !mHttpChannel ? NS_ERROR_NULL_POINTER :
         mHttpChannel->VisitRequestHeaders(aVisitor);
 }
 
 NS_IMETHODIMP
+nsViewSourceChannel::VisitNonDefaultRequestHeaders(nsIHttpHeaderVisitor *aVisitor)
+{
+    return !mHttpChannel ? NS_ERROR_NULL_POINTER :
+        mHttpChannel->VisitNonDefaultRequestHeaders(aVisitor);
+}
+
+NS_IMETHODIMP
 nsViewSourceChannel::GetAllowPipelining(bool *aAllowPipelining)
 {
     return !mHttpChannel ? NS_ERROR_NULL_POINTER :
         mHttpChannel->GetAllowPipelining(aAllowPipelining);
 }
 
 NS_IMETHODIMP
 nsViewSourceChannel::SetAllowPipelining(bool aAllowPipelining)