flush [ci skip]
[awesomized/libmemcached] / testing / lib / Connection.cpp
1 #include "Connection.hpp"
2
3 #include <cerrno>
4 #include <sys/poll.h>
5 #include <unistd.h>
6
7 Connection::Connection(socket_or_port_t socket_or_port) {
8 if (holds_alternative<string>(socket_or_port)) {
9 const auto path = get<string>(socket_or_port);
10 const auto safe = path.c_str();
11 const auto zlen = path.length() + 1;
12 const auto ulen = sizeof(sockaddr_un) - sizeof(sa_family_t);
13
14 if (zlen >= ulen) {
15 throw invalid_argument(error({"socket(): path too long '", path, "'"}));
16 }
17
18 if (0 > (sock = socket(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0))) {
19 throw runtime_error(error({"socket(): ", strerror(errno)}));
20 }
21
22 auto sa = reinterpret_cast<sockaddr_un *>(&addr);
23 sa->sun_family = AF_UNIX;
24 copy(safe, safe + zlen, sa->sun_path);
25
26 size = UNIX;
27
28 } else {
29 if (0 > (sock = socket(AF_INET6, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0))) {
30 throw runtime_error(error({"socket(): ", strerror(errno)}));
31 }
32
33 const auto port = get<int>(socket_or_port);
34 auto sa = reinterpret_cast<struct sockaddr_in6 *>(&addr);
35 sa->sin6_family = AF_INET6;
36 sa->sin6_port = htons(static_cast<unsigned short>(port));
37 sa->sin6_addr = IN6ADDR_LOOPBACK_INIT;
38
39 size = INET6;
40 }
41 }
42
43 Connection::~Connection() {
44 close();
45 }
46
47 void swap(Connection &a, Connection &b) {
48 a.swap(b);
49 }
50
51 void Connection::swap(Connection &conn) {
52 Connection copy(conn);
53 conn.sock = sock;
54 conn.addr = addr;
55 conn.size = size;
56 conn.last_err = last_err;
57 sock = exchange(copy.sock, -1);
58 addr = copy.addr;
59 size = copy.size;
60 last_err = copy.last_err;
61 }
62
63 Connection::Connection(const Connection &conn) {
64 if (conn.sock > -1) {
65 sock = dup(conn.sock);
66 }
67 addr = conn.addr;
68 size = conn.size;
69 last_err = conn.last_err;
70 }
71
72 Connection &Connection::operator=(const Connection &conn) {
73 Connection copy(conn);
74 copy.swap(*this);
75 return *this;
76 }
77
78 Connection::Connection(Connection &&conn) noexcept {
79 close();
80 swap(conn);
81 }
82
83 Connection &Connection::operator=(Connection &&conn) noexcept {
84 Connection copy(forward<Connection>(conn));
85 copy.swap(*this);
86 return *this;
87 }
88
89 void Connection::close() {
90 if (sock > -1) {
91 ::close(sock);
92 sock = -1;
93 last_err = -1;
94 }
95 }
96
97 int Connection::getError() {
98 int err = -1;
99 socklen_t len = sizeof(int);
100 if (sock > -1) {
101 errno = 0;
102 if (0 > getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len)) {
103 err = errno;
104 }
105 }
106 last_err = err;
107 return err;
108 }
109
110 int Connection::getLastError() {
111 if (last_err == -1) {
112 return getError();
113 }
114 return last_err;
115 }
116
117 bool Connection::isWritable() {
118 pollfd fd{sock, POLLOUT, 0};
119 if (1 > poll(&fd, 1, 0)) {
120 return false;
121 }
122 if (fd.revents & (POLLNVAL|POLLERR|POLLHUP)) {
123 return false;
124 }
125 return fd.revents & POLLOUT;
126 }
127
128 bool Connection::isOpen() {
129 if (sock > -1){
130 if (isWritable()) {
131 return getError() == 0;
132 } else if (open()) {
133 if (isWritable()) {
134 return getError() == 0;
135 }
136 }
137 }
138 return false;
139 }
140
141 bool Connection::open() {
142 if (connected) {
143 return true;
144 }
145 connect_again:
146 errno = 0;
147 if (0 == ::connect(sock, reinterpret_cast<sockaddr *>(&addr), size)) {
148 connected = true;
149 return true;
150 }
151
152 switch (errno) {
153 case EINTR:
154 goto connect_again;
155 case EISCONN:
156 connected = true;
157 [[fallthrough]];
158 case EAGAIN:
159 case EALREADY:
160 case EINPROGRESS:
161 return true;
162
163 default:
164 return false;
165 }
166 }
167
168 string Connection::error(const initializer_list<string> &args) {
169 stringstream ss;
170
171 for (auto &arg : args) {
172 ss << arg;
173 }
174
175 cerr << ss.str() << endl;
176 return ss.str();
177 }