Bug 1321612 - Revert the previously-landed fix for causing bug 1341932. a=jcristau
authorHonza Bambas <honzab.moz@firemni.cz>
Thu, 23 Feb 2017 13:18:32 -0500
changeset 378675 0c629c46857407e5c817eb888692f4556c5ac6c1
parent 378674 d444b70460452ff070567124d3f9bde851905f83
child 378676 c2ddbee95da8f4f3fdb2155b4040393f81b191ee
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjcristau
bugs1321612, 1341932
milestone53.0a2
Bug 1321612 - Revert the previously-landed fix for causing bug 1341932. a=jcristau
netwerk/streamconv/converters/nsMultiMixedConv.cpp
netwerk/streamconv/converters/nsMultiMixedConv.h
netwerk/test/unit/test_multipart_streamconv-byte-by-byte.js
netwerk/test/unit/test_multipart_streamconv.js
netwerk/test/unit/test_multipart_streamconv_missing_boundary_lead_dashes.js
netwerk/test/unit/xpcshell.ini
--- a/netwerk/streamconv/converters/nsMultiMixedConv.cpp
+++ b/netwerk/streamconv/converters/nsMultiMixedConv.cpp
@@ -15,17 +15,34 @@
 #include "nsIStreamConverterService.h"
 #include "nsICacheInfoChannel.h"
 #include <algorithm>
 #include "nsContentSecurityManager.h"
 #include "nsHttp.h"
 #include "nsNetUtil.h"
 #include "nsIURI.h"
 #include "nsHttpHeaderArray.h"
-#include "mozilla/AutoRestore.h"
+
+//
+// Helper function for determining the length of data bytes up to
+// the next multipart token.  A token is usually preceded by a LF
+// or CRLF delimiter.
+// 
+static uint32_t
+LengthToToken(const char *cursor, const char *token)
+{
+    uint32_t len = token - cursor;
+    // Trim off any LF or CRLF preceding the token
+    if (len && *(token-1) == '\n') {
+        --len;
+        if (len && *(token-2) == '\r')
+            --len;
+    }
+    return len;
+}
 
 nsPartChannel::nsPartChannel(nsIChannel *aMultipartChannel, uint32_t aPartID,
                              nsIStreamListener* aListener) :
   mMultipartChannel(aMultipartChannel),
   mListener(aListener),
   mStatus(NS_OK),
   mContentLength(UINT64_MAX),
   mIsByteRangeRequest(false),
@@ -454,383 +471,371 @@ nsMultiMixedConv::AsyncConvertData(const
     // WARNING: this listener must be able to handle multiple OnStartRequest, OnDataAvail()
     //  and OnStopRequest() call combinations. We call of series of these for each sub-part
     //  in the raw stream.
     mFinalListener = aListener;
 
     return NS_OK;
 }
 
+// AutoFree implementation to prevent memory leaks
+class AutoFree
+{
+public:
+  AutoFree() : mBuffer(nullptr) {}
+
+  explicit AutoFree(char *buffer) : mBuffer(buffer) {}
+
+  ~AutoFree() {
+    free(mBuffer);
+  }
+
+  AutoFree& operator=(char *buffer) {
+    mBuffer = buffer;
+    return *this;
+  }
+
+  operator char*() const {
+    return mBuffer;
+  }
+private:
+  char *mBuffer;
+};
+
+// nsIStreamListener implementation
+NS_IMETHODIMP
+nsMultiMixedConv::OnDataAvailable(nsIRequest *request, nsISupports *context,
+                                  nsIInputStream *inStr, uint64_t sourceOffset,
+                                  uint32_t count) {
+    nsresult rv = NS_OK;
+    AutoFree buffer(nullptr);
+    uint32_t bufLen = 0, read = 0;
+
+    NS_ASSERTION(request, "multimixed converter needs a request");
+
+    nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
+    if (NS_FAILED(rv)) return rv;
+
+    // fill buffer
+    {
+        bufLen = count + mBufLen;
+        NS_ENSURE_TRUE((bufLen >= count) && (bufLen >= mBufLen),
+                       NS_ERROR_FAILURE);
+        buffer = (char *) malloc(bufLen);
+        if (!buffer)
+            return NS_ERROR_OUT_OF_MEMORY;
+
+        if (mBufLen) {
+            // incorporate any buffered data into the parsing
+            memcpy(buffer, mBuffer, mBufLen);
+            free(mBuffer);
+            mBuffer = 0;
+            mBufLen = 0;
+        }
+        
+        rv = inStr->Read(buffer + (bufLen - count), count, &read);
+
+        if (NS_FAILED(rv) || read == 0) return rv;
+        NS_ASSERTION(read == count, "poor data size assumption");
+    }
+
+    char *cursor = buffer;
+
+    if (mFirstOnData) {
+        // this is the first OnData() for this request. some servers
+        // don't bother sending a token in the first "part." This is
+        // illegal, but we'll handle the case anyway by shoving the
+        // boundary token in for the server.
+        mFirstOnData = false;
+        NS_ASSERTION(!mBufLen, "this is our first time through, we can't have buffered data");
+        const char * token = mToken.get();
+
+        PushOverLine(cursor, bufLen);
+
+        bool needMoreChars = bufLen < mTokenLen + 2;
+        nsAutoCString firstBuffer(buffer, bufLen);
+        int32_t posCR = firstBuffer.Find("\r");
+
+        if (needMoreChars || (posCR == kNotFound)) {
+            // we don't have enough data yet to make this comparison.
+            // skip this check, and try again the next time OnData()
+            // is called.
+            mFirstOnData = true;
+        } else if (!PL_strnstr(cursor, token, mTokenLen + 2)) {
+            char *newBuffer = (char *) realloc(buffer, bufLen + mTokenLen + 1);
+            if (!newBuffer)
+                return NS_ERROR_OUT_OF_MEMORY;
+            buffer = newBuffer;
+
+            memmove(buffer + mTokenLen + 1, buffer, bufLen);
+            memcpy(buffer, token, mTokenLen);
+            buffer[mTokenLen] = '\n';
+
+            bufLen += (mTokenLen + 1);
+
+            // need to reset cursor to the buffer again (bug 100595)
+            cursor = buffer;
+        }
+    }
+
+    char *token = nullptr;
+
+    if (mProcessingHeaders) {
+        // we were not able to process all the headers
+        // for this "part" given the previous buffer given to 
+        // us in the previous OnDataAvailable callback.
+        bool done = false;
+        rv = ParseHeaders(channel, cursor, bufLen, &done);
+        if (NS_FAILED(rv)) return rv;
+
+        if (done) {
+            mProcessingHeaders = false;
+            rv = SendStart(channel);
+            if (NS_FAILED(rv)) return rv;
+        }
+    }
+
+    int32_t tokenLinefeed = 1;
+    while ( (token = FindToken(cursor, bufLen)) ) {
+
+        if (((token + mTokenLen) < (cursor + bufLen)) &&
+            (*(token + mTokenLen + 1) == '-')) {
+            // This was the last delimiter so we can stop processing
+            rv = SendData(cursor, LengthToToken(cursor, token));
+            if (NS_FAILED(rv)) return rv;
+            if (mPartChannel) {
+                mPartChannel->SetIsLastPart();
+            }
+            return SendStop(NS_OK);
+        }
+
+        if (!mNewPart && token > cursor) {
+            // headers are processed, we're pushing data now.
+            NS_ASSERTION(!mProcessingHeaders, "we should be pushing raw data");
+            rv = SendData(cursor, LengthToToken(cursor, token));
+            bufLen -= token - cursor;
+            if (NS_FAILED(rv)) return rv;
+        }
+        // XXX else NS_ASSERTION(token == cursor, "?");
+        token += mTokenLen;
+        bufLen -= mTokenLen;
+        tokenLinefeed = PushOverLine(token, bufLen);
+
+        if (mNewPart) {
+            // parse headers
+            mNewPart = false;
+            cursor = token;
+            bool done = false;
+            rv = ParseHeaders(channel, cursor, bufLen, &done);
+            if (NS_FAILED(rv)) return rv;
+
+            if (done) {
+                rv = SendStart(channel);
+                if (NS_FAILED(rv)) return rv;
+            }
+            else {
+                // we haven't finished processing header info.
+                // we'll break out and try to process later.
+                mProcessingHeaders = true;
+                break;
+            }
+        }
+        else {
+            mNewPart = true;
+            // Reset state so we don't carry it over from part to part
+            mContentType.Truncate();
+            mContentLength = UINT64_MAX;
+            mContentDisposition.Truncate();
+            mIsByteRangeRequest = false;
+            mByteRangeStart = 0;
+            mByteRangeEnd = 0;
+            
+            rv = SendStop(NS_OK);
+            if (NS_FAILED(rv)) return rv;
+            // reset the token to front. this allows us to treat
+            // the token as a starting token.
+            token -= mTokenLen + tokenLinefeed;
+            bufLen += mTokenLen + tokenLinefeed;
+            cursor = token;
+        }
+    }
+
+    // at this point, we want to buffer up whatever amount (bufLen)
+    // we have leftover. However, we *always* want to ensure that
+    // we buffer enough data to handle a broken token.
+
+    // carry over
+    uint32_t bufAmt = 0;
+    if (mProcessingHeaders)
+        bufAmt = bufLen;
+    else if (bufLen) {
+        // if the data ends in a linefeed, and we're in the middle
+        // of a "part" (ie. mPartChannel exists) don't bother
+        // buffering, go ahead and send the data we have. Otherwise
+        // if we don't have a channel already, then we don't even
+        // have enough info to start a part, go ahead and buffer
+        // enough to collect a boundary token.
+        if (!mPartChannel || !(cursor[bufLen-1] == nsCRT::LF) )
+            bufAmt = std::min(mTokenLen - 1, bufLen);
+    }
+
+    if (bufAmt) {
+        rv = BufferData(cursor + (bufLen - bufAmt), bufAmt);
+        if (NS_FAILED(rv)) return rv;
+        bufLen -= bufAmt;
+    }
+
+    if (bufLen) {
+        rv = SendData(cursor, bufLen);
+        if (NS_FAILED(rv)) return rv;
+    }
+
+    return rv;
+}
+
+
 // nsIRequestObserver implementation
 NS_IMETHODIMP
