Index: pkg/analysis_server/tool/instrumentation/server.dart |
diff --git a/pkg/analysis_server/tool/instrumentation/server.dart b/pkg/analysis_server/tool/instrumentation/server.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..324244c9d3bb0d67bc1c189d89f7c16aef360578 |
--- /dev/null |
+++ b/pkg/analysis_server/tool/instrumentation/server.dart |
@@ -0,0 +1,236 @@ |
+// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+import 'dart:async'; |
+import 'dart:collection'; |
+import 'dart:io'; |
+ |
+import 'log/log.dart'; |
+import 'page/log_page.dart'; |
+import 'page/stats_page.dart'; |
+import 'page/task_page.dart'; |
+ |
+/** |
+ * An exception that is thrown when a request is received that cannot be |
+ * handled. |
+ */ |
+class UnknownRequest implements Exception {} |
+ |
+/** |
+ * A simple web server. |
+ */ |
+class WebServer { |
+ /** |
+ * The path to the page containing a single page from the instrumentation log. |
+ */ |
+ static final String logPath = '/log'; |
+ |
+ /** |
+ * The path to the page containing statistics about the instrumentation log. |
+ */ |
+ static final String statsPath = '/stats'; |
+ |
+ /** |
+ * The path to the page containing statistics about the instrumentation log. |
+ */ |
+ static final String taskPath = '/task'; |
+ |
+ /** |
+ * The content type for HTML responses. |
+ */ |
+ static final ContentType _htmlContent = |
+ new ContentType("text", "html", charset: "utf-8"); |
+ |
+ /** |
+ * The instrumentation log being served up. |
+ */ |
+ final InstrumentationLog log; |
+ |
+ /** |
+ * Future that is completed with the HTTP server once it is running. |
+ */ |
+ Future<HttpServer> _server; |
+ |
+ /** |
+ * Initialize a newly created server. |
+ */ |
+ WebServer(this.log); |
+ |
+ Map<String, String> getParameterMap(HttpRequest request) { |
+ Map<String, String> parameterMap = new HashMap<String, String>(); |
+ String query = request.uri.query; |
+ if (query != null && query.isNotEmpty) { |
+ List<String> pairs = query.split('&'); |
+ for (String pair in pairs) { |
+ List<String> parts = pair.split('='); |
+ String value = parts[1].trim(); |
+ value = value.replaceAll('+', ' '); |
+ parameterMap[parts[0].trim()] = value; |
+ } |
+ } |
+ return parameterMap; |
+ } |
+ |
+ /** |
+ * Return a table mapping the names of properties to the values of those |
+ * properties that is extracted from the given HTTP [request]. |
+ */ |
+ Future<Map<String, String>> getValueMap(HttpRequest request) async { |
+ StringBuffer buffer = new StringBuffer(); |
+ await request.forEach((List<int> element) { |
+ for (int code in element) { |
+ buffer.writeCharCode(code); |
+ } |
+ }); |
+ Map<String, String> valueMap = new HashMap<String, String>(); |
+ String parameters = buffer.toString(); |
+ if (parameters.isNotEmpty) { |
+ List<String> pairs = parameters.split('&'); |
+ for (String pair in pairs) { |
+ List<String> parts = pair.split('='); |
+ String value = parts[1].trim(); |
+ value = value.replaceAll('+', ' '); |
+ valueMap[parts[0].trim()] = value; |
+ } |
+ } |
+ return valueMap; |
+ } |
+ |
+ /** |
+ * Begin serving HTTP requests over the given [port]. |
+ */ |
+ void serveHttp(int port) { |
+ _server = HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, port); |
+ _server.then(_handleServer).catchError((_) {/* Ignore errors. */}); |
+ } |
+ |
+ /** |
+ * Handle a GET [request] received by the HTTP server. |
+ */ |
+ void _handleGetRequest(HttpRequest request) { |
+ StringBuffer buffer = new StringBuffer(); |
+ try { |
+ String path = request.uri.path; |
+ if (path == logPath) { |
+ _writeLogPage(request, buffer); |
+ } else if (path == statsPath) { |
+ _writeStatsPage(request, buffer); |
+ } else if (path == taskPath) { |
+ _writeTaskPage(request, buffer); |
+ } else { |
+ _returnUnknownRequest(request); |
+ return; |
+ } |
+ } on UnknownRequest { |
+ _returnUnknownRequest(request); |
+ return; |
+ } catch (exception, stackTrace) { |
+ HttpResponse response = request.response; |
+ response.statusCode = HttpStatus.OK; |
+ response.headers.contentType = _htmlContent; |
+ StringBuffer buffer = new StringBuffer(); |
+ buffer.write('<p><b>Exception while composing page:</b></p>'); |
+ buffer.write('<p>$exception</p>'); |
+ buffer.write('<p>'); |
+ _writeStackTrace(buffer, stackTrace); |
+ buffer.write('</p>'); |
+ response.write(buffer.toString()); |
+ response.close(); |
+ return; |
+ } |
+ |
+ HttpResponse response = request.response; |
+ response.statusCode = HttpStatus.OK; |
+ response.headers.contentType = _htmlContent; |
+ response.write(buffer.toString()); |
+ response.close(); |
+ } |
+ |
+ /** |
+ * Handle a POST [request] received by the HTTP server. |
+ */ |
+ Future<Null> _handlePostRequest(HttpRequest request) async { |
+ _returnUnknownRequest(request); |
+ } |
+ |
+ /** |
+ * Attach a listener to a newly created HTTP server. |
+ */ |
+ void _handleServer(HttpServer httpServer) { |
+ httpServer.listen((HttpRequest request) { |
+ String method = request.method; |
+ if (method == 'GET') { |
+ _handleGetRequest(request); |
+ } else if (method == 'POST') { |
+ _handlePostRequest(request); |
+ } else { |
+ _returnUnknownRequest(request); |
+ } |
+ }); |
+ } |
+ |
+ /** |
+ * Return an error in response to an unrecognized request received by the HTTP |
+ * server. |
+ */ |
+ void _returnUnknownRequest(HttpRequest request) { |
+ HttpResponse response = request.response; |
+ response.statusCode = HttpStatus.NOT_FOUND; |
+ response.headers.contentType = |
+ new ContentType("text", "html", charset: "utf-8"); |
+ response.write( |
+ '<html><head></head><body><h3>Page not found: "${request.uri.path}".</h3></body></html>'); |
+ response.close(); |
+ } |
+ |
+ void _writeLogPage(HttpRequest request, StringBuffer buffer) { |
+ Map<String, String> parameterMap = getParameterMap(request); |
+ String startIndex = parameterMap['start']; |
+ LogPage page = new LogPage(log); |
+ if (startIndex != null) { |
+ page.pageStart = int.parse(startIndex); |
+ } else { |
+ page.pageStart = 0; |
+ } |
+ page.pageLength = 25; |
+ page.writePage(buffer); |
+ } |
+ |
+ /** |
+ * Write a representation of the given [stackTrace] to the given [sink]. |
+ */ |
+ void _writeStackTrace(StringSink sink, StackTrace stackTrace) { |
+ if (stackTrace != null) { |
+ String trace = stackTrace.toString().replaceAll('#', '<br>#'); |
+ if (trace.startsWith('<br>#')) { |
+ trace = trace.substring(4); |
+ } |
+ sink.write('<p>'); |
+ sink.write(trace); |
+ sink.write('</p>'); |
+ } |
+ } |
+ |
+ void _writeStatsPage(HttpRequest request, StringBuffer buffer) { |
+ new StatsPage(log).writePage(buffer); |
+ } |
+ |
+ void _writeTaskPage(HttpRequest request, StringBuffer buffer) { |
+ Map<String, String> parameterMap = getParameterMap(request); |
+ String analysisStart = parameterMap['analysisStart']; |
+ String start = parameterMap['start']; |
+ TaskPage page = new TaskPage(log); |
+ if (analysisStart == null) { |
+ throw new UnknownRequest(); |
+ } |
+ page.analysisStart = int.parse(analysisStart); |
+ if (start != null) { |
+ page.pageStart = int.parse(start); |
+ } else { |
+ page.pageStart = 0; |
+ } |
+ page.pageLength = 25; |
+ page.writePage(buffer); |
+ } |
+} |