blob: ea620f5dff9d65c4f34cafe698993732d4a2dcfc [file] [log] [blame]
// 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.
/// A dev-time only server.
library services_dev;
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:args/args.dart';
import 'package:logging/logging.dart';
import 'package:rpc/rpc.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf;
import 'src/common.dart';
import 'src/common_server.dart';
import 'src/common_server_impl.dart';
import 'src/common_server_proto.dart';
import 'src/flutter_web.dart';
import 'src/sdk_manager.dart';
import 'src/server_cache.dart';
import 'src/shelf_cors.dart' as shelf_cors;
final Logger _logger = Logger('services');
void main(List<String> args) {
final parser = ArgParser();
parser.addOption('port', abbr: 'p', defaultsTo: '8080');
parser.addOption('server-url', defaultsTo: 'http://localhost');
final result = parser.parse(args);
final port = int.tryParse(result['port'] as String);
if (port == null) {
'Could not parse port value "${result['port']}" into a number.');
final sdk = sdkPath;
void printExit(String doc) {
if (result['discovery'] as bool) {
final serverUrl = result['server-url'] as String;
EndpointsServer.generateDiscovery(SdkManager.flutterSdk, serverUrl)
Logger.root.level = Level.FINER;
Logger.root.onRecord.listen((LogRecord record) {
if (record.stackTrace != null) print(record.stackTrace);
EndpointsServer.serve(sdk, port).then((EndpointsServer server) {'Listening on port ${server.port}');
class EndpointsServer {
static Future<EndpointsServer> serve(String sdkPath, int port) {
final endpointsServer = EndpointsServer._(sdkPath, port);
return shelf
.serve(endpointsServer.handler, InternetAddress.anyIPv4, port)
.then((HttpServer server) {
endpointsServer.server = server;
return endpointsServer;
static Future<String> generateDiscovery(
FlutterSdk flutterSdk, String serverUrl) async {
final flutterWebManager = FlutterWebManager(flutterSdk);
final commonServerImpl = CommonServerImpl(
final commonServer = CommonServer(commonServerImpl);
await commonServerImpl.init();
final apiServer = ApiServer(apiPrefix: '/api', prettyPrint: true)
final uri = Uri.parse('/api/discovery/v1/apis/dartservices/v1/rest');
final request = HttpApiRequest('GET', uri, <String, dynamic>{},
final response = await apiServer.handleHttpApiRequest(request);
return utf8.decode(await response.body.first);
final int port;
HttpServer server;
Pipeline pipeline;
Handler handler;
ApiServer apiServer;
bool discoveryEnabled;
CommonServer commonServer;
CommonServerProto commonServerProto;
FlutterWebManager flutterWebManager;
EndpointsServer._(String sdkPath, this.port) {
discoveryEnabled = false;
flutterWebManager = FlutterWebManager(SdkManager.flutterSdk);
final commonServerImpl = CommonServerImpl(
commonServer = CommonServer(commonServerImpl);
commonServerProto = CommonServerProto(commonServerImpl);
apiServer = ApiServer(apiPrefix: '/api', prettyPrint: true)
pipeline = Pipeline()
handler = pipeline.addHandler((request) {
if (request.requestedUri.path.startsWith(PROTO_API_URL_PREFIX)) {
return commonServerProto.router.handler(request);
return _apiHandler(request);
Future<Response> _apiHandler(Request request) {
if (!discoveryEnabled) {
discoveryEnabled = true;
// NOTE: We could read in the request body here and parse it similar to the
// _parseRequest method to determine content-type and dispatch to e.g. a
// plain text handler if we want to support that.
final apiRequest = HttpApiRequest(
request.method, request.requestedUri, request.headers,;
// Promote text/plain requests to application/json.
if (apiRequest.headers['content-type'] == 'text/plain; charset=utf-8') {
apiRequest.headers['content-type'] = 'application/json; charset=utf-8';
return apiServer
.then((HttpApiResponse apiResponse) {
// TODO(jcollins-g): use sendApiResponse helper?
return Response(apiResponse.status,
body: apiResponse.body,
headers: Map<String, String>.from(apiResponse.headers));
Response printUsage(Request request, dynamic e, StackTrace stackTrace) {
return Response.ok('''
Dart Services server
View the available API calls at /api/discovery/v1/apis/dartservices/v1/rest.
Error: $e
Stack Trace: ${stackTrace.toString()}
Middleware _createCustomCorsHeadersMiddleware() {
return shelf_cors.createCorsHeadersMiddleware(corsHeaders: <String, String>{
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Origin, X-Requested-With, Content-Type, Accept, x-goog-api-client'
class _ServerContainer implements ServerContainer {
String get version => '1.0';
class _Cache implements ServerCache {
Future<String> get(String key) => Future<String>.value(null);
Future<void> set(String key, String value, {Duration expiration}) =>
Future<void> remove(String key) => Future<void>.value();
Future<void> shutdown() => Future<void>.value();