OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library dart2js_incremental.server; | 5 library dart2js_incremental.server; |
6 | 6 |
7 import 'dart:io'; | 7 import 'dart:io'; |
8 | 8 |
9 import 'dart:async' show | 9 import 'dart:async' show |
10 Completer, | 10 Completer, |
11 Future, | 11 Future, |
12 Stream, | 12 Stream, |
| 13 StreamController, |
13 StreamSubscription; | 14 StreamSubscription; |
14 | 15 |
15 import 'dart:convert' show | 16 import 'dart:convert' show |
16 HtmlEscape, | 17 HtmlEscape, |
17 JSON, | 18 JSON, |
18 UTF8; | 19 UTF8; |
19 | 20 |
20 import 'src/options.dart'; | 21 import 'src/options.dart'; |
21 | 22 |
22 import 'compiler.dart' show | 23 import 'compiler.dart' show |
23 CompilerEvent, | 24 CompilerEvent, |
24 IncrementalKind, | 25 IncrementalKind, |
25 compile; | 26 compile; |
26 | 27 |
27 class Conversation { | 28 class Conversation { |
28 HttpRequest request; | 29 HttpRequest request; |
29 HttpResponse response; | 30 HttpResponse response; |
30 | 31 |
31 static const String PACKAGES_PATH = '/packages'; | 32 static const String PACKAGES_PATH = '/packages'; |
32 | 33 |
33 static const String CONTENT_TYPE = HttpHeaders.CONTENT_TYPE; | 34 static const String CONTENT_TYPE = HttpHeaders.CONTENT_TYPE; |
34 | 35 |
35 static Uri documentRoot = Uri.base; | 36 static Uri documentRoot = Uri.base; |
36 | 37 |
37 static Uri packageRoot = Uri.base.resolve('packages/'); | 38 static Uri packageRoot = Uri.base.resolve('packages/'); |
38 | 39 |
39 static Map<Uri, Future<String>> generatedFiles = | 40 static Map<Uri, Future<String>> generatedFiles = |
40 new Map<Uri, Future<String>>(); | 41 new Map<Uri, Future<String>>(); |
41 | 42 |
| 43 static Map<Uri, StreamController<String>> updateControllers = |
| 44 new Map<Uri, StreamController<String>>(); |
| 45 |
42 Conversation(this.request, this.response); | 46 Conversation(this.request, this.response); |
43 | 47 |
44 onClosed(_) { | 48 onClosed(_) { |
45 if (response.statusCode == HttpStatus.OK) return; | 49 if (response.statusCode == HttpStatus.OK) return; |
46 print('Request for ${request.uri} ${response.statusCode}'); | 50 print('Request for ${request.uri} ${response.statusCode}'); |
47 } | 51 } |
48 | 52 |
49 Future notFound(Uri uri) { | 53 Future notFound(Uri uri) { |
50 response | 54 response |
51 ..headers.set(CONTENT_TYPE, 'text/html') | 55 ..headers.set(CONTENT_TYPE, 'text/html') |
52 ..statusCode = HttpStatus.NOT_FOUND | 56 ..statusCode = HttpStatus.NOT_FOUND |
53 ..write(htmlInfo("Not Found", "The file '$uri' could not be found.")); | 57 ..write(htmlInfo("Not Found", "The file '$uri' could not be found.")); |
54 return response.close(); | 58 return response.close(); |
55 } | 59 } |
56 | 60 |
57 Future badRequest(String problem) { | 61 Future badRequest(String problem) { |
58 response | 62 response |
59 ..headers.set(CONTENT_TYPE, 'text/html') | 63 ..headers.set(CONTENT_TYPE, 'text/html') |
60 ..statusCode = HttpStatus.BAD_REQUEST | 64 ..statusCode = HttpStatus.BAD_REQUEST |
61 ..write( | 65 ..write( |
62 htmlInfo("Bad request", "Bad request '${request.uri}': $problem")); | 66 htmlInfo("Bad request", "Bad request '${request.uri}': $problem")); |
63 return response.close(); | 67 return response.close(); |
64 } | 68 } |
65 | 69 |
66 Future handleSocket() async { | 70 Future handleSocket() async { |
67 if (false && request.uri.path == '/ws/watch') { | 71 StreamController<String> controller = updateControllers[request.uri]; |
| 72 if (controller != null) { |
68 WebSocket socket = await WebSocketTransformer.upgrade(request); | 73 WebSocket socket = await WebSocketTransformer.upgrade(request); |
69 socket.add(JSON.encode({'create': []})); | 74 print( |
70 // WatchHandler handler = new WatchHandler(socket, files); | 75 "Patches to ${request.uri} will be pushed to " |
71 // handlers.add(handler); | 76 "${request.connectionInfo.remoteAddress.host}:" |
72 // socket.listen( | 77 "${request.connectionInfo.remotePort}."); |
73 // handler.onData, cancelOnError: true, onDone: handler.onDone); | 78 controller.stream.pipe(socket); |
74 } else { | 79 } else { |
75 response.done | 80 response.done |
76 .then(onClosed) | 81 .then(onClosed) |
77 .catchError(onError); | 82 .catchError(onError); |
78 return await notFound(request.uri); | 83 return await notFound(request.uri); |
79 } | 84 } |
80 } | 85 } |
81 | 86 |
82 Future handle() { | 87 Future handle() { |
83 response.done | 88 response.done |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
123 if (path.endsWith('.html')) { | 128 if (path.endsWith('.html')) { |
124 response.headers.set(CONTENT_TYPE, 'text/html'); | 129 response.headers.set(CONTENT_TYPE, 'text/html'); |
125 } else if (path.endsWith('.dart')) { | 130 } else if (path.endsWith('.dart')) { |
126 response.headers.set(CONTENT_TYPE, 'application/dart'); | 131 response.headers.set(CONTENT_TYPE, 'application/dart'); |
127 } else if (path.endsWith('.js')) { | 132 } else if (path.endsWith('.js')) { |
128 response.headers.set(CONTENT_TYPE, 'application/javascript'); | 133 response.headers.set(CONTENT_TYPE, 'application/javascript'); |
129 } else if (path.endsWith('.ico')) { | 134 } else if (path.endsWith('.ico')) { |
130 response.headers.set(CONTENT_TYPE, 'image/x-icon'); | 135 response.headers.set(CONTENT_TYPE, 'image/x-icon'); |
131 } else if (path.endsWith('.appcache')) { | 136 } else if (path.endsWith('.appcache')) { |
132 response.headers.set(CONTENT_TYPE, 'text/cache-manifest'); | 137 response.headers.set(CONTENT_TYPE, 'text/cache-manifest'); |
| 138 } else if (path.endsWith('.css')) { |
| 139 response.headers.set(CONTENT_TYPE, 'text/css'); |
| 140 } else if (path.endsWith('.png')) { |
| 141 response.headers.set(CONTENT_TYPE, 'image/png'); |
133 } | 142 } |
134 } | 143 } |
135 | 144 |
136 Future handleNonExistingFile(Uri uri) async { | 145 Future handleNonExistingFile(Uri uri) async { |
137 String path = uri.path; | 146 String path = uri.path; |
138 String generated = await generatedFiles[request.uri]; | 147 String generated = await generatedFiles[request.uri]; |
139 if (generated != null) { | 148 if (generated != null) { |
140 print("Serving ${request.uri} from memory."); | 149 print("Serving ${request.uri} from memory."); |
141 setContentType(path); | 150 setContentType(path); |
142 response.write(generated); | 151 response.write(generated); |
143 return await response.close(); | 152 return await response.close(); |
144 } | 153 } |
145 if (path.endsWith('.dart.js')) { | 154 if (path.endsWith('.dart.js')) { |
146 Uri dartScript = uri.resolve(path.substring(0, path.length - 3)); | 155 Uri dartScript = uri.resolve(path.substring(0, path.length - 3)); |
147 if (await new File.fromUri(dartScript).exists()) { | 156 if (await new File.fromUri(dartScript).exists()) { |
148 return await compileToJavaScript(dartScript); | 157 return await compileToJavaScript(dartScript); |
149 } | 158 } |
150 } | 159 } |
151 return await notFound(request.uri); | 160 return await notFound(request.uri); |
152 } | 161 } |
153 | 162 |
154 compileToJavaScript(Uri dartScript) { | 163 compileToJavaScript(Uri dartScript) { |
155 Uri outputUri = request.uri; | 164 Uri outputUri = request.uri; |
156 Completer<String> completer = new Completer<String>(); | 165 Completer<String> completer = new Completer<String>(); |
157 generatedFiles[outputUri] = completer.future; | 166 generatedFiles[outputUri] = completer.future; |
158 print("Compiling $dartScript to $outputUri"); | 167 StreamController controller = updateControllers[outputUri]; |
| 168 if (controller != null) { |
| 169 controller.close(); |
| 170 } |
| 171 updateControllers[outputUri] = new StreamController<String>.broadcast(); |
| 172 print("Compiling $dartScript to $outputUri."); |
159 StreamSubscription<CompilerEvent> subscription; | 173 StreamSubscription<CompilerEvent> subscription; |
160 subscription = compile(dartScript).listen((CompilerEvent event) { | 174 subscription = compile(dartScript).listen((CompilerEvent event) { |
161 subscription.onData( | 175 subscription.onData( |
162 (CompilerEvent event) => onCompilerEvent(completer, event)); | 176 (CompilerEvent event) => onCompilerEvent(completer, event)); |
163 if (event.kind != IncrementalKind.FULL) { | 177 if (event.kind != IncrementalKind.FULL) { |
164 notFound(request.uri); | 178 notFound(request.uri); |
165 // TODO(ahe): Do something about this situation. | 179 // TODO(ahe): Do something about this situation. |
166 } else { | 180 } else { |
| 181 print("Done compiling $dartScript to $outputUri."); |
167 completer.complete(event['.js']); | 182 completer.complete(event['.js']); |
168 setContentType(outputUri.path); | 183 setContentType(outputUri.path); |
169 response.write(event['.js']); | 184 response.write(event['.js']); |
170 response.close(); | 185 response.close(); |
171 } | 186 } |
172 }); | 187 }); |
173 } | 188 } |
174 | 189 |
175 onCompilerEvent(Completer completer, CompilerEvent event) { | 190 onCompilerEvent(Completer completer, CompilerEvent event) { |
176 Uri outputUri = request.uri; | 191 Uri outputUri = request.uri; |
177 print("Got ${event.kind} for $outputUri"); | 192 print("Got ${event.kind} for $outputUri"); |
178 | 193 |
179 switch (event.kind) { | 194 switch (event.kind) { |
180 case IncrementalKind.FULL: | 195 case IncrementalKind.FULL: |
181 generatedFiles[outputUri] = new Future.value(event['.js']); | 196 generatedFiles[outputUri] = new Future.value(event['.js']); |
182 break; | 197 break; |
183 | 198 |
184 case IncrementalKind.INCREMENTAL: | 199 case IncrementalKind.INCREMENTAL: |
185 generatedFiles[outputUri] = completer.future.then( | 200 generatedFiles[outputUri] = completer.future.then( |
186 (String full) => '$full\n\n${event.compiler.allUpdates()}'); | 201 (String full) => '$full\n\n${event.compiler.allUpdates()}'); |
| 202 pushUpdates(event.updates); |
187 break; | 203 break; |
188 | 204 |
189 case IncrementalKind.ERROR: | 205 case IncrementalKind.ERROR: |
190 generatedFiles.removeKey(outputUri); | 206 generatedFiles.removeKey(outputUri); |
191 break; | 207 break; |
192 } | 208 } |
193 } | 209 } |
194 | 210 |
| 211 void pushUpdates(String updates) { |
| 212 if (updates == null) return; |
| 213 StreamController<String> controller = updateControllers[request.uri]; |
| 214 if (controller == null) return; |
| 215 print("Adding updates to controller"); |
| 216 controller.add(updates); |
| 217 } |
| 218 |
195 Future dispatch() async { | 219 Future dispatch() async { |
196 try { | 220 try { |
197 return await WebSocketTransformer.isUpgradeRequest(request) | 221 return await WebSocketTransformer.isUpgradeRequest(request) |
198 ? handleSocket() | 222 ? handleSocket() |
199 : handle(); | 223 : handle(); |
200 } catch (e, s) { | 224 } catch (e, s) { |
201 onError(e, s); | 225 onError(e, s); |
202 } | 226 } |
203 } | 227 } |
204 | 228 |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
268 int port = options.port; | 292 int port = options.port; |
269 try { | 293 try { |
270 HttpServer server = await HttpServer.bind(host, port); | 294 HttpServer server = await HttpServer.bind(host, port); |
271 print('HTTP server started on http://$host:${server.port}/'); | 295 print('HTTP server started on http://$host:${server.port}/'); |
272 server.listen(Conversation.onRequest, onError: Conversation.onStaticError); | 296 server.listen(Conversation.onRequest, onError: Conversation.onStaticError); |
273 } catch (e) { | 297 } catch (e) { |
274 print("HttpServer.bind error: $e"); | 298 print("HttpServer.bind error: $e"); |
275 exit(1); | 299 exit(1); |
276 }; | 300 }; |
277 } | 301 } |
OLD | NEW |