OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 #import("dart:io"); | |
6 #import("dart:isolate"); | |
7 | |
8 class ExpectedDataOutputStream implements OutputStream { | |
9 ExpectedDataOutputStream(List<int> this._data, | |
10 int this._cutoff, | |
11 bool this._closeAsError, | |
12 SocketMock this._socket); | |
13 | |
14 void set onNoPendingWrites(void callback()) { | |
15 _onNoPendingWrites = callback; | |
16 } | |
17 | |
18 void set onError(void callback(e)) { | |
19 _onError = callback; | |
20 } | |
21 | |
22 bool write(List data, [bool copyBuffer = true]) { | |
23 _onData(data); | |
24 return true; | |
25 } | |
26 | |
27 bool writeFrom(List data, [int offset = 0, int len]) { | |
28 if (len === null) len = data.length - offset; | |
29 _onData(data.getRange(offset, len)); | |
30 return true; | |
31 } | |
32 | |
33 void close() { | |
34 _socket.close(true); | |
35 } | |
36 | |
37 void _onData(List<int> data) { | |
38 // TODO(ajohnsen): To be removed, since the socket should not be written to | |
39 // after close. | |
40 if (_socket._closed) return; | |
41 Expect.isFalse(_written > _cutoff); | |
42 Expect.listEquals(data, _data.getRange(0, data.length)); | |
43 _data = _data.getRange(data.length, _data.length - data.length); | |
44 _written += data.length; | |
45 if (_written >= _cutoff) { | |
46 // Tell HttpServer that the socket have closed. | |
47 _socket._closeInternal(_closeAsError); | |
48 } | |
49 } | |
50 | |
51 Function _onNoPendingWrites; | |
52 Function _onError; | |
53 List<int> _data; | |
54 int _written = 0; | |
55 int _cutoff; | |
56 bool _closeAsError; | |
57 SocketMock _socket; | |
58 } | |
59 | |
60 class SocketMock implements Socket { | |
61 SocketMock(List<int> this._data, | |
62 List<int> expected, | |
63 int cutoff, | |
64 bool closeAsError) : | |
65 _hashCode = (Math.random() * (1 << 32)).toInt(), | |
66 _read = [] { | |
67 _outputStream = | |
68 new ExpectedDataOutputStream(expected, cutoff, closeAsError, this); | |
69 } | |
70 | |
71 int available() { | |
72 return _data.length; | |
73 } | |
74 | |
75 void _closeInternal([bool asError = false]) { | |
76 Expect.isFalse(_closed); | |
77 _closed = true; | |
78 _onClosedInternal(); | |
79 if (asError) { | |
80 _onError(new Exception("Socket closed unexpected")); | |
81 } else { | |
82 _onClosed(); | |
83 } | |
84 } | |
85 | |
86 int readList(List<int> buffer, int offset, int count) { | |
87 int max = Math.min(count, _data.length); | |
88 buffer.setRange(offset, max, _data); | |
89 _data = _data.getRange(max, _data.length - max); | |
90 return max; | |
91 } | |
92 | |
93 void close([bool halfClose = false]) { | |
94 if (!halfClose && !_closed) _closeInternal(); | |
95 } | |
96 | |
97 void set onData(void callback()) { | |
98 _onData = callback; | |
99 } | |
100 | |
101 void set onClosed(void callback()) { | |
102 _onClosed = callback; | |
103 } | |
104 | |
105 void set onError(void callback(e)) { | |
106 _onError = callback; | |
107 } | |
108 | |
109 OutputStream get outputStream() => _outputStream; | |
110 | |
111 int hashCode() => _hashCode; | |
112 | |
113 List<int> _read; | |
114 bool _closed = false; | |
115 int _hashCode; | |
116 Function _onData; | |
117 Function _onClosed; | |
118 Function _onError; | |
119 Function _onClosedInternal; | |
120 List<int> _data; | |
121 ExpectedDataOutputStream _outputStream; | |
122 } | |
123 | |
124 class ServerSocketMock implements ServerSocket { | |
125 ServerSocketMock(String addr, int this._port, int backlog) : | |
126 _sockets = new Set<Socket>(); | |
127 | |
128 void spawnSocket(var data, String response, int cutOff, bool closeAsError) { | |
129 if (data is String) data = data.charCodes(); | |
130 SocketMock socket = new SocketMock(data, | |
131 response.charCodes(), | |
132 cutOff, | |
133 closeAsError); | |
134 _sockets.add(socket); | |
135 ReceivePort port = new ReceivePort(); | |
136 socket._onClosedInternal = () { | |
137 // The server should always close the connection. | |
138 _sockets.remove(socket); | |
139 port.close(); | |
140 }; | |
141 // Tell HttpServer that a connection have come to life. | |
142 _onConnection(socket); | |
143 // Start 'sending' data. | |
144 socket._onData(); | |
145 } | |
146 | |
147 void close() { | |
148 Expect.fail("Don't close the connection, we attach to this socket"); | |
149 } | |
150 | |
151 void set onConnection(void callback(Socket connection)) { | |
152 _onConnection = callback; | |
153 } | |
154 | |
155 void set onError(void callback(e)) { | |
156 _onError = callback; | |
157 } | |
158 | |
159 int get port() => _port; | |
160 | |
161 int _port; | |
162 Function _onConnection; | |
163 Function _onError; | |
164 Set<Socket> _sockets; | |
165 } | |
166 | |
167 void testSocketClose() { | |
168 ServerSocketMock serverSocket = new ServerSocketMock("0.0.0.0", 5432, 5); | |
169 | |
170 HttpServer server = new HttpServer(); | |
171 server.listenOn(serverSocket); | |
172 void testContent(String request, | |
173 String response, | |
174 [int okayFrom = 0, | |
175 bool expectError = true]) { | |
176 // Inner callback to actually run a given setting. | |
177 void runSettings(int cutoff, | |
178 bool closeAsError, | |
179 bool expectError) { | |
180 server.defaultRequestHandler = | |
181 (HttpRequest request, HttpResponse response) { | |
182 request.inputStream.onData = () { | |
183 }; | |
184 request.inputStream.onClosed = () { | |
185 response.outputStream.close(); | |
186 }; | |
187 }; | |
188 | |
189 if (expectError) { | |
190 ReceivePort port = new ReceivePort(); | |
191 server.onError = (Exception error) { | |
192 port.close(); | |
193 }; | |
194 } else { | |
195 server.onError = (Exception error) { | |
196 Expect.fail("An error was not expected: $error"); | |
197 }; | |
198 } | |
199 | |
200 serverSocket.spawnSocket(request, response, cutoff, closeAsError); | |
201 // TODO(ajohnsen): Validate HttpServers number of connections. | |
202 } | |
203 for (int i = 1; i < response.length; i++) { | |
204 bool _expectError = expectError && i < response.length - okayFrom; | |
205 runSettings(i, false, _expectError); | |
206 runSettings(i, true, _expectError); | |
207 } | |
208 } | |
209 testContent( | |
210 "GET / HTTP/1.1\r\nKeep-Alive: False\r\n\r\n", | |
211 "HTTP/1.1 200 OK\r\ntransfer-encoding: chunked\r\nconnection: close" + | |
212 "\r\n\r\n0\r\n\r\n"); | |
213 | |
214 server.close(); | |
215 } | |
216 | |
217 void main() { | |
218 testSocketClose(); | |
219 } | |
OLD | NEW |