| // Copyright (c) 2014, 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:io'; |
| |
| import 'package:analysis_server/src/socket_server.dart'; |
| import 'package:analysis_server/src/status/diagnostics.dart'; |
| |
| /** |
| * Instances of the class [AbstractGetHandler] handle GET requests. |
| */ |
| abstract class AbstractGetHandler { |
| /** |
| * Handle a GET request received by the HTTP server. |
| */ |
| void handleGetRequest(HttpRequest request); |
| } |
| |
| /** |
| * An [AbstractGetHandler] that always returns the given error message. |
| */ |
| class ErrorGetHandler extends AbstractGetHandler { |
| final String message; |
| |
| ErrorGetHandler(this.message); |
| |
| @override |
| void handleGetRequest(HttpRequest request) { |
| HttpResponse response = request.response; |
| response.statusCode = HttpStatus.NOT_FOUND; |
| response.headers.contentType = ContentType.TEXT; |
| response.write(message); |
| response.close(); |
| } |
| } |
| |
| /** |
| * Instances of the class [HttpServer] implement a simple HTTP server. The |
| * server: |
| * |
| * - listens for an UPGRADE request in order to start an analysis server |
| * - serves diagnostic information as html pages |
| */ |
| class HttpAnalysisServer { |
| /** |
| * Number of lines of print output to capture. |
| */ |
| static const int MAX_PRINT_BUFFER_LENGTH = 1000; |
| |
| /** |
| * An object that can handle either a WebSocket connection or a connection |
| * to the client over stdio. |
| */ |
| SocketServer socketServer; |
| |
| /** |
| * An object that can handle GET requests. |
| */ |
| AbstractGetHandler getHandler; |
| |
| /** |
| * Future that is completed with the HTTP server once it is running. |
| */ |
| Future<HttpServer> _serverFuture; |
| |
| /** |
| * Last PRINT_BUFFER_LENGTH lines printed. |
| */ |
| List<String> _printBuffer = <String>[]; |
| |
| /** |
| * Initialize a newly created HTTP server. |
| */ |
| HttpAnalysisServer(this.socketServer); |
| |
| /** |
| * Return the port this server is bound to. |
| */ |
| Future<int> get boundPort async => (await _serverFuture)?.port; |
| |
| void close() { |
| _serverFuture.then((HttpServer server) { |
| server.close(); |
| }); |
| } |
| |
| /** |
| * Record that the given line was printed out by the analysis server. |
| */ |
| void recordPrint(String line) { |
| _printBuffer.add(line); |
| if (_printBuffer.length > MAX_PRINT_BUFFER_LENGTH) { |
| _printBuffer.removeRange( |
| 0, _printBuffer.length - MAX_PRINT_BUFFER_LENGTH); |
| } |
| } |
| |
| /** |
| * Begin serving HTTP requests over the given port. |
| */ |
| Future<int> serveHttp([int initialPort]) async { |
| if (_serverFuture != null) { |
| return boundPort; |
| } |
| |
| try { |
| _serverFuture = |
| HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, initialPort ?? 0); |
| |
| HttpServer server = await _serverFuture; |
| _handleServer(server); |
| return server.port; |
| } catch (ignore) { |
| // If we can't bind to the specified port, don't remember the broken |
| // server. |
| _serverFuture = null; |
| |
| return null; |
| } |
| } |
| |
| /** |
| * Handle a GET request received by the HTTP server. |
| */ |
| Future<Null> _handleGetRequest(HttpRequest request) async { |
| if (getHandler == null) { |
| getHandler = new DiagnosticsSite(socketServer, _printBuffer); |
| } |
| await getHandler.handleGetRequest(request); |
| } |
| |
| /** |
| * Attach a listener to a newly created HTTP server. |
| */ |
| void _handleServer(HttpServer httpServer) { |
| httpServer.listen((HttpRequest request) async { |
| List<String> updateValues = request.headers[HttpHeaders.UPGRADE]; |
| if (request.method == 'GET') { |
| await _handleGetRequest(request); |
| } else if (updateValues != null && |
| updateValues.indexOf('websocket') >= 0) { |
| // We no longer support serving analysis server communications over |
| // WebSocket connections. |
| HttpResponse response = request.response; |
| response.statusCode = HttpStatus.NOT_FOUND; |
| response.headers.contentType = ContentType.TEXT; |
| response.write( |
| 'WebSocket connections not supported (${request.uri.path}).'); |
| response.close(); |
| } 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 = ContentType.TEXT; |
| response.write('Not found'); |
| response.close(); |
| } |
| } |