Bug 562681, tests and small fixes, r=smaug
authorwfernandom2004@gmail.com
Fri, 18 Jun 2010 23:48:42 +0300
changeset 43840 637c840ed18efc95f8f84fb0076c6fe6cb92ad7b
parent 43839 5e9c61c8303cf3196b6cc51728ec36ec3c43ecc3
child 43841 fcde44a631be82cfb6e896e980de22dfad778bb8
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs562681
milestone1.9.3a6pre
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 562681, tests and small fixes, r=smaug
content/base/src/nsWebSocket.cpp
content/base/test/Makefile.in
content/base/test/file_websocket_http_resource.txt
content/base/test/file_websocket_wsh.py
content/base/test/test_websocket.html
netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
netwerk/protocol/http/nsHttpChannelAuthProvider.h
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -275,21 +275,23 @@ private:
   // disconnect.
   void ForceClose();
 
   nsresult DoConnect();
   nsresult HandleNewInputString(PRUint32 aStart);
   nsresult AddAuthorizationHeaders(nsCString& aAuthHeaderStr,
                                    PRBool     aIsProxyAuth);
   nsresult AddCookiesToRequest(nsCString& aAuthHeaderStr);
-  void GenerateSecKey(nsCString& aKey,
-                      PRUint32 *aNumber);
+
+  // Returns the related number of the generated key.
+  PRUint32 GenerateSecKey(nsCString& aKey);
   nsresult GenerateRequestKeys(nsCString& aKey1,
                                nsCString& aKey2,
                                nsCString& aKey3);
