| // 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:gcloud/service_scope.dart' as ss; | |
| import 'src/appengine_internal.dart' as appengine_internal; | |
| import 'src/client_context.dart'; | |
| export 'package:gcloud/http.dart'; | |
| export 'src/appengine_context.dart'; | |
| export 'src/client_context.dart'; | |
| export 'src/errors.dart'; | |
| export 'src/logging.dart'; | |
| const Symbol _APPENGINE_CONTEXT = #appengine.context; | |
| /// Starts serving requests coming to this AppEngine application. | |
| /// | |
| /// This function will start an HTTP server and will forward new HTTP requests | |
| /// to [handler]. | |
| /// | |
| /// The [handler] will be executed inside a new request handler zone for every | |
| /// new request. This will isolate different requests from each other. | |
| /// Each [handler] has access to a [ClientContext] using the [context] getter | |
| /// in this library. It can be used to access appengine services, e.g. | |
| /// datastore. | |
| /// | |
| /// In case an uncaught error occurs inside the request handler, the request | |
| /// will be closed with an "500 Internal Server Error", if possible, and the | |
| /// given [onError] handler will be called. | |
| /// | |
| /// The [onError] function can take either the error object, or the error object | |
| /// and a stack as an argument. If [onError] was not provided, errors will get | |
| /// printed out to the stdout of this process. | |
| /// | |
| /// You can provide a [port] if you want to run the HTTP server on a different | |
| /// port than the `8080` default. | |
| /// | |
| /// The optional [shared] argument specifies whether additional AppEngine | |
| /// servers can bind to the same `port`. If `shared` is `true` and more | |
| /// AppEngine servers from this isolate or other isolates are bound to the | |
| /// port, then the incoming connections will be distributed among all the bound | |
| /// servers. Connections can be distributed over multiple isolates this way. | |
| /// | |
| /// The optional [onAcceptingConnections] callback, if provided, will be | |
| /// notified when the server is accepting connections on [port]. The `address` | |
| /// and `port` arguments that are passed to the callback specify the address | |
| /// and port that the server is listening on. | |
| /// | |
| /// The returned `Future` will complete when the HTTP server has been shutdown | |
| /// and is no longer serving requests. | |
| Future runAppEngine( | |
| void Function(HttpRequest request) handler, { | |
| Function? onError, | |
| int port = 8080, | |
| bool shared = false, | |
| void Function(InternetAddress address, int port)? onAcceptingConnections, | |
| }) { | |
| void Function(Object, StackTrace)? errorHandler; | |
| if (onError != null) { | |
| if (onError is ZoneUnaryCallback) { | |
| errorHandler = (error, stack) => onError(error); | |
| } else if (onError is ZoneBinaryCallback) { | |
| errorHandler = onError; | |
| } else { | |
| throw ArgumentError( | |
| 'The [onError] argument must take either one or two arguments.'); | |
| } | |
| } | |
| return appengine_internal.runAppEngine( | |
| (HttpRequest request, ClientContext context) { | |
| ss.register(_APPENGINE_CONTEXT, context); | |
| handler(request); | |
| }, errorHandler, | |
| port: port, | |
| shared: shared, | |
| onAcceptingConnections: onAcceptingConnections); | |
| } | |
| /// Returns `true`, if the incoming request is an AppEngine cron job request. | |
| /// | |
| /// To schedule cron jobs for your application, you must create a `cron.yaml` | |
| /// file in the project root alongside your `app.yaml`. The following is an | |
| /// example of such a `cron.yaml` file: | |
| /// ```yaml | |
| /// cron: | |
| /// - description: 'daily database cleanup job' | |
| /// url: '/tasks/database-cleanup' | |
| /// schedule: 'every 24 hours' | |
| /// target: 'default' | |
| /// ``` | |
| /// | |
| /// When the cronjob from the example above is triggered AppEngine will send | |
| /// an HTTP request to the `default` service for the resource | |
| /// `/tasks/database-cleanup`. In practice anyone can instigate such a request, | |
| /// but AppEngine will send the request from `'10.0.0.1'` and include a special | |
| /// header. This function will validate the origin of the [request] to | |
| /// ensure that it does indeed originate from | |
| /// [AppEngine's cron job scheduler][1]. | |
| /// | |
| /// [1]: https://cloud.google.com/appengine/docs/flexible/python/scheduling-jobs-with-cron-yaml#validating_cron_requests | |
| bool isCronJobRequest(HttpRequest request) { | |
| return request.headers['X-Appengine-Cron']?.contains('true') == true; | |
| } | |
| /// Runs [callback] inside a new service scope with appengine services added. | |
| /// | |
| /// The services available to `callback` are all non-request specific appengine | |
| /// services e.g. `dbService`. | |
| /// | |
| /// See `package:gcloud/service_scope.dart` for more information on service | |
| /// scopes. | |
| /// | |
| /// Here is an example on how this can be used: | |
| /// | |
| /// import 'dart:async'; | |
| /// import 'dart:io'; | |
| /// import 'package:appengine/appengine.dart'; | |
| /// | |
| /// Future backgroundWork() { | |
| /// return dbService.query(Person).run().toList().then((persons) { | |
| /// // Do something with `persons`. | |
| /// }); | |
| /// } | |
| /// | |
| /// void mainHandler(HttpRequest request) { | |
| /// dbService.query(Greeting).run().toList().then((greetings) { | |
| /// request.response | |
| /// ..write('Number of greetings: ${greetings.length}') | |
| /// ..close(); | |
| /// }); | |
| /// } | |
| /// | |
| /// main() { | |
| /// withAppEngineServices(() { | |
| /// return Future.wait([ | |
| /// runAppEngine(mainHandler), | |
| /// backgroundWork(), | |
| /// ]); | |
| /// }); | |
| /// } | |
| Future withAppEngineServices(Future Function() callback) { | |
| return appengine_internal.withAppEngineServices(callback); | |
| } | |
| /// Returns the [ClientContext] of the current request. | |
| /// | |
| /// This getter can only be called inside a request handler which was passed to | |
| /// [runAppEngine]. | |
| ClientContext get context => ss.lookup(_APPENGINE_CONTEXT) as ClientContext; | |
| /// Will register for log events produced by `package:logging` and forwards | |
| /// log records to the AppEngine logging service. | |
| /// | |
| /// Errors and exceptions logged with a stacktrace and severity `severe` or | |
| /// higher will also be reported to [Stackdriver Error Reporting][1] when | |
| /// running on AppEngine. | |
| /// | |
| /// [1]: https://cloud.google.com/error-reporting/ | |
| void useLoggingPackageAdaptor() { | |
| appengine_internal.useLoggingPackageAdaptor(); | |
| } |