Bug 1277019 - Change header and status parsing methods to take nsACString instead of char array. r=mayhemer
authorDecky Coss <coss@cosstropolis.com>
Tue, 31 May 2016 12:42:15 -0400
changeset 349478 a79fd415865ed313ffae5119b27f0a057237b7fb
parent 349477 2882620cd75fe1984479f4d6a9b1982ee95a00e4
child 349479 4760f549a0209f827af565c9e1515e09552837fa
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmayhemer
bugs1277019
milestone50.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 1277019 - Change header and status parsing methods to take nsACString instead of char array. r=mayhemer MozReview-Commit-ID: 7ZNwvnTMCzo
netwerk/protocol/http/InterceptedChannel.cpp
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpChunkedDecoder.cpp
netwerk/protocol/http/nsHttpHeaderArray.cpp
netwerk/protocol/http/nsHttpHeaderArray.h
netwerk/protocol/http/nsHttpRequestHead.cpp
netwerk/protocol/http/nsHttpRequestHead.h
netwerk/protocol/http/nsHttpResponseHead.cpp
netwerk/protocol/http/nsHttpResponseHead.h
netwerk/protocol/http/nsHttpTransaction.cpp
netwerk/protocol/http/nsHttpTransaction.h
netwerk/streamconv/converters/nsMultiMixedConv.cpp
testing/web-platform/meta/MANIFEST.json
testing/web-platform/meta/cors/allow-headers.htm.ini
testing/web-platform/meta/cors/origin.htm.ini
testing/web-platform/meta/cors/remote-origin.htm.ini
testing/web-platform/tests/XMLHttpRequest/headers-normalize-response.htm
testing/web-platform/tests/XMLHttpRequest/resources/parse-headers.py
--- a/netwerk/protocol/http/InterceptedChannel.cpp
+++ b/netwerk/protocol/http/InterceptedChannel.cpp
@@ -89,28 +89,28 @@ InterceptedChannelBase::DoSynthesizeStat
 
     // Always assume HTTP 1.1 for synthesized responses.
     nsAutoCString statusLine;
     statusLine.AppendLiteral("HTTP/1.1 ");
     statusLine.AppendInt(aStatus);
     statusLine.AppendLiteral(" ");
     statusLine.Append(aReason);
 
