// Copyright 2019 The Chromium 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 CAST_STREAMING_ENVIRONMENT_H_ #define CAST_STREAMING_ENVIRONMENT_H_ #include #include #include #include #include "absl/types/span.h" #include "platform/api/time.h" #include "platform/api/udp_socket.h" #include "platform/base/ip_address.h" namespace openscreen { namespace cast { // Provides the common environment for operating system resources shared by // multiple components. class Environment : public UdpSocket::Client { public: class PacketConsumer { public: virtual void OnReceivedPacket(const IPEndpoint& source, Clock::time_point arrival_time, std::vector packet) = 0; protected: virtual ~PacketConsumer(); }; // Consumers of the environment's UDP socket should be careful to check the // socket's state before accessing its methods, especially // GetBoundLocalEndpoint(). If the environment is |kStarting|, the // local endpoint may not be set yet and will be zero initialized. enum class SocketState { // Socket is still initializing. Usually the UDP socket bind is // the last piece. kStarting, // The socket is ready for use and has been bound. kReady, // The socket is either closed (normally or due to an error) or in an // invalid state. Currently the environment does not create a new socket // in this case, so to be used again the environment itself needs to be // recreated. kInvalid }; // Classes concerned with the Environment's UDP socket state may inherit from // |Subscriber| and then |Subscribe|. class SocketSubscriber { public: // Event that occurs when the environment is ready for use. virtual void OnSocketReady() = 0; // Event that occurs when the environment has experienced a fatal error. virtual void OnSocketInvalid(Error error) = 0; }; // Construct with the given clock source and TaskRunner. Creates and // internally-owns a UdpSocket, and immediately binds it to the given // |local_endpoint|. If embedders do not care what interface/address the UDP // socket is bound on, they may omit that argument. Environment(ClockNowFunctionPtr now_function, TaskRunner* task_runner, const IPEndpoint& local_endpoint = IPEndpoint::kAnyV6()); ~Environment() override; ClockNowFunctionPtr now_function() const { return now_function_; } Clock::time_point now() const { return now_function_(); } TaskRunner* task_runner() const { return task_runner_; } // Returns the local endpoint the socket is bound to, or the zero IPEndpoint // if socket creation/binding failed. // // Note: This method is virtual to allow unit tests to fake that there really // is a bound socket. virtual IPEndpoint GetBoundLocalEndpoint() const; // Get/Set the remote endpoint. This is separate from the constructor because // the remote endpoint is, in some cases, discovered only after receiving a // packet. const IPEndpoint& remote_endpoint() const { return remote_endpoint_; } void set_remote_endpoint(const IPEndpoint& endpoint) { remote_endpoint_ = endpoint; } // Returns the current state of the UDP socket. This method is virtual // to allow tests to simulate socket state. SocketState socket_state() const { return state_; } void set_socket_state_for_testing(SocketState state) { state_ = state; } // Subscribe to socket changes. Callers can unsubscribe by passing // nullptr. void SetSocketSubscriber(SocketSubscriber* subscriber); // Start/Resume delivery of incoming packets to the given |packet_consumer|. // Delivery will continue until DropIncomingPackets() is called. void ConsumeIncomingPackets(PacketConsumer* packet_consumer); // Stop delivery of incoming packets, dropping any that do come in. All // internal references to the PacketConsumer that was provided in the last // call to ConsumeIncomingPackets() are cleared. void DropIncomingPackets(); // Returns the maximum packet size for the network. This will always return a // value of at least kRequiredNetworkPacketSize. int GetMaxPacketSize() const; // Sends the given |packet| to the remote endpoint, best-effort. // set_remote_endpoint() must be called beforehand with a valid IPEndpoint. // // Note: This method is virtual to allow unit tests to intercept packets // before they actually head-out through the socket. virtual void SendPacket(absl::Span packet); protected: Environment() : now_function_(nullptr), task_runner_(nullptr) {} // Protected so that they can be set by the MockEnvironment for testing. ClockNowFunctionPtr now_function_; TaskRunner* task_runner_; private: // UdpSocket::Client implementation. void OnBound(UdpSocket* socket) final; void OnError(UdpSocket* socket, Error error) final; void OnSendError(UdpSocket* socket, Error error) final; void OnRead(UdpSocket* socket, ErrorOr packet_or_error) final; // The UDP socket bound to the local endpoint that was passed into the // constructor, or null if socket creation failed. const std::unique_ptr socket_; // These are externally set/cleared. Behaviors are described in getter/setter // method comments above. IPEndpoint local_endpoint_{}; IPEndpoint remote_endpoint_{}; PacketConsumer* packet_consumer_ = nullptr; SocketState state_ = SocketState::kStarting; SocketSubscriber* socket_subscriber_ = nullptr; }; } // namespace cast } // namespace openscreen #endif // CAST_STREAMING_ENVIRONMENT_H_