test_net_addr.js (5606B)
1 "use strict"; 2 3 var CC = Components.Constructor; 4 5 const ServerSocket = CC( 6 "@mozilla.org/network/server-socket;1", 7 "nsIServerSocket", 8 "init" 9 ); 10 11 /** 12 * TestServer: A single instance of this is created as |serv|. When created, 13 * it starts listening on the loopback address on port |serv.port|. Tests will 14 * connect to it after setting |serv.acceptCallback|, which is invoked after it 15 * accepts a connection. 16 * 17 * Within |serv.acceptCallback|, various properties of |serv| can be used to 18 * run checks. After the callback, the connection is closed, but the server 19 * remains listening until |serv.stop| 20 * 21 * Note: TestServer can only handle a single connection at a time. Tests 22 * should use run_next_test at the end of |serv.acceptCallback| to start the 23 * following test which creates a connection. 24 */ 25 function TestServer() { 26 this.reset(); 27 28 // start server. 29 // any port (-1), loopback only (true), default backlog (-1) 30 this.listener = ServerSocket(-1, true, -1); 31 this.port = this.listener.port; 32 info("server: listening on " + this.port); 33 this.listener.asyncListen(this); 34 } 35 36 TestServer.prototype = { 37 onSocketAccepted(socket, trans) { 38 info("server: got client connection"); 39 40 // one connection at a time. 41 if (this.input !== null) { 42 try { 43 socket.close(); 44 } catch (ignore) {} 45 do_throw("Test written to handle one connection at a time."); 46 } 47 48 try { 49 this.input = trans.openInputStream(0, 0, 0); 50 this.output = trans.openOutputStream(0, 0, 0); 51 this.selfAddr = trans.getScriptableSelfAddr(); 52 this.peerAddr = trans.getScriptablePeerAddr(); 53 54 this.acceptCallback(); 55 } catch (e) { 56 /* In a native callback such as onSocketAccepted, exceptions might not 57 * get output correctly or logged to test output. Send them through 58 * do_throw, which fails the test immediately. */ 59 do_report_unexpected_exception(e, "in TestServer.onSocketAccepted"); 60 } 61 62 this.reset(); 63 }, 64 65 onStopListening() {}, 66 67 /** 68 * Called to close a connection and clean up properties. 69 */ 70 reset() { 71 if (this.input) { 72 try { 73 this.input.close(); 74 } catch (ignore) {} 75 } 76 if (this.output) { 77 try { 78 this.output.close(); 79 } catch (ignore) {} 80 } 81 82 this.input = null; 83 this.output = null; 84 this.acceptCallback = null; 85 this.selfAddr = null; 86 this.peerAddr = null; 87 }, 88 89 /** 90 * Cleanup for TestServer and this test case. 91 */ 92 stop() { 93 this.reset(); 94 try { 95 this.listener.close(); 96 } catch (ignore) {} 97 }, 98 }; 99 100 /** 101 * Helper function. 102 * Compares two nsINetAddr objects and ensures they are logically equivalent. 103 */ 104 function checkAddrEqual(lhs, rhs) { 105 Assert.equal(lhs.family, rhs.family); 106 107 if (lhs.family === Ci.nsINetAddr.FAMILY_INET) { 108 Assert.equal(lhs.address, rhs.address); 109 Assert.equal(lhs.port, rhs.port); 110 } 111 112 /* TODO: fully support ipv6 and local */ 113 } 114 115 /** 116 * An instance of SocketTransportService, used to create connections. 117 */ 118 var sts; 119 120 /** 121 * Single instance of TestServer 122 */ 123 var serv; 124 125 /** 126 * A place for individual tests to place Objects of importance for access 127 * throughout asynchronous testing. Particularly important for any output or 128 * input streams opened, as cleanup of those objects (by the garbage collector) 129 * causes the stream to close and may have other side effects. 130 */ 131 var testDataStore = null; 132 133 /** 134 * IPv4 test. 135 */ 136 function testIpv4() { 137 testDataStore = { 138 transport: null, 139 ouput: null, 140 }; 141 142 serv.acceptCallback = function () { 143 // disable the timeoutCallback 144 serv.timeoutCallback = function () {}; 145 146 var selfAddr = testDataStore.transport.getScriptableSelfAddr(); 147 var peerAddr = testDataStore.transport.getScriptablePeerAddr(); 148 149 // check peerAddr against expected values 150 Assert.equal(peerAddr.family, Ci.nsINetAddr.FAMILY_INET); 151 Assert.equal(peerAddr.port, testDataStore.transport.port); 152 Assert.equal(peerAddr.port, serv.port); 153 Assert.equal(peerAddr.address, "127.0.0.1"); 154 155 // check selfAddr against expected values 156 Assert.equal(selfAddr.family, Ci.nsINetAddr.FAMILY_INET); 157 Assert.equal(selfAddr.address, "127.0.0.1"); 158 159 // check that selfAddr = server.peerAddr and vice versa. 160 checkAddrEqual(selfAddr, serv.peerAddr); 161 checkAddrEqual(peerAddr, serv.selfAddr); 162 163 testDataStore = null; 164 executeSoon(run_next_test); 165 }; 166 167 // Useful timeout for debugging test hangs 168 /*serv.timeoutCallback = function(tname) { 169 if (tname === 'testIpv4') 170 do_throw('testIpv4 never completed a connection to TestServ'); 171 }; 172 do_timeout(connectTimeout, function(){ serv.timeoutCallback('testIpv4'); });*/ 173 174 testDataStore.transport = sts.createTransport( 175 [], 176 "127.0.0.1", 177 serv.port, 178 null, 179 null 180 ); 181 /* 182 * Need to hold |output| so that the output stream doesn't close itself and 183 * the associated connection. 184 */ 185 testDataStore.output = testDataStore.transport.openOutputStream( 186 Ci.nsITransport.OPEN_BLOCKING, 187 0, 188 0 189 ); 190 191 /* NEXT: 192 * openOutputStream -> onSocketAccepted -> acceptedCallback -> run_next_test 193 * OR (if the above timeout is uncommented) 194 * <connectTimeout lapses> -> timeoutCallback -> do_throw 195 */ 196 } 197 198 /** 199 * Running the tests. 200 */ 201 function run_test() { 202 sts = Cc["@mozilla.org/network/socket-transport-service;1"].getService( 203 Ci.nsISocketTransportService 204 ); 205 serv = new TestServer(); 206 207 registerCleanupFunction(function () { 208 serv.stop(); 209 }); 210 211 add_test(testIpv4); 212 /* TODO: testIpv6 */ 213 /* TODO: testLocal */ 214 215 run_next_test(); 216 }