-    (*mSynthesizedResponseHead)->ParseStatusLine(statusLine.get());
+    (*mSynthesizedResponseHead)->ParseStatusLine(statusLine);
     return NS_OK;
 }
 
 nsresult
 InterceptedChannelBase::DoSynthesizeHeader(const nsACString& aName, const nsACString& aValue)
 {
     EnsureSynthesizedResponse();
 
     nsAutoCString header = aName + NS_LITERAL_CSTRING(": ") + aValue;
     // Overwrite any existing header.
-    nsresult rv = (*mSynthesizedResponseHead)->ParseHeaderLine(header.get());
+    nsresult rv = (*mSynthesizedResponseHead)->ParseHeaderLine(header);
     NS_ENSURE_SUCCESS(rv, rv);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 InterceptedChannelBase::GetConsoleReportCollector(nsIConsoleReportCollector** aCollectorOut)
 {
   MOZ_ASSERT(aCollectorOut);
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -3554,17 +3554,17 @@ nsHttpChannel::OnCacheEntryCheck(nsICach
     // line and headers in the form Firefox uses them internally (no dupicate
     // headers, etc.).
     rv = entry->GetMetaDataElement("response-head", getter_Copies(buf));
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Parse string stored in a "response-head" metadata element.
     // These response headers will be merged with the orignal headers (i.e. the
     // headers stored in a "original-response-headers" metadata element).
-    rv = mCachedResponseHead->ParseCachedHead((char *) buf.get());
+    rv = mCachedResponseHead->ParseCachedHead(buf.get());
     NS_ENSURE_SUCCESS(rv, rv);
     buf.Adopt(0);
 
     bool isCachedRedirect = WillRedirect(mCachedResponseHead);
 
     // Do not return 304 responses from the cache, and also do not return
     // any other non-redirect 3xx responses from the cache (see bug 759043).
     NS_ENSURE_TRUE((mCachedResponseHead->Status() / 100 != 3) ||
--- a/netwerk/protocol/http/nsHttpChunkedDecoder.cpp
+++ b/netwerk/protocol/http/nsHttpChunkedDecoder.cpp
@@ -108,17 +108,17 @@ nsHttpChunkedDecoder::ParseChunkRemainin
 
         if (mWaitEOF) {
             if (*buf) {
                 LOG(("got trailer: %s\n", buf));
                 // allocate a header array for the trailers on demand
                 if (!mTrailers) {
                     mTrailers = new nsHttpHeaderArray();
                 }
-                mTrailers->ParseHeaderLine(buf);
+                mTrailers->ParseHeaderLine(nsDependentCSubstring(buf, count));
             }
             else {
                 mWaitEOF = false;
                 mReachedEOF = true;
                 LOG(("reached end of chunked-body\n"));
             }
         }
         else if (*buf) {
--- a/netwerk/protocol/http/nsHttpHeaderArray.cpp
+++ b/netwerk/protocol/http/nsHttpHeaderArray.cpp
@@ -303,66 +303,68 @@ nsHttpHeaderArray::VisitHeaders(nsIHttpH
         if NS_FAILED(rv) {
             return rv;
         }
     }
     return NS_OK;
 }
 
 /*static*/ nsresult
-nsHttpHeaderArray::ParseHeaderLine(const char *line,
+nsHttpHeaderArray::ParseHeaderLine(const nsACString& line,
                                    nsHttpAtom *hdr,
-                                   char **val)
+                                   nsACString *val)
 {
     //
     // BNF from section 4.2 of RFC 2616:
     //
     //   message-header = field-name ":" [ field-value ]
     //   field-name     = token
     //   field-value    = *( field-content | LWS )
     //   field-content  = <the OCTETs making up the field-value
     //                     and consisting of either *TEXT or combinations
     //                     of token, separators, and quoted-string>
     //
 
     // We skip over mal-formed headers in the hope that we'll still be able to
     // do something useful with the response.
-    char *p = (char *) strchr(line, ':');
-    if (!p) {
-        LOG(("malformed header [%s]: no colon\n", line));
+    int32_t split = line.FindChar(':');
+
+    if (split == kNotFound) {
+        LOG(("malformed header [%s]: no colon\n",
+            PromiseFlatCString(line).get()));
         return NS_ERROR_FAILURE;
     }
 
+    const nsACString& sub = Substring(line, 0, split);
+    const nsACString& sub2 = Substring(
+        line, split + 1, line.Length() - split - 1);
+
     // make sure we have a valid token for the field-name
-    if (!nsHttp::IsValidToken(line, p)) {
-        LOG(("malformed header [%s]: field-name not a token\n", line));
+    if (!nsHttp::IsValidToken(sub)) {
+        LOG(("malformed header [%s]: field-name not a token\n",
+            PromiseFlatCString(line).get()));
         return NS_ERROR_FAILURE;
     }
 
-    *p = 0; // null terminate field-name
-
-    nsHttpAtom atom = nsHttp::ResolveAtom(line);
+    nsHttpAtom atom = nsHttp::ResolveAtom(sub);
     if (!atom) {
-        LOG(("failed to resolve atom [%s]\n", line));
+        LOG(("failed to resolve atom [%s]\n", PromiseFlatCString(line).get()));
         return NS_ERROR_FAILURE;
     }
 
     // skip over whitespace
-    p = net_FindCharNotInSet(++p, HTTP_LWS);
+    char *p = net_FindCharNotInSet(
+        sub2.BeginReading(), sub2.EndReading(), HTTP_LWS);
 
     // trim trailing whitespace - bug 86608
-    char *p2 = net_RFindCharNotInSet(p, HTTP_LWS);
-
-    *++p2 = 0; // null terminate header value; if all chars starting at |p|
-               // consisted of LWS, then p2 would have pointed at |p-1|, so
-               // the prefix increment is always valid.
+    char *p2 = net_RFindCharNotInSet(p, sub2.EndReading(), HTTP_LWS);
 
     // assign return values
     if (hdr) *hdr = atom;
-    if (val) *val = p;
+    if (val) val->Assign(p, p2 - p + 1);
 
     return NS_OK;
 }
 
 void
 nsHttpHeaderArray::Flatten(nsACString &buf, bool pruneProxyHeaders,
                            bool pruneTransients)
 {
--- a/netwerk/protocol/http/nsHttpHeaderArray.h
+++ b/netwerk/protocol/http/nsHttpHeaderArray.h
@@ -91,19 +91,19 @@ public:
         eFilterResponse,
         eFilterResponseOriginal
     };
 
     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).
-    static nsresult ParseHeaderLine(const char *line,
+    static nsresult ParseHeaderLine(const nsACString& line,
                                     nsHttpAtom *header=nullptr,
-                                    char **value=nullptr);
+                                    nsACString* value=nullptr);
 
     void Flatten(nsACString &, bool pruneProxyHeaders, bool pruneTransients);
     void FlattenOriginalHeader(nsACString &);
 
     uint32_t Count() const { return mHeaders.Length(); }
 
     const char *PeekHeaderAt(uint32_t i, nsHttpAtom &header) const;
 
--- a/netwerk/protocol/http/nsHttpRequestHead.cpp
+++ b/netwerk/protocol/http/nsHttpRequestHead.cpp
@@ -244,31 +244,32 @@ nsHttpRequestHead::ParsedMethod()
 bool
 nsHttpRequestHead::EqualsMethod(ParsedMethodType aType)
 {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     return mParsedMethod == aType;
 }
 
 void
-nsHttpRequestHead::ParseHeaderSet(char *buffer)
+nsHttpRequestHead::ParseHeaderSet(const char *buffer)
 {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     nsHttpAtom hdr;
-    char *val;
+    nsAutoCString val;
     while (buffer) {
-        char *eof = strchr(buffer, '\r');
+        const char *eof = strchr(buffer, '\r');
         if (!eof) {
             break;
         }
-        *eof = '\0';
-        if (NS_SUCCEEDED(nsHttpHeaderArray::ParseHeaderLine(buffer,
-                                                            &hdr,
-                                                            &val))) {
-            mHeaders.SetHeaderFromNet(hdr, nsDependentCString(val), false);
+        if (NS_SUCCEEDED(nsHttpHeaderArray::ParseHeaderLine(
+            nsDependentCSubstring(buffer, eof - buffer),
+            &hdr,
+            &val))) {
+
+            mHeaders.SetHeaderFromNet(hdr, val, false);
         }
         buffer = eof + 1;
         if (*buffer == '\n') {
             buffer++;
         }
     }
 }
 
--- a/netwerk/protocol/http/nsHttpRequestHead.h
+++ b/netwerk/protocol/http/nsHttpRequestHead.h
@@ -93,34 +93,34 @@ public:
     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);
+    void ParseHeaderSet(const 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;
 
     // We are using ReentrantMonitor instead of a Mutex because VisitHeader
-    // function calls nsIHttpHeaderVisitor::VisitHeader while under lock. 
+    // function calls nsIHttpHeaderVisitor::VisitHeader while under lock.
     ReentrantMonitor  mReentrantMonitor;
 
     // During VisitHeader we sould not allow cal to SetHeader.
     bool mInVisitHeaders;
 };
 
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/nsHttpResponseHead.cpp
+++ b/netwerk/protocol/http/nsHttpResponseHead.cpp
@@ -265,43 +265,41 @@ nsHttpResponseHead::FlattenNetworkOrigin
     if (mVersion == NS_HTTP_VERSION_0_9) {
         return;
     }
 
     mHeaders.FlattenOriginalHeader(buf);
 }
 
 nsresult
-nsHttpResponseHead::ParseCachedHead(char *block)
+nsHttpResponseHead::ParseCachedHead(const char *block)
 {
     ReentrantMonitorAutoEnter monitor(mReentrantMonitor);
     LOG(("nsHttpResponseHead::ParseCachedHead [this=%p]\n", this));
 
     // this command works on a buffer as prepared by Flatten, as such it is
     // not very forgiving ;-)
 
     char *p = PL_strstr(block, "\r\n");
     if (!p)
         return NS_ERROR_UNEXPECTED;
 
-    *p = 0;
-    ParseStatusLine_locked(block);
+    ParseStatusLine_locked(nsDependentCSubstring(block, p - block));
 
     do {
         block = p + 2;
 
         if (*block == 0)
             break;
 
         p = PL_strstr(block, "\r\n");
         if (!p)
             return NS_ERROR_UNEXPECTED;
 
-        *p = 0;
-        ParseHeaderLine_locked(block, false);
+        ParseHeaderLine_locked(nsDependentCSubstring(block, p - block), false);
 
     } while (1);
 
     return NS_OK;
 }
 
 nsresult
 nsHttpResponseHead::ParseCachedOriginalHeaders(char *block)
@@ -313,36 +311,38 @@ nsHttpResponseHead::ParseCachedOriginalH
     // as such it is not very forgiving ;-)
 
     if (!block) {
         return NS_ERROR_UNEXPECTED;
     }
 
     char *p = block;
     nsHttpAtom hdr = {0};
-    char *val;
+    nsAutoCString val;
     nsresult rv;
 
     do {
         block = p;
 
         if (*block == 0)
             break;
 
         p = PL_strstr(block, "\r\n");
         if (!p)
             return NS_ERROR_UNEXPECTED;
 
         *p = 0;
-        if (NS_FAILED(nsHttpHeaderArray::ParseHeaderLine(block, &hdr, &val))) {
+        if (NS_FAILED(nsHttpHeaderArray::ParseHeaderLine(
+            nsDependentCString(block, p - block), &hdr, &val))) {
+
             return NS_OK;
         }
 
         rv = mHeaders.SetResponseHeaderFromCache(hdr,
-                                                 nsDependentCString(val),
+                                                 val,
                                                  nsHttpHeaderArray::eVarietyResponseNetOriginal);
 
         if (NS_FAILED(rv)) {
             return rv;
         }
 
         p = p + 2;
     } while (1);
@@ -500,111 +500,122 @@ nsHttpResponseHead::AssignDefaultStatusT
         break;
     default:
         mStatusText.AssignLiteral("No Reason Phrase");
         break;
     }
 }
 
 void
-nsHttpResponseHead::ParseStatusLine(const char *line)
+nsHttpResponseHead::ParseStatusLine(const nsACString &line)
 {
+
     ReentrantMonitorAutoEnter monitor(mReentrantMonitor);
     ParseStatusLine_locked(line);
 }
 
 void
-nsHttpResponseHead::ParseStatusLine_locked(const char *line)
+nsHttpResponseHead::ParseStatusLine_locked(const nsACString &line)
 {
     //
     // Parse Status-Line:: HTTP-Version SP Status-Code SP Reason-Phrase CRLF
     //
 
+    const char *start = line.BeginReading();
+    const char *end = line.EndReading();
+    const char *p = start;
+
     // HTTP-Version
-    ParseVersion(line);
+    ParseVersion(start);
 
-    if ((mVersion == NS_HTTP_VERSION_0_9) || !(line = PL_strchr(line, ' '))) {
+    int32_t index = line.FindChar(' ');
+
+    if ((mVersion == NS_HTTP_VERSION_0_9) || (index == -1)) {
         mStatus = 200;
         AssignDefaultStatusText();
     }
     else {
         // Status-Code
-        mStatus = (uint16_t) atoi(++line);
+        p += index + 1;
+        mStatus = (uint16_t) atoi(p);
         if (mStatus == 0) {
             LOG(("mal-formed response status; assuming status = 200\n"));
             mStatus = 200;
         }
 
         // Reason-Phrase is whatever is remaining of the line
-        if (!(line = PL_strchr(line, ' '))) {
+        index = line.FindChar(' ', p - start);
+        if (index == -1) {
             AssignDefaultStatusText();
         }
-        else
-            mStatusText = nsDependentCString(++line);
+        else {
+            p = start + index + 1;
+            mStatusText = nsDependentCSubstring(p, end - p);
+        }
     }
 
     LOG(("Have status line [version=%u status=%u statusText=%s]\n",
         unsigned(mVersion), unsigned(mStatus), mStatusText.get()));
 }
 
 nsresult
-nsHttpResponseHead::ParseHeaderLine(const char *line)
+nsHttpResponseHead::ParseHeaderLine(const nsACString &line)
 {
     ReentrantMonitorAutoEnter monitor(mReentrantMonitor);
     return ParseHeaderLine_locked(line, true);
 }
 
 nsresult
-nsHttpResponseHead::ParseHeaderLine_locked(const char *line, bool originalFromNetHeaders)
+nsHttpResponseHead::ParseHeaderLine_locked(const nsACString &line, bool originalFromNetHeaders)
 {
     nsHttpAtom hdr = {0};
-    char *val;
+    nsAutoCString val;
 
     if (NS_FAILED(nsHttpHeaderArray::ParseHeaderLine(line, &hdr, &val))) {
         return NS_OK;
     }
     nsresult rv;
     if (originalFromNetHeaders) {
         rv = mHeaders.SetHeaderFromNet(hdr,
-                                       nsDependentCString(val),
+                                       val,
                                        true);
     } else {
         rv = mHeaders.SetResponseHeaderFromCache(hdr,
-                                                 nsDependentCString(val),
+                                                 val,
                                                  nsHttpHeaderArray::eVarietyResponse);
     }
     if (NS_FAILED(rv)) {
         return rv;
     }
 
     // leading and trailing LWS has been removed from |val|
 
     // handle some special case headers...
     if (hdr == nsHttp::Content_Length) {
         int64_t len;
         const char *ignored;
         // permit only a single value here.
-        if (nsHttp::ParseInt64(val, &ignored, &len)) {
+        if (nsHttp::ParseInt64(val.get(), &ignored, &len)) {
             mContentLength = len;
         }
         else {
             // If this is a negative content length then just ignore it
-            LOG(("invalid content-length! %s\n", val));
+            LOG(("invalid content-length! %s\n", val.get()));
         }
     }
     else if (hdr == nsHttp::Content_Type) {
-        LOG(("ParseContentType [type=%s]\n", val));
+        LOG(("ParseContentType [type=%s]\n", val.get()));
         bool dummy;
-        net_ParseContentType(nsDependentCString(val),
+        net_ParseContentType(val,
                              mContentType, mContentCharset, &dummy);
     }
     else if (hdr == nsHttp::Cache_Control)
-        ParseCacheControl(val);
+        ParseCacheControl(val.get());
     else if (hdr == nsHttp::Pragma)
-        ParsePragma(val);
+        ParsePragma(val.get());
     return NS_OK;
 }
 
 // From section 13.2.3 of RFC2616, we compute the current age of a cached
 // response as follows:
 //
 //    currentAge = max(max(0, responseTime - dateValue), ageValue)
 //               + now - requestTime
--- a/netwerk/protocol/http/nsHttpResponseHead.h
+++ b/netwerk/protocol/http/nsHttpResponseHead.h
@@ -84,25 +84,25 @@ public:
     void FlattenNetworkOriginalHeaders(nsACString &buf);
 
     // The next 2 functions parse flattened response head and original net headers.
     // They are used when we are reading an entry from the cache.
     //
     // To keep proper order of the original headers we MUST call
     // ParseCachedOriginalHeaders FIRST and then ParseCachedHead.
     //
-    // block must be null terminated. parsing is destructive.
-    nsresult ParseCachedHead(char *block);
+    // block must be null terminated.
+    nsresult ParseCachedHead(const char *block);
     nsresult ParseCachedOriginalHeaders(char *block);
 
-    // parse the status line. line must be null terminated.
-    void ParseStatusLine(const char *line);
+    // parse the status line.
+    void ParseStatusLine(const nsACString &line);
 
-    // parse a header line. line must be null terminated. parsing is destructive.
-    nsresult ParseHeaderLine(const char *line);
+    // parse a header line.
+    nsresult ParseHeaderLine(const nsACString &line);
 
     // cache validation support methods
     nsresult ComputeFreshnessLifetime(uint32_t *);
     nsresult ComputeCurrentAge(uint32_t now, uint32_t requestTime,
                                uint32_t *result);
     bool MustValidate();
     bool MustValidateIfExpired();
 
@@ -139,18 +139,18 @@ public:
 private:
     nsresult SetHeader_locked(nsHttpAtom h, const nsACString &v,
                               bool m=false);
     void AssignDefaultStatusText();
     void ParseVersion(const char *);
     void ParseCacheControl(const char *);
     void ParsePragma(const char *);
 
-    void ParseStatusLine_locked(const char *line);
-    nsresult ParseHeaderLine_locked(const char *line, bool originalFromNetHeaders);
+    void ParseStatusLine_locked(const nsACString &line);
+    nsresult ParseHeaderLine_locked(const nsACString &line, bool originalFromNetHeaders);
 
     // these return failure if the header does not exist.
     nsresult ParseDateHeader(nsHttpAtom header, uint32_t *result) const;
 
     bool ExpiresInPast_locked() const;
     nsresult GetAgeValue_locked(uint32_t *result) const;
     nsresult GetExpiresValue_locked(uint32_t *result) const;
     nsresult GetMaxAgeValue_locked(uint32_t *result) const;
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -1361,19 +1361,19 @@ nsHttpTransaction::LocateHttpStart(char 
             firstByte = false;
         buf++;
         len--;
     }
     return 0;
 }
 
 nsresult
-nsHttpTransaction::ParseLine(char *line)
+nsHttpTransaction::ParseLine(nsACString &line)
 {
-    LOG(("nsHttpTransaction::ParseLine [%s]\n", line));
+    LOG(("nsHttpTransaction::ParseLine [%s]\n", PromiseFlatCString(line).get()));
     nsresult rv = NS_OK;
 
     if (!mHaveStatusLine) {
         mResponseHead->ParseStatusLine(line);
         mHaveStatusLine = true;
         // XXX this should probably never happen
         if (mResponseHead->Version() == NS_HTTP_VERSION_0_9)
             mHaveAllHeaders = true;
@@ -1391,17 +1391,17 @@ nsHttpTransaction::ParseLineSegment(char
 
     if (!mLineBuf.IsEmpty() && mLineBuf.Last() == '\n') {
         // trim off the new line char, and if this segment is
         // not a continuation of the previous or if we haven't
         // parsed the status line yet, then parse the contents
         // of mLineBuf.
         mLineBuf.Truncate(mLineBuf.Length() - 1);
         if (!mHaveStatusLine || (*segment != ' ' && *segment != '\t')) {
-            nsresult rv = ParseLine(mLineBuf.BeginWriting());
+            nsresult rv = ParseLine(mLineBuf);
             mLineBuf.Truncate();
             if (NS_FAILED(rv)) {
                 gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
                     mConnInfo, nsHttpConnectionMgr::RedCorruptedContent,
                     nullptr, 0);
                 return rv;
             }
         }
@@ -1470,17 +1470,17 @@ nsHttpTransaction::ParseHead(char *buf,
             // tolerate only minor junk before the status line
             mHttpResponseMatched = true;
             char *p = LocateHttpStart(buf, std::min<uint32_t>(count, 11), true);
             if (!p) {
                 // Treat any 0.9 style response of a put as a failure.
                 if (mRequestHead->IsPut())
                     return NS_ERROR_ABORT;
 
-                mResponseHead->ParseStatusLine("");
+                mResponseHead->ParseStatusLine(EmptyCString());
                 mHaveStatusLine = true;
                 mHaveAllHeaders = true;
                 return NS_OK;
             }
             if (p > buf) {
                 // skip over the junk
                 mInvalidResponseBytesRead += p - buf;
                 *countRead = p - buf;
--- a/netwerk/protocol/http/nsHttpTransaction.h
+++ b/netwerk/protocol/http/nsHttpTransaction.h
@@ -169,17 +169,17 @@ public:
 private:
     friend class DeleteHttpTransaction;
     virtual ~nsHttpTransaction();
 
     nsresult Restart();
     nsresult RestartInProgress();
     char    *LocateHttpStart(char *buf, uint32_t len,
                              bool aAllowPartialMatch);
-    nsresult ParseLine(char *line);
+    nsresult ParseLine(nsACString &line);
     nsresult ParseLineSegment(char *seg, uint32_t len);
     nsresult ParseHead(char *, uint32_t count, uint32_t *countRead);
     nsresult HandleContentStart();
     nsresult HandleContent(char *, uint32_t count, uint32_t *contentRead, uint32_t *contentRemaining);
     nsresult ProcessData(char *, uint32_t, uint32_t *);
     void     DeleteSelfOnConsumerThread();
     void     ReleaseBlockingTransaction();
 
--- a/netwerk/streamconv/converters/nsMultiMixedConv.cpp
+++ b/netwerk/streamconv/converters/nsMultiMixedConv.cpp
@@ -1210,19 +1210,18 @@ nsMultiMixedConv::ParseHeaders(nsIChanne
             done = true;
             break;
         }
 
         char tmpChar = *newLine;
         *newLine = '\0'; // cursor is now null terminated
 
         if (mResponseHead) {
-            // ParseHeaderLine is destructive. We create a copy
             nsAutoCString tmpHeader(cursor);
-            mResponseHead->ParseHeaderLine(tmpHeader.get());
+            mResponseHead->ParseHeaderLine(tmpHeader);
         }
 
         char *colon = (char *) strchr(cursor, ':');
         if (colon) {
             *colon = '\0';
             nsAutoCString headerStr(cursor);
             headerStr.CompressWhitespace();
             *colon = ':';
@@ -1345,9 +1344,8 @@ NS_NewMultiMixedConv(nsMultiMixedConv** 
 
     *aMultiMixedConv = new nsMultiMixedConv();
     if (! *aMultiMixedConv)
         return NS_ERROR_OUT_OF_MEMORY;
 
     NS_ADDREF(*aMultiMixedConv);
     return NS_OK;
 }
-
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -12527,16 +12527,20 @@
         "path": "XMLHttpRequest/getresponseheader-special-characters.htm",
         "url": "/XMLHttpRequest/getresponseheader-special-characters.htm"
       },
       {
         "path": "XMLHttpRequest/getresponseheader-unsent-opened-state.htm",
         "url": "/XMLHttpRequest/getresponseheader-unsent-opened-state.htm"
       },
       {
+        "path": "XMLHttpRequest/headers-normalize-response.htm",
+        "url": "/XMLHttpRequest/headers-normalize-response.htm"
+      },
+      {
         "path": "XMLHttpRequest/interfaces.html",
         "url": "/XMLHttpRequest/interfaces.html"
       },
       {
         "path": "XMLHttpRequest/open-after-abort.htm",
         "url": "/XMLHttpRequest/open-after-abort.htm"
       },
       {
deleted file mode 100644
--- a/testing/web-platform/meta/cors/allow-headers.htm.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[allow-headers.htm]
-  type: testharness
-  [Disallow origin: http://web-platform.test:8000\\0]
-    expected: FAIL
-
-  [Disallow origin: *\\0]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/cors/origin.htm.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[origin.htm]
-  type: testharness
-  [Disallow origin: http://web-platform.test:8000\\0]
-    expected: FAIL
-
-  [Disallow origin: *\\0]
-    expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/cors/remote-origin.htm.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[remote-origin.htm]
-  type: testharness
-  [Disallow origin: http://www1.web-platform.test:8000\\0]
-    expected: FAIL
-
-  [Disallow origin: *\\0]
-    expected: FAIL
-
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/XMLHttpRequest/headers-normalize-response.htm
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Whitespace and null in header values</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=support.js?pipe=sub></script>
+
+<h1>Whitespace and null in response header values</h1>
+
+<div id=log></div>
+
+<script>
+
+function matchHeaderValue(val) {
+    test(function () {
+        var client = new XMLHttpRequest();
+        var trimmed = val.trim();
+        client.open("GET", "resources/parse-headers.py?my-custom-header="+encodeURIComponent(val), false);
+        client.send();
+        var r = client.getResponseHeader("My-Custom-Header");
+
+        assert_equals(r, trimmed);
+    }, "Header value: " + val.replace(/\t/g, "[tab]").replace(/ /g, "_").replace("\0", "\\0"));
+}
+
+matchHeaderValue("hello world\0");
+matchHeaderValue("\0hello world");
+matchHeaderValue("hello\0world");
+matchHeaderValue("  hello world");
+matchHeaderValue("hello world  ");
+matchHeaderValue("  hello world  ");
+matchHeaderValue("\thello world");
+matchHeaderValue("hello world\t");
+matchHeaderValue("\thello world\t");
+matchHeaderValue("hello      world");
+matchHeaderValue("hello\tworld");
+matchHeaderValue("\0");
+matchHeaderValue("   ");
+matchHeaderValue("\t");
+matchHeaderValue("");
+
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/XMLHttpRequest/resources/parse-headers.py
@@ -0,0 +1,12 @@
+import json
+
+def main(request, response):
+
+    content = ""
+    if "my-custom-header" in request.GET:
+        val = request.GET.first("my-custom-header")
+        print "header is about to be set to '{}'".format(val)
+        response.headers.set("My-Custom-Header", val)
+        print "header was set"
+
+    return content