diff --git a/google/cloud/functions/framework.h b/google/cloud/functions/framework.h index 438a7c8e..bc795191 100644 --- a/google/cloud/functions/framework.h +++ b/google/cloud/functions/framework.h @@ -15,6 +15,7 @@ #ifndef FUNCTIONS_FRAMEWORK_CPP_GOOGLE_CLOUD_FUNCTIONS_FRAMEWORK_H #define FUNCTIONS_FRAMEWORK_CPP_GOOGLE_CLOUD_FUNCTIONS_FRAMEWORK_H +#include "google/cloud/functions/function.h" #include "google/cloud/functions/user_functions.h" #include "google/cloud/functions/version.h" #include @@ -22,13 +23,51 @@ namespace google::cloud::functions { FUNCTIONS_FRAMEWORK_CPP_INLINE_NAMESPACE_BEGIN +/** + * Runs function wrapped by @p handler. + * + * Starts a HTTP server at the address and listening endpoint described by + * @p argv, invoking @p handler to handle any HTTP request. + * + * If @p handler wraps a function with the Cloud Event signature, then the + * incoming HTTP requests **MUST** conform to the Cloud Events [HTTP protocol + * binding specification][cloud-events-spec]. + * + * @note + * When deploying code to Google Cloud Functions applications should **not** use + * this function directly. The buildpack will automatically create a `main()` + * and invoke `Run()` with the correct parameters. We recommend that application + * developers use this function only for local development and integration + * tests. + * + * @par Example + * @code + * namespace gcf = ::google::cloud::functions; + * + * extern gcf::HttpResponse MyHandler(gcf::HttpRequest); + * + * int main(int argc, char* argv[]) { + * return gcf::Run(argc, argv, gcf::MakeFunction(MyHandler)); + * } + * @endcode + * + * @see ParseOptions for more details of the command-line arguments used by this + * function. + * + * [cloud-events-spec]: + * https://github.com/cloudevents/spec/blob/v1.0/http-protocol-binding.md + */ +int Run(int argc, char const* const argv[], Function const& handler) noexcept; + /** * Run the given function, invoking it to handle HTTP requests. * + * @deprecated Prefer using the overload consuming a `Function` object. + * * Starts a HTTP server at the address and listening endpoint described by * @p argv, invoking @p handler to handle any HTTP request. * - * When deploying code to Google Cloud Functions applications should**not** use + * When deploying code to Google Cloud Functions applications should **not** use * this function directly. The buildpack will automatically create a `main()` * and invoke `Run()` with the correct parameters. We recommend that application * developers use this function only for local development and integration @@ -53,11 +92,13 @@ int Run(int argc, char const* const argv[], UserHttpFunction handler) noexcept; /** * Run the given function, invoking it to handle Cloud Events. * + * @deprecated Prefer using the overload consuming a `Function` object. + * * Starts a HTTP server at the address and listening endpoint described by * @p argv, invoking @p handler to handle any HTTP request which *MUST* conform * to the Cloud Events [HTTP protocol binding][cloud-events-spec]. * - * When deploying code to Google Cloud Functions applications should**not** use + * When deploying code to Google Cloud Functions applications should **not** use * this function directly. The buildpack will automatically create a `main()` * and invoke `Run()` with the correct parameters. We recommend that application * developers use this function only for local development and integration diff --git a/google/cloud/functions/integration_tests/cloud_event_conformance.cc b/google/cloud/functions/integration_tests/cloud_event_conformance.cc index ee9e9472..66d04510 100644 --- a/google/cloud/functions/integration_tests/cloud_event_conformance.cc +++ b/google/cloud/functions/integration_tests/cloud_event_conformance.cc @@ -63,6 +63,6 @@ void CloudEventConformance(functions::CloudEvent const& ev) { int main(int argc, char* argv[]) { return google::cloud::functions_internal::RunForTest( - argc, argv, CloudEventConformance, [] { return shutdown_server.load(); }, - [](int /*port*/) {}); + argc, argv, functions::MakeFunction(CloudEventConformance), + [] { return shutdown_server.load(); }, [](int /*port*/) {}); } diff --git a/google/cloud/functions/integration_tests/cloud_event_handler.cc b/google/cloud/functions/integration_tests/cloud_event_handler.cc index 9d0a18ab..48370a64 100644 --- a/google/cloud/functions/integration_tests/cloud_event_handler.cc +++ b/google/cloud/functions/integration_tests/cloud_event_handler.cc @@ -38,6 +38,6 @@ void CloudEventHandler(CloudEvent const& event) { int main(int argc, char* argv[]) { return google::cloud::functions_internal::RunForTest( - argc, argv, CloudEventHandler, [] { return shutdown_server.load(); }, - [](int /*port*/) {}); + argc, argv, google::cloud::functions::MakeFunction(CloudEventHandler), + [] { return shutdown_server.load(); }, [](int /*port*/) {}); } diff --git a/google/cloud/functions/integration_tests/echo_server.cc b/google/cloud/functions/integration_tests/echo_server.cc index 09389232..e5eff03d 100644 --- a/google/cloud/functions/integration_tests/echo_server.cc +++ b/google/cloud/functions/integration_tests/echo_server.cc @@ -71,6 +71,6 @@ HttpResponse EchoServer(HttpRequest const& request) { int main(int argc, char* argv[]) { return google::cloud::functions_internal::RunForTest( - argc, argv, EchoServer, [] { return shutdown_server.load(); }, - [](int /*port*/) {}); + argc, argv, google::cloud::functions::MakeFunction(EchoServer), + [] { return shutdown_server.load(); }, [](int /*port*/) {}); } diff --git a/google/cloud/functions/integration_tests/http_conformance.cc b/google/cloud/functions/integration_tests/http_conformance.cc index 2694d53e..33e6c3bd 100644 --- a/google/cloud/functions/integration_tests/http_conformance.cc +++ b/google/cloud/functions/integration_tests/http_conformance.cc @@ -30,6 +30,6 @@ functions::HttpResponse HttpConformance(functions::HttpRequest const& request) { int main(int argc, char* argv[]) { return google::cloud::functions_internal::RunForTest( - argc, argv, HttpConformance, [] { return shutdown_server.load(); }, - [](int /*port*/) {}); + argc, argv, google::cloud::functions::MakeFunction(HttpConformance), + [] { return shutdown_server.load(); }, [](int /*port*/) {}); } diff --git a/google/cloud/functions/internal/framework_impl.cc b/google/cloud/functions/internal/framework_impl.cc index e53f5a3b..4a4a43d9 100644 --- a/google/cloud/functions/internal/framework_impl.cc +++ b/google/cloud/functions/internal/framework_impl.cc @@ -13,7 +13,7 @@ // limitations under the License. #include "google/cloud/functions/internal/framework_impl.h" -#include "google/cloud/functions/internal/call_user_function.h" +#include "google/cloud/functions/internal/function_impl.h" #include "google/cloud/functions/internal/parse_options.h" #include "google/cloud/functions/version.h" #include @@ -34,12 +34,12 @@ namespace be = boost::beast; namespace asio = boost::asio; using tcp = boost::asio::ip::tcp; -template -void HandleSession(tcp::socket socket, UserFunction const& user_function) { +void HandleSession(tcp::socket socket, Handler const& handler) { auto report_error = [](be::error_code ec, char const* what) { // TODO(#35) - maybe replace with Boost.Log std::cerr << what << ": " << ec.message() << "\n"; }; + be::error_code ec; for (;;) { be::flat_buffer buffer; @@ -49,7 +49,7 @@ void HandleSession(tcp::socket socket, UserFunction const& user_function) { if (ec == be::http::error::end_of_stream) break; if (ec) return report_error(ec, "read"); auto const keep_alive = request.keep_alive(); - auto response = CallUserFunction(user_function, std::move(request)); + auto response = handler(std::move(request)); // Flush any buffered output, as the application may be shutdown immediately // after the HTTP response is sent. std::cout << std::flush; @@ -65,8 +65,8 @@ void HandleSession(tcp::socket socket, UserFunction const& user_function) { socket.shutdown(tcp::socket::shutdown_send, ec); } -template -int RunForTestImpl(int argc, char const* const argv[], UserFunction&& function, +int RunForTestImpl(int argc, char const* const argv[], + functions::Function const& function, std::function const& shutdown, std::function const& actual_port) { auto vm = ParseOptions(argc, argv); @@ -74,16 +74,18 @@ int RunForTestImpl(int argc, char const* const argv[], UserFunction&& function, auto address = asio::ip::make_address(vm["address"].as()); auto port = vm["port"].as(); + auto target = vm["target"].as(); asio::io_context ioc{1}; tcp::acceptor acceptor{ioc, {address, static_cast(port)}}; acceptor.listen(boost::asio::socket_base::max_connections); actual_port(acceptor.local_endpoint().port()); - auto handle_session = - [h = std::forward(function)](tcp::socket socket) { - HandleSession(std::move(socket), h); - }; + auto handler = FunctionImpl::GetImpl(function)->GetHandler(target); + + auto handle_session = [h = std::move(handler)](tcp::socket socket) { + HandleSession(std::move(socket), h); + }; auto cleanup = [](std::vector> sessions, auto wait) { std::vector> running; @@ -124,11 +126,10 @@ int RunForTestImpl(int argc, char const* const argv[], UserFunction&& function, return 0; } -template -int RunImpl(int argc, char const* const argv[], UserFunction&& f) noexcept try { +int RunImpl(int argc, char const* const argv[], + functions::Function const& f) noexcept try { return RunForTestImpl( - argc, argv, std::forward(f), [] { return false; }, - [](int /*unused*/) {}); + argc, argv, f, [] { return false; }, [](int /*unused*/) {}); } catch (std::exception const& ex) { std::cerr << "Standard C++ exception thrown " << ex.what() << "\n"; return 1; @@ -140,17 +141,10 @@ int RunImpl(int argc, char const* const argv[], UserFunction&& f) noexcept try { } // namespace int RunForTest(int argc, char const* const argv[], - functions::UserHttpFunction handler, - std::function const& shutdown, - std::function const& actual_port) { - return RunForTestImpl(argc, argv, std::move(handler), shutdown, actual_port); -} - -int RunForTest(int argc, char const* const argv[], - functions::UserCloudEventFunction handler, + functions::Function const& handler, std::function const& shutdown, std::function const& actual_port) { - return RunForTestImpl(argc, argv, std::move(handler), shutdown, actual_port); + return RunForTestImpl(argc, argv, handler, shutdown, actual_port); } FUNCTIONS_FRAMEWORK_CPP_INLINE_NAMESPACE_END @@ -159,13 +153,19 @@ FUNCTIONS_FRAMEWORK_CPP_INLINE_NAMESPACE_END namespace google::cloud::functions { FUNCTIONS_FRAMEWORK_CPP_INLINE_NAMESPACE_BEGIN +int Run(int argc, char const* const argv[], Function const& handler) noexcept { + return functions_internal::RunImpl(argc, argv, handler); +} + int Run(int argc, char const* const argv[], UserHttpFunction handler) noexcept { - return functions_internal::RunImpl(argc, argv, std::move(handler)); + return functions_internal::RunImpl( + argc, argv, functions::MakeFunction(std::move(handler))); } int Run(int argc, char const* const argv[], UserCloudEventFunction handler) noexcept { - return functions_internal::RunImpl(argc, argv, std::move(handler)); + return functions_internal::RunImpl( + argc, argv, functions::MakeFunction(std::move(handler))); } FUNCTIONS_FRAMEWORK_CPP_INLINE_NAMESPACE_END diff --git a/google/cloud/functions/internal/framework_impl.h b/google/cloud/functions/internal/framework_impl.h index 0a7f6f3d..70b2be67 100644 --- a/google/cloud/functions/internal/framework_impl.h +++ b/google/cloud/functions/internal/framework_impl.h @@ -15,6 +15,7 @@ #ifndef FUNCTIONS_FRAMEWORK_CPP_GOOGLE_CLOUD_FUNCTIONS_INTERNAL_FRAMEWORK_IMPL_H #define FUNCTIONS_FRAMEWORK_CPP_GOOGLE_CLOUD_FUNCTIONS_INTERNAL_FRAMEWORK_IMPL_H +#include "google/cloud/functions/function.h" #include "google/cloud/functions/user_functions.h" #include "google/cloud/functions/version.h" #include @@ -24,13 +25,7 @@ FUNCTIONS_FRAMEWORK_CPP_INLINE_NAMESPACE_BEGIN /// Implement functions::Run(), with additional helpers for testing. int RunForTest(int argc, char const* const argv[], - functions::UserHttpFunction handler, - std::function const& shutdown, - std::function const& actual_port); - -/// Implement functions::Run(), with additional helpers for testing. -int RunForTest(int argc, char const* const argv[], - functions::UserCloudEventFunction handler, + functions::Function const& handler, std::function const& shutdown, std::function const& actual_port); diff --git a/google/cloud/functions/internal/framework_impl_test.cc b/google/cloud/functions/internal/framework_impl_test.cc index 63ec7e35..b2adf745 100644 --- a/google/cloud/functions/internal/framework_impl_test.cc +++ b/google/cloud/functions/internal/framework_impl_test.cc @@ -110,7 +110,8 @@ TEST(FrameworkTest, Http) { auto run = [&](int argc, char const* const argv[], functions::UserHttpFunction f) { return RunForTest( - argc, argv, std::move(f), [&shutdown]() { return shutdown.load(); }, + argc, argv, functions::MakeFunction(std::move(f)), + [&shutdown]() { return shutdown.load(); }, [&port_p](int port) mutable { port_p.set_value(port); }); }; auto done = std::async(std::launch::async, run, static_cast(kTestArgc), @@ -143,7 +144,8 @@ TEST(FrameworkTest, CloudEvent) { auto run = [&](int argc, char const* const argv[], functions::UserCloudEventFunction f) { return RunForTest( - argc, argv, std::move(f), [&shutdown]() { return shutdown.load(); }, + argc, argv, functions::MakeFunction(std::move(f)), + [&shutdown]() { return shutdown.load(); }, [&port_p](int port) mutable { port_p.set_value(port); }); }; auto done = std::async(std::launch::async, run, static_cast(kTestArgc),