| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 part of vmservice_io; | 5 part of vmservice_io; |
| 6 | 6 |
| 7 class WebSocketClient extends Client { |
| 8 static const int PARSE_ERROR_CODE = 4000; |
| 9 static const int BINARY_MESSAGE_ERROR_CODE = 4001; |
| 10 static const int NOT_MAP_ERROR_CODE = 4002; |
| 11 final WebSocket socket; |
| 12 |
| 13 WebSocketClient(this.socket, service) : super(service) { |
| 14 socket.listen((message) => onWebSocketMessage(message)); |
| 15 socket.done.then((_) => close()); |
| 16 } |
| 17 |
| 18 void onWebSocketMessage(message) { |
| 19 if (message is String) { |
| 20 var map; |
| 21 try { |
| 22 map = JSON.decode(message); |
| 23 } catch (e) { |
| 24 socket.close(PARSE_ERROR_CODE, 'Message parse error: $e'); |
| 25 return; |
| 26 } |
| 27 if (map is! Map) { |
| 28 socket.close(NOT_MAP_ERROR_CODE, 'Message must be a JSON map.'); |
| 29 return; |
| 30 } |
| 31 onMessage(new Message.fromMap(map)); |
| 32 } else { |
| 33 socket.close(BINARY_MESSAGE_ERROR_CODE, 'message must be a string.'); |
| 34 } |
| 35 } |
| 36 |
| 37 void post(var response) { |
| 38 try { |
| 39 socket.add(response); |
| 40 } catch (_) { |
| 41 // Error posting over WebSocket. |
| 42 } |
| 43 } |
| 44 |
| 45 Map toJson() { |
| 46 Map map = super.toJson(); |
| 47 map['type'] = 'WebSocketClient'; |
| 48 map['socket'] = '$socket'; |
| 49 return map; |
| 50 } |
| 51 } |
| 52 |
| 53 |
| 54 class HttpRequestClient extends Client { |
| 55 static ContentType jsonContentType = ContentType.parse('application/json'); |
| 56 final HttpRequest request; |
| 57 |
| 58 HttpRequestClient(this.request, service) : super(service); |
| 59 |
| 60 void post(var response) { |
| 61 request.response..headers.contentType = jsonContentType |
| 62 ..write(response) |
| 63 ..close(); |
| 64 close(); |
| 65 } |
| 66 |
| 67 Map toJson() { |
| 68 Map map = super.toJson(); |
| 69 map['type'] = 'HttpRequestClient'; |
| 70 map['request'] = '$request'; |
| 71 return map; |
| 72 } |
| 73 } |
| 74 |
| 7 class Server { | 75 class Server { |
| 76 static const WEBSOCKET_PATH = '/ws'; |
| 77 String defaultPath = '/index.html'; |
| 8 int port; | 78 int port; |
| 9 static ContentType jsonContentType = ContentType.parse('application/json'); | 79 |
| 10 final VmService service; | 80 final VMService service; |
| 11 HttpServer _server; | 81 HttpServer _server; |
| 12 | 82 |
| 13 Server(this.service, this.port); | 83 Server(this.service, this.port); |
| 14 | 84 |
| 15 void _sendResponse(HttpRequest request, String response) { | |
| 16 request.response..headers.contentType = jsonContentType | |
| 17 ..write(response) | |
| 18 ..close(); | |
| 19 } | |
| 20 | |
| 21 void _requestHandler(HttpRequest request) { | 85 void _requestHandler(HttpRequest request) { |
| 22 // Allow cross origin requests. | 86 // Allow cross origin requests. |
| 23 request.response.headers.add('Access-Control-Allow-Origin', '*'); | 87 request.response.headers.add('Access-Control-Allow-Origin', '*'); |
| 24 | 88 |
| 25 final String path = | 89 final String path = |
| 26 request.uri.path == '/' ? '/index.html' : request.uri.path; | 90 request.uri.path == '/' ? defaultPath : request.uri.path; |
| 27 | 91 |
| 28 var resource = Resource.resources[path]; | 92 var resource = Resource.resources[path]; |
| 29 if (resource != null) { | 93 if (resource != null) { |
| 30 // Serving up a static resource (e.g. .css, .html, .png). | 94 // Serving up a static resource (e.g. .css, .html, .png). |
| 31 request.response.headers.contentType = | 95 request.response.headers.contentType = |
| 32 ContentType.parse(resource.mimeType); | 96 ContentType.parse(resource.mimeType); |
| 33 request.response.add(resource.data); | 97 request.response.add(resource.data); |
| 34 request.response.close(); | 98 request.response.close(); |
| 35 return; | 99 return; |
| 36 } | 100 } |
| 37 | 101 |
| 38 var serviceRequest = new ServiceRequest(); | 102 if (path == WEBSOCKET_PATH) { |
| 39 var r = serviceRequest.parse(request.uri); | 103 WebSocketTransformer.upgrade(request).then((WebSocket webSocket) { |
| 40 if (r) { | 104 new WebSocketClient(webSocket, service); |
| 41 var f = service.runningIsolates.route(serviceRequest); | |
| 42 assert(f != null); | |
| 43 f.then((_) { | |
| 44 _sendResponse(request, serviceRequest.response); | |
| 45 }).catchError((e) { | |
| 46 // Error replying over HTTP. | |
| 47 }); | 105 }); |
| 48 return; | 106 return; |
| 49 } | 107 } |
| 50 _sendResponse(request, serviceRequest.response); | 108 |
| 109 var message = new Message.fromUri(request.uri); |
| 110 var client = new HttpRequestClient(request, service); |
| 111 message.future.then(client.post); |
| 112 // Send message for processing. |
| 113 service.route(message); |
| 51 } | 114 } |
| 52 | 115 |
| 53 Future startServer() { | 116 Future startServer() { |
| 54 return HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, port).then((s) { | 117 return HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, port).then((s) { |
| 55 // Only display message when port is automatically selected. | 118 // Only display message when port is not automatically selected. |
| 56 var display_message = (port == 0); | 119 var display_message = (port == 0); |
| 57 // Retrieve port. | 120 // Retrieve port. |
| 58 port = s.port; | 121 port = s.port; |
| 59 _server = s; | 122 _server = s; |
| 60 _server.listen(_requestHandler); | 123 _server.listen(_requestHandler); |
| 61 if (display_message) { | 124 if (display_message) { |
| 62 print('VmService listening on port $port'); | 125 print('VmService listening on port $port'); |
| 63 } | 126 } |
| 64 return s; | 127 return s; |
| 65 }); | 128 }); |
| 66 } | 129 } |
| 67 } | 130 } |
| OLD | NEW |