Bug 831392 - Server sent events, no reconnection. r=honzab
authorWellington Fernando de Macedo <wfernandom2004@gmail.com>
Sat, 08 Nov 2014 12:51:38 -0200
changeset 242625 6dbd7d78e775e90d694ed49613d50aaacfd07185
parent 242624 9e7f014425c17491442972fbea261a64b98f055b
child 242626 8f6cfc3bbdf2eefb431196bad83495712fc5d781
push idunknown
push userunknown
push dateunknown
reviewershonzab
bugs831392
milestone36.0a1
Bug 831392 - Server sent events, no reconnection. r=honzab
dom/base/EventSource.cpp
dom/base/EventSource.h
dom/base/test/test_bug338583.html
--- a/dom/base/EventSource.cpp
+++ b/dom/base/EventSource.cpp
@@ -57,17 +57,16 @@ namespace dom {
 EventSource::EventSource(nsPIDOMWindow* aOwnerWindow) :
   DOMEventTargetHelper(aOwnerWindow),
   mStatus(PARSE_STATE_OFF),
   mFrozen(false),
   mErrorLoadOnRedirect(false),
   mGoingToDispatchAllMessages(false),
   mWithCredentials(false),
   mWaitingForOnStopRequest(false),
-  mInterrupted(false),
   mLastConvertionResult(NS_OK),
   mReadyState(CONNECTING),
   mScriptLine(0),
   mInnerWindowID(0)
 {
 }
 
 EventSource::~EventSource()
@@ -336,39 +335,40 @@ EventSource::OnStartRequest(nsIRequest *
                             nsISupports *ctxt)
 {
   nsresult rv = CheckHealthOfRequestCallback(aRequest);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  bool requestSucceeded;
-  rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoCString contentType;
-  rv = httpChannel->GetContentType(contentType);
+  nsresult status;
+  rv = aRequest->GetStatus(&status);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsresult status;
-  aRequest->GetStatus(&status);
-
-  if (NS_FAILED(status) || !requestSucceeded ||
-      !contentType.EqualsLiteral(TEXT_EVENT_STREAM)) {
-    DispatchFailConnection();
-    return NS_ERROR_NOT_AVAILABLE;
+  if (NS_FAILED(status)) {
+    // EventSource::OnStopRequest will evaluate if it shall either reestablish
+    // or fail the connection
+    return NS_ERROR_ABORT;
   }
 
   uint32_t httpStatus;
   rv = httpChannel->GetResponseStatus(&httpStatus);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (httpStatus != 200) {
-    mInterrupted = true;
+    DispatchFailConnection();
+    return NS_ERROR_ABORT;
+  }
+
+  nsAutoCString contentType;
+  rv = httpChannel->GetContentType(contentType);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!contentType.EqualsLiteral(TEXT_EVENT_STREAM)) {
     DispatchFailConnection();
     return NS_ERROR_ABORT;
   }
 
   nsCOMPtr<nsIRunnable> event =
     NS_NewRunnableMethod(this, &EventSource::AnnounceConnection);
   NS_ENSURE_STATE(event);
 
@@ -449,40 +449,48 @@ EventSource::OnStopRequest(nsIRequest *a
                            nsresult aStatusCode)
 {
   mWaitingForOnStopRequest = false;
 
   if (mReadyState == CLOSED) {
     return NS_ERROR_ABORT;
   }
 
-  if (NS_FAILED(aStatusCode)) {
+  // "Network errors that prevents the connection from being established in the
+  //  first place (e.g. DNS errors), must cause the user agent to asynchronously
+  //  reestablish the connection.
+  //
+  //  (...) the cancelation of the fetch algorithm by the user agent (e.g. in
+  //  response to window.stop() or the user canceling the network connection
+  //  manually) must cause the user agent to fail the connection.
+
+  if (NS_FAILED(aStatusCode) &&
+      aStatusCode != NS_ERROR_CONNECTION_REFUSED &&
+      aStatusCode != NS_ERROR_NET_TIMEOUT &&
+      aStatusCode != NS_ERROR_NET_RESET &&
+      aStatusCode != NS_ERROR_NET_INTERRUPT &&
+      aStatusCode != NS_ERROR_PROXY_CONNECTION_REFUSED &&
+      aStatusCode != NS_ERROR_DNS_LOOKUP_QUEUE_FULL) {
     DispatchFailConnection();
-    return aStatusCode;
+    return NS_ERROR_ABORT;
   }
 
-  nsresult rv;
-  nsresult healthOfRequestResult = CheckHealthOfRequestCallback(aRequest);
-  if (NS_SUCCEEDED(healthOfRequestResult) &&
-      mLastConvertionResult == NS_PARTIAL_MORE_INPUT) {
-    // we had an incomplete UTF8 char at the end of the stream
-    rv = ParseCharacter(REPLACEMENT_CHAR);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
+  nsresult rv = CheckHealthOfRequestCallback(aRequest);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   ClearFields();
 
   nsCOMPtr<nsIRunnable> event =
     NS_NewRunnableMethod(this, &EventSource::ReestablishConnection);
   NS_ENSURE_STATE(event);
 
   rv = NS_DispatchToMainThread(event);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return healthOfRequestResult;
+  return NS_OK;
 }
 
 /**
  * Simple helper class that just forwards the redirect callback back
  * to the EventSource.
  */
 class AsyncVerifyRedirectCallbackFwr MOZ_FINAL : public nsIAsyncVerifyRedirectCallback
 {
@@ -864,21 +872,16 @@ EventSource::ResetConnection()
 
 void
 EventSource::ReestablishConnection()
 {
   if (mReadyState == CLOSED) {
     return;
   }
 
-  if (mReadyState != OPEN) {
-    NS_WARNING("Unexpected mReadyState!!!");
-    return;
-  }
-
   nsresult rv = ResetConnection();
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to reset the connection!!!");
     return;
   }
 
   rv = CheckInnerWindowCorrectness();
   if (NS_FAILED(rv)) {
@@ -989,17 +992,17 @@ EventSource::ConsoleError()
 {
   nsAutoCString targetSpec;
   nsresult rv = mSrc->GetSpec(targetSpec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ConvertUTF8toUTF16 specUTF16(targetSpec);
   const char16_t *formatStrings[] = { specUTF16.get() };
 
-  if (mReadyState == CONNECTING && !mInterrupted) {
+  if (mReadyState == CONNECTING) {
     rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
                              MOZ_UTF16("connectionFailure"),
                              formatStrings, ArrayLength(formatStrings));
   } else {
     rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
                              MOZ_UTF16("netInterrupt"),
                              formatStrings, ArrayLength(formatStrings));
   }
--- a/dom/base/EventSource.h
+++ b/dom/base/EventSource.h
@@ -216,17 +216,16 @@ protected:
   };
   ParserStatus mStatus;
 
   bool mFrozen;
   bool mErrorLoadOnRedirect;
   bool mGoingToDispatchAllMessages;
   bool mWithCredentials;
   bool mWaitingForOnStopRequest;
-  bool mInterrupted;
 
   // used while reading the input streams
   nsCOMPtr<nsIUnicodeDecoder> mUnicodeDecoder;
   nsresult mLastConvertionResult;
   nsString mLastFieldName;
   nsString mLastFieldValue;
 
   nsCOMPtr<nsILoadGroup> mLoadGroup;
--- a/dom/base/test/test_bug338583.html
+++ b/dom/base/test/test_bug338583.html
@@ -348,39 +348,39 @@ https://bugzilla.mozilla.org/show_bug.cg
       ok(gEventSourceObj3_g.hits['fn_onmessage'] == 0, "Test 3.g failed");
       gEventSourceObj3_g.close();
       setTestHasFinished(test_id);
     }, parseInt(1500*stress_factor));
   }
 
   function fnMessageListenerTest3h(e) {
     fnMessageListenerTest3h.msg_ok = (fnMessageListenerTest3h.msg_ok && e.data == "ok");
-    fnMessageListenerTest3h.id_ok = (fnMessageListenerTest3h.msg_ok && e.lastEventId == "");
+    fnMessageListenerTest3h.id_ok = (fnMessageListenerTest3h.id_ok && e.lastEventId == "");
   }
 
   function doTest3_h(test_id) {
     gEventSourceObj3_h = new EventSource("badMessageEvent2.eventsource");
 
-    gEventSourceObj3_h.addEventListener('message event', fnMessageListenerTest3h, true);
+    gEventSourceObj3_h.addEventListener('message', fnMessageListenerTest3h, true);
     fnMessageListenerTest3h.msg_ok = true;
     fnMessageListenerTest3h.id_ok = true;
 
     gEventSourceObj3_h.onmessage = fn_onmessage;
     gEventSourceObj3_h.hits = [];
     gEventSourceObj3_h.hits['fn_onmessage'] = 0;
 
     setTimeout(function() {
       ok(gEventSourceObj3_h.hits['fn_onmessage'] > 1, "Test 3.h.1 failed");
       if (gEventSourceObj3_h.hits['fn_onmessage'] > 1) {
         ok(fnMessageListenerTest3h.msg_ok, "Test 3.h.2 failed");
         ok(fnMessageListenerTest3h.id_ok, "Test 3.h.3 failed");
       }
       gEventSourceObj3_h.close();
       setTestHasFinished(test_id);
-    }, parseInt(3000*stress_factor));
+    }, parseInt(6000*stress_factor));
   }
 
 // in order to test (4)
 //   a) close the object when it is in use, which is being processed and that is expected
 //      to dispatch more eventlisteners
 //   b) remove an eventlistener in use
 
   function fn_onmessage4_a(e)