+
   PRBool UsingHttpProxy();
   nsresult Reset();
   void RemoveFromLoadGroup();
   nsresult ProcessHeaders();
   nsresult PostData(nsCString *aBuffer, PRBool aIsMessage);
   nsresult PrintErrorOnConsole(const char       *aBundleURI,
                                const PRUnichar  *aError,
                                const PRUnichar **aFormatStrings,
@@ -781,17 +783,17 @@ IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGI
                         secWebSocketKey1Header, secWebSocketKey2Header,
                         numberRequestHeaders };
   nsAutoTArray<PRUint32, numberRequestHeaders> headersToSend;
   for (PRUint32 i = 0; i < numberRequestHeaders; ++i) {
     headersToSend.AppendElement(i);
   }
 
   while (!headersToSend.IsEmpty()) {
-    PRUint8 headerPosToSendNow = random() % headersToSend.Length();
+    PRUint8 headerPosToSendNow = rand() % headersToSend.Length();
     eRequestHeader headerToSendNow =
       static_cast<eRequestHeader>(headersToSend[headerPosToSendNow]);
 
     switch (headerToSendNow)
     {
       case upgradeHeader:
       {
         buf->AppendLiteral("Upgrade: WebSocket\r\n");
@@ -1319,54 +1321,53 @@ nsWebSocketEstablishedConnection::AddCoo
     aStr.AppendLiteral("Cookie: ");
     aStr.Append(cookieValue);
     aStr.AppendLiteral("\r\n");
   }
 
   return NS_OK;
 }
 
-void
-nsWebSocketEstablishedConnection::GenerateSecKey(nsCString& aKey,
-                                                 PRUint32 *aNumber)
+PRUint32
+nsWebSocketEstablishedConnection::GenerateSecKey(nsCString& aKey)
 {
   PRUint32 i;
 
-  PRUint32 spaces = random() % 12 + 1;
+  PRUint32 spaces = rand() % 12 + 1;
   PRUint32 max = PR_UINT32_MAX / spaces;
-  PRUint32 number = random() % max;
+  PRUint32 number = rand() % max;
   PRUint32 product = number * spaces;
 
   nsCAutoString key;
 
   key.AppendInt(product);
 
   // Insert between one and twelve random characters from the ranges
   // U+0021 to U+002F and U+003A to U+007E into the key at random
   // positions.
-  PRUint32 numberOfCharsToInsert = random() % 12 + 1;
+  PRUint32 numberOfCharsToInsert = rand() % 12 + 1;
   for (i = 0; i < numberOfCharsToInsert; ++i) {
-    PRUint32 posToInsert = random() % key.Length();
+    PRUint32 posToInsert = rand() % key.Length();
     char charToInsert =
-      random() % 2 == 0 ?
-        static_cast<char>(0x21 + (random() % (0x2F - 0x21 + 1))) :
-        static_cast<char>(0x3A + (random() % (0x7E - 0x3A + 1)));
+      rand() % 2 == 0 ?
+        static_cast<char>(0x21 + (rand() % (0x2F - 0x21 + 1))) :
+        static_cast<char>(0x3A + (rand() % (0x7E - 0x3A + 1)));
 
     key.Insert(charToInsert, posToInsert);
   }
 
   // Insert /spaces/ U+0020 SPACE characters into the key at random
   // positions other than the start or end of the string.
   for (i = 0; i < spaces; ++i) {
-    PRUint32 posToInsert = random() % (key.Length() - 1) + 1;
+    PRUint32 posToInsert = rand() % (key.Length() - 1) + 1;
     key.Insert(static_cast<char>(0x20), posToInsert);
   }
 
   aKey = key;
-  *aNumber = number;
+  return number;
 }
 
 nsresult
 nsWebSocketEstablishedConnection::GenerateRequestKeys(nsCString& aKey1,
                                                       nsCString& aKey2,
                                                       nsCString& aKey3)
 {
   nsresult rv;
@@ -1374,25 +1375,25 @@ nsWebSocketEstablishedConnection::Genera
 
   nsCAutoString key_1;
   PRUint32 number_1;
 
   nsCAutoString key_2;
   PRUint32 number_2;
 
   // generate the sec-keys headers values
-  GenerateSecKey(key_1, &number_1);
-  GenerateSecKey(key_2, &number_2);
+  number_1 = GenerateSecKey(key_1);
+  number_2 = GenerateSecKey(key_2);
 
   // key3 must be a string consisting of eight random bytes
   nsCAutoString key_3;
   for (i = 0; i < 8; ++i) {
     // get a byte between 1 and 255. 0x00 was discarted to prevent possible
-    // issues in ws servers. 
-    key_3 += static_cast<char>(random() % 0xff + 1);
+    // issues in ws servers.
+    key_3 += static_cast<char>(rand() % 0xff + 1);
   }
 
   // since we have the keys, we calculate the server md5 challenge response,
   // which is the md5 string of the concatenation of /number_1/, expressed as
   // a big-endian 32 bit integer, /number_2/, expressed as a big-endian
   // 32 bit integer, and the eight bytes of /key_3/
 
   nsCOMPtr<nsICryptoHash> md5CryptoHash =
@@ -1595,21 +1596,21 @@ nsWebSocketEstablishedConnection::Init(n
 
 nsresult
 nsWebSocketEstablishedConnection::DoConnect()
 {
   NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
 
   nsresult rv;
 
-  mStatus = CONN_CONNECTING;
-
   rv = AddWSConnecting();
   ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
 
+  mStatus = CONN_CONNECTING;
+
   nsCOMPtr<nsISocketTransportService> sts =
     do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
   ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
 
   // configure the socket type based on the connection type requested.
   const char* types[1];
   nsAdoptingCString value =
     nsContentUtils::GetCharPref("network.http.default-socket-type");
@@ -2893,16 +2894,19 @@ nsWebSocket::Initialize(nsISupports* aOw
   if (aArgc == 2) {
     jsstr = JS_ValueToString(aContext, aArgv[1]);
     if (!jsstr) {
       return NS_ERROR_DOM_SYNTAX_ERR;
     }
     protocolParam.
       Assign(reinterpret_cast<const PRUnichar*>(JS_GetStringChars(jsstr)),
              JS_GetStringLength(jsstr));
+    if (protocolParam.IsEmpty()) {
+      return NS_ERROR_DOM_SYNTAX_ERR;
+    }
   }
 
   nsCOMPtr<nsPIDOMWindow> ownerWindow = do_QueryInterface(aOwner);
   NS_ENSURE_STATE(ownerWindow);
 
   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aOwner);
   NS_ENSURE_STATE(sgo);
   nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext();
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -393,16 +393,19 @@ include $(topsrcdir)/config/rules.mk
 		file_bug548193.sjs \
 		test_html_colors_quirks.html \
 		test_html_colors_standards.html \
 		test_bug571390.xul \
 		test_websocket_hello.html \
 		file_websocket_hello_wsh.py \
 		test_ws_basic_tests.html \
 		file_ws_basic_tests_wsh.py \
+		test_websocket.html \
+		file_websocket_wsh.py \
+		file_websocket_http_resource.txt \
 		$(NULL)
 
 # This test fails on the Mac for some reason
 ifneq (,$(filter gtk2 windows,$(MOZ_WIDGET_TOOLKIT)))
 _TEST_FILES2 += 	test_copyimage.html \
 		$(NULL)
 endif
 
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_websocket_http_resource.txt
@@ -0,0 +1,1 @@
+server data
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_websocket_wsh.py
@@ -0,0 +1,67 @@
+from mod_pywebsocket import msgutil
+
+import time
+import sys
+
+# see the list of tests in test_websocket.html
+
+def web_socket_do_extra_handshake(request):
+  if request.ws_protocol == "test 6":
+    sys.exit(0)
+  elif request.ws_protocol == "test 19":
+    time.sleep(180)
+    pass
+  elif request.ws_protocol == "test 8":
+    time.sleep(5)
+    pass
+  elif request.ws_protocol == "test 9":
+    time.sleep(5)
+    pass
+  elif request.ws_protocol == "test 10.1":
+    time.sleep(5)
+    pass
+  else:
+    pass
+
+def web_socket_transfer_data(request):
+  if request.ws_protocol == "test 9":
+    msgutil.close_connection(request)
+  elif request.ws_protocol == "test 11":
+    resp = "wrong message"
+    if msgutil.receive_message(request) == "client data":
+      resp = "server data"
+    msgutil.send_message(request, resp.decode('utf-8'))
+  elif request.ws_protocol == "test 13":
+    # first one binary message containing the byte 0x61 ('a')
+    request.connection.write('\xff\x01\x61')
+    # after a bad utf8 message
+    request.connection.write('\x01\x61\xff')
+    msgutil.close_connection(request)
+  elif request.ws_protocol == "test 14":
+    request.connection.write('\xff\x00')
+    msgutil.send_message(request, "server data")
+  elif request.ws_protocol == "test 15":
+    sys.exit (0)
+  elif request.ws_protocol == "test 17":
+    while not request.client_terminated:
+      msgutil.send_message(request, "server data")
+      time.sleep(1)
+    msgutil.send_message(request, "server data")
+    sys.exit(0)
+  elif request.ws_protocol == "test 18":
+    resp = "wrong message"
+    if msgutil.receive_message(request) == "1":
+      resp = "2"
+    msgutil.send_message(request, resp.decode('utf-8'))
+    resp = "wrong message"
+    if msgutil.receive_message(request) == "3":
+      resp = "4"
+    msgutil.send_message(request, resp.decode('utf-8'))
+    resp = "wrong message"
+    if msgutil.receive_message(request) == "5":
+      resp = "あいうえお"
+    msgutil.send_message(request, resp.decode('utf-8'))
+  elif request.ws_protocol == "test 10.1" or request.ws_protocol == "test 10.2":
+    msgutil.close_connection(request)
+  while not request.client_terminated:
+    msgutil.receive_message(request)
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_websocket.html
@@ -0,0 +1,461 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"></meta>
+  <title>WebSocket test</title>
+  <script type="text/javascript" src="/MochiKit/packed.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="testWebSocket()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=472529">Mozilla Bug </a>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/*
+ * tests:
+ *  1. client tries to connect to a http scheme location;
+ *  2. client tries to connect to an http resource;
+ *  3. client tries to connect to an non-existent ws server;
+ *  4. client tries to connect using a relative url;
+ *  5. client uses an invalid protocol value;
+ *  6. server closes the tcp connection before establishing the ws connection;
+ *  7. client calls close() and the server sends the close frame in
+ *     acknowledgement;
+ *  8. client closes the connection before the ws connection is established;
+ *  9. client sends a message before the ws connection is established;
+ * 10. assure serialization of the connections;
+ * 11. a simple hello echo;
+ * 12. client sends a message with bad bytes;
+ * 13. server sends an invalid message;
+ * 14. server sends the close frame, it doesn't close the tcp connection and
+ *     it keeps sending normal ws messages;
+ * 15. server closes the tcp connection, but it doesn't send the close frame;
+ * 16. client calls close() and tries to send a message;
+ * 17. client calls close() and the server keeps sending messages and it doesn't
+ *     send the close frame;
+ * 18. counter and encoding check;
+ * 19. server takes too long to establish the ws connection;
+ */
+
+var first_test = 1;
+var last_test = 19;
+
+var current_test = 1;
+
+var timeoutToAbortTest = 60000;
+var all_ws = [];
+
+function shouldNotOpen(e)
+{
+  var ws = e.target;
+  ok(false, "onopen shouldn't be called on test " + ws._testNumber + "!");
+  if (ws._timeoutToSucceed != undefined) {
+    clearTimeout(ws._timeoutToSucceed);
+  }
+}
+
+function shouldNotReceiveCloseEvent(e)
+{
+  var ws = e.target;
+  ok(false, "onclose shouldn't be called on test " + ws._testNumber + "!");
+  if (ws._timeoutToSucceed != undefined) {
+    clearTimeout(ws._timeoutToSucceed);
+  }
+}
+
+function shouldCloseCleanly(e)
+{
+  var ws = e.target;
+  ok(e.wasClean, "the ws connection in test " + ws._testNumber + " should be closed cleanly");
+  if (ws._timeoutToSucceed != undefined) {
+    clearTimeout(ws._timeoutToSucceed);
+  }
+}
+
+function shouldCloseNotCleanly(e)
+{
+  var ws = e.target;
+  ok(!e.wasClean, "the ws connection in test " + ws._testNumber + " shouldn't be closed cleanly");
+  if (ws._timeoutToSucceed != undefined) {
+    clearTimeout(ws._timeoutToSucceed);
+  }
+}
+
+function CreateTestWS(ws_location, ws_protocol)
+{
+  var ws;
+
+  try {
+    if (ws_protocol == undefined) {
+      ws = new WebSocket(ws_location);
+    } else {
+      ws = new WebSocket(ws_location, ws_protocol);
+    }
+
+    ws.onerror = function(e)
+    {
+      ok(false, "onerror called on test " + e.target._testNumber + "!");
+    };
+    ws._testNumber = current_test;
+    ws.addEventListener("close", function(e)
+    {
+      if (ws._receivedCloseEvent != undefined) {
+        ws._receivedCloseEvent = true;
+      }
+    }, false);
+  }
+  catch (e) {
+    throw e;
+  }
+  finally {
+    current_test++;
+  }
+
+  all_ws.push(ws);
+  return ws;
+}
+
+function doTest(number)
+{
+  if (doTest.timeoutId !== null) {
+    clearTimeout(doTest.timeoutId);
+    doTest.timeoutId = null;
+  }
+
+  if (number > last_test) {
+    setTimeout(finishWSTest, 10000);  // wait for the close events be dispatched
+    return;
+  }
+
+  $("feedback").innerHTML = "executing test: " + number + " of " + last_test + " tests.";
+
+  var fnTest = eval("test" + number + "");
+
+  if (fnTest._started === true) {
+    doTest(number + 1);
+    return;
+  }
+
+  doTest.timeoutId = setTimeout(function()
+  {
+    ok(false, "test " + number + " took too long to finish!");
+    doTest(number + 1);
+  }, timeoutToAbortTest);
+
+  fnTest._started = true;
+  fnTest();
+}
+doTest.timeoutId = null;
+
+function test1()
+{
+  try {
+    var ws = CreateTestWS("http://mochi.test:8888/tests/content/base/test/file_websocket");
+    ok(false, "test1 failed");
+  }
+  catch (e) {
+    ok(true, "test1 failed");
+  }
+  doTest(2);
+}
+
+function test2()
+{
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket_http_resource.txt");
+  ws.onopen = shouldNotOpen;
+  ws.onclose = shouldNotReceiveCloseEvent;
+  doTest(3);
+}
+
+function test3()
+{
+  var ws = CreateTestWS("ws://this.websocket.server.probably.does.not.exist");
+  ws.onopen = shouldNotOpen;
+  ws.onclose = shouldNotReceiveCloseEvent;
+  doTest(4);
+}
+
+function test4()
+{
+  try {
+    var ws = CreateTestWS("file_websocket");
+    ok(false, "test4 failed");
+  }
+  catch (e) {
+    ok(true, "test4 failed");
+  }
+  doTest(5);
+}
+
+function test5()
+{
+  try {
+    var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "");
+    ok(false, "couldn't accept an empty string in the protocol parameter");
+  }
+  catch (e) {
+    ok(true, "couldn't accept an empty string in the protocol parameter");
+  }
+  current_test--; // CreateTestWS incremented this
+  try {
+    var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "\n");
+    ok(false, "couldn't accept any not printable ASCII character in the protocol parameter");
+  }
+  catch (e) {
+    ok(true, "couldn't accept any not printable ASCII character in the protocol parameter");
+  }
+  doTest(6);
+}
+
+function test6()
+{
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 6");
+  ws.onopen = shouldNotOpen;
+  ws.onclose = shouldNotReceiveCloseEvent;
+  doTest(7);
+}
+
+function test7()
+{
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 7");
+  ws.onopen = function()
+  {
+    ws.close();
+  }
+  ws.onclose = function(e)
+  {
+    shouldCloseCleanly(e);
+    doTest(8);
+  };
+  ws._receivedCloseEvent = false;
+}
+
+function test8()
+{
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 8");
+  ws.onopen = shouldNotOpen;
+  ws.onclose = function(e)
+  {
+    shouldCloseNotCleanly(e);
+    doTest(9);
+  };
+
+  ws._receivedCloseEvent = false;
+  ws.close();
+}
+
+function test9()
+{
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 9");
+  ws.onclose = shouldCloseCleanly;
+  ws._receivedCloseEvent = false;
+
+  try {
+    ws.send("client data");
+    ok(false, "Couldn't send data before connecting!");
+  }
+  catch (e) {
+    ok(true, "Couldn't send data before connecting!");
+  }
+  doTest(10);
+}
+
+function test10()
+{
+  var ws1 = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 10.1");
+  current_test--; // CreateTestWS incremented this
+  var ws2 = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 10.2");
+
+  var ws2CanConnect = false;
+
+  // the server will delay ws1 for 5 seconds
+
+  ws1.onopen = function()
+  {
+    ws2CanConnect = true;
+  }
+
+  ws2.onopen = function()
+  {
+    ok(ws2CanConnect, "shouldn't connect yet in test 10!");
+    doTest(11);
+  }
+}
+
+function test11()
+{
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 11");
+  ok(ws.readyState == 0, "bad readyState in test 11!");
+  ws.onopen = function()
+  {
+    ok(ws.readyState == 1, "bad readyState in test 11!");
+    ws.send("client data");
+  }
+  ws.onmessage = function(e)
+  {
+    ok(e.data == "server data", "bad received message in test 11!");
+    ws.close();
+    ok(ws.readyState == 2, "bad readyState in test 11!");
+  }
+  ws.onclose = function(e)
+  {
+    ok(ws.readyState == 3, "bad readyState in test 11!");
+    shouldCloseCleanly(e);
+    doTest(12);
+  }
+}
+
+function test12()
+{
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 12");
+  ws.onopen = function()
+  {
+    try {
+      // send an unpaired surrogate
+      ws.send(String.fromCharCode(0xD800));
+      ok(false, "couldn't send an unpaired surrogate!");
+    }
+    catch (e) {
+      ok(true, "couldn't send an unpaired surrogate!");
+    }
+    ws.close();
+    ws._receivedCloseEvent = false;
+    doTest(13);
+  };
+}
+
+function test13()
+{
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 13");
+  ws._timesCalledOnError = 0;
+  ws.onerror = function()
+  {
+    ws._timesCalledOnError++;
+    if (ws._timesCalledOnError == 2) {
+      doTest(14);
+      ok(true, "test13 succeeded");
+    }
+  }
+  ws.onclose = shouldCloseCleanly;
+  ws._receivedCloseEvent = false;
+}
+
+function test14()
+{
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 14");
+  ws.onmessage = function()
+  {
+    ok(false, "shouldn't received message after the server sent the close frame");
+  }
+  ws.onclose = function(e)
+  {
+    shouldCloseCleanly(e);
+    doTest(15);
+  };
+  ws._receivedCloseEvent = false;
+}
+
+function test15()
+{
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 15");
+  ws.onclose = function(e)
+  {
+    shouldCloseNotCleanly(e);
+    doTest(16);
+  };
+  ws._receivedCloseEvent = false;
+}
+
+function test16()
+{
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 16");
+  ws.onopen = function()
+  {
+    ws.close();
+    ok(!ws.send("client data"), "shouldn't send message after calling close()");
+    doTest(17);
+  }
+  ws.onmessage = function()
+  {
+    ok(false, "shouldn't send message after calling close()");
+  }
+  ws.onclose = shouldCloseCleanly;
+  ws._receivedCloseEvent = false;
+}
+
+function test17()
+{
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 17");
+  ws.onopen = function()
+  {
+    ws.close();
+  }
+  ws.onclose = function(e)
+  {
+    shouldCloseNotCleanly(e);
+    doTest(18);
+  };
+  ws._receivedCloseEvent = false;
+}
+
+function test18()
+{
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 18");
+  var counter = 1;
+  ws.onopen = function()
+  {
+    ws.send(counter);
+  }
+  ws.onmessage = function(e)
+  {
+    if (counter == 5) {
+      ok(e.data == "あいうえお");
+      ws.close();
+      doTest(19);
+    } else {
+      ok(e.data == counter+1, "bad counter");
+      counter += 2;
+      ws.send(counter);
+    }
+  }
+  ws.onclose = shouldCloseCleanly;
+  ws._receivedCloseEvent = false;
+}
+
+function test19()
+{
+  var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 19");
+  ws.onopen = shouldNotOpen;
+  ws.onclose = shouldNotReceiveCloseEvent;
+  doTest(20);
+}
+
+function finishWSTest()
+{
+  for (i = 0; i < all_ws.length; ++i) {
+    if (all_ws[i]._receivedCloseEvent === false) {
+      ok(false, "didn't called close on test " + all_ws[i]._testNumber + "!");
+    }
+  }
+  SimpleTest.finish();
+}
+
+function testWebSocket ()
+{
+  doTest(first_test);
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+
+<div id="feedback">
+</div>
+
+</body>
+</html>