| // 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, |
| 'bin/server', |
| ); |
| } |
| |
| final String _pubspec = ''' |
| name: __projectName__ |
| description: A server app using the shelf package and Docker. |
| version: 1.0.0 |
| # homepage: https://www.example.com |
| |
| environment: |
| ${common.sdkConstraint} |
| |
| dependencies: |
| args: ^2.0.0 |
| shelf: ^1.1.0 |
| shelf_router: ^1.0.0 |
| |
| dev_dependencies: |
| http: ^0.13.0 |
| lints: ^1.0.0 |
| test_process: ^2.0.0 |
| test: ^1.15.0 |
| '''; |
| |
| 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 = params(request, '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); |
| |
| // 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 compile exe bin/server.dart -o bin/server |
| |
| # 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/bin/server /app/bin/ |
| |
| # 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 'package:http/http.dart'; |
| import 'package:test/test.dart'; |
| import 'package:test_process/test_process.dart'; |
| |
| void main() { |
| final port = '8080'; |
| final host = 'http://0.0.0.0:$port'; |
| |
| setUp(() async { |
| await TestProcess.start( |
| 'dart', |
| ['run', 'bin/server.dart'], |
| environment: {'PORT': port}, |
| ); |
| }); |
| |
| 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); |
| }); |
| } |
| '''; |