-nsMultiMixedConv::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
-{
+nsMultiMixedConv::OnStartRequest(nsIRequest *request, nsISupports *ctxt) {
     // we're assuming the content-type is available at this stage
-    NS_ASSERTION(mBoundary.IsEmpty(), "a second on start???");
+    NS_ASSERTION(mToken.IsEmpty(), "a second on start???");
+    const char *bndry = nullptr;
+    nsAutoCString delimiter;
+    nsresult rv = NS_OK;
+    mContext = ctxt;
 
-    nsresult rv;
+    mFirstOnData = true;
+    mTotalSent   = 0;
 
-    mContext = ctxt;
-    mTotalSent   = 0;
-    mChannel = do_QueryInterface(request, &rv);
+    nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
     if (NS_FAILED(rv)) return rv;
 
-    nsAutoCString contentType;
-
     // ask the HTTP channel for the content-type and extract the boundary from it.
-    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
+    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel, &rv);
     if (NS_SUCCEEDED(rv)) {
-        rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-type"), contentType);
+        rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-type"), delimiter);
         if (NS_FAILED(rv)) {
             return rv;
         }
     } else {
         // try asking the channel directly
-        rv = mChannel->GetContentType(contentType);
+        rv = channel->GetContentType(delimiter);
         if (NS_FAILED(rv)) {
             return NS_ERROR_FAILURE;
         }
     }
 
-    Tokenizer p(contentType);
-    p.SkipUntil(Token::Char(';'));
-    if (!p.CheckChar(';')) {
-        return NS_ERROR_CORRUPTED_CONTENT;
-    }
-    p.SkipWhites();
-    if (!p.CheckWord("boundary")) {
-        return NS_ERROR_CORRUPTED_CONTENT;
-    }
-    p.SkipWhites();
-    if (!p.CheckChar('=')) {
-        return NS_ERROR_CORRUPTED_CONTENT;
-    }
-    p.SkipWhites();
-    Unused << p.ReadUntil(Token::Char(';'), mBoundary);
-    mBoundary.Trim(" \""); // ignoring potential quoted string formatting violations
-    if (mBoundary.IsEmpty()) {
-        return NS_ERROR_CORRUPTED_CONTENT;
+    bndry = strstr(delimiter.BeginWriting(), "boundary");
+
+    if (!bndry) {
+        return NS_ERROR_FAILURE;
     }
 
-    mHeaderTokens[HEADER_CONTENT_TYPE] =
-      mTokenizer.AddCustomToken("content-type", mTokenizer.CASE_INSENSITIVE, false);
-    mHeaderTokens[HEADER_CONTENT_LENGTH] =
-      mTokenizer.AddCustomToken("content-length", mTokenizer.CASE_INSENSITIVE, false);
-    mHeaderTokens[HEADER_CONTENT_DISPOSITION] =
-      mTokenizer.AddCustomToken("content-disposition", mTokenizer.CASE_INSENSITIVE, false);
-    mHeaderTokens[HEADER_SET_COOKIE] =
-      mTokenizer.AddCustomToken("set-cookie", mTokenizer.CASE_INSENSITIVE, false);
-    mHeaderTokens[HEADER_CONTENT_RANGE] =
-      mTokenizer.AddCustomToken("content-range", mTokenizer.CASE_INSENSITIVE, false);
-    mHeaderTokens[HEADER_RANGE] =
-      mTokenizer.AddCustomToken("range", mTokenizer.CASE_INSENSITIVE, false);
+    bndry = strchr(bndry, '=');
+    if (!bndry) return NS_ERROR_FAILURE;
+
+    bndry++; // move past the equals sign
+
+    char *attrib = (char *) strchr(bndry, ';');
+    if (attrib) *attrib = '\0';
 
-    mLFToken = mTokenizer.AddCustomToken("\n", mTokenizer.CASE_SENSITIVE, false);
-    mCRLFToken = mTokenizer.AddCustomToken("\r\n", mTokenizer.CASE_SENSITIVE, false);
+    nsAutoCString boundaryString(bndry);
+    if (attrib) *attrib = ';';
 
-    SwitchToControlParsing();
+    boundaryString.Trim(" \"");
 
-    mBoundaryToken =
-      mTokenizer.AddCustomToken(mBoundary, mTokenizer.CASE_SENSITIVE);
-    mBoundaryTokenWithDashes =
-      mTokenizer.AddCustomToken(NS_LITERAL_CSTRING("--") + mBoundary, mTokenizer.CASE_SENSITIVE);
+    mToken = boundaryString;
+    mTokenLen = boundaryString.Length();
+
+    if (mTokenLen == 0) {
+        return NS_ERROR_FAILURE;
+    }
 
     return NS_OK;
 }
 
-// nsIStreamListener implementation
-NS_IMETHODIMP
-nsMultiMixedConv::OnDataAvailable(nsIRequest *request, nsISupports *context,
-                                  nsIInputStream *inStr, uint64_t sourceOffset,
-                                  uint32_t count)
-{
-    // Failing these assertions may indicate that some of the target listeners of this converter
-    // is looping the thead queue, which is harmful to how we collect the raw (content) data.
-    MOZ_DIAGNOSTIC_ASSERT(!mInOnDataAvailable, "nsMultiMixedConv::OnDataAvailable reentered!");
-    MOZ_DIAGNOSTIC_ASSERT(!mRawData, "There are unsent data from the previous tokenizer feed!");
-
-    if (mInOnDataAvailable) {
-        // The multipart logic is incapable of being reentered.
-        return NS_ERROR_UNEXPECTED;
-    }
-
-    mozilla::AutoRestore<bool> restore(mInOnDataAvailable);
-    mInOnDataAvailable = true;
-
-    nsresult rv_feed = mTokenizer.FeedInput(inStr, count);
-    // We must do this every time.  Regardless if something has failed during the
-    // parsing process.  Otherwise the raw data reference would not be thrown away.
-    nsresult rv_send = SendData();
-
-    return NS_FAILED(rv_send) ? rv_send : rv_feed;
-}
-
 NS_IMETHODIMP
 nsMultiMixedConv::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
                                 nsresult aStatus)
 {
-    nsresult rv;
-
-    if (mBoundary.IsEmpty()) { // no token, no love.
+    if (mToken.IsEmpty()) { // no token, no love.
         return NS_ERROR_FAILURE;
     }
 
     if (mPartChannel) {
         mPartChannel->SetIsLastPart();
 
-        MOZ_DIAGNOSTIC_ASSERT(!mRawData, "There are unsent data from the previous tokenizer feed!");
-
-        rv = mTokenizer.FinishInput();
-        if (NS_SUCCEEDED(aStatus)) {
-            aStatus = rv;
+        // we've already called SendStart() (which sets up the mPartChannel,
+        // and fires an OnStart()) send any data left over, and then fire the stop.
+        if (mBufLen > 0 && mBuffer) {
+            (void) SendData(mBuffer, mBufLen);
+            // don't bother checking the return value here, if the send failed
+            // we're done anyway as we're in the OnStop() callback.
+            free(mBuffer);
+            mBuffer = nullptr;
+            mBufLen = 0;
         }
-        rv = SendData();
-        if (NS_SUCCEEDED(aStatus)) {
-            aStatus = rv;
-        }
-
         (void) SendStop(aStatus);
-    } else if (NS_FAILED(aStatus) && !mRequestListenerNotified) {
+    } else if (NS_FAILED(aStatus)) {
         // underlying data production problem. we should not be in
         // the middle of sending data. if we were, mPartChannel,
-        // above, would have been non-null.
+        // above, would have been true.
 
-        (void) mFinalListener->OnStartRequest(request, ctxt);
+        // if we send the start, the URI Loader's m_targetStreamListener, may
+        // be pointing at us causing a nice stack overflow.  So, don't call 
+        // OnStartRequest!  -  This breaks necko's semantecs. 
+        //(void) mFinalListener->OnStartRequest(request, ctxt);
+
         (void) mFinalListener->OnStopRequest(request, ctxt, aStatus);
     }
 
     return NS_OK;
 }
 
-nsresult
-nsMultiMixedConv::ConsumeToken(Token const & token)
-{
-  nsresult rv;
-
-  switch (mParserState) {
-    case BOUNDARY:
-      if (token.Equals(mBoundaryTokenWithDashes)) {
-        // The server first used boundary '--boundary'.  Hence, we no longer
-        // accept plain 'boundary' token as a delimiter.
-        mTokenizer.RemoveCustomToken(mBoundaryToken);
-        mParserState = BOUNDARY_CRLF;
-        break;
-      }
-      if (token.Equals(mBoundaryToken)) {
-        // And here the opposite from the just above block...
-        mTokenizer.RemoveCustomToken(mBoundaryTokenWithDashes);
-        mParserState = BOUNDARY_CRLF;
-        break;
-      }
-
-      // In case the server didn't send the first boundary...
-      // This may be either a line-feed or a header.  Turn header tokens
-      // on and retry in a shifted parser state.
-      mParserState = HEADER_NAME;
-      mResponseHeader = HEADER_UNKNOWN;
-      SetHeaderTokensEnabled(true);
-      mTokenizer.Rollback();
-      break;
-
-    case BOUNDARY_CRLF:
-      if (token.Equals(Token::NewLine())) {
-        mParserState = HEADER_NAME;
-        mResponseHeader = HEADER_UNKNOWN;
-        HeadersToDefault();
-        SetHeaderTokensEnabled(true);
-        break;
-      }
-      return NS_ERROR_CORRUPTED_CONTENT;
-
-    case HEADER_NAME:
-      SetHeaderTokensEnabled(false);
-      if (token.Equals(Token::NewLine())) {
-        mParserState = BODY_INIT;
-        SwitchToBodyParsing();
-        break;
-      }
-      for (uint32_t h = HEADER_CONTENT_TYPE; h < HEADER_UNKNOWN; ++h) {
-        if (token.Equals(mHeaderTokens[h])) {
-          mResponseHeader = static_cast<EHeader>(h);
-          break;
-        }
-      }
-      mParserState = HEADER_SEP;
-      break;
-
-    case HEADER_SEP:
-      if (token.Equals(Token::Char(':'))) {
-        mParserState = HEADER_VALUE;
-        mResponseHeaderValue.Truncate();
-        break;
-      }
-      if (mResponseHeader == HEADER_UNKNOWN) {
-        // If the header is not of any we understand, just pass everything till ':'
-        break;
-      }
-      if (token.Equals(Token::Whitespace())) {
-        // Accept only header-name traling whitespaces after known headers
-        break;
-      }
-      return NS_ERROR_CORRUPTED_CONTENT;
-
-    case HEADER_VALUE:
-      if (token.Equals(Token::Whitespace()) && mResponseHeaderValue.IsEmpty()) {
-        // Eat leading whitespaces
-        break;
-      }
-      if (token.Equals(Token::NewLine())) {
-        nsresult rv = ProcessHeader();
-        if (NS_FAILED(rv)) {
-          return rv;
-        }
-        mParserState = HEADER_NAME;
-        mResponseHeader = HEADER_UNKNOWN;
-        SetHeaderTokensEnabled(true);
-      } else {
-        mResponseHeaderValue.Append(token.Fragment());
-      }
-      break;
-
-    case BODY_INIT:
-      rv = SendStart();
-      if (NS_FAILED(rv)) {
-        return rv;
-      }
-      mParserState = BODY;
-      MOZ_FALLTHROUGH;
-
-    case BODY: {
-      if (!token.Equals(mLFToken) && !token.Equals(mCRLFToken)) {
-        if (token.Equals(mBoundaryTokenWithDashes) ||
-            token.Equals(mBoundaryToken)) {
-          // Allow CRLF to NOT be part of the boundary as well
-          SwitchToControlParsing();
-          mParserState = TRAIL_DASH1;
-          break;
-        }
-        AccumulateData(token);
-        break;
-      }
-
-      // After CRLF we must explicitly check for boundary.  If found,
-      // that CRLF is part of the boundary and must not be send to the
-      // data listener.
-      Token token2;
-      if (!mTokenizer.Next(token2)) {
-        // Note: this will give us the CRLF token again when more data
-        // or OnStopRequest arrive.  I.e. we will enter BODY case in
-        // the very same state as we are now and start this block over.
-        mTokenizer.NeedMoreInput();
-        break;
-      }
-      if (token2.Equals(mBoundaryTokenWithDashes) ||
-          token2.Equals(mBoundaryToken)) {
-        SwitchToControlParsing();
-        mParserState = TRAIL_DASH1;
-        break;
-      }
-
-      AccumulateData(token);
-      AccumulateData(token2);
-      break;
-    }
-
-    case TRAIL_DASH1:
-      if (token.Equals(Token::NewLine())) {
-        rv = SendStop(NS_OK);
-        if (NS_FAILED(rv)) {
-          return rv;
-        }
-        mParserState = BOUNDARY_CRLF;
-        mTokenizer.Rollback();
-        break;
-      }
-      if (token.Equals(Token::Char('-'))) {
-        mParserState = TRAIL_DASH2;
-        break;
-      }
-      return NS_ERROR_CORRUPTED_CONTENT;
-
-    case TRAIL_DASH2:
-      if (token.Equals(Token::Char('-'))) {
-        mPartChannel->SetIsLastPart();
-        // SendStop calls SendData first.
-        rv = SendStop(NS_OK);
-        if (NS_FAILED(rv)) {
-          return rv;
-        }
-        mParserState = EPILOGUE;
-        break;
-      }
-      return NS_ERROR_CORRUPTED_CONTENT;
-
-    case EPILOGUE:
-      // Just ignore
-      break;
-
-    default:
-      MOZ_ASSERT(false, "Missing parser state handling branch");
-      break;
-  } // switch
-
-  return NS_OK;
-}
-
-void
-nsMultiMixedConv::SetHeaderTokensEnabled(bool aEnable)
-{
-    for (uint32_t h = HEADER_FIRST; h < HEADER_UNKNOWN; ++h) {
-        mTokenizer.EnableCustomToken(mHeaderTokens[h], aEnable);
-    }
-}
-
-void
-nsMultiMixedConv::SwitchToBodyParsing()
-{
-    mTokenizer.SetTokenizingMode(Tokenizer::Mode::CUSTOM_ONLY);
-    mTokenizer.EnableCustomToken(mLFToken, true);
-    mTokenizer.EnableCustomToken(mCRLFToken, true);
-    mTokenizer.EnableCustomToken(mBoundaryTokenWithDashes, true);
-    mTokenizer.EnableCustomToken(mBoundaryToken, true);
-}
-
-void
-nsMultiMixedConv::SwitchToControlParsing()
-{
-    mTokenizer.SetTokenizingMode(Tokenizer::Mode::FULL);
-    mTokenizer.EnableCustomToken(mLFToken, false);
-    mTokenizer.EnableCustomToken(mCRLFToken, false);
-    mTokenizer.EnableCustomToken(mBoundaryTokenWithDashes, false);
-    mTokenizer.EnableCustomToken(mBoundaryToken, false);
-}
 
 // nsMultiMixedConv methods
 nsMultiMixedConv::nsMultiMixedConv() :
-    mCurrentPartID(0),
-    mInOnDataAvailable(false),
-    mTokenizer([this](Token const& token, mozilla::IncrementalTokenizer&) -> nsresult {
-        return this->ConsumeToken(token);
-    })
+  mCurrentPartID(0)
 {
+    mTokenLen           = 0;
+    mNewPart            = true;
     mContentLength      = UINT64_MAX;
+    mBuffer             = nullptr;
+    mBufLen             = 0;
+    mProcessingHeaders  = false;
     mByteRangeStart     = 0;
     mByteRangeEnd       = 0;
     mTotalSent          = 0;
     mIsByteRangeRequest = false;
-    mParserState        = INIT;
-    mRawData            = nullptr;
-    mRequestListenerNotified = false;
+}
+
+nsMultiMixedConv::~nsMultiMixedConv() {
+    NS_ASSERTION(!mBuffer, "all buffered data should be gone");
+    if (mBuffer) {
+        free(mBuffer);
+        mBuffer = nullptr;
+    }
 }
 
-nsMultiMixedConv::~nsMultiMixedConv() {}
+nsresult
+nsMultiMixedConv::BufferData(char *aData, uint32_t aLen) {
+    NS_ASSERTION(!mBuffer, "trying to over-write buffer");
+
+    char *buffer = (char *) malloc(aLen);
+    if (!buffer) return NS_ERROR_OUT_OF_MEMORY;
+
+    memcpy(buffer, aData, aLen);
+    mBuffer = buffer;
+    mBufLen = aLen;
+    return NS_OK;
+}
+
 
 nsresult
-nsMultiMixedConv::SendStart()
-{
+nsMultiMixedConv::SendStart(nsIChannel *aChannel) {
     nsresult rv = NS_OK;
 
     nsCOMPtr<nsIStreamListener> partListener(mFinalListener);
     if (mContentType.IsEmpty()) {
         mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
         nsCOMPtr<nsIStreamConverterService> serv =
             do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
         if (NS_SUCCEEDED(rv)) {
@@ -843,20 +848,20 @@ nsMultiMixedConv::SendStart()
             if (NS_SUCCEEDED(rv)) {
                 partListener = converter;
             }
         }
     }
 
     // if we already have an mPartChannel, that means we never sent a Stop()
     // before starting up another "part." that would be bad.
-    MOZ_ASSERT(!mPartChannel, "tisk tisk, shouldn't be overwriting a channel");
+    NS_ASSERTION(!mPartChannel, "tisk tisk, shouldn't be overwriting a channel");
 
     nsPartChannel *newChannel;
-    newChannel = new nsPartChannel(mChannel, mCurrentPartID++, partListener);
+    newChannel = new nsPartChannel(aChannel, mCurrentPartID++, partListener);
     if (!newChannel)
         return NS_ERROR_OUT_OF_MEMORY;
 
     if (mIsByteRangeRequest) {
         newChannel->InitializeByteRange(mByteRangeStart, mByteRangeEnd);
     }
 
     mTotalSent = 0;
@@ -881,185 +886,236 @@ nsMultiMixedConv::SendStart()
     (void)mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup));
 
     // Add the new channel to the load group (if any)
     if (loadGroup) {
         rv = loadGroup->AddRequest(mPartChannel, nullptr);
         if (NS_FAILED(rv)) return rv;
     }
 
-    // This prevents artificial call to OnStart/StopRequest when the root
-    // channel fails.  Since now it's ensured to keep with the nsIStreamListener
-    // contract every time.
-    mRequestListenerNotified = true;
-
     // Let's start off the load. NOTE: we don't forward on the channel passed
     // into our OnDataAvailable() as it's the root channel for the raw stream.
     return mPartChannel->SendOnStartRequest(mContext);
 }
 
+
 nsresult
-nsMultiMixedConv::SendStop(nsresult aStatus)
-{
-    // Make sure we send out all accumulcated data prior call to OnStopRequest.
-    // If there is no data, this is a no-op.
-    nsresult rv = SendData();
-    if (NS_SUCCEEDED(aStatus)) {
-        aStatus = rv;
-    }
+nsMultiMixedConv::SendStop(nsresult aStatus) {
+    
+    nsresult rv = NS_OK;
     if (mPartChannel) {
         rv = mPartChannel->SendOnStopRequest(mContext, aStatus);
         // don't check for failure here, we need to remove the channel from 
         // the loadgroup.
 
         // Remove the channel from its load group (if any)
         nsCOMPtr<nsILoadGroup> loadGroup;
         (void) mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup));
         if (loadGroup) 
             (void) loadGroup->RemoveRequest(mPartChannel, mContext, aStatus);
     }
 
     mPartChannel = nullptr;
     return rv;
 }
 
