| // 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. |
| |
| library http.server; |
| |
| import 'dart:io'; |
| |
| import 'package:args/args.dart'; |
| |
| import 'src/analysis_server.dart'; |
| import 'src/channel.dart'; |
| import 'src/domain_context.dart'; |
| import 'src/domain_server.dart'; |
| import 'src/get_handler.dart'; |
| |
| /** |
| * Instances of the class [HttpServer] implement a simple HTTP server. The |
| * primary responsibility of this server is to listen for an UPGRADE request and |
| * to start an analysis server |
| */ |
| class HttpAnalysisServer { |
| /** |
| * The name of the application that is used to start a server. |
| */ |
| static const BINARY_NAME = 'server'; |
| |
| /** |
| * The name of the option used to print usage information. |
| */ |
| static const String HELP_OPTION = "help"; |
| |
| /** |
| * The name of the option used to specify the port to which the server will |
| * connect. |
| */ |
| static const String PORT_OPTION = "port"; |
| |
| /** |
| * The analysis server that was created when an UPGRADE request was received, |
| * or `null` if no such request has yet been received. |
| */ |
| AnalysisServer server; |
| |
| /** |
| * An object that can handle GET requests. |
| */ |
| GetHandler getHandler; |
| |
| /** |
| * Initialize a newly created HTTP server. |
| */ |
| HttpAnalysisServer(); |
| |
| /** |
| * Use the given command-line arguments to start this server. |
| */ |
| void start(List<String> args) { |
| ArgParser parser = new ArgParser(); |
| parser.addFlag( |
| HELP_OPTION, |
| help: "print this help message without starting a server", |
| defaultsTo: false, |
| negatable: false); |
| parser.addOption( |
| PORT_OPTION, |
| help: "[port] the port on which the server will listen"); |
| |
| ArgResults results = parser.parse(args); |
| if (results[HELP_OPTION]) { |
| _printUsage(parser); |
| return; |
| } |
| if (results[PORT_OPTION] == null) { |
| print('Missing required port number'); |
| print(''); |
| _printUsage(parser); |
| return; |
| } |
| |
| try { |
| int port = int.parse(results[PORT_OPTION]); |
| HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, port).then(_handleServer); |
| print('Listening on port $port'); |
| } on FormatException { |
| print('Invalid port number: ${results[PORT_OPTION]}'); |
| print(''); |
| _printUsage(parser); |
| return; |
| } |
| } |
| |
| /** |
| * Attach a listener to a newly created HTTP server. |
| */ |
| void _handleServer(HttpServer server) { |
| server.listen((HttpRequest request) { |
| List<String> updateValues = request.headers[HttpHeaders.UPGRADE]; |
| if (updateValues != null && updateValues.indexOf('websocket') >= 0) { |
| if (server != null) { |
| _returnServerAlreadyStarted(request); |
| return; |
| } |
| WebSocketTransformer.upgrade(request).then((WebSocket websocket) { |
| _handleWebSocket(websocket); |
| }); |
| } else if (request.method == 'GET') { |
| _handleGetRequest(request); |
| } else { |
| _returnUnknownRequest(request); |
| } |
| }); |
| } |
| |
| /** |
| * Handle a GET request received by the HTTP server. |
| */ |
| void _handleGetRequest(HttpRequest request) { |
| if (getHandler == null) { |
| getHandler = new GetHandler(); |
| getHandler.server = server; |
| } |
| getHandler.handleGetRequest(request); |
| } |
| |
| /** |
| * Handle an UPGRADE request received by the HTTP server by creating and |
| * running an analysis server on a [WebSocket]-based communication channel. |
| */ |
| void _handleWebSocket(WebSocket socket) { |
| server = new AnalysisServer(new WebSocketChannel(socket)); |
| _initializeHandlers(server); |
| if (getHandler != null) { |
| getHandler.server = server; |
| } |
| server.run(); |
| } |
| |
| /** |
| * Initialize the handlers to be used by the given [server]. |
| */ |
| void _initializeHandlers(AnalysisServer server) { |
| server.handlers = [ |
| new ServerDomainHandler(server), |
| new ContextDomainHandler(server), |
| ]; |
| } |
| |
| /** |
| * Print information about how to use the server. |
| */ |
| void _printUsage(ArgParser parser) { |
| print('Usage: $BINARY_NAME [flags]'); |
| print(''); |
| print('Supported flags are:'); |
| print(parser.getUsage()); |
| } |
| |
| /** |
| * Return an error in response to an UPGRADE request received after the server |
| * has already been started by a previous UPGRADE request. |
| */ |
| void _returnServerAlreadyStarted(HttpRequest request) { |
| HttpResponse response = request.response; |
| response.statusCode = HttpStatus.SERVICE_UNAVAILABLE; |
| response.headers.add(HttpHeaders.CONTENT_TYPE, "text/plain"); |
| response.write('The server has already been started'); |
| response.close(); |
| } |
| |
| /** |
| * 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.add(HttpHeaders.CONTENT_TYPE, "text/plain"); |
| response.write('Not found'); |
| response.close(); |
| } |
| } |