blob: 972da8f7bc353ecb743faae51f169d3ec740b324 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// ============================================================================
// This is an accompanying example code for the C++ server tutorial. Head over
// there for the full walk-through:
// https://fuchsia.dev/fuchsia-src/development/languages/fidl/tutorials/cpp/basics/server
// ============================================================================
// [START includes]
#include <fidl/fuchsia.examples/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/component/outgoing/cpp/outgoing_directory.h>
#include <lib/syslog/cpp/macros.h>
// [END includes]
// [START server]
class EchoImpl : public fidl::Server<fuchsia_examples::Echo> {
public:
// [START impl]
// The handler for `fuchsia.examples/Echo.EchoString`.
//
// For two-way methods (those with a response) like this one, the completer is
// used complete the call: either to send the reply via |completer.Reply|, or
// close the channel via |completer.Close|.
//
// |EchoStringRequest| exposes the same API as the request struct domain
// object, that is |fuchsia_examples::EchoEchoStringRequest|.
// [START impl-echo-string]
void EchoString(EchoStringRequest& request, EchoStringCompleter::Sync& completer) override {
// Call |Reply| to reply synchronously with the request value.
completer.Reply({{.response = request.value()}});
}
// [END impl-echo-string]
// The handler for `fuchsia.examples/Echo.SendString`.
//
// For fire-and-forget methods like this one, the completer is normally not
// used, but its |Close(zx_status_t)| method can be used to close the channel,
// either when the protocol has reached its intended terminal state or the
// server encountered an unrecoverable error.
//
// |SendStringRequest| exposes the same API as the request struct domain
// object, that is |fuchsia_examples::EchoSendStringRequest|.
void SendString(SendStringRequest& request, SendStringCompleter::Sync& completer) override {
ZX_ASSERT(binding_ref_.has_value());
// Handle a SendString request by sending an |OnString| event (an
// unsolicited server-to-client message) back to the client.
fit::result result = fidl::SendEvent(*binding_ref_)->OnString({request.value()});
if (!result.is_ok()) {
FX_LOGS(ERROR) << "Error sending event: " << result.error_value();
}
}
// [END impl]
// [START bind_server]
// Bind a new implementation of |EchoImpl| to handle requests coming from
// the server endpoint |server_end|.
static void BindSelfManagedServer(async_dispatcher_t* dispatcher,
fidl::ServerEnd<fuchsia_examples::Echo> server_end) {
// Create a new instance of |EchoImpl|.
std::unique_ptr impl = std::make_unique<EchoImpl>();
EchoImpl* impl_ptr = impl.get();
// |fidl::BindServer| takes a FIDL protocol server implementation and a
// channel. It asynchronously reads requests off the channel, decodes them
// and dispatches them to the correct handler on the server implementation.
//
// The FIDL protocol server implementation can be passed as a
// |std::shared_ptr|, |std::unique_ptr|, or raw pointer. For shared and
// unique pointers, the binding will manage the lifetime of the
// implementation object. For raw pointers, it's up to the caller to ensure
// that the implementation object outlives the binding but does not leak.
//
// See the documentation comment of |fidl::BindServer|.
fidl::ServerBindingRef binding_ref = fidl::BindServer(
dispatcher, std::move(server_end), std::move(impl), std::mem_fn(&EchoImpl::OnUnbound));
// Put the returned |binding_ref| into the |EchoImpl| object.
impl_ptr->binding_ref_.emplace(std::move(binding_ref));
}
// This method is passed to the |BindServer| call as the last argument,
// which means it will be called when the connection is torn down.
// In this example we use it to log some connection lifecycle information.
// Production code could do more things such as resource cleanup.
void OnUnbound(fidl::UnbindInfo info, fidl::ServerEnd<fuchsia_examples::Echo> server_end) {
// |is_user_initiated| returns true if the server code called |Close| on a
// completer, or |Unbind| / |Close| on the |binding_ref_|, to proactively
// teardown the connection. These cases are usually part of normal server
// shutdown, so logging is unnecessary.
if (info.is_user_initiated()) {
return;
}
if (info.is_peer_closed()) {
// If the peer (the client) closed their endpoint, log that as INFO.
FX_LOGS(INFO) << "Client disconnected";
} else {
// Treat other unbind causes as errors.
FX_LOGS(ERROR) << "Server error: " << info;
}
}
// [END bind_server]
// [START binding_ref]
private:
// `ServerBindingRef` can be used to:
// - Control the binding, such as to unbind the server from the channel or
// close the channel.
// - Send events back to the client.
// See the documentation comments on |fidl::ServerBindingRef|.
std::optional<fidl::ServerBindingRef<fuchsia_examples::Echo>> binding_ref_;
// [END binding_ref]
};
// [END server]
// [START main]
int main(int argc, const char** argv) {
// [START serve-out-dir]
// The event loop is used to asynchronously listen for incoming connections
// and requests from the client. The following initializes the loop, and
// obtains the dispatcher, which will be used when binding the server
// implementation to a channel.
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
async_dispatcher_t* dispatcher = loop.dispatcher();
// Create an |OutgoingDirectory| instance.
//
// The |component::OutgoingDirectory| class serves the outgoing directory for
// our component. This directory is where the outgoing FIDL protocols are
// installed so that they can be provided to other components.
component::OutgoingDirectory outgoing = component::OutgoingDirectory(dispatcher);
// The `ServeFromStartupInfo()` function sets up the outgoing directory with
// the startup handle. The startup handle is a handle provided to every
// component by the system, so that they can serve capabilities (e.g. FIDL
// protocols) to other components.
zx::result result = outgoing.ServeFromStartupInfo();
if (result.is_error()) {
FX_LOGS(ERROR) << "Failed to serve outgoing directory: " << result.status_string();
return -1;
}
// [END serve-out-dir]
// Register a handler for components trying to connect to fuchsia.examples.Echo.
result = outgoing.AddUnmanagedProtocol<fuchsia_examples::Echo>(
[dispatcher](fidl::ServerEnd<fuchsia_examples::Echo> server_end) {
FX_LOGS(INFO) << "Incoming connection for "
<< fidl::DiscoverableProtocolName<fuchsia_examples::Echo>;
EchoImpl::BindSelfManagedServer(dispatcher, std::move(server_end));
});
if (result.is_error()) {
FX_LOGS(ERROR) << "Failed to add Echo protocol: " << result.status_string();
return -1;
}
FX_LOGS(INFO) << "Running C++ echo server with natural types";
// This runs the event loop and blocks until the loop is quit or shutdown.
// See documentation comments on |async::Loop|.
loop.Run();
return 0;
}
// [END main]