| // Copyright (c) 2021, 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 '../templates.dart'; | |
| import 'common.dart' as common; | |
| /// A generator for a server app using `package:shelf`. | |
| class ServerShelfGenerator extends DefaultGenerator { | |
| ServerShelfGenerator() | |
| : super( | |
| 'server-shelf', | |
| 'Server app', | |
| 'A server app using package:shelf.', | |
| categories: const ['dart', 'server'], | |
| ) { | |
| addFile('.gitignore', common.gitignore); | |
| addFile('analysis_options.yaml', common.analysisOptions); | |
| addFile('CHANGELOG.md', common.changelog); | |
| addFile('pubspec.yaml', _pubspec); | |
| addFile('README.md', _readme); | |
| addFile('Dockerfile', _dockerfile); | |
| addFile('.dockerignore', _dockerignore); | |
| addFile('test/server_test.dart', _test); | |
| setEntrypoint(addFile('bin/server.dart', _main)); | |
| } | |
| @override | |
| String getInstallInstructions(String directory, {String? scriptPath}) => | |
| super.getInstallInstructions(directory, scriptPath: 'bin/server'); | |
| } | |
| final String _pubspec = | |
| ''' | |
| name: __projectName__ | |
| description: A server app using the shelf package and Docker. | |
| version: 1.0.0 | |
| # repository: https://github.com/my_org/my_repo | |
| environment: | |
| ${common.sdkConstraint} | |
| dependencies: | |
| shelf: ^1.4.2 | |
| shelf_router: ^1.1.2 | |
| dev_dependencies: | |
| http: ^1.2.2 | |
| lints: ^6.0.0 | |
| test: ^1.25.6 | |
| '''; | |
| final String _readme = ''' | |
| A server app built using [Shelf](https://pub.dev/packages/shelf), | |
| configured to enable running with [Docker](https://www.docker.com/). | |
| This sample code handles HTTP GET requests to `/` and `/echo/<message>` | |
| # Running the sample | |
| ## Running with the Dart SDK | |
| You can run the example with the [Dart SDK](https://dart.dev/get-dart) | |
| like this: | |
| ``` | |
| \$ dart run bin/server.dart | |
| Server listening on port 8080 | |
| ``` | |
| And then from a second terminal: | |
| ``` | |
| \$ curl http://0.0.0.0:8080 | |
| Hello, World! | |
| \$ curl http://0.0.0.0:8080/echo/I_love_Dart | |
| I_love_Dart | |
| ``` | |
| ## Running with Docker | |
| If you have [Docker Desktop](https://www.docker.com/get-started) installed, you | |
| can build and run with the `docker` command: | |
| ``` | |
| \$ docker build . -t myserver | |
| \$ docker run -it -p 8080:8080 myserver | |
| Server listening on port 8080 | |
| ``` | |
| And then from a second terminal: | |
| ``` | |
| \$ curl http://0.0.0.0:8080 | |
| Hello, World! | |
| \$ curl http://0.0.0.0:8080/echo/I_love_Dart | |
| I_love_Dart | |
| ``` | |
| You should see the logging printed in the first terminal: | |
| ``` | |
| 2021-05-06T15:47:04.620417 0:00:00.000158 GET [200] / | |
| 2021-05-06T15:47:08.392928 0:00:00.001216 GET [200] /echo/I_love_Dart | |
| ``` | |
| '''; | |
| final String _main = r''' | |
| import 'dart:io'; | |
| import 'package:shelf/shelf.dart'; | |
| import 'package:shelf/shelf_io.dart'; | |
| import 'package:shelf_router/shelf_router.dart'; | |
| // Configure routes. | |
| final _router = Router() | |
| ..get('/', _rootHandler) | |
| ..get('/echo/<message>', _echoHandler); | |
| Response _rootHandler(Request req) { | |
| return Response.ok('Hello, World!\n'); | |
| } | |
| Response _echoHandler(Request request) { | |
| final message = request.params['message']; | |
| return Response.ok('$message\n'); | |
| } | |
| void main(List<String> args) async { | |
| // Use any available host or container IP (usually `0.0.0.0`). | |
| final ip = InternetAddress.anyIPv4; | |
| // Configure a pipeline that logs requests. | |
| final handler = Pipeline() | |
| .addMiddleware(logRequests()) | |
| .addHandler(_router.call); | |
| // For running in containers, we respect the PORT environment variable. | |
| final port = int.parse(Platform.environment['PORT'] ?? '8080'); | |
| final server = await serve(handler, ip, port); | |
| print('Server listening on port ${server.port}'); | |
| } | |
| '''; | |
| final String _dockerfile = r''' | |
| # Use latest stable channel SDK. | |
| FROM dart:stable AS build | |
| # Resolve app dependencies. | |
| WORKDIR /app | |
| COPY pubspec.* ./ | |
| RUN dart pub get | |
| # Copy app source code (except anything in .dockerignore) and AOT compile app. | |
| COPY . . | |
| RUN dart build cli --target bin/server.dart -o output | |
| # Build minimal serving image from AOT-compiled `/server` | |
| # and the pre-built AOT-runtime in the `/runtime/` directory of the base image. | |
| FROM scratch | |
| COPY --from=build /runtime/ / | |
| COPY --from=build /app/output/bundle/ /app/ | |
| # Start server. | |
| EXPOSE 8080 | |
| CMD ["/app/bin/server"] | |
| '''; | |
| final String _dockerignore = r''' | |
| .dockerignore | |
| Dockerfile | |
| build/ | |
| .dart_tool/ | |
| .git/ | |
| .github/ | |
| .gitignore | |
| .idea/ | |
| .packages | |
| '''; | |
| final String _test = r''' | |
| import 'dart:io'; | |
| import 'package:http/http.dart'; | |
| import 'package:test/test.dart'; | |
| void main() { | |
| final port = '8080'; | |
| final host = 'http://0.0.0.0:$port'; | |
| late Process p; | |
| setUp(() async { | |
| p = await Process.start( | |
| 'dart', | |
| ['run', 'bin/server.dart'], | |
| environment: {'PORT': port}, | |
| ); | |
| // Wait for server to start and print to stdout. | |
| await p.stdout.first; | |
| }); | |
| tearDown(() => p.kill()); | |
| test('Root', () async { | |
| final response = await get(Uri.parse('$host/')); | |
| expect(response.statusCode, 200); | |
| expect(response.body, 'Hello, World!\n'); | |
| }); | |
| test('Echo', () async { | |
| final response = await get(Uri.parse('$host/echo/hello')); | |
| expect(response.statusCode, 200); | |
| expect(response.body, 'hello\n'); | |
| }); | |
| test('404', () async { | |
| final response = await get(Uri.parse('$host/foobar')); | |
| expect(response.statusCode, 404); | |
| }); | |
| } | |
| '''; |