a6ad152315
Change-Id: I6e4186d2a877275db2f04881a6aa6d154d4f8693 Reviewed-on: https://dart-review.googlesource.com/55820 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
239 lines
6.9 KiB
Dart
239 lines
6.9 KiB
Dart
// 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.loopbackIPv4, 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 groupId = parameterMap['group'];
|
|
String startIndex = parameterMap['start'];
|
|
LogPage page = new LogPage(log);
|
|
page.selectedGroup = EntryGroup.withId(groupId ?? 'nonTask');
|
|
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);
|
|
}
|
|
}
|