| 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 |