netwerk/test/unit/test_unix_domain.js

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

     1 // Exercise Unix domain sockets.
     3 const CC = Components.Constructor;
     5 const UnixServerSocket = CC("@mozilla.org/network/server-socket;1",
     6                             "nsIServerSocket",
     7                             "initWithFilename");
     9 const ScriptableInputStream = CC("@mozilla.org/scriptableinputstream;1",
    10                                  "nsIScriptableInputStream",
    11                                  "init");
    13 const IOService = Cc["@mozilla.org/network/io-service;1"]
    14                   .getService(Ci.nsIIOService);
    15 const socketTransportService = Cc["@mozilla.org/network/socket-transport-service;1"]
    16                                .getService(Ci.nsISocketTransportService);
    18 const threadManager = Cc["@mozilla.org/thread-manager;1"].getService();
    20 const allPermissions = parseInt("777", 8);
    22 function run_test()
    23 {
    24   // If we're on Windows, simply check for graceful failure.
    25   if ("@mozilla.org/windows-registry-key;1" in Cc) {
    26     test_not_supported();
    27     return;
    28   }
    30   add_test(test_echo);
    31   add_test(test_name_too_long);
    32   add_test(test_no_directory);
    33   add_test(test_no_such_socket);
    34   add_test(test_address_in_use);
    35   add_test(test_file_in_way);
    36   add_test(test_create_permission);
    37   add_test(test_connect_permission);
    38   add_test(test_long_socket_name);
    39   add_test(test_keep_when_offline);
    41   run_next_test();
    42 }
    44 // Check that creating a Unix domain socket fails gracefully on Windows.
    45 function test_not_supported()
    46 {
    47   let socketName = do_get_tempdir();
    48   socketName.append('socket');
    49   do_print("creating socket: " + socketName.path);
    51   do_check_throws_nsIException(() => new UnixServerSocket(socketName, allPermissions, -1),
    52                                "NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED");
    54   do_check_throws_nsIException(() => socketTransportService.createUnixDomainTransport(socketName),
    55                                "NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED");
    56 }
    58 // Actually exchange data with Unix domain sockets.
    59 function test_echo()
    60 {
    61   let log = '';
    63   let socketName = do_get_tempdir();
    64   socketName.append('socket');
    66   // Create a server socket, listening for connections.
    67   do_print("creating socket: " + socketName.path);
    68   let server = new UnixServerSocket(socketName, allPermissions, -1);
    69   server.asyncListen({
    70     onSocketAccepted: function(aServ, aTransport) {
    71       do_print("called test_echo's onSocketAccepted");
    72       log += 'a';
    74       do_check_eq(aServ, server);
    76       let connection = aTransport;
    78       // Check the server socket's self address.
    79       let connectionSelfAddr = connection.getScriptableSelfAddr();
    80       do_check_eq(connectionSelfAddr.family, Ci.nsINetAddr.FAMILY_LOCAL);
    81       do_check_eq(connectionSelfAddr.address, socketName.path);
    83       // The client socket is anonymous, so the server transport should
    84       // have an empty peer address.
    85       do_check_eq(connection.host, '');
    86       do_check_eq(connection.port, 0);
    87       let connectionPeerAddr = connection.getScriptablePeerAddr();
    88       do_check_eq(connectionPeerAddr.family, Ci.nsINetAddr.FAMILY_LOCAL);
    89       do_check_eq(connectionPeerAddr.address, '');
    91       let serverAsyncInput = connection.openInputStream(0, 0, 0).QueryInterface(Ci.nsIAsyncInputStream);
    92       let serverOutput = connection.openOutputStream(0, 0, 0);
    94       serverAsyncInput.asyncWait(function (aStream) {
    95         do_print("called test_echo's server's onInputStreamReady");
    96         let serverScriptableInput = new ScriptableInputStream(aStream);
    98         // Receive data from the client, and send back a response.
    99         do_check_eq(serverScriptableInput.readBytes(17), "Mervyn Murgatroyd");
   100         do_print("server has read message from client");
   101         serverOutput.write("Ruthven Murgatroyd", 18);
   102         do_print("server has written to client");
   103       }, 0, 0, threadManager.currentThread);
   104     },
   106     onStopListening: function(aServ, aStatus) {
   107       do_print("called test_echo's onStopListening");
   108       log += 's';
   110       do_check_eq(aServ, server);
   111       do_check_eq(log, 'acs');
   113       run_next_test();
   114     }
   115   });
   117   // Create a client socket, and connect to the server.
   118   let client = socketTransportService.createUnixDomainTransport(socketName);
   119   do_check_eq(client.host, socketName.path);
   120   do_check_eq(client.port, 0);
   122   let clientAsyncInput = client.openInputStream(0, 0, 0).QueryInterface(Ci.nsIAsyncInputStream);
   123   let clientInput = new ScriptableInputStream(clientAsyncInput);
   124   let clientOutput = client.openOutputStream(0, 0, 0);
   126   clientOutput.write("Mervyn Murgatroyd", 17);
   127   do_print("client has written to server");
   129   clientAsyncInput.asyncWait(function (aStream) {
   130     do_print("called test_echo's client's onInputStreamReady");
   131     log += 'c';
   133     do_check_eq(aStream, clientAsyncInput);
   135     // Now that the connection has been established, we can check the
   136     // transport's self and peer addresses.
   137     let clientSelfAddr = client.getScriptableSelfAddr();
   138     do_check_eq(clientSelfAddr.family, Ci.nsINetAddr.FAMILY_LOCAL);
   139     do_check_eq(clientSelfAddr.address, '');
   141     do_check_eq(client.host, socketName.path); // re-check, but hey
   142     let clientPeerAddr = client.getScriptablePeerAddr();
   143     do_check_eq(clientPeerAddr.family, Ci.nsINetAddr.FAMILY_LOCAL);
   144     do_check_eq(clientPeerAddr.address, socketName.path);
   146     do_check_eq(clientInput.readBytes(18), "Ruthven Murgatroyd");
   147     do_print("client has read message from server");
   149     server.close();
   150   }, 0, 0, threadManager.currentThread);
   151 }
   153 // Create client and server sockets using a path that's too long.
   154 function test_name_too_long()
   155 {
   156   let socketName = do_get_tempdir();
   157   // The length limits on all the systems NSPR supports are a bit past 100.
   158   socketName.append(new Array(1000).join('x'));
   160   // The length must be checked before we ever make any system calls --- we
   161   // have to create the sockaddr first --- so it's unambiguous which error
   162   // we should get here.
   164   do_check_throws_nsIException(() => new UnixServerSocket(socketName, 0, -1),
   165                                "NS_ERROR_FILE_NAME_TOO_LONG");
   167   // Unlike most other client socket errors, this one gets reported
   168   // immediately, as we can't even initialize the sockaddr with the given
   169   // name.
   170   do_check_throws_nsIException(() => socketTransportService.createUnixDomainTransport(socketName),
   171                                "NS_ERROR_FILE_NAME_TOO_LONG");
   173   run_next_test();
   174 }
   176 // Try creating a socket in a directory that doesn't exist.
   177 function test_no_directory()
   178 {
   179   let socketName = do_get_tempdir();
   180   socketName.append('directory-that-does-not-exist');
   181   socketName.append('socket');
   183   do_check_throws_nsIException(() => new UnixServerSocket(socketName, 0, -1),
   184                                "NS_ERROR_FILE_NOT_FOUND");
   186   run_next_test();
   187 }
   189 // Try connecting to a server socket that isn't there.
   190 function test_no_such_socket()
   191 {
   192   let socketName = do_get_tempdir();
   193   socketName.append('nonexistent-socket');
   195   let client = socketTransportService.createUnixDomainTransport(socketName);
   196   let clientAsyncInput = client.openInputStream(0, 0, 0).QueryInterface(Ci.nsIAsyncInputStream);
   197   clientAsyncInput.asyncWait(function (aStream) {
   198     do_print("called test_no_such_socket's onInputStreamReady");
   200     do_check_eq(aStream, clientAsyncInput);
   202     // nsISocketTransport puts off actually creating sockets as long as
   203     // possible, so the error in connecting doesn't actually show up until
   204     // this point.
   205     do_check_throws_nsIException(() => clientAsyncInput.available(),
   206                                  "NS_ERROR_FILE_NOT_FOUND");
   208     clientAsyncInput.close();
   209     client.close(Cr.NS_OK);
   211     run_next_test();
   212   }, 0, 0, threadManager.currentThread);
   213 }
   215 // Creating a socket with a name that another socket is already using is an
   216 // error.
   217 function test_address_in_use()
   218 {
   219   let socketName = do_get_tempdir();
   220   socketName.append('socket-in-use');
   222   // Create one server socket.
   223   let server = new UnixServerSocket(socketName, allPermissions, -1);
   225   // Now try to create another with the same name.
   226   do_check_throws_nsIException(() => new UnixServerSocket(socketName, allPermissions, -1),
   227                                "NS_ERROR_SOCKET_ADDRESS_IN_USE");
   229   run_next_test();
   230 }
   232 // Creating a socket with a name that is already a file is an error.
   233 function test_file_in_way()
   234 {
   235   let socketName = do_get_tempdir();
   236   socketName.append('file_in_way');
   238   // Create a file with the given name.
   239   socketName.create(Ci.nsIFile.NORMAL_FILE_TYPE, allPermissions);
   241   // Try to create a socket with the same name.
   242   do_check_throws_nsIException(() => new UnixServerSocket(socketName, allPermissions, -1),
   243                                "NS_ERROR_SOCKET_ADDRESS_IN_USE");
   245   // Try to create a socket under a name that uses that as a parent directory.
   246   socketName.append('socket');
   247   do_check_throws_nsIException(() => new UnixServerSocket(socketName, 0, -1),
   248                                "NS_ERROR_FILE_NOT_DIRECTORY");
   250   run_next_test();
   251 }
   253 // It is not permitted to create a socket in a directory which we are not
   254 // permitted to execute, or create files in.
   255 function test_create_permission()
   256 {
   257   let dirName = do_get_tempdir();
   258   dirName.append('unfriendly');
   260   let socketName = dirName.clone();
   261   socketName.append('socket');
   263   // The test harness has difficulty cleaning things up if we don't make
   264   // everything writable before we're done.
   265   try {
   266     // Create a directory which we are not permitted to search.
   267     dirName.create(Ci.nsIFile.DIRECTORY_TYPE, 0);
   269     // Try to create a socket in that directory. Because Linux returns EACCES
   270     // when a 'connect' fails because of a local firewall rule,
   271     // nsIServerSocket returns NS_ERROR_CONNECTION_REFUSED in this case.
   272     do_check_throws_nsIException(() => new UnixServerSocket(socketName, allPermissions, -1),
   273                                  "NS_ERROR_CONNECTION_REFUSED");
   275     // Grant read and execute permission, but not write permission on the directory.
   276     dirName.permissions = parseInt("0555", 8);
   278     // This should also fail; we need write permission.
   279     do_check_throws_nsIException(() => new UnixServerSocket(socketName, allPermissions, -1),
   280                                  "NS_ERROR_CONNECTION_REFUSED");
   282   } finally {
   283     // Make the directory writable, so the test harness can clean it up.
   284     dirName.permissions = allPermissions;
   285   }
   287   // This should succeed, since we now have all the permissions on the
   288   // directory we could want.
   289   do_check_instanceof(new UnixServerSocket(socketName, allPermissions, -1),
   290                       Ci.nsIServerSocket);
   292   run_next_test();
   293 }
   295 // To connect to a Unix domain socket, we need search permission on the
   296 // directories containing it, and some kind of permission or other on the
   297 // socket itself.
   298 function test_connect_permission()
   299 {
   300   // This test involves a lot of callbacks, but they're written out so that
   301   // the actual control flow proceeds from top to bottom.
   302   let log = '';
   304   // Create a directory which we are permitted to search - at first.
   305   let dirName = do_get_tempdir();
   306   dirName.append('inhospitable');
   307   dirName.create(Ci.nsIFile.DIRECTORY_TYPE, allPermissions);
   309   let socketName = dirName.clone();
   310   socketName.append('socket');
   312   // Create a server socket in that directory, listening for connections,
   313   // and accessible.
   314   let server = new UnixServerSocket(socketName, allPermissions, -1);
   315   server.asyncListen({ onSocketAccepted: socketAccepted, onStopListening: stopListening });
   317   // Make the directory unsearchable.
   318   dirName.permissions = 0;
   320   let client3;
   322   let client1 = socketTransportService.createUnixDomainTransport(socketName);
   323   let client1AsyncInput = client1.openInputStream(0, 0, 0).QueryInterface(Ci.nsIAsyncInputStream);
   324   client1AsyncInput.asyncWait(function (aStream) {
   325     do_print("called test_connect_permission's client1's onInputStreamReady");
   326     log += '1';
   328     // nsISocketTransport puts off actually creating sockets as long as
   329     // possible, so the error doesn't actually show up until this point.
   330     do_check_throws_nsIException(() => client1AsyncInput.available(),
   331                                  "NS_ERROR_CONNECTION_REFUSED");
   333     client1AsyncInput.close();
   334     client1.close(Cr.NS_OK);
   336     // Make the directory searchable, but make the socket inaccessible.
   337     dirName.permissions = allPermissions;
   338     socketName.permissions = 0;
   340     let client2 = socketTransportService.createUnixDomainTransport(socketName);
   341     let client2AsyncInput = client2.openInputStream(0, 0, 0).QueryInterface(Ci.nsIAsyncInputStream);
   342     client2AsyncInput.asyncWait(function (aStream) {
   343       do_print("called test_connect_permission's client2's onInputStreamReady");
   344       log += '2';
   346       do_check_throws_nsIException(() => client2AsyncInput.available(),
   347                                    "NS_ERROR_CONNECTION_REFUSED");
   349       client2AsyncInput.close();
   350       client2.close(Cr.NS_OK);
   352       // Now make everything accessible, and try one last time.
   353       socketName.permissions = allPermissions;
   355       client3 = socketTransportService.createUnixDomainTransport(socketName);
   357       let client3Output = client3.openOutputStream(0, 0, 0);
   358       client3Output.write("Hanratty", 8);
   360       let client3AsyncInput = client3.openInputStream(0, 0, 0).QueryInterface(Ci.nsIAsyncInputStream);
   361       client3AsyncInput.asyncWait(client3InputStreamReady, 0, 0, threadManager.currentThread);
   362     }, 0, 0, threadManager.currentThread);
   363   }, 0, 0, threadManager.currentThread);
   365   function socketAccepted(aServ, aTransport) {
   366     do_print("called test_connect_permission's onSocketAccepted");
   367     log += 'a';
   369     let serverInput = aTransport.openInputStream(0, 0, 0).QueryInterface(Ci.nsIAsyncInputStream);
   370     let serverOutput = aTransport.openOutputStream(0, 0, 0);
   372     serverInput.asyncWait(function (aStream) {
   373       do_print("called test_connect_permission's socketAccepted's onInputStreamReady");
   374       log += 'i';
   376       // Receive data from the client, and send back a response.
   377       let serverScriptableInput = new ScriptableInputStream(serverInput);
   378       do_check_eq(serverScriptableInput.readBytes(8), "Hanratty");
   379       serverOutput.write("Ferlingatti", 11);
   380     }, 0, 0, threadManager.currentThread);
   381   }
   383   function client3InputStreamReady(aStream) {
   384     do_print("called client3's onInputStreamReady");
   385     log += '3';
   387     let client3Input = new ScriptableInputStream(aStream);
   389     do_check_eq(client3Input.readBytes(11), "Ferlingatti");
   391     client3.close(Cr.NS_OK);
   392     server.close();
   393   }
   395   function stopListening(aServ, aStatus) {
   396     do_print("called test_connect_permission's server's stopListening");
   397     log += 's';
   399     do_check_eq(log, '12ai3s');
   401     run_next_test();
   402   }
   403 }
   405 // Creating a socket with a long filename doesn't crash.
   406 function test_long_socket_name()
   407 {
   408   let socketName = do_get_tempdir();
   409   socketName.append(new Array(10000).join('long'));
   411   // Try to create a server socket with the long name.
   412   do_check_throws_nsIException(() => new UnixServerSocket(socketName, allPermissions, -1),
   413                                "NS_ERROR_FILE_NAME_TOO_LONG");
   415   // Try to connect to a socket with the long name.
   416   do_check_throws_nsIException(() => socketTransportService.createUnixDomainTransport(socketName),
   417                                "NS_ERROR_FILE_NAME_TOO_LONG");
   419   run_next_test();
   420 }
   422 // Going offline should not shut down Unix domain sockets.
   423 function test_keep_when_offline()
   424 {
   425   let log = '';
   427   let socketName = do_get_tempdir();
   428   socketName.append('keep-when-offline');
   430   // Create a listening socket.
   431   let listener = new UnixServerSocket(socketName, allPermissions, -1);
   432   listener.asyncListen({ onSocketAccepted: onAccepted, onStopListening: onStopListening });
   434   // Connect a client socket to the listening socket.
   435   let client = socketTransportService.createUnixDomainTransport(socketName);
   436   let clientOutput = client.openOutputStream(0, 0, 0);
   437   let clientInput = client.openInputStream(0, 0, 0);
   438   clientInput.asyncWait(clientReady, 0, 0, threadManager.currentThread);
   439   let clientScriptableInput = new ScriptableInputStream(clientInput);
   441   let server, serverInput, serverScriptableInput, serverOutput;
   443   // How many times has the server invited the client to go first?
   444   let count = 0;
   446   // The server accepted connection callback.
   447   function onAccepted(aListener, aServer) {
   448     do_print("test_keep_when_offline: onAccepted called");
   449     log += 'a';
   450     do_check_eq(aListener, listener);
   451     server = aServer;
   453     // Prepare to receive messages from the client.
   454     serverInput = server.openInputStream(0, 0, 0);
   455     serverInput.asyncWait(serverReady, 0, 0, threadManager.currentThread);
   456     serverScriptableInput = new ScriptableInputStream(serverInput);
   458     // Start a conversation with the client.
   459     serverOutput = server.openOutputStream(0, 0, 0);
   460     serverOutput.write("After you, Alphonse!", 20);
   461     count++;
   462   }
   464   // The client has seen its end of the socket close.
   465   function clientReady(aStream) {
   466     log += 'c';
   467     do_print("test_keep_when_offline: clientReady called: " + log);
   468     do_check_eq(aStream, clientInput);
   470     // If the connection has been closed, end the conversation and stop listening.
   471     let available;
   472     try {
   473       available = clientInput.available();
   474     } catch (ex) {
   475       do_check_instanceof(ex, Ci.nsIException);
   476       do_check_eq(ex.result, Cr.NS_BASE_STREAM_CLOSED);
   478       do_print("client received end-of-stream; closing client output stream");
   479       log += ')';
   481       client.close(Cr.NS_OK);
   483       // Now both output streams have been closed, and both input streams
   484       // have received the close notification. Stop listening for
   485       // connections.
   486       listener.close();
   487     }
   489     if (available) {
   490       // Check the message from the server.
   491       do_check_eq(clientScriptableInput.readBytes(20), "After you, Alphonse!");
   493       // Write our response to the server.
   494       clientOutput.write("No, after you, Gaston!", 22);
   496       // Ask to be called again, when more input arrives.
   497       clientInput.asyncWait(clientReady, 0, 0, threadManager.currentThread);
   498     }
   499   }
   501   function serverReady(aStream) {
   502     log += 's';
   503     do_print("test_keep_when_offline: serverReady called: " + log);
   504     do_check_eq(aStream, serverInput);
   506     // Check the message from the client.
   507     do_check_eq(serverScriptableInput.readBytes(22), "No, after you, Gaston!");
   509     // This should not shut things down: Unix domain sockets should
   510     // remain open in offline mode.
   511     if (count == 5) {
   512       IOService.offline = true;
   513       log += 'o';
   514     }
   516     if (count < 10) {
   517       // Insist.
   518       serverOutput.write("After you, Alphonse!", 20);
   519       count++;
   521       // As long as the input stream is open, always ask to be called again
   522       // when more input arrives.
   523       serverInput.asyncWait(serverReady, 0, 0, threadManager.currentThread);
   524     } else if (count == 10) {
   525       // After sending ten times and receiving ten replies, we're not
   526       // going to send any more. Close the server's output stream; the
   527       // client's input stream should see this.
   528       do_print("closing server transport");
   529       server.close(Cr.NS_OK);
   530       log += '(';
   531     }
   532   }
   534   // We have stopped listening.
   535   function onStopListening(aServ, aStatus) {
   536     do_print("test_keep_when_offline: onStopListening called");
   537     log += 'L';
   538     do_check_eq(log, 'acscscscscsocscscscscs(c)L');
   540     do_check_eq(aServ, listener);
   541     do_check_eq(aStatus, Cr.NS_BINDING_ABORTED);
   543     run_next_test();
   544   }
   545 }

mercurial