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