blob: 034a3fb1fcfd0e15e71a88a3a689e2a88ab06645 [file] [log] [blame]
Jiaming Wang35b4ea32020-10-06 15:23:48 -07001// 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
15#include "pw_stream/socket_stream.h"
16namespace pw::stream {
17
18static constexpr uint32_t kMaxConcurrentUser = 1;
Jason Graffius322d5942021-01-28 16:01:28 -080019static constexpr char kLocalhostAddress[] = "127.0.0.1";
Jiaming Wang35b4ea32020-10-06 15:23:48 -070020
Jason Graffiusef52f3a2021-01-28 15:41:51 -080021SocketStream::~SocketStream() { Close(); }
22
Jiaming Wang35b4ea32020-10-06 15:23:48 -070023// Listen to the port and return after a client is connected
Jason Graffius322d5942021-01-28 16:01:28 -080024Status SocketStream::Serve(uint16_t port) {
Jiaming Wang35b4ea32020-10-06 15:23:48 -070025 listen_port_ = port;
26 socket_fd_ = socket(AF_INET, SOCK_STREAM, 0);
27 if (socket_fd_ == kInvalidFd) {
28 return Status::Internal();
29 }
30
31 struct sockaddr_in addr;
32 addr.sin_family = AF_INET;
33 addr.sin_port = htons(listen_port_);
34 addr.sin_addr.s_addr = INADDR_ANY;
35
36 int result =
37 bind(socket_fd_, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
38 if (result < 0) {
39 return Status::Internal();
40 }
41
42 result = listen(socket_fd_, kMaxConcurrentUser);
43 if (result < 0) {
44 return Status::Internal();
45 }
46
47 socklen_t len = sizeof(sockaddr_client_);
48
49 conn_fd_ =
50 accept(socket_fd_, reinterpret_cast<sockaddr*>(&sockaddr_client_), &len);
51 if (conn_fd_ < 0) {
52 return Status::Internal();
53 }
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -080054 return OkStatus();
Jiaming Wang35b4ea32020-10-06 15:23:48 -070055}
56
Jason Graffius322d5942021-01-28 16:01:28 -080057Status SocketStream::SocketStream::Connect(const char* host, uint16_t port) {
58 conn_fd_ = socket(AF_INET, SOCK_STREAM, 0);
59
60 sockaddr_in addr;
61 addr.sin_family = AF_INET;
62 addr.sin_port = htons(port);
63
64 if (host == nullptr) {
65 host = kLocalhostAddress;
66 }
67
68 if (inet_pton(AF_INET, host, &addr.sin_addr) <= 0) {
69 return Status::Unknown();
70 }
71
72 int result = connect(
73 conn_fd_, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
74 if (result < 0) {
75 return Status::Unknown();
76 }
77
78 return OkStatus();
79}
80
Jason Graffiusef52f3a2021-01-28 15:41:51 -080081void SocketStream::Close() {
82 if (socket_fd_ != kInvalidFd) {
83 close(socket_fd_);
84 socket_fd_ = kInvalidFd;
85 }
86
87 if (conn_fd_ != kInvalidFd) {
88 close(conn_fd_);
89 conn_fd_ = kInvalidFd;
90 }
91}
92
Jiaming Wang35b4ea32020-10-06 15:23:48 -070093Status SocketStream::DoWrite(std::span<const std::byte> data) {
94 ssize_t bytes_sent = send(conn_fd_, data.data(), data.size_bytes(), 0);
95
96 if (bytes_sent < 0 || static_cast<uint64_t>(bytes_sent) != data.size()) {
97 return Status::Internal();
98 }
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -080099 return OkStatus();
Jiaming Wang35b4ea32020-10-06 15:23:48 -0700100}
101
102StatusWithSize SocketStream::DoRead(ByteSpan dest) {
103 ssize_t bytes_rcvd = recv(conn_fd_, dest.data(), dest.size_bytes(), 0);
104 if (bytes_rcvd < 0) {
105 return StatusWithSize::Internal();
106 }
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800107 return StatusWithSize(bytes_rcvd);
Jiaming Wang35b4ea32020-10-06 15:23:48 -0700108}
109
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800110}; // namespace pw::stream