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