| 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 { | 7 class WebSocketClient extends Client { |
| 8 static const int PARSE_ERROR_CODE = 4000; | 8 static const int PARSE_ERROR_CODE = 4000; |
| 9 static const int BINARY_MESSAGE_ERROR_CODE = 4001; | 9 static const int BINARY_MESSAGE_ERROR_CODE = 4001; |
| 10 static const int NOT_MAP_ERROR_CODE = 4002; | 10 static const int NOT_MAP_ERROR_CODE = 4002; |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 72 map['socket'] = '$socket'; | 72 map['socket'] = '$socket'; |
| 73 return map; | 73 return map; |
| 74 } | 74 } |
| 75 } | 75 } |
| 76 | 76 |
| 77 | 77 |
| 78 class HttpRequestClient extends Client { | 78 class HttpRequestClient extends Client { |
| 79 static ContentType jsonContentType = | 79 static ContentType jsonContentType = |
| 80 new ContentType("application", "json", charset: "utf-8"); | 80 new ContentType("application", "json", charset: "utf-8"); |
| 81 final HttpRequest request; | 81 final HttpRequest request; |
| 82 final List<String> _allowedOrigins; | |
| 83 | 82 |
| 84 HttpRequestClient(this.request, VMService service, this._allowedOrigins) | 83 HttpRequestClient(this.request, VMService service) |
| 85 : super(service, sendEvents:false); | 84 : super(service, sendEvents:false); |
| 86 | 85 |
| 87 disconnect() { | 86 disconnect() { |
| 88 request.response.close(); | 87 request.response.close(); |
| 89 close(); | 88 close(); |
| 90 } | 89 } |
| 91 | 90 |
| 92 void post(dynamic result) { | 91 void post(dynamic result) { |
| 93 if (result == null) { | 92 if (result == null) { |
| 94 close(); | 93 close(); |
| 95 return; | 94 return; |
| 96 } | 95 } |
| 97 HttpResponse response = request.response; | 96 HttpResponse response = request.response; |
| 97 // We closed the connection for bad origins earlier. |
| 98 response.headers.add('Access-Control-Allow-Origin', '*'); |
| 98 response.headers.contentType = jsonContentType; | 99 response.headers.contentType = jsonContentType; |
| 99 final origins = request.headers['Origin']; | |
| 100 if ((origins != null) && (origins.isNotEmpty)) { | |
| 101 final uri = Uri.parse(origins.first); | |
| 102 final noPortOrigin = new Uri(host: uri.host, scheme: uri.scheme).origin; | |
| 103 if (_allowedOrigins.contains(noPortOrigin)) { | |
| 104 response.headers.add('Access-Control-Allow-Origin', uri.origin); | |
| 105 } | |
| 106 } | |
| 107 if (result is String) { | 100 if (result is String) { |
| 108 response.write(result); | 101 response.write(result); |
| 109 } else { | 102 } else { |
| 110 assert(result is List); | 103 assert(result is List); |
| 111 Uint8List cstring = result[0]; // Already in UTF-8. | 104 Uint8List cstring = result[0]; // Already in UTF-8. |
| 112 response.add(cstring); | 105 response.add(cstring); |
| 113 } | 106 } |
| 114 response.close(); | 107 response.close(); |
| 115 close(); | 108 close(); |
| 116 } | 109 } |
| 117 | 110 |
| 118 dynamic toJson() { | 111 dynamic toJson() { |
| 119 Map map = super.toJson(); | 112 Map map = super.toJson(); |
| 120 map['type'] = 'HttpRequestClient'; | 113 map['type'] = 'HttpRequestClient'; |
| 121 map['request'] = '$request'; | 114 map['request'] = '$request'; |
| 122 return map; | 115 return map; |
| 123 } | 116 } |
| 124 } | 117 } |
| 125 | 118 |
| 126 class Server { | 119 class Server { |
| 127 static const WEBSOCKET_PATH = '/ws'; | 120 static const WEBSOCKET_PATH = '/ws'; |
| 128 static const ROOT_REDIRECT_PATH = '/index.html'; | 121 static const ROOT_REDIRECT_PATH = '/index.html'; |
| 129 | 122 |
| 130 final VMService _service; | 123 final VMService _service; |
| 131 final String _ip; | 124 final String _ip; |
| 132 final int _port; | 125 final int _port; |
| 133 final bool _originCheckDisabled; | 126 final bool _originCheckDisabled; |
| 134 final List<String> _allowedOrigins = <String>[]; | |
| 135 HttpServer _server; | 127 HttpServer _server; |
| 136 bool get running => _server != null; | 128 bool get running => _server != null; |
| 137 bool _displayMessages = false; | 129 bool _displayMessages = false; |
| 138 | 130 |
| 139 Server(this._service, this._ip, this._port, this._originCheckDisabled) { | 131 Server(this._service, this._ip, this._port, this._originCheckDisabled) { |
| 140 _displayMessages = (_ip != '127.0.0.1' || _port != 8181); | 132 _displayMessages = (_ip != '127.0.0.1' || _port != 8181); |
| 141 } | 133 } |
| 142 | 134 |
| 143 void _addOrigin(String host, String port) { | 135 bool _isAllowedOrigin(String origin) { |
| 144 if (port == null) { | 136 Uri uri; |
| 145 String origin = 'http://$host'; | 137 try { |
| 146 _allowedOrigins.add(origin); | 138 uri = Uri.parse(origin); |
| 147 } else { | 139 } catch (_) { |
| 148 String origin = 'http://$host:$port'; | 140 return false; |
| 149 _allowedOrigins.add(origin); | |
| 150 } | 141 } |
| 151 } | |
| 152 | 142 |
| 153 bool _isAllowedOrigin(String origin) { | 143 // Explicitly add localhost and 127.0.0.1 on any port (necessary for |
| 154 for (String allowedOrigin in _allowedOrigins) { | 144 // adb port forwarding). |
| 155 if (origin.startsWith(allowedOrigin)) { | 145 if ((uri.host == 'localhost') || |
| 156 return true; | 146 (uri.host == '127.0.0.1')) { |
| 157 } | 147 return true; |
| 158 } | 148 } |
| 149 |
| 150 if ((uri.port == _server.port) && |
| 151 ((uri.host == _server.address.address) || |
| 152 (uri.host == _server.address.host))) { |
| 153 return true; |
| 154 } |
| 155 |
| 159 return false; | 156 return false; |
| 160 } | 157 } |
| 161 | 158 |
| 162 bool _originCheck(HttpRequest request) { | 159 bool _originCheck(HttpRequest request) { |
| 163 if (_originCheckDisabled) { | 160 if (_originCheckDisabled) { |
| 164 // Always allow. | 161 // Always allow. |
| 165 return true; | 162 return true; |
| 166 } | 163 } |
| 167 // First check the web-socket specific origin. | 164 // First check the web-socket specific origin. |
| 168 List<String> origins = request.headers["Sec-WebSocket-Origin"]; | 165 List<String> origins = request.headers["Sec-WebSocket-Origin"]; |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 241 if (asset != null) { | 238 if (asset != null) { |
| 242 // Serving up a static asset (e.g. .css, .html, .png). | 239 // Serving up a static asset (e.g. .css, .html, .png). |
| 243 request.response.headers.contentType = | 240 request.response.headers.contentType = |
| 244 ContentType.parse(asset.mimeType); | 241 ContentType.parse(asset.mimeType); |
| 245 request.response.add(asset.data); | 242 request.response.add(asset.data); |
| 246 request.response.close(); | 243 request.response.close(); |
| 247 return; | 244 return; |
| 248 } | 245 } |
| 249 // HTTP based service request. | 246 // HTTP based service request. |
| 250 try { | 247 try { |
| 251 var client = new HttpRequestClient(request, _service, _allowedOrigins); | 248 var client = new HttpRequestClient(request, _service); |
| 252 var message = new Message.fromUri(client, request.uri); | 249 var message = new Message.fromUri(client, request.uri); |
| 253 client.onMessage(null, message); | 250 client.onMessage(null, message); |
| 254 } catch (e) { | 251 } catch (e) { |
| 255 print('Unexpected error processing HTTP request uri: ' | 252 print('Unexpected error processing HTTP request uri: ' |
| 256 '${request.uri}\n$e\n'); | 253 '${request.uri}\n$e\n'); |
| 257 rethrow; | 254 rethrow; |
| 258 } | 255 } |
| 259 } | 256 } |
| 260 | 257 |
| 261 Future startup() { | 258 Future startup() { |
| 262 if (_server != null) { | 259 if (_server != null) { |
| 263 // Already running. | 260 // Already running. |
| 264 return new Future.value(this); | 261 return new Future.value(this); |
| 265 } | 262 } |
| 266 | 263 |
| 267 // Clear allowed origins. | |
| 268 _allowedOrigins.clear(); | |
| 269 | |
| 270 var address = new InternetAddress(_ip); | 264 var address = new InternetAddress(_ip); |
| 271 // Startup HTTP server. | 265 // Startup HTTP server. |
| 272 return HttpServer.bind(address, _port).then((s) { | 266 return HttpServer.bind(address, _port).then((s) { |
| 273 _server = s; | 267 _server = s; |
| 274 _server.listen(_requestHandler, cancelOnError: true); | 268 _server.listen(_requestHandler, cancelOnError: true); |
| 275 var ip = _server.address.address.toString(); | 269 var ip = _server.address.address; |
| 276 var port = _server.port.toString(); | 270 var port = _server.port; |
| 277 // Add the numeric ip and host name to our allowed origins. | |
| 278 _addOrigin(ip, port); | |
| 279 _addOrigin(_server.address.host.toString(), port); | |
| 280 // Explicitly add localhost and 127.0.0.1 on any port (necessary for | |
| 281 // adb port forwarding). | |
| 282 _addOrigin('127.0.0.1', null); | |
| 283 _addOrigin('localhost', null); | |
| 284 if (_displayMessages) { | 271 if (_displayMessages) { |
| 285 print('Observatory listening on http://$ip:$port'); | 272 print('Observatory listening on http://$ip:$port'); |
| 286 } | 273 } |
| 287 // Server is up and running. | 274 // Server is up and running. |
| 288 _notifyServerState(ip, _server.port); | 275 _notifyServerState(ip, _server.port); |
| 289 onServerAddressChange('http://$ip:$port'); | 276 onServerAddressChange('http://$ip:$port'); |
| 290 return this; | 277 return this; |
| 291 }).catchError((e, st) { | 278 }).catchError((e, st) { |
| 292 print('Could not start Observatory HTTP server:\n$e\n$st\n'); | 279 print('Could not start Observatory HTTP server:\n$e\n$st\n'); |
| 293 _notifyServerState("", 0); | 280 _notifyServerState("", 0); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 329 _notifyServerState("", 0); | 316 _notifyServerState("", 0); |
| 330 onServerAddressChange(null); | 317 onServerAddressChange(null); |
| 331 return this; | 318 return this; |
| 332 }); | 319 }); |
| 333 } | 320 } |
| 334 | 321 |
| 335 } | 322 } |
| 336 | 323 |
| 337 void _notifyServerState(String ip, int port) | 324 void _notifyServerState(String ip, int port) |
| 338 native "VMServiceIO_NotifyServerState"; | 325 native "VMServiceIO_NotifyServerState"; |
| OLD | NEW |