blob: 33367a42aa7295b829ebe026664a6809a14f688e [file] [log] [blame]
Mark Slee9f0c6512007-02-28 23:58:26 +00001// Copyright (c) 2006- Facebook
2// Distributed under the Thrift Software License
3//
4// See accompanying file LICENSE or visit the Thrift site at:
5// http://developers.facebook.com/thrift/
6
Mark Sleee8540632006-05-30 09:24:40 +00007#include <sys/socket.h>
Mark Sleea5a783f2007-03-02 19:41:08 +00008#include <sys/select.h>
Mark Sleee8540632006-05-30 09:24:40 +00009#include <netinet/in.h>
Mark Slee29050782006-09-29 00:12:30 +000010#include <netinet/tcp.h>
Mark Slee6d56eb92007-07-06 22:28:15 +000011#include <netdb.h>
Mark Sleea5a783f2007-03-02 19:41:08 +000012#include <fcntl.h>
Mark Slee8d7e1f62006-06-07 06:48:56 +000013#include <errno.h>
Mark Sleee8540632006-05-30 09:24:40 +000014
Marc Slemkod42a2c22006-08-10 03:30:18 +000015#include "TSocket.h"
16#include "TServerSocket.h"
Marc Slemko16698852006-08-04 03:16:10 +000017#include <boost/shared_ptr.hpp>
Mark Sleee8540632006-05-30 09:24:40 +000018
Marc Slemko6f038a72006-08-03 18:58:09 +000019namespace facebook { namespace thrift { namespace transport {
20
Martin Kraemere6c4fa62007-07-09 19:08:25 +000021using namespace std;
Marc Slemko16698852006-08-04 03:16:10 +000022using namespace boost;
23
Mark Sleee8540632006-05-30 09:24:40 +000024TServerSocket::TServerSocket(int port) :
Mark Slee29050782006-09-29 00:12:30 +000025 port_(port),
Martin Kraemeree341cb2007-02-05 21:40:38 +000026 serverSocket_(-1),
Mark Slee29050782006-09-29 00:12:30 +000027 acceptBacklog_(1024),
28 sendTimeout_(0),
Mark Sleea5a783f2007-03-02 19:41:08 +000029 recvTimeout_(0),
boz1ea81ce2007-05-14 23:04:33 +000030 retryLimit_(0),
31 retryDelay_(0),
Mark Slee561b5362007-03-09 19:26:29 +000032 intSock1_(-1),
33 intSock2_(-1) {}
Mark Slee29050782006-09-29 00:12:30 +000034
35TServerSocket::TServerSocket(int port, int sendTimeout, int recvTimeout) :
36 port_(port),
Martin Kraemeree341cb2007-02-05 21:40:38 +000037 serverSocket_(-1),
Mark Slee29050782006-09-29 00:12:30 +000038 acceptBacklog_(1024),
39 sendTimeout_(sendTimeout),
Mark Sleea5a783f2007-03-02 19:41:08 +000040 recvTimeout_(recvTimeout),
boz1ea81ce2007-05-14 23:04:33 +000041 retryLimit_(0),
42 retryDelay_(0),
Mark Slee561b5362007-03-09 19:26:29 +000043 intSock1_(-1),
44 intSock2_(-1) {}
Mark Sleee8540632006-05-30 09:24:40 +000045
46TServerSocket::~TServerSocket() {
47 close();
48}
49
Mark Slee29050782006-09-29 00:12:30 +000050void TServerSocket::setSendTimeout(int sendTimeout) {
51 sendTimeout_ = sendTimeout;
52}
53
54void TServerSocket::setRecvTimeout(int recvTimeout) {
55 recvTimeout_ = recvTimeout;
56}
57
boz1ea81ce2007-05-14 23:04:33 +000058void TServerSocket::setRetryLimit(int retryLimit) {
59 retryLimit_ = retryLimit;
60}
61
62void TServerSocket::setRetryDelay(int retryDelay) {
63 retryDelay_ = retryDelay;
64}
65
Mark Slee8d7e1f62006-06-07 06:48:56 +000066void TServerSocket::listen() {
Mark Slee561b5362007-03-09 19:26:29 +000067 int sv[2];
68 if (-1 == socketpair(AF_LOCAL, SOCK_STREAM, 0, sv)) {
boz6ded7752007-06-05 22:41:18 +000069 GlobalOutput("TServerSocket::init()");
Mark Slee561b5362007-03-09 19:26:29 +000070 intSock1_ = -1;
71 intSock2_ = -1;
72 } else {
Mark Sleee02385b2007-06-09 01:21:16 +000073 intSock1_ = sv[1];
74 intSock2_ = sv[0];
Mark Slee561b5362007-03-09 19:26:29 +000075 }
76
Mark Slee6d56eb92007-07-06 22:28:15 +000077 struct addrinfo hints, *res, *res0;
78 int error;
79 char port[sizeof("65536") + 1];
80 memset(&hints, 0, sizeof(hints));
81 hints.ai_family = PF_UNSPEC;
82 hints.ai_socktype = SOCK_STREAM;
83 hints.ai_flags = AI_PASSIVE;
84 sprintf(port, "%d", port_);
85
86 // Wildcard address
87 error = getaddrinfo(NULL, port, &hints, &res0);
88 if (error) {
89 fprintf(stderr, "getaddrinfo %d: %s\n", error, gai_strerror(error));
90 close();
91 throw TTransportException(TTransportException::NOT_OPEN, "Could not resolve host for server socket.");
92 }
93
94 // Pick the ipv6 address first since ipv4 addresses can be mapped
95 // into ipv6 space.
96 for (res = res0; res; res = res->ai_next) {
97 if (res->ai_family == AF_INET6 || res->ai_next == NULL)
98 break;
99 }
100
101 serverSocket_ = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
Mark Sleee8540632006-05-30 09:24:40 +0000102 if (serverSocket_ == -1) {
boz6ded7752007-06-05 22:41:18 +0000103 GlobalOutput("TServerSocket::listen() socket");
Mark Sleee8540632006-05-30 09:24:40 +0000104 close();
Mark Sleef9831082007-02-20 20:59:21 +0000105 throw TTransportException(TTransportException::NOT_OPEN, "Could not create server socket.");
Mark Sleee8540632006-05-30 09:24:40 +0000106 }
107
108 // Set reusaddress to prevent 2MSL delay on accept
109 int one = 1;
110 if (-1 == setsockopt(serverSocket_, SOL_SOCKET, SO_REUSEADDR,
111 &one, sizeof(one))) {
boz6ded7752007-06-05 22:41:18 +0000112 GlobalOutput("TServerSocket::listen() SO_REUSEADDR");
Mark Sleee8540632006-05-30 09:24:40 +0000113 close();
Mark Sleef9831082007-02-20 20:59:21 +0000114 throw TTransportException(TTransportException::NOT_OPEN, "Could not set SO_REUSEADDR");
Mark Sleee8540632006-05-30 09:24:40 +0000115 }
116
Mark Slee29050782006-09-29 00:12:30 +0000117 // Defer accept
118 #ifdef TCP_DEFER_ACCEPT
119 if (-1 == setsockopt(serverSocket_, SOL_SOCKET, TCP_DEFER_ACCEPT,
120 &one, sizeof(one))) {
boz6ded7752007-06-05 22:41:18 +0000121 GlobalOutput("TServerSocket::listen() TCP_DEFER_ACCEPT");
Mark Slee29050782006-09-29 00:12:30 +0000122 close();
Mark Sleef9831082007-02-20 20:59:21 +0000123 throw TTransportException(TTransportException::NOT_OPEN, "Could not set TCP_DEFER_ACCEPT");
Mark Slee29050782006-09-29 00:12:30 +0000124 }
125 #endif // #ifdef TCP_DEFER_ACCEPT
126
Mark Sleee8540632006-05-30 09:24:40 +0000127 // Turn linger off, don't want to block on calls to close
128 struct linger ling = {0, 0};
129 if (-1 == setsockopt(serverSocket_, SOL_SOCKET, SO_LINGER,
130 &ling, sizeof(ling))) {
Mark Sleee8540632006-05-30 09:24:40 +0000131 close();
boz6ded7752007-06-05 22:41:18 +0000132 GlobalOutput("TServerSocket::listen() SO_LINGER");
Mark Sleef9831082007-02-20 20:59:21 +0000133 throw TTransportException(TTransportException::NOT_OPEN, "Could not set SO_LINGER");
Mark Sleee8540632006-05-30 09:24:40 +0000134 }
135
Mark Slee29050782006-09-29 00:12:30 +0000136 // TCP Nodelay, speed over bandwidth
137 if (-1 == setsockopt(serverSocket_, IPPROTO_TCP, TCP_NODELAY,
138 &one, sizeof(one))) {
139 close();
boz6ded7752007-06-05 22:41:18 +0000140 GlobalOutput("setsockopt TCP_NODELAY");
Mark Sleef9831082007-02-20 20:59:21 +0000141 throw TTransportException(TTransportException::NOT_OPEN, "Could not set TCP_NODELAY");
Mark Slee29050782006-09-29 00:12:30 +0000142 }
143
Mark Sleea5a783f2007-03-02 19:41:08 +0000144 // Set NONBLOCK on the accept socket
145 int flags = fcntl(serverSocket_, F_GETFL, 0);
146 if (flags == -1) {
147 throw TTransportException(TTransportException::NOT_OPEN, "fcntl() failed");
148 }
Mark Slee561b5362007-03-09 19:26:29 +0000149
Mark Sleea5a783f2007-03-02 19:41:08 +0000150 if (-1 == fcntl(serverSocket_, F_SETFL, flags | O_NONBLOCK)) {
151 throw TTransportException(TTransportException::NOT_OPEN, "fcntl() failed");
152 }
153
boz1ea81ce2007-05-14 23:04:33 +0000154 // prepare the port information
boz1ea81ce2007-05-14 23:04:33 +0000155 // we may want to try to bind more than once, since SO_REUSEADDR doesn't
156 // always seem to work. The client can configure the retry variables.
157 int retries = 0;
158 do {
Mark Slee6d56eb92007-07-06 22:28:15 +0000159 if (0 == bind(serverSocket_, res->ai_addr, res->ai_addrlen)) {
boz1ea81ce2007-05-14 23:04:33 +0000160 break;
161 }
162
163 // use short circuit evaluation here to only sleep if we need to
164 } while ((retries++ < retryLimit_) && (sleep(retryDelay_) == 0));
165
Mark Slee6d56eb92007-07-06 22:28:15 +0000166 // free addrinfo
167 freeaddrinfo(res0);
168
boz1ea81ce2007-05-14 23:04:33 +0000169 // throw an error if we failed to bind properly
170 if (retries > retryLimit_) {
Mark Sleee8540632006-05-30 09:24:40 +0000171 char errbuf[1024];
172 sprintf(errbuf, "TServerSocket::listen() BIND %d", port_);
boz6ded7752007-06-05 22:41:18 +0000173 GlobalOutput(errbuf);
Mark Sleee8540632006-05-30 09:24:40 +0000174 close();
Mark Sleef9831082007-02-20 20:59:21 +0000175 throw TTransportException(TTransportException::NOT_OPEN, "Could not bind");
Mark Sleee8540632006-05-30 09:24:40 +0000176 }
177
178 // Call listen
179 if (-1 == ::listen(serverSocket_, acceptBacklog_)) {
boz6ded7752007-06-05 22:41:18 +0000180 GlobalOutput("TServerSocket::listen() LISTEN");
Mark Sleee8540632006-05-30 09:24:40 +0000181 close();
Mark Sleef9831082007-02-20 20:59:21 +0000182 throw TTransportException(TTransportException::NOT_OPEN, "Could not listen");
Mark Sleee8540632006-05-30 09:24:40 +0000183 }
184
185 // The socket is now listening!
Mark Sleee8540632006-05-30 09:24:40 +0000186}
187
Marc Slemko16698852006-08-04 03:16:10 +0000188shared_ptr<TTransport> TServerSocket::acceptImpl() {
Martin Kraemer10640d82007-02-03 01:59:12 +0000189 if (serverSocket_ < 0) {
Mark Sleef9831082007-02-20 20:59:21 +0000190 throw TTransportException(TTransportException::NOT_OPEN, "TServerSocket not listening");
Mark Sleee8540632006-05-30 09:24:40 +0000191 }
192
Mark Sleea5a783f2007-03-02 19:41:08 +0000193 fd_set fds;
194
Aditya Agarwal7859a572007-05-31 01:33:07 +0000195 int maxEintrs = 5;
196 int numEintrs = 0;
197
Mark Sleea5a783f2007-03-02 19:41:08 +0000198 while (true) {
199 FD_ZERO(&fds);
200 FD_SET(serverSocket_, &fds);
Mark Slee561b5362007-03-09 19:26:29 +0000201 if (intSock2_ >= 0) {
202 FD_SET(intSock2_, &fds);
Mark Sleea5a783f2007-03-02 19:41:08 +0000203 }
Mark Slee561b5362007-03-09 19:26:29 +0000204 int ret = select(serverSocket_+1, &fds, NULL, NULL, NULL);
Mark Sleea5a783f2007-03-02 19:41:08 +0000205
Mark Slee561b5362007-03-09 19:26:29 +0000206 if (ret < 0) {
Aditya Agarwal7859a572007-05-31 01:33:07 +0000207 // error cases
bozf83c9db2007-05-31 23:38:37 +0000208 if (errno == EINTR && (numEintrs++ < maxEintrs)) {
Aditya Agarwal7859a572007-05-31 01:33:07 +0000209 // EINTR needs to be handled manually and we can tolerate
210 // a certain number
211 continue;
212 }
boz6ded7752007-06-05 22:41:18 +0000213 GlobalOutput("TServerSocket::acceptImpl() select -1");
Mark Sleea5a783f2007-03-02 19:41:08 +0000214 throw TTransportException(TTransportException::UNKNOWN);
Mark Slee561b5362007-03-09 19:26:29 +0000215 } else if (ret > 0) {
216 // Check for an interrupt signal
217 if (intSock2_ >= 0 && FD_ISSET(intSock2_, &fds)) {
218 int8_t buf;
219 if (-1 == recv(intSock2_, &buf, sizeof(int8_t), 0)) {
boz6ded7752007-06-05 22:41:18 +0000220 GlobalOutput("TServerSocket::acceptImpl() interrupt receive");
Mark Slee561b5362007-03-09 19:26:29 +0000221 }
222 throw TTransportException(TTransportException::INTERRUPTED);
223 }
224 // Check for the actual server socket being ready
225 if (FD_ISSET(serverSocket_, &fds)) {
226 break;
227 }
228 } else {
boz6ded7752007-06-05 22:41:18 +0000229 GlobalOutput("TServerSocket::acceptImpl() select 0");
Mark Slee561b5362007-03-09 19:26:29 +0000230 throw TTransportException(TTransportException::UNKNOWN);
Mark Sleea5a783f2007-03-02 19:41:08 +0000231 }
232 }
233
Mark Slee6d56eb92007-07-06 22:28:15 +0000234 struct sockaddr_storage clientAddress;
Mark Sleee8540632006-05-30 09:24:40 +0000235 int size = sizeof(clientAddress);
236 int clientSocket = ::accept(serverSocket_,
237 (struct sockaddr *) &clientAddress,
238 (socklen_t *) &size);
239
Martin Kraemeree341cb2007-02-05 21:40:38 +0000240 if (clientSocket < 0) {
boz6ded7752007-06-05 22:41:18 +0000241 GlobalOutput("TServerSocket::accept()");
Martin Kraemere6c4fa62007-07-09 19:08:25 +0000242 char b_error[1024];
243 strerror_r(errno, b_error, sizeof(b_error));
244 throw TTransportException(TTransportException::UNKNOWN, string("ERROR:") + b_error);
Mark Sleee8540632006-05-30 09:24:40 +0000245 }
Mark Sleea5a783f2007-03-02 19:41:08 +0000246
247 // Make sure client socket is blocking
248 int flags = fcntl(clientSocket, F_GETFL, 0);
249 if (flags == -1) {
boz6ded7752007-06-05 22:41:18 +0000250 GlobalOutput("TServerSocket::select() fcntl GETFL");
Martin Kraemere6c4fa62007-07-09 19:08:25 +0000251 char b_error[1024];
252 strerror_r(errno, b_error, sizeof(b_error));
253 throw TTransportException(TTransportException::UNKNOWN, string("ERROR:") + b_error);
Mark Sleea5a783f2007-03-02 19:41:08 +0000254 }
255 if (-1 == fcntl(clientSocket, F_SETFL, flags & ~O_NONBLOCK)) {
boz6ded7752007-06-05 22:41:18 +0000256 GlobalOutput("TServerSocket::select() fcntl SETFL");
Martin Kraemere6c4fa62007-07-09 19:08:25 +0000257 char b_error[1024];
258 strerror_r(errno, b_error, sizeof(b_error));
259 throw TTransportException(TTransportException::UNKNOWN, string("ERROR:") + b_error);
Mark Sleea5a783f2007-03-02 19:41:08 +0000260 }
Martin Kraemer10640d82007-02-03 01:59:12 +0000261
Mark Slee29050782006-09-29 00:12:30 +0000262 shared_ptr<TSocket> client(new TSocket(clientSocket));
263 if (sendTimeout_ > 0) {
264 client->setSendTimeout(sendTimeout_);
265 }
266 if (recvTimeout_ > 0) {
267 client->setRecvTimeout(recvTimeout_);
Mark Sleea5a783f2007-03-02 19:41:08 +0000268 }
Aditya Agarwal7859a572007-05-31 01:33:07 +0000269
Mark Slee29050782006-09-29 00:12:30 +0000270 return client;
Mark Sleee8540632006-05-30 09:24:40 +0000271}
272
Mark Slee561b5362007-03-09 19:26:29 +0000273void TServerSocket::interrupt() {
274 if (intSock1_ >= 0) {
275 int8_t byte = 0;
276 if (-1 == send(intSock1_, &byte, sizeof(int8_t), 0)) {
boz6ded7752007-06-05 22:41:18 +0000277 GlobalOutput("TServerSocket::interrupt()");
Mark Slee561b5362007-03-09 19:26:29 +0000278 }
279 }
280}
281
Mark Sleee8540632006-05-30 09:24:40 +0000282void TServerSocket::close() {
Martin Kraemeree341cb2007-02-05 21:40:38 +0000283 if (serverSocket_ >= 0) {
Mark Sleee8540632006-05-30 09:24:40 +0000284 shutdown(serverSocket_, SHUT_RDWR);
285 ::close(serverSocket_);
286 }
Mark Slee561b5362007-03-09 19:26:29 +0000287 if (intSock1_ >= 0) {
288 ::close(intSock1_);
289 }
290 if (intSock2_ >= 0) {
291 ::close(intSock2_);
292 }
Martin Kraemeree341cb2007-02-05 21:40:38 +0000293 serverSocket_ = -1;
Mark Slee561b5362007-03-09 19:26:29 +0000294 intSock1_ = -1;
295 intSock2_ = -1;
Mark Sleee8540632006-05-30 09:24:40 +0000296}
Marc Slemko6f038a72006-08-03 18:58:09 +0000297
298}}} // facebook::thrift::transport