Bug 1247982 - Lock request headers. r=mcmanus
authorDragana Damjanovic <dd.mozilla@gmail.com>
Tue, 03 May 2016 00:06:00 +0200
changeset 295804 2f32c58b741bc8d3b128b1d2ef6ffde6c7a2a733
parent 295803 aa730410c52c1edc12fd53fe45b8824c78ae890c
child 295805 3aff4dae71ac0870dfab0483691ebef02c5330db
push id76095
push usercbook@mozilla.com
push dateTue, 03 May 2016 08:48:23 +0000
treeherdermozilla-inbound@e93c12bc1ffc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus
bugs1247982
milestone49.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 1247982 - Lock request headers. r=mcmanus
netwerk/base/Predictor.cpp
netwerk/protocol/http/ConnectionDiagnostics.cpp
netwerk/protocol/http/Http2Session.cpp
netwerk/protocol/http/Http2Stream.cpp
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelParent.cpp
netwerk/protocol/http/SpdyStream31.cpp
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpChannel.h
netwerk/protocol/http/nsHttpConnection.cpp
netwerk/protocol/http/nsHttpHandler.cpp
netwerk/protocol/http/nsHttpHandler.h
netwerk/protocol/http/nsHttpHeaderArray.cpp
netwerk/protocol/http/nsHttpHeaderArray.h
netwerk/protocol/http/nsHttpRequestHead.cpp
netwerk/protocol/http/nsHttpRequestHead.h
netwerk/protocol/http/nsHttpTransaction.cpp
--- a/netwerk/base/Predictor.cpp
+++ b/netwerk/base/Predictor.cpp
@@ -2417,17 +2417,18 @@ Predictor::UpdateCacheability(nsIURI *so
 
   if (lci && lci->IsPrivate()) {
     PREDICTOR_LOG(("Predictor::UpdateCacheability in PB mode - ignoring"));
     return;
   }
 
   RefPtr<Predictor> self = sSelf;
   if (self) {
-    const nsCString method = requestHead.Method();
+    nsAutoCString method;
+    requestHead.Method(method);
     self->UpdateCacheabilityInternal(sourceURI, targetURI, httpStatus,
                                      method);
   }
 }
 
 void
 Predictor::UpdateCacheabilityInternal(nsIURI *sourceURI, nsIURI *targetURI,
                                       uint32_t httpStatus,
--- a/netwerk/protocol/http/ConnectionDiagnostics.cpp
+++ b/netwerk/protocol/http/ConnectionDiagnostics.cpp
@@ -231,18 +231,19 @@ Http2Session::PrintDiagnostics(nsCString
 }
 
 void
 nsHttpTransaction::PrintDiagnostics(nsCString &log)
 {
   if (!mRequestHead)
     return;
 
-  log.AppendPrintf("     ::: uri = %s\n",
-                   nsAutoCString(mRequestHead->RequestURI()).get());
+  nsAutoCString requestURI;
+  mRequestHead->RequestURI(requestURI);
+  log.AppendPrintf("     ::: uri = %s\n", requestURI.get());
   log.AppendPrintf("     caps = 0x%x\n", mCaps);
   log.AppendPrintf("     priority = %d\n", mPriority);
   log.AppendPrintf("     restart count = %u\n", mRestartCount);
   log.AppendPrintf("     classification = 0x%x\n", mClassification);
 }
 
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -2133,17 +2133,17 @@ Http2Session::RecvAltSvc(Http2Session *s
     if (NS_FAILED(self->SetInputFrameDataStream(self->mInputFrameID)) ||
         !self->mInputFrameDataStream->Transaction() ||
         !self->mInputFrameDataStream->Transaction()->RequestHead()) {
       LOG3(("Http2Session::RecvAltSvc %p got frame w/o origin on invalid stream", self));
       self->ResetDownstreamState();
       return NS_OK;
     }
 
-    origin.Assign(self->mInputFrameDataStream->Transaction()->RequestHead()->Origin());
+    self->mInputFrameDataStream->Transaction()->RequestHead()->Origin(origin);
   } else if (!self->mInputFrameID) {
     // ID 0 streams must supply their own origin
     if (origin.IsEmpty()) {
       LOG(("Http2Session %p Alt-Svc Stream 0 has empty origin\n", self));
       self->ResetDownstreamState();
       return NS_OK;
     }
   } else {
--- a/netwerk/protocol/http/Http2Stream.cpp
+++ b/netwerk/protocol/http/Http2Stream.cpp
@@ -383,17 +383,17 @@ Http2Stream::ParseHttpRequestHeaders(con
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   MOZ_ASSERT(mUpstreamState == GENERATING_HEADERS);
   MOZ_ASSERT(!mRequestHeadersDone);
 
   LOG3(("Http2Stream::ParseHttpRequestHeaders %p avail=%d state=%x",
         this, avail, mUpstreamState));
 
   mFlatHttpRequestHeaders.Append(buf, avail);
-  const nsHttpRequestHead *head = mTransaction->RequestHead();
+  nsHttpRequestHead *head = mTransaction->RequestHead();
 
   // We can use the simple double crlf because firefox is the
   // only client we are parsing
   int32_t endHeader = mFlatHttpRequestHeaders.Find("\r\n\r\n");
 
   if (endHeader == kNotFound) {
     // We don't have all the headers yet
     LOG3(("Http2Stream::ParseHttpRequestHeaders %p "
@@ -410,19 +410,21 @@ Http2Stream::ParseHttpRequestHeaders(con
   mFlatHttpRequestHeaders.SetLength(endHeader + 2);
   *countUsed = avail - (oldLen - endHeader) + 4;
   mRequestHeadersDone = 1;
 
   nsAutoCString authorityHeader;
   nsAutoCString hashkey;
   head->GetHeader(nsHttp::Host, authorityHeader);
 
+  nsAutoCString requestURI;
+  head->RequestURI(requestURI);
   CreatePushHashKey(nsDependentCString(head->IsHTTPS() ? "https" : "http"),
                     authorityHeader, mSession->Serial(),
-                    head->RequestURI(),
+                    requestURI,
                     mOrigin, hashkey);
 
   // check the push cache for GET
   if (head->IsGet()) {
     // from :scheme, :authority, :path
     nsIRequestContext *requestContext = mTransaction->RequestContext();
     SpdyPushCache *cache = nullptr;
     if (requestContext) {
@@ -482,19 +484,21 @@ Http2Stream::GenerateOpen()
   // be monotonically increasing amongst new streams on this
   // session
   mStreamID = mSession->RegisterStreamID(this);
   MOZ_ASSERT(mStreamID & 1, "Http2 Stream Channel ID must be odd");
   MOZ_ASSERT(!mOpenGenerated);
 
   mOpenGenerated = 1;
 
-  const nsHttpRequestHead *head = mTransaction->RequestHead();
+  nsHttpRequestHead *head = mTransaction->RequestHead();
+  nsAutoCString requestURI;
+  head->RequestURI(requestURI);
   LOG3(("Http2Stream %p Stream ID 0x%X [session=%p] for URI %s\n",
-        this, mStreamID, mSession, nsCString(head->RequestURI()).get()));
+        this, mStreamID, mSession, requestURI.get()));
 
   if (mStreamID >= 0x80000000) {
     // streamID must fit in 31 bits. Evading This is theoretically possible
     // because stream ID assignment is asynchronous to stream creation
     // because of the protocol requirement that the new stream ID
     // be monotonically increasing. In reality this is really not possible
     // because new streams stop being added to a session with millions of
     // IDs still available and no race condition is going to bridge that gap;
@@ -524,19 +528,23 @@ Http2Stream::GenerateOpen()
       return NS_ERROR_UNEXPECTED;
     }
 
     authorityHeader = ci->GetOrigin();
     authorityHeader.Append(':');
     authorityHeader.AppendInt(ci->OriginPort());
   }
 
+  nsAutoCString method;
+  nsAutoCString path;
+  head->Method(method);
+  head->Path(path);
   mSession->Compressor()->EncodeHeaderBlock(mFlatHttpRequestHeaders,
-                                            head->Method(),
-                                            head->Path(),
+                                            method,
+                                            path,
                                             authorityHeader,
                                             scheme,
                                             head->IsConnect(),
                                             compressedData);
 
   int64_t clVal = mSession->Compressor()->GetParsedContentLength();
   if (clVal != -1) {
     mRequestBodyLenRemaining = clVal;
@@ -592,17 +600,17 @@ Http2Stream::GenerateOpen()
   EnsureBuffer(mTxInlineFrame, messageSize,
                mTxInlineFrameUsed, mTxInlineFrameSize);
 
   mTxInlineFrameUsed += messageSize;
   UpdatePriorityDependency();
   LOG3(("Http2Stream %p Generating %d bytes of HEADERS for stream 0x%X with "
         "priority weight %u dep 0x%X frames %u uri=%s\n",
         this, mTxInlineFrameUsed, mStreamID, mPriorityWeight,
-        mPriorityDependency, numFrames, nsCString(head->RequestURI()).get()));
+        mPriorityDependency, numFrames, requestURI.get()));
 
   uint32_t outputOffset = 0;
   uint32_t compressedDataOffset = 0;
   for (uint32_t idx = 0; idx < numFrames; ++idx) {
     uint32_t flags, frameLen;
     bool lastFrame = (idx == numFrames - 1);
 
     flags = 0;
@@ -638,17 +646,17 @@ Http2Stream::GenerateOpen()
     outputOffset += frameLen;
   }
 
   Telemetry::Accumulate(Telemetry::SPDY_SYN_SIZE, compressedData.Length());
 
   // The size of the input headers is approximate
   uint32_t ratio =
     compressedData.Length() * 100 /
-    (11 + head->RequestURI().Length() +
+    (11 + requestURI.Length() +
      mFlatHttpRequestHeaders.Length());
 
   mFlatHttpRequestHeaders.Truncate();
   Telemetry::Accumulate(Telemetry::SPDY_SYN_RATIO, ratio);
   return NS_OK;
 }
 
 void
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -47,16 +47,17 @@
 #include "nsILoadGroupChild.h"
 #include "mozilla/ConsoleReportCollector.h"
 #include "LoadInfo.h"
 #include "nsISSLSocketControl.h"
 #include "mozilla/Telemetry.h"
 #include "nsIURL.h"
 #include "nsIConsoleService.h"
 #include "mozilla/BinarySearch.h"
+#include "nsIHttpHeaderVisitor.h"
 
 #include <algorithm>
 
 namespace mozilla {
 namespace net {
 
 HttpBaseChannel::HttpBaseChannel()
   : mStartPos(UINT64_MAX)
@@ -184,17 +185,17 @@ HttpBaseChannel::Init(nsIURI *aURI,
   // Set request headers
   nsAutoCString hostLine;
   rv = nsHttpHandler::GenerateHostPort(host, port, hostLine);
   if (NS_FAILED(rv)) return rv;
 
   rv = mRequestHead.SetHeader(nsHttp::Host, hostLine);
   if (NS_FAILED(rv)) return rv;
 
-  rv = gHttpHandler->AddStandardRequestHeaders(&mRequestHead.Headers(), isHTTPS);
+  rv = gHttpHandler->AddStandardRequestHeaders(&mRequestHead, isHTTPS);
   if (NS_FAILED(rv)) return rv;
 
   nsAutoCString type;
   if (aProxyInfo && NS_SUCCEEDED(aProxyInfo->GetType(type)) &&
       !type.EqualsLiteral("unknown"))
     mProxyInfo = aProxyInfo;
 
   return rv;
@@ -1172,17 +1173,17 @@ HttpBaseChannel::GetEncodedBodySize(uint
 {
   *aEncodedBodySize = mEncodedBodySize;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::GetRequestMethod(nsACString& aMethod)
 {
-  aMethod = mRequestHead.Method();
+  mRequestHead.Method(aMethod);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::SetRequestMethod(const nsACString& aMethod)
 {
   ENSURE_CALLED_BEFORE_CONNECT();
 
@@ -1598,24 +1599,24 @@ HttpBaseChannel::SetEmptyRequestHeader(c
   }
 
   return mRequestHead.SetEmptyHeader(atom);
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::VisitRequestHeaders(nsIHttpHeaderVisitor *visitor)
 {
-  return mRequestHead.Headers().VisitHeaders(visitor);
+  return mRequestHead.VisitHeaders(visitor);
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::VisitNonDefaultRequestHeaders(nsIHttpHeaderVisitor *visitor)
 {
-  return mRequestHead.Headers().VisitHeaders(
-      visitor, nsHttpHeaderArray::eFilterSkipDefault);
+  return mRequestHead.VisitHeaders(visitor,
+                                   nsHttpHeaderArray::eFilterSkipDefault);
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::GetResponseHeader(const nsACString &header, nsACString &value)
 {
   value.Truncate();
 
   if (!mResponseHead)
@@ -2725,16 +2726,45 @@ bool IsHeaderBlacklistedForRedirectCopy(
     }
   };
 
   size_t unused;
   return BinarySearchIf(blackList, 0, ArrayLength(blackList),
                         HttpAtomComparator(aHeader), &unused);
 }
 
+class SetupReplacementChannelHeaderVisitor final : public nsIHttpHeaderVisitor
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  explicit SetupReplacementChannelHeaderVisitor(nsIHttpChannel *aChannel)
+    : mChannel(aChannel)
+  {
+  }
+
+  NS_IMETHOD VisitHeader(const nsACString& aHeader,
+                         const nsACString& aValue) override
+  {
+    nsHttpAtom atom = nsHttp::ResolveAtom(aHeader);
+    if (!IsHeaderBlacklistedForRedirectCopy(atom)) {
+      mChannel->SetRequestHeader(aHeader, aValue, false);
+    }
+    return NS_OK;
+  }
+private:
+  ~SetupReplacementChannelHeaderVisitor()
+  {
+  }
+
+  nsCOMPtr<nsIHttpChannel> mChannel;
+};
+
+NS_IMPL_ISUPPORTS(SetupReplacementChannelHeaderVisitor, nsIHttpHeaderVisitor)
+
 nsresult
 HttpBaseChannel::SetupReplacementChannel(nsIURI       *newURI,
                                          nsIChannel   *newChannel,
                                          bool          preserveMethod,
                                          uint32_t      redirectFlags)
 {
   LOG(("HttpBaseChannel::SetupReplacementChannel "
      "[this=%p newChannel=%p preserveMethod=%d]",
@@ -2803,51 +2833,57 @@ HttpBaseChannel::SetupReplacementChannel
     if (mUploadStream && (uploadChannel2 || uploadChannel)) {
       // rewind upload stream
       nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream);
       if (seekable)
         seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
 
       // replicate original call to SetUploadStream...
       if (uploadChannel2) {
-        const char *ctype = mRequestHead.PeekHeader(nsHttp::Content_Type);
-        if (!ctype)
-          ctype = "";
-        const char *clen  = mRequestHead.PeekHeader(nsHttp::Content_Length);
-        int64_t len = clen ? nsCRT::atoll(clen) : -1;
+        nsAutoCString ctype;
+        // If header is not present mRequestHead.HasHeaderValue will truncated
+        // it.
+        mRequestHead.GetHeader(nsHttp::Content_Type, ctype);
+        nsAutoCString clen;
+        mRequestHead.GetHeader(nsHttp::Content_Length, clen);
+        nsAutoCString method;
+        mRequestHead.Method(method);
+        int64_t len = clen.IsEmpty() ? -1 : nsCRT::atoll(clen.get());
         uploadChannel2->ExplicitSetUploadStream(
-                                  mUploadStream, nsDependentCString(ctype), len,
-                                  mRequestHead.Method(),
+                                  mUploadStream, ctype, len,
+                                  method,
                                   mUploadStreamHasHeaders);
       } else {
         if (mUploadStreamHasHeaders) {
           uploadChannel->SetUploadStream(mUploadStream, EmptyCString(),
                            -1);
         } else {
-          const char *ctype =
-            mRequestHead.PeekHeader(nsHttp::Content_Type);
-          const char *clen =
-            mRequestHead.PeekHeader(nsHttp::Content_Length);
-          if (!ctype) {
-            ctype = "application/octet-stream";
+          nsAutoCString ctype;
+          if (NS_FAILED(mRequestHead.GetHeader(nsHttp::Content_Type, ctype))) {
+            ctype =  NS_LITERAL_CSTRING("application/octet-stream");
           }
-          if (clen) {
+          nsAutoCString clen;
+          if (NS_SUCCEEDED(mRequestHead.GetHeader(nsHttp::Content_Length, clen))
+              &&
+              !clen.IsEmpty()) {
             uploadChannel->SetUploadStream(mUploadStream,
-                                           nsDependentCString(ctype),
-                                           nsCRT::atoll(clen));
+                                           ctype,
+                                           nsCRT::atoll(clen.get()));
           }
         }
       }
     }
     // since preserveMethod is true, we need to ensure that the appropriate
     // request method gets set on the channel, regardless of whether or not
     // we set the upload stream above. This means SetRequestMethod() will
     // be called twice if ExplicitSetUploadStream() gets called above.
 
-    httpChannel->SetRequestMethod(mRequestHead.Method());
+    nsAutoCString method;
+    mRequestHead.Method(method);
+    httpChannel->SetRequestMethod(method);
   }
   // convey the referrer if one was used for this channel to the next one
   if (mReferrer)
     httpChannel->SetReferrerWithPolicy(mReferrer, mReferrerPolicy);
   // convey the mAllowPipelining and mAllowSTS flags
   httpChannel->SetAllowPipelining(mAllowPipelining);
   httpChannel->SetAllowSTS(mAllowSTS);
   // convey the new redirection limit
@@ -2967,28 +3003,19 @@ HttpBaseChannel::SetupReplacementChannel
         mAllRedirectsPassTimingAllowCheck &&
         oldTimedChannel->TimingAllowCheck(principal));
     }
   }
 
   if (redirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL |
                        nsIChannelEventSink::REDIRECT_STS_UPGRADE)) {
     // Copy non-origin related headers to the new channel.
-    nsHttpHeaderArray& requestHeaders = mRequestHead.Headers();
-    uint32_t requestHeaderCount = requestHeaders.Count();
-    for (uint32_t i = 0; i < requestHeaderCount; ++i) {
-      nsHttpAtom header;
-      const char *val = requestHeaders.PeekHeaderAt(i, header);
-      if (!val || IsHeaderBlacklistedForRedirectCopy(header)) {
-          continue;
-      }
-
-      httpChannel->SetRequestHeader(nsDependentCString(header.get()),
-                                    nsDependentCString(val), false);
-    }
+    nsCOMPtr<nsIHttpHeaderVisitor> visitor =
+      new SetupReplacementChannelHeaderVisitor(httpChannel);
+    mRequestHead.VisitHeaders(visitor);
   }
 
   // This channel has been redirected. Don't report timing info.
   mTimingEnabled = false;
   return NS_OK;
 }
 
 // Redirect Tracking
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -446,17 +446,17 @@ HttpChannelChild::OnStartRequest(const n
   rv = container->SetData(cacheKey);
   if (NS_FAILED(rv)) {
     Cancel(rv);
     return;
   }
   mCacheKey = container;
 
   // replace our request headers with what actually got sent in the parent
-  mRequestHead.Headers() = requestHeaders;
+  mRequestHead.SetHeaders(requestHeaders);
 
   // Note: this is where we would notify "http-on-examine-response" observers.
   // We have deliberately disabled this for child processes (see bug 806753)
   //
   // gHttpHandler->OnExamineResponse(this);
 
   mTracingEnabled = false;
 
@@ -1736,19 +1736,19 @@ HttpChannelChild::AsyncOpen(nsIStreamLis
 
   // Port checked in parent, but duplicate here so we can return with error
   // immediately
   nsresult rv;
   rv = NS_CheckPortSafety(mURI);
   if (NS_FAILED(rv))
     return rv;
 
-  const char *cookieHeader = mRequestHead.PeekHeader(nsHttp::Cookie);
-  if (cookieHeader) {
-    mUserSetCookieHeader = cookieHeader;
+  nsAutoCString cookie;
+  if (NS_SUCCEEDED(mRequestHead.GetHeader(nsHttp::Cookie, cookie))) {
+    mUserSetCookieHeader = cookie;
   }
 
   AddCookiesToRequest();
 
   //
   // NOTE: From now on we must return NS_OK; all errors must be handled via
   // OnStart/OnStopRequest
   //
@@ -1849,17 +1849,17 @@ HttpChannelChild::ContinueAsyncOpen()
   SerializeURI(mURI, openArgs.uri());
   SerializeURI(mOriginalURI, openArgs.original());
   SerializeURI(mDocumentURI, openArgs.doc());
   SerializeURI(mReferrer, openArgs.referrer());
   openArgs.referrerPolicy() = mReferrerPolicy;
   SerializeURI(mAPIRedirectToURI, openArgs.apiRedirectTo());
   openArgs.loadFlags() = mLoadFlags;
   openArgs.requestHeaders() = mClientSetRequestHeaders;
-  openArgs.requestMethod() = mRequestHead.Method();
+  mRequestHead.Method(openArgs.requestMethod());
 
   nsTArray<mozilla::ipc::FileDescriptor> fds;
   SerializeInputStream(mUploadStream, openArgs.uploadStream(), fds);
 
   if (mResponseHead) {
     openArgs.synthesizedResponseHead() = *mResponseHead;
     openArgs.suspendAfterSynthesizeResponse() =
       mSuspendParentAfterSynthesizeResponse;
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -1052,31 +1052,35 @@ HttpChannelParent::OnStartRequest(nsIReq
     }
 
     nsresult rv = container->GetData(&cacheKeyValue);
     if (NS_FAILED(rv)) {
       return rv;
     }
   }
 
+  // !!! We need to lock headers and please don't forget to unlock them !!!
+  requestHead->Lock();
+  nsresult rv = NS_OK;
   if (mIPCClosed ||
       !SendOnStartRequest(channelStatus,
                           responseHead ? *responseHead : nsHttpResponseHead(),
                           !!responseHead,
                           requestHead->Headers(),
                           isFromCache,
                           mCacheEntry ? true : false,
                           expirationTime, cachedCharset, secInfoSerialization,
                           mChannel->GetSelfAddr(), mChannel->GetPeerAddr(),
                           redirectCount,
                           cacheKeyValue))
   {
-    return NS_ERROR_UNEXPECTED;
+    rv = NS_ERROR_UNEXPECTED;
   }
-  return NS_OK;
+  requestHead->Unlock();
+  return rv;
 }
 
 NS_IMETHODIMP
 HttpChannelParent::OnStopRequest(nsIRequest *aRequest,
                                  nsISupports *aContext,
                                  nsresult aStatusCode)
 {
   LOG(("HttpChannelParent::OnStopRequest: [this=%p aRequest=%p status=%x]\n",
--- a/netwerk/protocol/http/SpdyStream31.cpp
+++ b/netwerk/protocol/http/SpdyStream31.cpp
@@ -292,20 +292,21 @@ SpdyStream31::ParseHttpRequestHeaders(co
   uint32_t oldLen = mFlatHttpRequestHeaders.Length();
   mFlatHttpRequestHeaders.SetLength(endHeader + 2);
   *countUsed = avail - (oldLen - endHeader) + 4;
   mRequestHeadersDone = 1;
 
   nsAutoCString hostHeader;
   nsAutoCString hashkey;
   mTransaction->RequestHead()->GetHeader(nsHttp::Host, hostHeader);
-
+  nsAutoCString requestURI;
+  mTransaction->RequestHead()->RequestURI(requestURI);
   CreatePushHashKey(nsDependentCString(mTransaction->RequestHead()->IsHTTPS() ? "https" : "http"),
                     hostHeader, mSession->Serial(),
-                    mTransaction->RequestHead()->RequestURI(),
+                    requestURI,
                     mOrigin, hashkey);
 
   // check the push cache for GET
   if (mTransaction->RequestHead()->IsGet()) {
     // from :scheme, :host, :path
     nsIRequestContext *requestContext = mTransaction->RequestContext();
     SpdyPushCache *cache = nullptr;
     if (requestContext)
@@ -414,17 +415,17 @@ SpdyStream31::GenerateSynFrame()
     versionHeader = NS_LITERAL_CSTRING("HTTP/1.1");
   else
     versionHeader = NS_LITERAL_CSTRING("HTTP/1.0");
 
   // use mRequestHead() to get a sense of how big to make the hash,
   // even though we are parsing the actual text stream because
   // it is legit to append headers.
   nsClassHashtable<nsCStringHashKey, nsCString>
-    hdrHash(mTransaction->RequestHead()->Headers().Count());
+    hdrHash(mTransaction->RequestHead()->HeaderCount());
 
   const char *beginBuffer = mFlatHttpRequestHeaders.BeginReading();
 
   // need to hash all the headers together to remove duplicates, special
   // headers, etc..
 
   int32_t crlfIndex = mFlatHttpRequestHeaders.Find("\r\n");
   while (true) {
@@ -478,36 +479,39 @@ SpdyStream31::GenerateSynFrame()
   }
 
   mTxInlineFrameUsed = 18;
 
   // Do not naively log the request headers here beacuse they might
   // contain auth. The http transaction already logs the sanitized request
   // headers at this same level so it is not necessary to do so here.
 
-  const char *methodHeader = mTransaction->RequestHead()->Method().get();
-  LOG3(("Stream method %p 0x%X %s\n", this, mStreamID, methodHeader));
+  nsAutoCString method;
+  mTransaction->RequestHead()->Method(method);
+  LOG3(("Stream method %p 0x%X %s\n", this, mStreamID, method.get()));
 
   // The header block length
   uint16_t count = hdrHash.Count() + 4; /* :method, :path, :version, :host */
   if (mTransaction->RequestHead()->IsConnect()) {
     mRequestBodyLenRemaining = 0x0fffffffffffffffULL;
   } else {
     ++count; // :scheme used if not connect
   }
   CompressToFrame(count);
 
   // :method, :path, :version comprise a HTTP/1 request line, so send those first
   // to make life easy for any gateways
   CompressToFrame(NS_LITERAL_CSTRING(":method"));
-  CompressToFrame(methodHeader, strlen(methodHeader));
+  CompressToFrame(method);
 
   CompressToFrame(NS_LITERAL_CSTRING(":path"));
   if (!mTransaction->RequestHead()->IsConnect()) {
-    CompressToFrame(mTransaction->RequestHead()->Path());
+    nsAutoCString path;
+    mTransaction->RequestHead()->Path(path);
+    CompressToFrame(path);
   } else {
     MOZ_ASSERT(mTransaction->QuerySpdyConnectTransaction());
     mIsTunnel = true;
     // Connect places host:port in :path. Don't use default port.
     nsHttpConnectionInfo *ci = mTransaction->ConnectionInfo();
     if (!ci) {
       return NS_ERROR_UNEXPECTED;
     }
@@ -566,21 +570,22 @@ SpdyStream31::GenerateSynFrame()
     // for other HTTP extension methods, rely on the content-length
     // to determine whether or not to put fin on syn
     mSentFinOnData = 1;
     mTxInlineFrame[4] = SpdySession31::kFlag_Data_FIN;
   }
 
   Telemetry::Accumulate(Telemetry::SPDY_SYN_SIZE, mTxInlineFrameUsed - 18);
 
+  nsAutoCString requestURI;
+  mTransaction->RequestHead()->RequestURI(requestURI);
   // The size of the input headers is approximate
   uint32_t ratio =
     (mTxInlineFrameUsed - 18) * 100 /
-    (11 + mTransaction->RequestHead()->RequestURI().Length() +
-     mFlatHttpRequestHeaders.Length());
+    (11 + requestURI.Length() + mFlatHttpRequestHeaders.Length());
 
   Telemetry::Accumulate(Telemetry::SPDY_SYN_RATIO, ratio);
   return NS_OK;
 }
 
 void
 SpdyStream31::AdjustInitialWindow()
 {
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -726,19 +726,21 @@ nsHttpChannel::SetupTransaction()
         //
         // disable pipelining if:
         //   (1) pipelining has been disabled by config
         //   (2) pipelining has been disabled by connection mgr info
         //   (3) request corresponds to a top-level document load (link click)
         //   (4) request method is non-idempotent
         //   (5) request is marked slow (e.g XHR)
         //
+        nsAutoCString method;
+        mRequestHead.Method(method);
         if (!mAllowPipelining ||
            (mLoadFlags & (LOAD_INITIAL_DOCUMENT_URI | INHIBIT_PIPELINE)) ||
-            !SafeForPipelining(mRequestHead.ParsedMethod(), mRequestHead.Method())) {
+            !SafeForPipelining(mRequestHead.ParsedMethod(), method)) {
             LOG(("  pipelining disallowed\n"));
             mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
         }
     }
 
     if (!mAllowSpdy)
         mCaps |= NS_HTTP_DISALLOW_SPDY;
 
@@ -2323,17 +2325,17 @@ nsHttpChannel::ResolveProxy()
         rv = pps->AsyncResolve(static_cast<nsIChannel*>(this), mProxyResolveFlags,
                                this, getter_AddRefs(mProxyRequest));
     }
 
     return rv;
 }
 
 bool
-nsHttpChannel::ResponseWouldVary(nsICacheEntry* entry) const
+nsHttpChannel::ResponseWouldVary(nsICacheEntry* entry)
 {
     nsresult rv;
     nsAutoCString buf, metaKey;
     mCachedResponseHead->GetHeader(nsHttp::Vary, buf);
     if (!buf.IsEmpty()) {
         NS_NAMED_LITERAL_CSTRING(prefix, "request-");
 
         // enumerate the elements of the Vary header...
@@ -2367,43 +2369,47 @@ nsHttpChannel::ResponseWouldVary(nsICach
             nsXPIDLCString lastVal;
             entry->GetMetaDataElement(metaKey.get(), getter_Copies(lastVal));
             LOG(("nsHttpChannel::ResponseWouldVary [channel=%p] "
                      "stored value = \"%s\"\n",
                  this, lastVal.get()));
 
             // Look for value of "Cookie" in the request headers
             nsHttpAtom atom = nsHttp::ResolveAtom(token);
-            const char *newVal = mRequestHead.PeekHeader(atom);
+            nsAutoCString newVal;
+            bool hasHeader = NS_SUCCEEDED(mRequestHead.GetHeader(atom,
+                                                                 newVal));
             if (!lastVal.IsEmpty()) {
                 // value for this header in cache, but no value in request
-                if (!newVal)
+                if (!hasHeader) {
                     return true; // yes - response would vary
+                }
 
                 // If this is a cookie-header, stored metadata is not
                 // the value itself but the hash. So we also hash the
                 // outgoing value here in order to compare the hashes
                 nsAutoCString hash;
                 if (atom == nsHttp::Cookie) {
-                    rv = Hash(newVal, hash);
+                    rv = Hash(newVal.get(), hash);
                     // If hash failed, be conservative (the cached hash
                     // exists at this point) and claim response would vary
                     if (NS_FAILED(rv))
                         return true;
-                    newVal = hash.get();
+                    newVal = hash;
 
                     LOG(("nsHttpChannel::ResponseWouldVary [this=%p] " \
                             "set-cookie value hashed to %s\n",
-                         this, newVal));
+                         this, newVal.get()));
                 }
 
-                if (strcmp(newVal, lastVal))
+                if (!newVal.Equals(lastVal)) {
                     return true; // yes, response would vary
-
-            } else if (newVal) { // old value is empty, but newVal is set
+                }
+
+            } else if (hasHeader) { // old value is empty, but newVal is set
                 return true;
             }
 
             // next token...
             token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
         }
     }
     return false;
@@ -2461,20 +2467,21 @@ nsHttpChannel::EnsureAssocReq()
     assoc_val = nullptr;
     endofmethod = net_FindCharInSet(method, HTTP_LWS);
     if (endofmethod)
         assoc_val = net_FindCharNotInSet(endofmethod, HTTP_LWS);
     if (!assoc_val)
         return NS_OK;
 
     // check the method
-    int32_t methodlen = strlen(mRequestHead.Method().get());
-    if ((methodlen != (endofmethod - method)) ||
+    nsAutoCString methodHead;
+    mRequestHead.Method(methodHead);
+    if ((((int32_t)methodHead.Length()) != (endofmethod - method)) ||
         PL_strncmp(method,
-                   mRequestHead.Method().get(),
+                   methodHead.get(),
                    endofmethod - method)) {
         LOG(("  Assoc-Req failure Method %s", method));
         if (mConnectionInfo)
             gHttpHandler->ConnMgr()->
                 PipelineFeedbackInfo(mConnectionInfo,
                                      nsHttpConnectionMgr::RedCorruptedContent,
                                      nullptr, 0);
 
@@ -2482,17 +2489,17 @@ nsHttpChannel::EnsureAssocReq()
             do_GetService(NS_CONSOLESERVICE_CONTRACTID);
         if (consoleService) {
             nsAutoString message
                 (NS_LITERAL_STRING("Failed Assoc-Req. Received "));
             AppendASCIItoUTF16(
                 mResponseHead->PeekHeader(nsHttp::Assoc_Req),
                 message);
             message += NS_LITERAL_STRING(" expected method ");
-            AppendASCIItoUTF16(mRequestHead.Method().get(), message);
+            AppendASCIItoUTF16(methodHead, message);
             consoleService->LogStringMessage(message.get());
         }
 
         if (gHttpHandler->EnforceAssocReq())
             return NS_ERROR_CORRUPTED_CONTENT;
         return NS_OK;
     }
 
@@ -2975,20 +2982,20 @@ nsHttpChannel::ContinueProcessFallback(n
     return NS_OK;
 }
 
 // Determines if a request is a byte range request for a subrange,
 // i.e. is a byte range request, but not a 0- byte range request.
 static bool
 IsSubRangeRequest(nsHttpRequestHead &aRequestHead)
 {
-    if (!aRequestHead.PeekHeader(nsHttp::Range))
+    nsAutoCString byteRange;
+    if (NS_FAILED(aRequestHead.GetHeader(nsHttp::Range, byteRange))) {
         return false;
-    nsAutoCString byteRange;
-    aRequestHead.GetHeader(nsHttp::Range, byteRange);
+    }
     return !byteRange.EqualsLiteral("bytes=0-");
 }
 
 nsresult
 nsHttpChannel::OpenCacheEntry(bool isHttps)
 {
     // Handle correctly mCacheEntriesToWaitFor
     AutoCacheWaitFlags waitFlags(this);
@@ -3248,21 +3255,21 @@ nsHttpChannel::OnCacheEntryCheck(nsICach
     nsresult rv = NS_OK;
 
     LOG(("nsHttpChannel::OnCacheEntryCheck enter [channel=%p entry=%p]",
         this, entry));
 
     // Remember the request is a custom conditional request so that we can
     // process any 304 response correctly.
     mCustomConditionalRequest =
-        mRequestHead.PeekHeader(nsHttp::If_Modified_Since) ||
-        mRequestHead.PeekHeader(nsHttp::If_None_Match) ||
-        mRequestHead.PeekHeader(nsHttp::If_Unmodified_Since) ||
-        mRequestHead.PeekHeader(nsHttp::If_Match) ||
-        mRequestHead.PeekHeader(nsHttp::If_Range);
+        mRequestHead.HasHeader(nsHttp::If_Modified_Since) ||
+        mRequestHead.HasHeader(nsHttp::If_None_Match) ||
+        mRequestHead.HasHeader(nsHttp::If_Unmodified_Since) ||
+        mRequestHead.HasHeader(nsHttp::If_Match) ||
+        mRequestHead.HasHeader(nsHttp::If_Range);
 
     // Be pessimistic: assume the cache entry has no useful data.
     *aResult = ENTRY_WANTED;
     mCachedContentIsValid = false;
 
     nsXPIDLCString buf;
 
     // Get the method that was used to generate the cached response
@@ -3484,23 +3491,23 @@ nsHttpChannel::OnCacheEntryCheck(nsICach
 
     // If a content signature is expected to be valid in this load,
     // set doValidation to force a signature check.
     if (!doValidation &&
         mLoadInfo && mLoadInfo->GetVerifySignedContent()) {
         doValidation = true;
     }
 
-    if (!doValidation && mRequestHead.PeekHeader(nsHttp::If_Match) &&
+    nsAutoCString requestedETag;
+    if (!doValidation &&
+        NS_SUCCEEDED(mRequestHead.GetHeader(nsHttp::If_Match, requestedETag)) &&
         (methodWasGet || methodWasHead)) {
-        const char *requestedETag, *cachedETag;
-        cachedETag = mCachedResponseHead->PeekHeader(nsHttp::ETag);
-        requestedETag = mRequestHead.PeekHeader(nsHttp::If_Match);
+        const char *cachedETag = mCachedResponseHead->PeekHeader(nsHttp::ETag);
         if (cachedETag && (!strncmp(cachedETag, "W/", 2) ||
-            strcmp(requestedETag, cachedETag))) {
+            !requestedETag.Equals(cachedETag))) {
             // User has defined If-Match header, if the cached entry is not
             // matching the provided header value or the cached ETag is weak,
             // force validation.
             doValidation = true;
         }
     }
 
     if (!doValidation) {
@@ -3513,17 +3520,17 @@ nsHttpChannel::OnCacheEntryCheck(nsICach
         // 2) the cache entry was generated w/o credentials, but would now
         //    require credentials (see bug 96705).
         //
         // NOTE: this does not apply to proxy authentication.
         //
         entry->GetMetaDataElement("auth", getter_Copies(buf));
         doValidation =
             (fromPreviousSession && !buf.IsEmpty()) ||
-            (buf.IsEmpty() && mRequestHead.PeekHeader(nsHttp::Authorization));
+            (buf.IsEmpty() && mRequestHead.HasHeader(nsHttp::Authorization));
     }
 
     // Bug #561276: We maintain a chain of cache-keys which returns cached
     // 3xx-responses (redirects) in order to detect cycles. If a cycle is
     // found, ignore the cached response and hit the net. Otherwise, use
     // the cached response and add the cache-key to the chain. Note that
     // a limited number of redirects (cached or not) is allowed and is
     // enforced independently of this mechanism
@@ -4426,18 +4433,19 @@ DoAddCacheEntryHeaders(nsHttpChannel *se
 
     LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] begin", self));
     // Store secure data in memory only
     if (securityInfo)
         entry->SetSecurityInfo(securityInfo);
 
     // Store the HTTP request method with the cache entry so we can distinguish
     // for example GET and HEAD responses.
-    rv = entry->SetMetaDataElement("request-method",
-                                   requestHead->Method().get());
+    nsAutoCString method;
+    requestHead->Method(method);
+    rv = entry->SetMetaDataElement("request-method", method.get());
     if (NS_FAILED(rv)) return rv;
 
     // Store the HTTP authorization scheme used if any...
     rv = StoreAuthorizationMetaData(entry, requestHead);
     if (NS_FAILED(rv)) return rv;
 
     // Iterate over the headers listed in the Vary response header, and
     // store the value of the corresponding request header so we can verify
@@ -4458,37 +4466,38 @@ DoAddCacheEntryHeaders(nsHttpChannel *se
 
             char *val = buf.BeginWriting(); // going to munge buf
             char *token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
             while (token) {
                 LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] " \
                         "processing %s", self, token));
                 if (*token != '*') {
                     nsHttpAtom atom = nsHttp::ResolveAtom(token);
-                    const char *val = requestHead->PeekHeader(atom);
+                    nsAutoCString val;
                     nsAutoCString hash;
-                    if (val) {
+                    if (NS_SUCCEEDED(requestHead->GetHeader(atom, val))) {
                         // If cookie-header, store a hash of the value
                         if (atom == nsHttp::Cookie) {
                             LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] " \
-                                    "cookie-value %s", self, val));
-                            rv = Hash(val, hash);
+                                    "cookie-value %s", self, val.get()));
+                            rv = Hash(val.get(), hash);
                             // If hash failed, store a string not very likely
                             // to be the result of subsequent hashes
-                            if (NS_FAILED(rv))
-                                val = "<hash failed>";
-                            else
-                                val = hash.get();
-
-                            LOG(("   hashed to %s\n", val));
+                            if (NS_FAILED(rv)) {
+                                val = NS_LITERAL_CSTRING("<hash failed>");
+                            } else {
+                                val = hash;
+                            }
+
+                            LOG(("   hashed to %s\n", val.get()));
                         }
 
                         // build cache meta data key and set meta data element...
                         metaKey = prefix + nsDependentCString(token);
-                        entry->SetMetaDataElement(metaKey.get(), val);
+                        entry->SetMetaDataElement(metaKey.get(), val.get());
                     } else {
                         LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] " \
                                 "clearing metadata for %s", self, token));
                         metaKey = prefix + nsDependentCString(token);
                         entry->SetMetaDataElement(metaKey.get(), nullptr);
                     }
                 }
                 token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
@@ -4527,23 +4536,24 @@ GetAuthType(const char *challenge, nsCSt
     else
         authType.Assign(challenge);
 }
 
 nsresult
 StoreAuthorizationMetaData(nsICacheEntry *entry, nsHttpRequestHead *requestHead)
 {
     // Not applicable to proxy authorization...
-    const char *val = requestHead->PeekHeader(nsHttp::Authorization);
-    if (!val)
+    nsAutoCString val;
+    if (NS_FAILED(requestHead->GetHeader(nsHttp::Authorization, val))) {
         return NS_OK;
+    }
 
     // eg. [Basic realm="wally world"]
     nsAutoCString buf;
-    GetAuthType(val, buf);
+    GetAuthType(val.get(), buf);
     return entry->SetMetaDataElement("auth", buf.get());
 }
 
 // Finalize the cache entry
 //  - may need to rewrite response headers if any headers changed
 //  - may need to recalculate the expiration time if any headers changed
 //  - called only for freshly written cache entries
 nsresult
@@ -5167,18 +5177,18 @@ nsHttpChannel::AsyncOpen(nsIStreamListen
     }
 
     if (mInterceptCache != INTERCEPTED && ShouldIntercept()) {
         mInterceptCache = MAYBE_INTERCEPT;
         SetCouldBeSynthesized();
     }
 
     // Remember the cookie header that was set, if any
-    const char *cookieHeader = mRequestHead.PeekHeader(nsHttp::Cookie);
-    if (cookieHeader) {
+    nsAutoCString cookieHeader;
+    if (NS_SUCCEEDED(mRequestHead.GetHeader(nsHttp::Cookie, cookieHeader))) {
         mUserSetCookieHeader = cookieHeader;
     }
 
     AddCookiesToRequest();
 
     // notify "http-on-opening-request" observers, but not if this is a redirect
     if (!(mLoadFlags & LOAD_REPLACE)) {
         gHttpHandler->OnOpeningRequest(this);
@@ -5201,17 +5211,17 @@ nsHttpChannel::AsyncOpen(nsIStreamListen
     // record asyncopen time unconditionally and clear it if we
     // don't want it after OnModifyRequest() weighs in. But waiting for
     // that to complete would mean we don't include proxy resolution in the
     // timing.
     mAsyncOpenTime = TimeStamp::Now();
 
     // Remember we have Authorization header set here.  We need to check on it
     // just once and early, AsyncOpen is the best place.
-    mCustomAuthHeader = !!mRequestHead.PeekHeader(nsHttp::Authorization);
+    mCustomAuthHeader = mRequestHead.HasHeader(nsHttp::Authorization);
 
     // the only time we would already know the proxy information at this
     // point would be if we were proxying a non-http protocol like ftp
     if (!mProxyInfo && NS_SUCCEEDED(ResolveProxy()))
         return NS_OK;
 
     rv = BeginConnect();
     if (NS_FAILED(rv))
@@ -5423,17 +5433,17 @@ nsHttpChannel::BeginConnect()
     // when proxying only use the pipeline bit if ProxyPipelining() allows it.
     if (!mConnectionInfo->UsingConnect() && mConnectionInfo->UsingHttpProxy()) {
         mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
         if (gHttpHandler->ProxyPipelining())
             mCaps |= NS_HTTP_ALLOW_PIPELINING;
     }
 
     // if this somehow fails we can go on without it
-    gHttpHandler->AddConnectionHeader(&mRequestHead.Headers(), mCaps);
+    gHttpHandler->AddConnectionHeader(&mRequestHead, mCaps);
 
     if (mLoadFlags & VALIDATE_ALWAYS || BYPASS_LOCAL_CACHE(mLoadFlags))
         mCaps |= NS_HTTP_REFRESH_DNS;
 
     if (!mLocalBlocklist && !mConnectionInfo->UsingHttpProxy() &&
         !(mLoadFlags & (LOAD_NO_NETWORK_IO | LOAD_ONLY_FROM_CACHE))) {
         // 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
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -413,17 +413,17 @@ private:
                rv == NS_ERROR_MALFORMED_URI;
     }
 
     // Create a aggregate set of the current notification callbacks
     // and ensure the transaction is updated to use it.
     void UpdateAggregateCallbacks();
 
     static bool HasQueryString(nsHttpRequestHead::ParsedMethodType method, nsIURI * uri);
-    bool ResponseWouldVary(nsICacheEntry* entry) const;
+    bool ResponseWouldVary(nsICacheEntry* entry);
     bool IsResumable(int64_t partialLen, int64_t contentLength,
                      bool ignoreMissingPartialLen = false) const;
     nsresult MaybeSetupByteRangeRequest(int64_t partialLen, int64_t contentLength,
                                         bool ignoreMissingPartialLen = false);
     nsresult SetupByteRangeRequest(int64_t partialLen);
     void UntieByteRangeRequest();
     void UntieValidationRequest();
     nsresult OpenCacheInputStream(nsICacheEntry* cacheEntry, bool startBuffering,
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -1030,31 +1030,35 @@ nsHttpConnection::OnHeadersAvailable(nsA
             MOZ_ASSERT(NS_SUCCEEDED(rv), "mSocketOut->AsyncWait failed");
         }
         else {
             LOG(("proxy CONNECT failed! endtoendssl=%d\n", isHttps));
             mTransaction->SetProxyConnectFailed();
         }
     }
 
-    const char *upgradeReq = requestHead->PeekHeader(nsHttp::Upgrade);
+    nsAutoCString upgradeReq;
+    bool hasUpgradeReq = NS_SUCCEEDED(requestHead->GetHeader(nsHttp::Upgrade,
+                                                             upgradeReq));
     // Don't use persistent connection for Upgrade unless there's an auth failure:
     // some proxies expect to see auth response on persistent connection.
-    if (upgradeReq && responseStatus != 401 && responseStatus != 407) {
+    if (hasUpgradeReq && responseStatus != 401 && responseStatus != 407) {
         LOG(("HTTP Upgrade in play - disable keepalive\n"));
         DontReuse();
     }
 
     if (responseStatus == 101) {
         const char *upgradeResp = responseHead->PeekHeader(nsHttp::Upgrade);
-        if (!upgradeReq || !upgradeResp ||
-            !nsHttp::FindToken(upgradeResp, upgradeReq,
+        if (!hasUpgradeReq || !upgradeResp ||
+            !nsHttp::FindToken(upgradeResp, upgradeReq.get(),
                                HTTP_HEADER_VALUE_SEPS)) {
             LOG(("HTTP 101 Upgrade header mismatch req = %s, resp = %s\n",
-                 upgradeReq, upgradeResp));
+                 upgradeReq.get(),
+                 upgradeResp ? upgradeResp :
+                               "RESPONSE's nsHttp::Upgrade is empty"));
             Close(NS_ERROR_ABORT);
         }
         else {
             LOG(("HTTP Upgrade Response to %s\n", upgradeResp));
         }
     }
 
     mLastHttpResponseVersion = responseHead->Version();
@@ -1860,21 +1864,23 @@ nsHttpConnection::MakeConnectString(nsAH
     // a CONNECT is always persistent
     request->SetHeader(nsHttp::Proxy_Connection, NS_LITERAL_CSTRING("keep-alive"));
     request->SetHeader(nsHttp::Connection, NS_LITERAL_CSTRING("keep-alive"));
 
     // all HTTP/1.1 requests must include a Host header (even though it
     // may seem redundant in this case; see bug 82388).
     request->SetHeader(nsHttp::Host, result);
 
-    const char *val = trans->RequestHead()->PeekHeader(nsHttp::Proxy_Authorization);
-    if (val) {
+    nsAutoCString val;
+    if (NS_SUCCEEDED(trans->RequestHead()->GetHeader(
+                         nsHttp::Proxy_Authorization,
+                         val))) {
         // we don't know for sure if this authorization is intended for the
         // SSL proxy, so we add it just in case.
-        request->SetHeader(nsHttp::Proxy_Authorization, nsDependentCString(val));
+        request->SetHeader(nsHttp::Proxy_Authorization, val);
     }
 
     result.Truncate();
     request->Flatten(result, false);
     result.AppendLiteral("\r\n");
     return NS_OK;
 }
 
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -444,17 +444,17 @@ nsHttpHandler::InitConnectionMgr()
                         mMaxPersistentConnectionsPerProxy,
                         mMaxRequestDelay,
                         mMaxPipelinedRequests,
                         mMaxOptimisticPipelinedRequests);
     return rv;
 }
 
 nsresult
-nsHttpHandler::AddStandardRequestHeaders(nsHttpHeaderArray *request, bool isSecure)
+nsHttpHandler::AddStandardRequestHeaders(nsHttpRequestHead *request, bool isSecure)
 {
     nsresult rv;
 
     // Add the "User-Agent" header
     rv = request->SetHeader(nsHttp::User_Agent, UserAgent(),
                             false, nsHttpHeaderArray::eVarietyDefault);
     if (NS_FAILED(rv)) return rv;
 
@@ -497,17 +497,17 @@ nsHttpHandler::AddStandardRequestHeaders
       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,
+nsHttpHandler::AddConnectionHeader(nsHttpRequestHead *request,
                                    uint32_t caps)
 {
     // RFC2616 section 19.6.2 states that the "Connection: keep-alive"
     // and "Keep-alive" request headers should not be sent by HTTP/1.1
     // user-agents.  But this is not a problem in practice, and the
     // alternative proxy-connection is worse. see 570283
 
     NS_NAMED_LITERAL_CSTRING(close, "close");
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -62,18 +62,18 @@ public:
     NS_DECL_NSIPROXIEDPROTOCOLHANDLER
     NS_DECL_NSIHTTPPROTOCOLHANDLER
     NS_DECL_NSIOBSERVER
     NS_DECL_NSISPECULATIVECONNECT
 
     nsHttpHandler();
 
     nsresult Init();
-    nsresult AddStandardRequestHeaders(nsHttpHeaderArray *, bool isSecure);
-    nsresult AddConnectionHeader(nsHttpHeaderArray *,
+    nsresult AddStandardRequestHeaders(nsHttpRequestHead *, bool isSecure);
+    nsresult AddConnectionHeader(nsHttpRequestHead *,
                                  uint32_t capabilities);
     bool     IsAcceptableEncoding(const char *encoding, bool isSecure);
 
     const nsAFlatCString &UserAgent();
 
     nsHttpVersion  HttpVersion()             { return mHttpVersion; }
     nsHttpVersion  ProxyHttpVersion()        { return mProxyHttpVersion; }
     uint8_t        ReferrerLevel()           { return mReferrerLevel; }
--- a/netwerk/protocol/http/nsHttpHeaderArray.cpp
+++ b/netwerk/protocol/http/nsHttpHeaderArray.cpp
@@ -132,16 +132,24 @@ nsHttpHeaderArray::GetHeader(nsHttpAtom 
     const nsEntry *entry = nullptr;
     LookupEntry(header, &entry);
     if (!entry)
         return NS_ERROR_NOT_AVAILABLE;
     result = entry->value;
     return NS_OK;
 }
 
+bool
+nsHttpHeaderArray::HasHeader(nsHttpAtom header) const
+{
+    const nsEntry *entry = nullptr;
+    LookupEntry(header, &entry);
+    return entry;
+}
+
 nsresult
 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) {
--- a/netwerk/protocol/http/nsHttpHeaderArray.h
+++ b/netwerk/protocol/http/nsHttpHeaderArray.h
@@ -54,16 +54,18 @@ public:
     }
 
     // Determine if the given header value exists.
     bool HasHeaderValue(nsHttpAtom header, const char *value) const
     {
         return FindHeaderValue(header, value) != nullptr;
     }
 
+    bool HasHeader(nsHttpAtom header) const;
+
     enum VisitorFilter
     {
         eFilterAll,
         eFilterSkipDefault,
     };
 
     nsresult VisitHeaders(nsIHttpHeaderVisitor *visitor, VisitorFilter filter = eFilterAll);
 
--- a/netwerk/protocol/http/nsHttpRequestHead.cpp
+++ b/netwerk/protocol/http/nsHttpRequestHead.cpp
@@ -2,41 +2,235 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // HttpLog.h should generally be included first
 #include "HttpLog.h"
 
 #include "nsHttpRequestHead.h"
+#include "nsIHttpHeaderVisitor.h"
 
 //-----------------------------------------------------------------------------
 // nsHttpRequestHead
 //-----------------------------------------------------------------------------
 
 namespace mozilla {
 namespace net {
 
 nsHttpRequestHead::nsHttpRequestHead()
     : mMethod(NS_LITERAL_CSTRING("GET"))
     , mVersion(NS_HTTP_VERSION_1_1)
     , mParsedMethod(kMethod_Get)
     , mHTTPS(false)
+    , mLock("nsHttpRequestHead.mLock")
 {
     MOZ_COUNT_CTOR(nsHttpRequestHead);
 }
 
 nsHttpRequestHead::~nsHttpRequestHead()
 {
     MOZ_COUNT_DTOR(nsHttpRequestHead);
 }
 
+// Don't use this function. It is only used by HttpChannelParent to avoid
+// copying of request headers!!!
+const nsHttpHeaderArray &
+nsHttpRequestHead::Headers() const
+{
+  mLock.AssertCurrentThreadOwns();
+  return mHeaders;
+}
+
+void
+nsHttpRequestHead::SetHeaders(const nsHttpHeaderArray& aHeaders)
+{
+  mHeaders = aHeaders;
+}
+
+void
+nsHttpRequestHead::SetVersion(nsHttpVersion version)
+{
+    MutexAutoLock lock(mLock);
+    mVersion = version;
+}
+
+void
+nsHttpRequestHead::SetRequestURI(const nsCSubstring &s)
+{
+    MutexAutoLock lock(mLock);
+    mRequestURI = s;
+}
+
+void
+nsHttpRequestHead::SetPath(const nsCSubstring &s)
+{
+    MutexAutoLock lock(mLock);
+    mPath = s;
+}
+
+uint32_t
+nsHttpRequestHead::HeaderCount()
+{
+    MutexAutoLock lock(mLock);
+    return mHeaders.Count();
+}
+
+nsresult
+nsHttpRequestHead::VisitHeaders(nsIHttpHeaderVisitor *visitor,
+                                nsHttpHeaderArray::VisitorFilter filter /* = nsHttpHeaderArray::eFilterAll*/)
+{
+    MutexAutoLock lock(mLock);
+    return mHeaders.VisitHeaders(visitor, filter);
+}
+
+void
+nsHttpRequestHead::Method(nsACString &aMethod)
+{
+    MutexAutoLock lock(mLock);
+    aMethod = mMethod;
+}
+
+nsHttpVersion
+nsHttpRequestHead::Version()
+{
+    MutexAutoLock lock(mLock);
+    return mVersion;
+}
+
+void
+nsHttpRequestHead::RequestURI(nsACString &aRequestURI)
+{
+    MutexAutoLock lock(mLock);
+    aRequestURI = mRequestURI;
+}
+
+void
+nsHttpRequestHead::Path(nsACString &aPath)
+{
+    MutexAutoLock lock(mLock);
+    aPath = mPath.IsEmpty() ? mRequestURI : mPath;
+}
+
+void
+nsHttpRequestHead::SetHTTPS(bool val)
+{
+    MutexAutoLock lock(mLock);
+    mHTTPS = val;
+}
+
+void
+nsHttpRequestHead::Origin(nsACString &aOrigin)
+{
+    MutexAutoLock lock(mLock);
+    aOrigin = mOrigin;
+}
+
+nsresult
+nsHttpRequestHead::SetHeader(nsHttpAtom h, const nsACString &v,
+                             bool m /*= false*/)
+{
+    MutexAutoLock lock(mLock);
+    return mHeaders.SetHeader(h, v, m);
+}
+
+nsresult
+nsHttpRequestHead::SetHeader(nsHttpAtom h, const nsACString &v, bool m,
+                             nsHttpHeaderArray::HeaderVariety variety)
+{
+    MutexAutoLock lock(mLock);
+    return mHeaders.SetHeader(h, v, m, variety);
+}
+
+nsresult
+nsHttpRequestHead::SetEmptyHeader(nsHttpAtom h)
+{
+    MutexAutoLock lock(mLock);
+    return mHeaders.SetEmptyHeader(h);
+}
+
+nsresult
+nsHttpRequestHead::GetHeader(nsHttpAtom h, nsACString &v)
+{
+    v.Truncate();
+    MutexAutoLock lock(mLock);
+    return mHeaders.GetHeader(h, v);
+}
+
+void
+nsHttpRequestHead::ClearHeader(nsHttpAtom h)
+{
+    MutexAutoLock lock(mLock);
+    mHeaders.ClearHeader(h);
+}
+
+void
+nsHttpRequestHead::ClearHeaders()
+{
+    MutexAutoLock lock(mLock);
+    mHeaders.Clear();
+}
+
+bool
+nsHttpRequestHead::HasHeader(nsHttpAtom h)
+{
+  MutexAutoLock lock(mLock);
+  return mHeaders.HasHeader(h);
+}
+
+bool
+nsHttpRequestHead::HasHeaderValue(nsHttpAtom h, const char *v)
+{
+    MutexAutoLock lock(mLock);
+    return mHeaders.HasHeaderValue(h, v);
+}
+
+nsresult
+nsHttpRequestHead::SetHeaderOnce(nsHttpAtom h, const char *v,
+                                 bool merge /*= false */)
+{
+    MutexAutoLock lock(mLock);
+    if (!merge || !mHeaders.HasHeaderValue(h, v)) {
+        return mHeaders.SetHeader(h, nsDependentCString(v), merge);
+    }
+    return NS_OK;
+}
+
+nsHttpRequestHead::ParsedMethodType
+nsHttpRequestHead::ParsedMethod()
+{
+    MutexAutoLock lock(mLock);
+    return mParsedMethod;
+}
+
+bool
+nsHttpRequestHead::EqualsMethod(ParsedMethodType aType)
+{
+    MutexAutoLock lock(mLock);
+    return mParsedMethod == aType;
+}
+
+void
+nsHttpRequestHead::ParseHeaderSet(char *buffer)
+{
+    MutexAutoLock lock(mLock);
+    mHeaders.ParseHeaderSet(buffer);
+}
+
+bool
+nsHttpRequestHead::IsHTTPS()
+{
+    MutexAutoLock lock(mLock);
+    return mHTTPS;
+}
+
 void
 nsHttpRequestHead::SetMethod(const nsACString &method)
 {
+    MutexAutoLock lock(mLock);
     mParsedMethod = kMethod_Custom;
     mMethod = method;
     if (!strcmp(mMethod.get(), "GET")) {
         mParsedMethod = kMethod_Get;
     } else if (!strcmp(mMethod.get(), "POST")) {
         mParsedMethod = kMethod_Post;
     } else if (!strcmp(mMethod.get(), "OPTIONS")) {
         mParsedMethod = kMethod_Options;
@@ -47,48 +241,54 @@ nsHttpRequestHead::SetMethod(const nsACS
     } else if (!strcmp(mMethod.get(), "PUT")) {
         mParsedMethod = kMethod_Put;
     } else if (!strcmp(mMethod.get(), "TRACE")) {
         mParsedMethod = kMethod_Trace;
     }
 }
 
 void
-nsHttpRequestHead::SetOrigin(const nsACString &scheme, const nsACString &host, int32_t port)
+nsHttpRequestHead::SetOrigin(const nsACString &scheme, const nsACString &host,
+                             int32_t port)
 {
+    MutexAutoLock lock(mLock);
     mOrigin.Assign(scheme);
     mOrigin.Append(NS_LITERAL_CSTRING("://"));
     mOrigin.Append(host);
     if (port >= 0) {
         mOrigin.Append(NS_LITERAL_CSTRING(":"));
         mOrigin.AppendInt(port);
     }
 }
 
 bool
-nsHttpRequestHead::IsSafeMethod() const
+nsHttpRequestHead::IsSafeMethod()
 {
-  // This code will need to be extended for new safe methods, otherwise
-  // they'll default to "not safe".
-    if (IsGet() || IsHead() || IsOptions() || IsTrace()) {
+    MutexAutoLock lock(mLock);
+    // This code will need to be extended for new safe methods, otherwise
+    // they'll default to "not safe".
+    if ((mParsedMethod == kMethod_Get) || (mParsedMethod == kMethod_Head) ||
+        (mParsedMethod == kMethod_Options) || (mParsedMethod == kMethod_Trace)
+       ) {
         return true;
     }
 
     if (mParsedMethod != kMethod_Custom) {
         return false;
     }
 
     return (!strcmp(mMethod.get(), "PROPFIND") ||
             !strcmp(mMethod.get(), "REPORT") ||
             !strcmp(mMethod.get(), "SEARCH"));
 }
 
 void
 nsHttpRequestHead::Flatten(nsACString &buf, bool pruneProxyHeaders)
 {
+    MutexAutoLock lock(mLock);
     // note: the first append is intentional.
 
     buf.Append(mMethod);
     buf.Append(' ');
     buf.Append(mRequestURI);
     buf.AppendLiteral(" HTTP/");
 
     switch (mVersion) {
--- a/netwerk/protocol/http/nsHttpRequestHead.h
+++ b/netwerk/protocol/http/nsHttpRequestHead.h
@@ -4,117 +4,120 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsHttpRequestHead_h__
 #define nsHttpRequestHead_h__
 
 #include "nsHttp.h"
 #include "nsHttpHeaderArray.h"
 #include "nsString.h"
+#include "mozilla/Mutex.h"
+
+class nsIHttpHeaderVisitor;
 
 namespace mozilla { namespace net {
 
 //-----------------------------------------------------------------------------
 // nsHttpRequestHead represents the request line and headers from an HTTP
 // request.
 //-----------------------------------------------------------------------------
 
 class nsHttpRequestHead
 {
 public:
     nsHttpRequestHead();
     ~nsHttpRequestHead();
 
-    void SetMethod(const nsACString &method);
-    void SetVersion(nsHttpVersion version) { mVersion = version; }
-    void SetRequestURI(const nsCSubstring &s) { mRequestURI = s; }
-    void SetPath(const nsCSubstring &s) { mPath = s; }
+    // The following function is only used in HttpChannelParent to avoid
+    // copying headers. If you use it be careful to do it only under
+    // nsHttpRequestHead lock!!!
+    const nsHttpHeaderArray &Headers() const;
+    void Lock() { mLock.Lock(); }
+    void Unlock() { mLock.Unlock(); }
 
-    const nsHttpHeaderArray &Headers() const { return mHeaders; }
-    nsHttpHeaderArray & Headers()          { return mHeaders; }
-    const nsCString &Method()        const { return mMethod; }
-    nsHttpVersion       Version()    const { return mVersion; }
-    const nsCSubstring &RequestURI() const { return mRequestURI; }
-    const nsCSubstring &Path()       const { return mPath.IsEmpty() ? mRequestURI : mPath; }
+    void SetHeaders(const nsHttpHeaderArray& aHeaders);
 
-    void SetHTTPS(bool val) { mHTTPS = val; }
-    bool IsHTTPS() const { return mHTTPS; }
-
-    void SetOrigin(const nsACString &scheme, const nsACString &host, int32_t port);
-    const nsCString &Origin() const { return mOrigin; }
+    void SetMethod(const nsACString &method);
+    void SetVersion(nsHttpVersion version);
+    void SetRequestURI(const nsCSubstring &s);
+    void SetPath(const nsCSubstring &s);
+    uint32_t HeaderCount();
 
-    const char *PeekHeader(nsHttpAtom h) const
-    {
-        return mHeaders.PeekHeader(h);
-    }
-    nsresult SetHeader(nsHttpAtom h, const nsACString &v, bool m=false) { return mHeaders.SetHeader(h, v, m); }
-    nsresult SetEmptyHeader(nsHttpAtom h) { return mHeaders.SetEmptyHeader(h); }
-    nsresult GetHeader(nsHttpAtom h, nsACString &v) const
-    {
-        return mHeaders.GetHeader(h, v);
-    }
-    void ClearHeader(nsHttpAtom h)                                           { mHeaders.ClearHeader(h); }
-    void ClearHeaders()                                                      { mHeaders.Clear(); }
+    // Using this function it is possible to itereate through all headers
+    // automatically under one lock.
+    nsresult VisitHeaders(nsIHttpHeaderVisitor *visitor,
+                          nsHttpHeaderArray::VisitorFilter filter =
+                              nsHttpHeaderArray::eFilterAll);
+    void Method(nsACString &aMethod);
+    nsHttpVersion Version();
+    void RequestURI(nsACString &RequestURI);
+    void Path(nsACString &aPath);
+    void SetHTTPS(bool val);
+    bool IsHTTPS();
 
-    const char *FindHeaderValue(nsHttpAtom h, const char *v) const
-    {
-        return mHeaders.FindHeaderValue(h, v);
-    }
-    bool HasHeaderValue(nsHttpAtom h, const char *v) const
-    {
-      return mHeaders.HasHeaderValue(h, v);
-    }
+    void SetOrigin(const nsACString &scheme, const nsACString &host,
+                   int32_t port);
+    void Origin(nsACString &aOrigin);
 
+    nsresult SetHeader(nsHttpAtom h, const nsACString &v, bool m=false);
+    nsresult SetHeader(nsHttpAtom h, const nsACString &v, bool m,
+                       nsHttpHeaderArray::HeaderVariety variety);
+    nsresult SetEmptyHeader(nsHttpAtom h);
+    nsresult GetHeader(nsHttpAtom h, nsACString &v);
+
+    void ClearHeader(nsHttpAtom h);
+    void ClearHeaders();
+
+    bool HasHeaderValue(nsHttpAtom h, const char *v);
+    // This function returns true if header is set even if it is an empty
+    // header.
+    bool HasHeader(nsHttpAtom h);
     void Flatten(nsACString &, bool pruneProxyHeaders = false);
 
     // Don't allow duplicate values
-    nsresult SetHeaderOnce(nsHttpAtom h, const char *v, bool merge = false)
-    {
-        if (!merge || !HasHeaderValue(h, v))
-            return mHeaders.SetHeader(h, nsDependentCString(v), merge);
-        return NS_OK;
-    }
+    nsresult SetHeaderOnce(nsHttpAtom h, const char *v, bool merge = false);
 
-    bool IsSafeMethod() const;
+    bool IsSafeMethod();
 
     enum ParsedMethodType
     {
         kMethod_Custom,
         kMethod_Get,
         kMethod_Post,
         kMethod_Options,
         kMethod_Connect,
         kMethod_Head,
         kMethod_Put,
         kMethod_Trace
     };
 
-    ParsedMethodType ParsedMethod() const { return mParsedMethod; }
-    bool EqualsMethod(ParsedMethodType aType) const { return mParsedMethod == aType; }
-    bool IsGet() const { return EqualsMethod(kMethod_Get); }
-    bool IsPost() const { return EqualsMethod(kMethod_Post); }
-    bool IsOptions() const { return EqualsMethod(kMethod_Options); }
-    bool IsConnect() const { return EqualsMethod(kMethod_Connect); }
-    bool IsHead() const { return EqualsMethod(kMethod_Head); }
-    bool IsPut() const { return EqualsMethod(kMethod_Put); }
-    bool IsTrace() const { return EqualsMethod(kMethod_Trace); }
-    void ParseHeaderSet(char *buffer) { mHeaders.ParseHeaderSet(buffer); }
-
+    ParsedMethodType ParsedMethod();
+    bool EqualsMethod(ParsedMethodType aType);
+    bool IsGet() { return EqualsMethod(kMethod_Get); }
+    bool IsPost() { return EqualsMethod(kMethod_Post); }
+    bool IsOptions() { return EqualsMethod(kMethod_Options); }
+    bool IsConnect() { return EqualsMethod(kMethod_Connect); }
+    bool IsHead() { return EqualsMethod(kMethod_Head); }
+    bool IsPut() { return EqualsMethod(kMethod_Put); }
+    bool IsTrace() { return EqualsMethod(kMethod_Trace); }
+    void ParseHeaderSet(char *buffer);
 private:
     // All members must be copy-constructable and assignable
     nsHttpHeaderArray mHeaders;
     nsCString         mMethod;
     nsHttpVersion     mVersion;
 
     // mRequestURI and mPath are strings instead of an nsIURI
     // because this is used off the main thread
     nsCString         mRequestURI;
     nsCString         mPath;
 
     nsCString         mOrigin;
     ParsedMethodType  mParsedMethod;
     bool              mHTTPS;
+
+    Mutex             mLock;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // nsHttpRequestHead_h__
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -176,37 +176,42 @@ nsHttpTransaction::~nsHttpTransaction()
 }
 
 nsHttpTransaction::Classifier
 nsHttpTransaction::Classify()
 {
     if (!(mCaps & NS_HTTP_ALLOW_PIPELINING))
         return (mClassification = CLASS_SOLO);
 
-    if (mRequestHead->PeekHeader(nsHttp::If_Modified_Since) ||
-        mRequestHead->PeekHeader(nsHttp::If_None_Match))
+    if (mRequestHead->HasHeader(nsHttp::If_Modified_Since) ||
+        mRequestHead->HasHeader(nsHttp::If_None_Match))
         return (mClassification = CLASS_REVALIDATION);
 
-    const char *accept = mRequestHead->PeekHeader(nsHttp::Accept);
-    if (accept && !PL_strncmp(accept, "image/", 6))
+    nsAutoCString accept;
+    bool hasAccept = NS_SUCCEEDED(mRequestHead->GetHeader(nsHttp::Accept, accept));
+    if (hasAccept && StringBeginsWith(accept, NS_LITERAL_CSTRING("image/"))) {
         return (mClassification = CLASS_IMAGE);
+    }
 
-    if (accept && !PL_strncmp(accept, "text/css", 8))
+    if (hasAccept && StringBeginsWith(accept, NS_LITERAL_CSTRING("text/css"))) {
         return (mClassification = CLASS_SCRIPT);
+    }
 
     mClassification = CLASS_GENERAL;
 
-    int32_t queryPos = mRequestHead->RequestURI().FindChar('?');
+    nsAutoCString requestURI;
+    mRequestHead->RequestURI(requestURI);
+    int32_t queryPos = requestURI.FindChar('?');
     if (queryPos == kNotFound) {
-        if (StringEndsWith(mRequestHead->RequestURI(),
+        if (StringEndsWith(requestURI,
                            NS_LITERAL_CSTRING(".js")))
             mClassification = CLASS_SCRIPT;
     }
     else if (queryPos >= 3 &&
-             Substring(mRequestHead->RequestURI(), queryPos - 3, 3).
+             Substring(requestURI, queryPos - 3, 3).
              EqualsLiteral(".js")) {
         mClassification = CLASS_SCRIPT;
     }
 
     return mClassification;
 }
 
 nsresult
@@ -294,17 +299,17 @@ nsHttpTransaction::Init(uint32_t caps,
     //   HTTP/1.0 requests containing an entity body must include a valid
     //   Content-Length header field.
     //
     // RFC2616 section 4.4:
     //   For compatibility with HTTP/1.0 applications, HTTP/1.1 requests
     //   containing a message-body MUST include a valid Content-Length header
     //   field unless the server is known to be HTTP/1.1 compliant.
     if ((requestHead->IsPost() || requestHead->IsPut()) &&
-        !requestBody && !requestHead->PeekHeader(nsHttp::Transfer_Encoding)) {
+        !requestBody && !requestHead->HasHeader(nsHttp::Transfer_Encoding)) {
         requestHead->SetHeader(nsHttp::Content_Length, NS_LITERAL_CSTRING("0"));
     }
 
     // grab a weak reference to the request head
     mRequestHead = requestHead;
 
     // make sure we eliminate any proxy specific headers from
     // the request if we are using CONNECT