blob: 1af8067cdf32adb2855cb51bef45215177516bff [file] [log] [blame]
// Copyright 2017 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.
#ifndef SRC_STORAGE_LIB_VFS_CPP_SERVICE_H_
#define SRC_STORAGE_LIB_VFS_CPP_SERVICE_H_
#include <lib/fit/function.h>
#include <lib/fit/traits.h>
#include <lib/zx/channel.h>
#include <type_traits>
#include <fbl/macros.h>
#include <fbl/ref_ptr.h>
#include "vnode.h"
namespace fs {
// A node which binds a channel to a service implementation when opened.
//
// This class is thread-safe.
class Service : public Vnode {
public:
// Construct with fbl::MakeRefCounted.
// Handler called to bind the provided channel to an implementation of the service.
using Connector = fit::function<zx_status_t(zx::channel channel)>;
private:
// Determines if |T| has a nested type |T::ProtocolType|.
template <typename, typename = void>
struct has_protocol_type : public std::false_type {};
template <typename T>
struct has_protocol_type<T, std::void_t<typename T::ProtocolType>> : public std::true_type{};
template <typename T>
static constexpr inline auto has_protocol_type_v = has_protocol_type<T>::value;
template <typename T>
using remove_cvref_t = typename std::remove_reference<typename std::remove_cv<T>::type>::type;
// Returns if |T| could potentially be a protocol connector:
// - It is not |Service|.
// - It cannot be converted to the untyped connector.
template <typename T>
static constexpr inline auto maybe_protocol_connector =
std::conjunction_v<std::negation<std::is_same<remove_cvref_t<T>, Service>>,
std::negation<std::is_convertible<T, Connector>>>;
public:
// Handler called to bind the provided channel to an implementation of the service. This version
// is typed to the exact FIDL protocol the handler will support.
template <typename Protocol>
using ProtocolConnector = fit::function<zx_status_t(fidl::ServerEnd<Protocol>)>;
// |Vnode| implementation:
fuchsia_io::NodeProtocolKinds GetProtocols() const final;
zx::result<fs::VnodeAttributes> GetAttributes() const final;
zx_status_t ConnectService(zx::channel channel) final;
protected:
friend fbl::internal::MakeRefCountedHelper<Service>;
friend fbl::RefPtr<Service>;
// Creates a service with the specified connector.
//
// If the |connector| is null, then incoming connection requests will be dropped.
explicit Service(Connector connector);
// Creates a service with the specified connector. This version is typed to the exact FIDL
// protocol the handler will support:
//
// auto service = fbl::MakeRefCounted<fs::Service>(
// [](fidl::ServerEnd<fidl_library::SomeProtocol> server_end) {
// // |server_end| speaks the |fidl_library::SomeProtocol| protocol.
// // Handle FIDL messages on |server_end|.
// });
//
// If the |connector| is null, then incoming connection requests will be dropped.
//
// The connector should be a callable taking a single |fidl::ServerEnd<ProtocolType>| as argument,
// and return a |zx_status_t|.
template <typename Callable, std::enable_if_t<maybe_protocol_connector<Callable>, bool> = true>
explicit Service(Callable&& connector)
: Service([connector = std::forward<Callable>(connector)](zx::channel channel) mutable {
static_assert(
std::is_same_v<typename fit::callable_traits<remove_cvref_t<Callable>>::return_type,
zx_status_t>,
"The protocol connector should return |zx_status_t|.");
static_assert(fit::callable_traits<remove_cvref_t<Callable>>::args::size == 1,
"The protocol connector should take exactly one argument.");
using FirstArg =
typename fit::callable_traits<remove_cvref_t<Callable>>::args::template at<0>;
static_assert(
has_protocol_type_v<FirstArg>,
"The first argument of the protocol connector should be |fidl::ServerEnd<T>|.");
using Protocol = typename FirstArg::ProtocolType;
return connector(fidl::ServerEnd<Protocol>(std::move(channel)));
}) {}
// Destroys the services and releases its connector.
~Service() override;
private:
Connector connector_;
DISALLOW_COPY_ASSIGN_AND_MOVE(Service);
};
} // namespace fs
#endif // SRC_STORAGE_LIB_VFS_CPP_SERVICE_H_