-void
-nsMultiMixedConv::AccumulateData(Token const & aToken)
-{
-    if (!mRawData) {
-        // This is the first read of raw data during this FeedInput loop
-        // of the incremental tokenizer.  All 'raw' tokens are coming from
-        // the same linear buffer, hence begining of this loop raw data
-        // is begining of the first raw token.  Length of this loop raw
-        // data is just sum of all 'raw' tokens we collect during this loop.
-        //
-        // It's ensured we flush (send to to the listener via OnDataAvailable)
-        // and nullify the collected raw data right after FeedInput call.
-        // Hence, the reference can't outlive the actual buffer.
-        mRawData = aToken.Fragment().BeginReading();
-        mRawDataLength = 0;
-    }
+nsresult
+nsMultiMixedConv::SendData(char *aBuffer, uint32_t aLen) {
 
-    mRawDataLength += aToken.Fragment().Length();
-}
-
-nsresult
-nsMultiMixedConv::SendData()
-{
-    nsresult rv;
-
-    if (!mRawData) {
-        return NS_OK;
-    }
-
-    nsACString::const_char_iterator rawData = mRawData;
-    mRawData = nullptr;
-
-    if (!mPartChannel) {
-        return NS_ERROR_FAILURE; // something went wrong w/ processing
-    }
+    nsresult rv = NS_OK;
+    
+    if (!mPartChannel) return NS_ERROR_FAILURE; // something went wrong w/ processing
 
     if (mContentLength != UINT64_MAX) {
         // make sure that we don't send more than the mContentLength
         // XXX why? perhaps the Content-Length header was actually wrong!!
-        if ((uint64_t(mRawDataLength) + mTotalSent) > mContentLength)
-            mRawDataLength = static_cast<uint32_t>(mContentLength - mTotalSent);
+        if ((uint64_t(aLen) + mTotalSent) > mContentLength)
+            aLen = static_cast<uint32_t>(mContentLength - mTotalSent);
 
-        if (mRawDataLength == 0)
+        if (aLen == 0)
             return NS_OK;
     }
 
     uint64_t offset = mTotalSent;
-    mTotalSent += mRawDataLength;
+    mTotalSent += aLen;
 
     nsCOMPtr<nsIStringInputStream> ss(
             do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv));
     if (NS_FAILED(rv))
         return rv;
 
-    rv = ss->ShareData(rawData, mRawDataLength);
-    mRawData = nullptr;
+    rv = ss->ShareData(aBuffer, aLen);
     if (NS_FAILED(rv))
         return rv;
 
     nsCOMPtr<nsIInputStream> inStream(do_QueryInterface(ss, &rv));
     if (NS_FAILED(rv)) return rv;
 
-    return mPartChannel->SendOnDataAvailable(mContext, inStream, offset, mRawDataLength);
+    return mPartChannel->SendOnDataAvailable(mContext, inStream, offset, aLen);
 }
 
