1 // Copyright 2020 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <cstdint> 17 18 #include "pw_result/result.h" 19 #include "pw_span/span.h" 20 #include "pw_stream/stream.h" 21 #include "pw_sync/lock_annotations.h" 22 #include "pw_sync/mutex.h" 23 24 namespace pw::stream { 25 26 class SocketStream : public NonSeekableReaderWriter { 27 public: 28 SocketStream() = default; 29 // Construct a SocketStream directly from a file descriptor. SocketStream(int connection_fd)30 explicit SocketStream(int connection_fd) : connection_fd_(connection_fd) { 31 // Mark as ready and take ownership of the connection by this object. 32 ready_ = true; 33 TakeConnection(); 34 } 35 36 // SocketStream objects are moveable but not copyable. 37 SocketStream& operator=(SocketStream&& other) { 38 MoveFrom(std::move(other)); 39 return *this; 40 } SocketStream(SocketStream && other)41 SocketStream(SocketStream&& other) noexcept { MoveFrom(std::move(other)); } 42 SocketStream(const SocketStream&) = delete; 43 SocketStream& operator=(const SocketStream&) = delete; 44 ~SocketStream()45 ~SocketStream() override { Close(); } 46 47 // Connect to a local or remote endpoint. Host may be either an IPv4 or IPv6 48 // address. If host is nullptr then the IPv4 localhost address is used 49 // instead. 50 Status Connect(const char* host, uint16_t port); 51 52 // Configures socket options. 53 int SetSockOpt(int level, 54 int optname, 55 const void* optval, 56 unsigned int optlen); 57 58 // Close the socket stream and release all resources 59 void Close(); 60 61 private: 62 static constexpr int kInvalidFd = -1; 63 64 class ConnectionOwnership { 65 public: ConnectionOwnership(SocketStream * socket_stream)66 explicit ConnectionOwnership(SocketStream* socket_stream) 67 : socket_stream_(socket_stream) { 68 fd_ = socket_stream_->TakeConnection(); 69 std::lock_guard lock(socket_stream_->connection_mutex_); 70 pipe_r_fd_ = socket_stream->connection_pipe_r_fd_; 71 } 72 ~ConnectionOwnership()73 ~ConnectionOwnership() { socket_stream_->ReleaseConnection(); } 74 fd()75 int fd() { return fd_; } 76 pipe_r_fd()77 int pipe_r_fd() { return pipe_r_fd_; } 78 79 private: 80 SocketStream* socket_stream_; 81 int fd_; 82 int pipe_r_fd_; 83 }; 84 85 Status DoWrite(span<const std::byte> data) override; 86 87 StatusWithSize DoRead(ByteSpan dest) override; 88 89 // Take ownership of the connection. There may be multiple owners. Each time 90 // TakeConnection is called, ReleaseConnection must be called to release 91 // ownership, even if the connection is not valid. 92 // 93 // Returns the connection fd or kInvalidFd if the connection is not valid. 94 int TakeConnection(); 95 int TakeConnectionWithLockHeld() 96 PW_EXCLUSIVE_LOCKS_REQUIRED(connection_mutex_); 97 98 // Release ownership of the connection. If no owners remain, close and clear 99 // the connection fds. 100 void ReleaseConnection(); 101 void ReleaseConnectionWithLockHeld() 102 PW_EXCLUSIVE_LOCKS_REQUIRED(connection_mutex_); 103 104 // Moves other to this. MoveFrom(SocketStream && other)105 void MoveFrom(SocketStream&& other) { 106 std::lock_guard lock(connection_mutex_); 107 std::lock_guard other_lock(other.connection_mutex_); 108 109 connection_own_count_ = other.connection_own_count_; 110 other.connection_own_count_ = 0; 111 ready_ = other.ready_; 112 other.ready_ = false; 113 connection_fd_ = other.connection_fd_; 114 other.connection_fd_ = kInvalidFd; 115 connection_pipe_r_fd_ = other.connection_pipe_r_fd_; 116 other.connection_pipe_r_fd_ = kInvalidFd; 117 connection_pipe_w_fd_ = other.connection_pipe_w_fd_; 118 other.connection_pipe_w_fd_ = kInvalidFd; 119 } 120 121 sync::Mutex connection_mutex_; 122 int connection_own_count_ PW_GUARDED_BY(connection_mutex_) = 0; 123 bool ready_ PW_GUARDED_BY(connection_mutex_) = false; 124 int connection_fd_ PW_GUARDED_BY(connection_mutex_) = kInvalidFd; 125 int connection_pipe_r_fd_ PW_GUARDED_BY(connection_mutex_) = kInvalidFd; 126 int connection_pipe_w_fd_ PW_GUARDED_BY(connection_mutex_) = kInvalidFd; 127 }; 128 129 /// `ServerSocket` wraps a POSIX-style server socket, producing a `SocketStream` 130 /// for each accepted client connection. 131 /// 132 /// Call `Listen` to create the socket and start listening for connections. 133 /// Then call `Accept` any number of times to accept client connections. 134 class ServerSocket { 135 public: 136 ServerSocket() = default; ~ServerSocket()137 ~ServerSocket() { Close(); } 138 139 ServerSocket(const ServerSocket& other) = delete; 140 ServerSocket& operator=(const ServerSocket& other) = delete; 141 142 // Listen for connections on the given port. 143 // If port is 0, a random unused port is chosen and can be retrieved with 144 // port(). 145 Status Listen(uint16_t port = 0); 146 147 // Accept a connection. Blocks until after a client is connected. 148 // On success, returns a SocketStream connected to the new client. 149 Result<SocketStream> Accept(); 150 151 // Close the server socket, preventing further connections. 152 void Close(); 153 154 // Returns the port this socket is listening on. port()155 uint16_t port() const { return port_; } 156 157 private: 158 static constexpr int kInvalidFd = -1; 159 160 class SocketOwnership { 161 public: SocketOwnership(ServerSocket * server_socket)162 explicit SocketOwnership(ServerSocket* server_socket) 163 : server_socket_(server_socket) { 164 fd_ = server_socket_->TakeSocket(); 165 std::lock_guard lock(server_socket->socket_mutex_); 166 pipe_r_fd_ = server_socket->socket_pipe_r_fd_; 167 } 168 ~SocketOwnership()169 ~SocketOwnership() { server_socket_->ReleaseSocket(); } 170 fd()171 int fd() { return fd_; } 172 pipe_r_fd()173 int pipe_r_fd() { return pipe_r_fd_; } 174 175 private: 176 ServerSocket* server_socket_; 177 int fd_; 178 int pipe_r_fd_; 179 }; 180 181 // Take ownership of the socket. There may be multiple owners. Each time 182 // TakeSocket is called, ReleaseSocket must be called to release ownership, 183 // even if the socket is not invalid. 184 // 185 // Returns the socket fd or kInvalidFd if the socket is not valid. 186 int TakeSocket(); 187 int TakeSocketWithLockHeld() PW_EXCLUSIVE_LOCKS_REQUIRED(socket_mutex_); 188 189 // Release ownership of the socket. If no owners remain, close and clear the 190 // socket fds. 191 void ReleaseSocket(); 192 void ReleaseSocketWithLockHeld() PW_EXCLUSIVE_LOCKS_REQUIRED(socket_mutex_); 193 194 uint16_t port_ = -1; 195 sync::Mutex socket_mutex_; 196 int socket_own_count_ PW_GUARDED_BY(socket_mutex_) = 0; 197 bool ready_ PW_GUARDED_BY(socket_mutex_) = false; 198 int socket_fd_ PW_GUARDED_BY(socket_mutex_) = kInvalidFd; 199 int socket_pipe_r_fd_ PW_GUARDED_BY(socket_mutex_) = kInvalidFd; 200 int socket_pipe_w_fd_ PW_GUARDED_BY(socket_mutex_) = kInvalidFd; 201 }; 202 203 } // namespace pw::stream 204