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