-void
-nsMultiMixedConv::HeadersToDefault()
-{
-    mContentLength = UINT64_MAX;
-    mContentType.Truncate();
-    mContentDisposition.Truncate();
-    mIsByteRangeRequest = false;
+int32_t
+nsMultiMixedConv::PushOverLine(char *&aPtr, uint32_t &aLen) {
+    int32_t chars = 0;
+    if ((aLen > 0) && (*aPtr == nsCRT::CR || *aPtr == nsCRT::LF)) {
+        if ((aLen > 1) && (aPtr[1] == nsCRT::LF))
+            chars++;
+        chars++;
+        aPtr += chars;
+        aLen -= chars;
+    }
+    return chars;
 }
 
 nsresult
-nsMultiMixedConv::ProcessHeader()
-{
-    mozilla::Tokenizer p(mResponseHeaderValue);
+nsMultiMixedConv::ParseHeaders(nsIChannel *aChannel, char *&aPtr, 
+                               uint32_t &aLen, bool *_retval) {
+    // NOTE: this data must be ascii.
+    // NOTE: aPtr is NOT null terminated!
+    nsresult rv = NS_OK;
+    char *cursor = aPtr, *newLine = nullptr;
+    uint32_t cursorLen = aLen;
+    bool done = false;
+    uint32_t lineFeedIncrement = 1;
+
+    mContentLength = UINT64_MAX; // XXX what if we were already called?
+    while (cursorLen && (newLine = (char *) memchr(cursor, nsCRT::LF, cursorLen))) {
+        // adjust for linefeeds
+        if ((newLine > cursor) && (newLine[-1] == nsCRT::CR) ) { // CRLF
+            lineFeedIncrement = 2;
+            newLine--;
+        }
+        else
+            lineFeedIncrement = 1; // reset
+
+        if (newLine == cursor) {
+            // move the newLine beyond the linefeed marker
+            NS_ASSERTION(cursorLen >= lineFeedIncrement, "oops!");
+
+            cursor += lineFeedIncrement;
+            cursorLen -= lineFeedIncrement;
+
+            done = true;
+            break;
+        }
+
+        char tmpChar = *newLine;
+        *newLine = '\0'; // cursor is now null terminated
+
+        char *colon = (char *) strchr(cursor, ':');
+        if (colon) {
+            *colon = '\0';
+            nsAutoCString headerStr(cursor);
+            headerStr.CompressWhitespace();
+            *colon = ':';
+
+            nsAutoCString headerVal(colon + 1);
+            headerVal.CompressWhitespace();
 
-    switch (mResponseHeader) {
-    case HEADER_CONTENT_TYPE:
-      mContentType = mResponseHeaderValue;
-      mContentType.CompressWhitespace();
-      break;
-    case HEADER_CONTENT_LENGTH:
-      p.SkipWhites();
-      if (!p.ReadInteger(&mContentLength)) {
-        return NS_ERROR_CORRUPTED_CONTENT;
-      }
-      break;
-    case HEADER_CONTENT_DISPOSITION:
-      mContentDisposition = mResponseHeaderValue;
-      mContentDisposition.CompressWhitespace();
-      break;
-    case HEADER_SET_COOKIE: {
-      nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(mChannel);
-      mResponseHeaderValue.CompressWhitespace();
-      if (httpInternal) {
-        httpInternal->SetCookie(mResponseHeaderValue.get());
-      }
-      break;
-    }
-    case HEADER_RANGE:
-    case HEADER_CONTENT_RANGE: {
-      if (!p.CheckWord("bytes") ||
-          !p.CheckWhite()) {
-        return NS_ERROR_CORRUPTED_CONTENT;
-      }
-      p.SkipWhites();
-      if (p.CheckChar('*')) {
-        mByteRangeStart = mByteRangeEnd = 0;
-      } else if (!p.ReadInteger(&mByteRangeStart) ||
-                 !p.CheckChar('-') ||
-                 !p.ReadInteger(&mByteRangeEnd)) {
-        return NS_ERROR_CORRUPTED_CONTENT;
-      }
-      mIsByteRangeRequest = true;
-      if (mContentLength == UINT64_MAX) {
-        mContentLength = uint64_t(mByteRangeEnd - mByteRangeStart + 1);
-      }
-      break;
-    }
-    case HEADER_UNKNOWN:
-      // We ignore anything else...
-      break;
+            // examine header
+            if (headerStr.LowerCaseEqualsLiteral("content-type")) {
+                mContentType = headerVal;
+            } else if (headerStr.LowerCaseEqualsLiteral("content-length")) {
+                mContentLength = nsCRT::atoll(headerVal.get());
+            } else if (headerStr.LowerCaseEqualsLiteral("content-disposition")) {
+                mContentDisposition = headerVal;
+            } else if (headerStr.LowerCaseEqualsLiteral("set-cookie")) {
+                nsCOMPtr<nsIHttpChannelInternal> httpInternal =
+                    do_QueryInterface(aChannel);
+                if (httpInternal) {
+                    httpInternal->SetCookie(headerVal.get());
+                }
+            } else if (headerStr.LowerCaseEqualsLiteral("content-range") || 
+                       headerStr.LowerCaseEqualsLiteral("range") ) {
+                // something like: Content-range: bytes 7000-7999/8000
+                char* tmpPtr;
+
+                tmpPtr = (char *) strchr(colon + 1, '/');
+                if (tmpPtr) 
+                    *tmpPtr = '\0';
+
+                // pass the bytes-unit and the SP
+                char *range = (char *) strchr(colon + 2, ' ');
+                if (!range)
+                    return NS_ERROR_FAILURE;
+
+                do {
+                    range++;
+                } while (*range == ' ');
+
+                if (range[0] == '*'){
+                    mByteRangeStart = mByteRangeEnd = 0;
+                }
+                else {
+                    tmpPtr = (char *) strchr(range, '-');
+                    if (!tmpPtr)
+                        return NS_ERROR_FAILURE;
+                    
+                    tmpPtr[0] = '\0';
+                    
+                    mByteRangeStart = nsCRT::atoll(range);
+                    tmpPtr++;
+                    mByteRangeEnd = nsCRT::atoll(tmpPtr);
+                }
+
+                mIsByteRangeRequest = true;
+                if (mContentLength == UINT64_MAX)
+                    mContentLength = uint64_t(mByteRangeEnd - mByteRangeStart + 1);
+            }
+        }
+        *newLine = tmpChar;
+        newLine += lineFeedIncrement;
+        cursorLen -= (newLine - cursor);
+        cursor = newLine;
     }
 
-    return NS_OK;
+    aPtr = cursor;
+    aLen = cursorLen;
+
+    *_retval = done;
+    return rv;
+}
+
+char *
+nsMultiMixedConv::FindToken(char *aCursor, uint32_t aLen) {
+    // strnstr without looking for null termination
+    const char *token = mToken.get();
+    char *cur = aCursor;
+
+    if (!(token && aCursor && *token)) {
+        NS_WARNING("bad data");
+        return nullptr;
+    }
+
+    for (; aLen >= mTokenLen; aCursor++, aLen--) {
+        if (!memcmp(aCursor, token, mTokenLen) ) {
+            if ((aCursor - cur) >= 2) {
+                // back the cursor up over a double dash for backwards compat.
+                if ((*(aCursor-1) == '-') && (*(aCursor-2) == '-')) {
+                    aCursor -= 2;
+                    aLen += 2;
+
+                    // we're playing w/ double dash tokens, adjust.
+                    mToken.Assign(aCursor, mTokenLen + 2);
+                    mTokenLen = mToken.Length();
+                }
+            }
+            return aCursor;
+        }
+    }
+
+    return nullptr;
 }
 
 nsresult
 NS_NewMultiMixedConv(nsMultiMixedConv** aMultiMixedConv)
 {
     NS_PRECONDITION(aMultiMixedConv != nullptr, "null ptr");
     if (! aMultiMixedConv)
         return NS_ERROR_NULL_POINTER;
 
     *aMultiMixedConv = new nsMultiMixedConv();
+    if (! *aMultiMixedConv)
+        return NS_ERROR_OUT_OF_MEMORY;
 
     NS_ADDREF(*aMultiMixedConv);
     return NS_OK;
 }
--- a/netwerk/streamconv/converters/nsMultiMixedConv.h
+++ b/netwerk/streamconv/converters/nsMultiMixedConv.h
@@ -9,17 +9,16 @@
 #include "nsIChannel.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsIByteRangeRequest.h"
 #include "nsILoadInfo.h"
 #include "nsIMultiPartChannel.h"
 #include "nsAutoPtr.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/IncrementalTokenizer.h"
 #include "nsHttpResponseHead.h"
 
 #define NS_MULTIMIXEDCONVERTER_CID                         \
 { /* 7584CE90-5B25-11d3-A175-0050041CAF44 */         \
     0x7584ce90,                                      \
     0x5b25,                                          \
     0x11d3,                                          \
     {0xa1, 0x75, 0x0, 0x50, 0x4, 0x1c, 0xaf, 0x44}       \
@@ -113,137 +112,65 @@ protected:
 //  --BoundaryToken[\r]\n
 //  content-type: foo/bar[\r]\n
 //  ... (other headers if any)
 //  [\r]\n (second line feed to delimit end of headers)
 //  data
 //  --BoundaryToken-- (end delimited by final "--")
 //
 // linebreaks can be either CRLF or LFLF. linebreaks preceding
-// boundary tokens are NOT considered part of the data. BoundaryToken
+// boundary tokens are considered part of the data. BoundaryToken
 // is any opaque string.
 //  
 //
 
 class nsMultiMixedConv : public nsIStreamConverter {
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSISTREAMCONVERTER
     NS_DECL_NSISTREAMLISTENER
     NS_DECL_NSIREQUESTOBSERVER
 
-    explicit nsMultiMixedConv();
+    nsMultiMixedConv();
 
 protected:
-    typedef mozilla::IncrementalTokenizer::Token Token;
-
     virtual ~nsMultiMixedConv();
 
-    nsresult SendStart();
-    void AccumulateData(Token const & aToken);
-    nsresult SendData();
+    nsresult SendStart(nsIChannel *aChannel);
     nsresult SendStop(nsresult aStatus);
+    nsresult SendData(char *aBuffer, uint32_t aLen);
+    nsresult ParseHeaders(nsIChannel *aChannel, char *&aPtr,
+                          uint32_t &aLen, bool *_retval);
+    int32_t  PushOverLine(char *&aPtr, uint32_t &aLen);
+    char *FindToken(char *aCursor, uint32_t aLen);
+    nsresult BufferData(char *aData, uint32_t aLen);
 
     // member data
+    bool                mNewPart;        // Are we processing the beginning of a part?
+    bool                mProcessingHeaders;
     nsCOMPtr<nsIStreamListener> mFinalListener; // this guy gets the converted data via his OnDataAvailable()
 
-    nsCOMPtr<nsIChannel> mChannel; // The channel as we get in in OnStartRequest call
+    nsCString           mToken;
+    uint32_t            mTokenLen;
+
     RefPtr<nsPartChannel> mPartChannel;   // the channel for the given part we're processing.
                                         // one channel per part.
     nsCOMPtr<nsISupports> mContext;
     nsCString           mContentType;
     nsCString           mContentDisposition;
     uint64_t            mContentLength;
+    
+    char                *mBuffer;
+    uint32_t            mBufLen;
     uint64_t            mTotalSent;
+    bool                mFirstOnData;   // used to determine if we're in our first OnData callback.
 
     // The following members are for tracking the byte ranges in
     // multipart/mixed content which specified the 'Content-Range:'
     // header...
     int64_t             mByteRangeStart;
     int64_t             mByteRangeEnd;
     bool                mIsByteRangeRequest;
-    // This flag is set first time we create a part channel.
-    // We use it to prevent duplicated OnStopRequest call on the listener
-    // when we fail from some reason to ever create a part channel that
-    // ensures correct notifications.
-    bool                mRequestListenerNotified;
 
     uint32_t            mCurrentPartID;
-
-    // Flag preventing reenter of OnDataAvailable in case the target listener
-    // ends up spinning the event loop.
-    bool                mInOnDataAvailable;
-
-    // Current state of the incremental parser
-    enum EParserState {
-      BOUNDARY,
-      BOUNDARY_CRLF,
-      HEADER_NAME,
-      HEADER_SEP,
-      HEADER_VALUE,
-      BODY_INIT,
-      BODY,
-      TRAIL_DASH1,
-      TRAIL_DASH2,
-      EPILOGUE,
-
-      INIT = BOUNDARY
-    } mParserState;
-
-    // Response part header value, valid when we find a header name
-    // we recognize.
-    enum EHeader : uint32_t {
-      HEADER_FIRST,
-      HEADER_CONTENT_TYPE = HEADER_FIRST,
-      HEADER_CONTENT_LENGTH,
-      HEADER_CONTENT_DISPOSITION,
-      HEADER_SET_COOKIE,
-      HEADER_CONTENT_RANGE,
-      HEADER_RANGE,
-      HEADER_UNKNOWN
-    } mResponseHeader;
-    // Cumulated value of a response header.
-    nsCString mResponseHeaderValue;
-
-    nsCString mBoundary;
-    mozilla::IncrementalTokenizer mTokenizer;
-
-    // When in the "body parsing" mode, see below, we cumulate raw data
-    // incrementally to mainly avoid any unnecessary granularity.
-    // mRawData points to the first byte in the tokenizer buffer where part
-    // body data begins or continues.  mRawDataLength is a cumulated length
-    // of that data during a single tokenizer input feed.  This is always
-    // flushed right after we fed the tokenizer.
-    nsACString::const_char_iterator mRawData;
-    nsACString::size_type mRawDataLength;
-
-    // At the start we don't know if the server will be sending boundary with
-    // or without the leading dashes.
-    Token mBoundaryToken;
-    Token mBoundaryTokenWithDashes;
-    // We need these custom tokens to allow finding CRLF when in the binary mode.
-    // CRLF before boundary is considered part of the boundary and not part of
-    // the data.
-    Token mLFToken;
-    Token mCRLFToken;
-    // Custom tokens for each of the response headers we recognize.
-    Token mHeaderTokens[HEADER_UNKNOWN];
-
-    // Resets values driven by part headers, like content type, to their defaults,
-    // called at the start of every part processing.
-    void HeadersToDefault();
-    // Processes captured value of mResponseHeader header.
-    nsresult ProcessHeader();
-    // Switches the parser and tokenizer state to "binary mode" which only searches
-    // for the 'CRLF boundary' delimiter.
-    void SwitchToBodyParsing();
-    // Switches to the default mode, we are in this mode when parsing headers and
-    // control data around the boundary delimiters.
-    void SwitchToControlParsing();
-    // Turns on or off recognition of the headers we recognize in part heads.
-    void SetHeaderTokensEnabled(bool aEnable);
-
-    // The main parser callback called by the IncrementalTokenizer
-    // instance from OnDataAvailable or OnStopRequest.
-    nsresult ConsumeToken(Token const & token);
 };
 
 #endif /* __nsmultimixedconv__h__ */
deleted file mode 100644
--- a/netwerk/test/unit/test_multipart_streamconv-byte-by-byte.js
+++ /dev/null
@@ -1,110 +0,0 @@
-Cu.import("resource://testing-common/httpd.js");
-Cu.import("resource://gre/modules/NetUtil.jsm");
-
-var httpserver = null;
-
-XPCOMUtils.defineLazyGetter(this, "uri", function() {
-  return "http://localhost:" + httpserver.identity.primaryPort + "/multipart";
-});
-
-function make_channel(url) {
-  return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true});
-}
-
-var multipartBody = "--boundary\r\n\r\nSome text\r\n--boundary\r\nContent-Type: text/x-test\r\n\r\n<?xml version='1.1'?>\r\n<root/>\r\n--boundary\r\n\r\n<?xml version='1.0'?><root/>\r\n--boundary--";
-
-function contentHandler(metadata, response)
-{
-  response.setHeader("Content-Type", 'multipart/mixed; boundary="boundary"');
-  response.processAsync();
-
-  var body = multipartBody;
-  function byteByByte()
-  {
-    if (!body.length) {
-      response.finish();
-      return;
-    }
-
-    var onebyte = body[0];
-    response.bodyOutputStream.write(onebyte, 1);
-    body = body.substring(1);
-    do_timeout(1, byteByByte);
-  }
-
-  do_timeout(1, byteByByte);
-}
-
-var numTests = 2;
-var testNum = 0;
-
-var testData =
-  [
-   { data: "Some text", type: "text/plain" },
-   { data: "<?xml version='1.1'?>\r\n<root/>", type: "text/x-test" },
-   { data: "<?xml version='1.0'?><root/>", type: "text/xml" }
-  ];
-
-function responseHandler(request, buffer)
-{
-  do_check_eq(buffer, testData[testNum].data);
-  do_check_eq(request.QueryInterface(Ci.nsIChannel).contentType,
-  testData[testNum].type);
-  if (++testNum == numTests)
-    httpserver.stop(do_test_finished);
-}
-
-var multipartListener = {
-  _buffer: "",
-  _index: 0,
-
-  QueryInterface: function(iid) {
-    if (iid.equals(Components.interfaces.nsIStreamListener) ||
-        iid.equals(Components.interfaces.nsIRequestObserver) ||
-        iid.equals(Components.interfaces.nsISupports))
-      return this;
-    throw Components.results.NS_ERROR_NO_INTERFACE;
-  },
-
-  onStartRequest: function(request, context) {
-    this._buffer = "";
-  },
-
-  onDataAvailable: function(request, context, stream, offset, count) {
-    try {
-      this._buffer = this._buffer.concat(read_stream(stream, count));
-      dump("BUFFEEE: " + this._buffer + "\n\n");
-    } catch (ex) {
-      do_throw("Error in onDataAvailable: " + ex);
-    }
-  },
-
-  onStopRequest: function(request, context, status) {
-    this._index++;
-    // Second part should be last part
-    do_check_eq(request.QueryInterface(Ci.nsIMultiPartChannel).isLastPart, this._index == testData.length);
-    try {
-      responseHandler(request, this._buffer);
-    } catch (ex) {
-      do_throw("Error in closure function: " + ex);
-    }
-  }
-};
-
-function run_test()
-{
-  httpserver = new HttpServer();
-  httpserver.registerPathHandler("/multipart", contentHandler);
-  httpserver.start(-1);
-
-  var streamConv = Cc["@mozilla.org/streamConverters;1"]
-                     .getService(Ci.nsIStreamConverterService);
-  var conv = streamConv.asyncConvertData("multipart/mixed",
-					 "*/*",
-					 multipartListener,
-					 null);
-
-  var chan = make_channel(uri);
-  chan.asyncOpen2(conv);
-  do_test_pending();
-}
--- a/netwerk/test/unit/test_multipart_streamconv.js
+++ b/netwerk/test/unit/test_multipart_streamconv.js
@@ -6,41 +6,40 @@ var httpserver = null;
 XPCOMUtils.defineLazyGetter(this, "uri", function() {
   return "http://localhost:" + httpserver.identity.primaryPort + "/multipart";
 });
 
 function make_channel(url) {
   return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true});
 }
 
