blob: 17ce33206ef03902109003063ce6bd56daead5ce [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.
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();
}