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 // Get the connection ready state (true if ready, false otherwise). 59 bool IsReady(); 60 61 // Close the socket stream and release all resources 62 void Close(); 63 64 private: 65 static constexpr int kInvalidFd = -1; 66 67 class ConnectionOwnership { 68 public: ConnectionOwnership(SocketStream * socket_stream)69 explicit ConnectionOwnership(SocketStream* socket_stream) 70 : socket_stream_(socket_stream) { 71 fd_ = socket_stream_->TakeConnection(); 72 std::lock_guard lock(socket_stream_->connection_mutex_); 73 pipe_r_fd_ = socket_stream->connection_pipe_r_fd_; 74 } 75 ~ConnectionOwnership()76 ~ConnectionOwnership() { socket_stream_->ReleaseConnection(); } 77 fd()78 int fd() { return fd_; } 79 pipe_r_fd()80 int pipe_r_fd() { return pipe_r_fd_; } 81 82 private: 83 SocketStream* socket_stream_; 84 int fd_; 85 int pipe_r_fd_; 86 }; 87 88 Status DoWrite(span<const std::byte> data) override; 89 90 StatusWithSize DoRead(ByteSpan dest) override; 91 92 // Take ownership of the connection. There may be multiple owners. Each time 93 // TakeConnection is called, ReleaseConnection must be called to release 94 // ownership, even if the connection is not valid. 95 // 96 // Returns the connection fd or kInvalidFd if the connection is not valid. 97 int TakeConnection(); 98 int TakeConnectionWithLockHeld() 99 PW_EXCLUSIVE_LOCKS_REQUIRED(connection_mutex_); 100 101 // Release ownership of the connection. If no owners remain, close and clear 102 // the connection fds. 103 void ReleaseConnection(); 104 void ReleaseConnectionWithLockHeld() 105 PW_EXCLUSIVE_LOCKS_REQUIRED(connection_mutex_); 106 107 // Moves other to this. MoveFrom(SocketStream && other)108 void MoveFrom(SocketStream&& other) { 109 std::lock_guard lock(connection_mutex_); 110 std::lock_guard other_lock(other.connection_mutex_); 111 112 connection_own_count_ = other.connection_own_count_; 113 other.connection_own_count_ = 0; 114 ready_ = other.ready_; 115 other.ready_ = false; 116 connection_fd_ = other.connection_fd_; 117 other.connection_fd_ = kInvalidFd; 118 connection_pipe_r_fd_ = other.connection_pipe_r_fd_; 119 other.connection_pipe_r_fd_ = kInvalidFd; 120 connection_pipe_w_fd_ = other.connection_pipe_w_fd_; 121 other.connection_pipe_w_fd_ = kInvalidFd; 122 } 123 124 sync::Mutex connection_mutex_; 125 int connection_own_count_ PW_GUARDED_BY(connection_mutex_) = 0; 126 bool ready_ PW_GUARDED_BY(connection_mutex_) = false; 127 int connection_fd_ PW_GUARDED_BY(connection_mutex_) = kInvalidFd; 128 int connection_pipe_r_fd_ PW_GUARDED_BY(connection_mutex_) = kInvalidFd; 129 int connection_pipe_w_fd_ PW_GUARDED_BY(connection_mutex_) = kInvalidFd; 130 }; 131 132 /// `ServerSocket` wraps a POSIX-style server socket, producing a `SocketStream` 133 /// for each accepted client connection. 134 /// 135 /// Call `Listen` to create the socket and start listening for connections. 136 /// Then call `Accept` any number of times to accept client connections. 137 class ServerSocket { 138 public: 139 ServerSocket() = default; ~ServerSocket()140 ~ServerSocket() { Close(); } 141 142 ServerSocket(const ServerSocket& other) = delete; 143 ServerSocket& operator=(const ServerSocket& other) = delete; 144 145 // Listen for connections on the given port. 146 // If port is 0, a random unused port is chosen and can be retrieved with 147 // port(). 148 Status Listen(uint16_t port = 0); 149 150 // Accept a connection. Blocks until after a client is connected. 151 // On success, returns a SocketStream connected to the new client. 152 Result<SocketStream> Accept(); 153 154 // Close the server socket, preventing further connections. 155 void Close(); 156 157 // Returns the port this socket is listening on. port()158 uint16_t port() const { return port_; } 159 160 private: 161 static constexpr int kInvalidFd = -1; 162 163 class SocketOwnership { 164 public: SocketOwnership(ServerSocket * server_socket)165 explicit SocketOwnership(ServerSocket* server_socket) 166 : server_socket_(server_socket) { 167 fd_ = server_socket_->TakeSocket(); 168 std::lock_guard lock(server_socket->socket_mutex_); 169 pipe_r_fd_ = server_socket->socket_pipe_r_fd_; 170 } 171 ~SocketOwnership()172 ~SocketOwnership() { server_socket_->ReleaseSocket(); } 173 fd()174 int fd() { return fd_; } 175 pipe_r_fd()176 int pipe_r_fd() { return pipe_r_fd_; } 177 178 private: 179 ServerSocket* server_socket_; 180 int fd_; 181 int pipe_r_fd_; 182 }; 183 184 // Take ownership of the socket. There may be multiple owners. Each time 185 // TakeSocket is called, ReleaseSocket must be called to release ownership, 186 // even if the socket is not invalid. 187 // 188 // Returns the socket fd or kInvalidFd if the socket is not valid. 189 int TakeSocket(); 190 int TakeSocketWithLockHeld() PW_EXCLUSIVE_LOCKS_REQUIRED(socket_mutex_); 191 192 // Release ownership of the socket. If no owners remain, close and clear the 193 // socket fds. 194 void ReleaseSocket(); 195 void ReleaseSocketWithLockHeld() PW_EXCLUSIVE_LOCKS_REQUIRED(socket_mutex_); 196 197 uint16_t port_ = -1; 198 sync::Mutex socket_mutex_; 199 int socket_own_count_ PW_GUARDED_BY(socket_mutex_) = 0; 200 bool ready_ PW_GUARDED_BY(socket_mutex_) = false; 201 int socket_fd_ PW_GUARDED_BY(socket_mutex_) = kInvalidFd; 202 int socket_pipe_r_fd_ PW_GUARDED_BY(socket_mutex_) = kInvalidFd; 203 int socket_pipe_w_fd_ PW_GUARDED_BY(socket_mutex_) = kInvalidFd; 204 }; 205 206 } // namespace pw::stream 207