-var multipartBody = "--boundary\r\n\r\nSome text\r\n--boundary\r\nContent-Type: text/x-test\r\n\r\n<?xml version='1.1'?>\r\n<root/>\r\n--boundary\r\n\r\n<?xml version='1.0'?><root/>\r\n--boundary--";
+var multipartBody = "--boundary\r\n\r\nSome text\r\n--boundary\r\n\r\n<?xml version='1.0'?><root/>\r\n--boundary--";
 
 function contentHandler(metadata, response)
 {
   response.setHeader("Content-Type", 'multipart/mixed; boundary="boundary"');
   response.bodyOutputStream.write(multipartBody, multipartBody.length);
 }
 
 var numTests = 2;
 var testNum = 0;
 
 var testData =
   [
    { data: "Some text", type: "text/plain" },
-   { data: "<?xml version='1.1'?>\r\n<root/>", type: "text/x-test" },
    { data: "<?xml version='1.0'?><root/>", type: "text/xml" }
   ];
 
 function responseHandler(request, buffer)
 {
-  do_check_eq(buffer, testData[testNum].data);
-  do_check_eq(request.QueryInterface(Ci.nsIChannel).contentType,
-  testData[testNum].type);
-  if (++testNum == numTests)
-    httpserver.stop(do_test_finished);
+    do_check_eq(buffer, testData[testNum].data);
+    do_check_eq(request.QueryInterface(Ci.nsIChannel).contentType,
+		testData[testNum].type);
+    if (++testNum == numTests)
+	httpserver.stop(do_test_finished);
 }
 
 var multipartListener = {
   _buffer: "",
   _index: 0,
 
   QueryInterface: function(iid) {
     if (iid.equals(Components.interfaces.nsIStreamListener) ||
@@ -61,17 +60,17 @@ var multipartListener = {
     } catch (ex) {
       do_throw("Error in onDataAvailable: " + ex);
     }
   },
 
   onStopRequest: function(request, context, status) {
     this._index++;
     // Second part should be last part
-    do_check_eq(request.QueryInterface(Ci.nsIMultiPartChannel).isLastPart, this._index == testData.length);
+    do_check_eq(request.QueryInterface(Ci.nsIMultiPartChannel).isLastPart, this._index == 2);
     try {
       responseHandler(request, this._buffer);
     } catch (ex) {
       do_throw("Error in closure function: " + ex);
     }
   }
 };
 
deleted file mode 100644
--- a/netwerk/test/unit/test_multipart_streamconv_missing_boundary_lead_dashes.js
+++ /dev/null
@@ -1,89 +0,0 @@
-Cu.import("resource://testing-common/httpd.js");
-Cu.import("resource://gre/modules/NetUtil.jsm");
-
-var httpserver = null;
-
-XPCOMUtils.defineLazyGetter(this, "uri", function() {
-  return "http://localhost:" + httpserver.identity.primaryPort + "/multipart";
-});
-
-function make_channel(url) {
-  return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true});
-}
-
-var multipartBody = "\r\nSome text\r\nboundary\r\n\r\n<?xml version='1.0'?><root/>\r\nboundary--";
-
-function contentHandler(metadata, response)
-{
-  response.setHeader("Content-Type", 'multipart/mixed; boundary="boundary"');
-  response.bodyOutputStream.write(multipartBody, multipartBody.length);
-}
-
-var numTests = 2;
-var testNum = 0;
-
-var testData =
-  [
-   { data: "Some text", type: "text/plain" },
-   { data: "<?xml version='1.0'?><root/>", type: "text/xml" }
-  ];
-
-function responseHandler(request, buffer)
-{
-    do_check_eq(buffer, testData[testNum].data);
-    do_check_eq(request.QueryInterface(Ci.nsIChannel).contentType,
-		testData[testNum].type);
-    if (++testNum == numTests)
-	httpserver.stop(do_test_finished);
-}
-
-var multipartListener = {
-  _buffer: "",
-
-  QueryInterface: function(iid) {
-    if (iid.equals(Components.interfaces.nsIStreamListener) ||
-        iid.equals(Components.interfaces.nsIRequestObserver) ||
-        iid.equals(Components.interfaces.nsISupports))
-      return this;
-    throw Components.results.NS_ERROR_NO_INTERFACE;
-  },
-
-  onStartRequest: function(request, context) {
-    this._buffer = "";
-  },
-
-  onDataAvailable: function(request, context, stream, offset, count) {
-    try {
-      this._buffer = this._buffer.concat(read_stream(stream, count));
-      dump("BUFFEEE: " + this._buffer + "\n\n");
-    } catch (ex) {
-      do_throw("Error in onDataAvailable: " + ex);
-    }
-  },
-
-  onStopRequest: function(request, context, status) {
-    try {
-      responseHandler(request, this._buffer);
-    } catch (ex) {
-      do_throw("Error in closure function: " + ex);
-    }
-  }
-};
-
-function run_test()
-{
-  httpserver = new HttpServer();
-  httpserver.registerPathHandler("/multipart", contentHandler);
-  httpserver.start(-1);
-
-  var streamConv = Cc["@mozilla.org/streamConverters;1"]
-                     .getService(Ci.nsIStreamConverterService);
-  var conv = streamConv.asyncConvertData("multipart/mixed",
-					 "*/*",
-					 multipartListener,
-					 null);
-
-  var chan = make_channel(uri);
-  chan.asyncOpen2(conv);
-  do_test_pending();
-}
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -236,18 +236,16 @@ run-sequentially = node server exception
 [test_localstreams.js]
 [test_large_port.js]
 [test_mismatch_last-modified.js]
 [test_MIME_params.js]
 [test_mozTXTToHTMLConv.js]
 [test_multipart_byteranges.js]
 [test_multipart_streamconv.js]
 [test_multipart_streamconv_missing_lead_boundary.js]
-[test_multipart_streamconv_missing_boundary_lead_dashes.js]
-[test_multipart_streamconv-byte-by-byte.js]
 [test_nestedabout_serialize.js]
 [test_net_addr.js]
 # Bug 732363: test fails on windows for unknown reasons.
 skip-if = os == "win"
 [test_nojsredir.js]
 [test_offline_status.js]
 [test_original_sent_received_head.js]
 [test_parse_content_type.js]