blob: 69d35bfc8c39027fc0d8047b3e4095dd3d3468a1 [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 Sleedd564972007-08-21 02:39:57 +00009#include <sys/types.h>
Mark Sleee8540632006-05-30 09:24:40 +000010#include <netinet/in.h>
Mark Slee29050782006-09-29 00:12:30 +000011#include <netinet/tcp.h>
Mark Slee6d56eb92007-07-06 22:28:15 +000012#include <netdb.h>
Mark Sleea5a783f2007-03-02 19:41:08 +000013#include <fcntl.h>
Mark Slee8d7e1f62006-06-07 06:48:56 +000014#include <errno.h>
Mark Sleee8540632006-05-30 09:24:40 +000015
Marc Slemkod42a2c22006-08-10 03:30:18 +000016#include "TSocket.h"
17#include "TServerSocket.h"
Marc Slemko16698852006-08-04 03:16:10 +000018#include <boost/shared_ptr.hpp>
Mark Sleee8540632006-05-30 09:24:40 +000019
Mark Slee256bdc42007-11-27 08:42:19 +000020namespace facebook { namespace thrift { namespace transport {
Marc Slemko6f038a72006-08-03 18:58:09 +000021
Martin Kraemere6c4fa62007-07-09 19:08:25 +000022using namespace std;
David Reissd4a269c2007-08-23 02:37:19 +000023using boost::shared_ptr;
Marc Slemko16698852006-08-04 03:16:10 +000024
Mark Sleee8540632006-05-30 09:24:40 +000025TServerSocket::TServerSocket(int port) :
Mark Slee29050782006-09-29 00:12:30 +000026 port_(port),
Martin Kraemeree341cb2007-02-05 21:40:38 +000027 serverSocket_(-1),
Mark Slee29050782006-09-29 00:12:30 +000028 acceptBacklog_(1024),
29 sendTimeout_(0),
Mark Sleea5a783f2007-03-02 19:41:08 +000030 recvTimeout_(0),
boz1ea81ce2007-05-14 23:04:33 +000031 retryLimit_(0),
32 retryDelay_(0),
Mark Slee561b5362007-03-09 19:26:29 +000033 intSock1_(-1),
34 intSock2_(-1) {}
Mark Slee29050782006-09-29 00:12:30 +000035
36TServerSocket::TServerSocket(int port, int sendTimeout, int recvTimeout) :
37 port_(port),
Martin Kraemeree341cb2007-02-05 21:40:38 +000038 serverSocket_(-1),
Mark Slee29050782006-09-29 00:12:30 +000039 acceptBacklog_(1024),
40 sendTimeout_(sendTimeout),
Mark Sleea5a783f2007-03-02 19:41:08 +000041 recvTimeout_(recvTimeout),
boz1ea81ce2007-05-14 23:04:33 +000042 retryLimit_(0),
43 retryDelay_(0),
Mark Slee561b5362007-03-09 19:26:29 +000044 intSock1_(-1),
45 intSock2_(-1) {}
Mark Sleee8540632006-05-30 09:24:40 +000046
47TServerSocket::~TServerSocket() {
48 close();
49}
50
Mark Slee29050782006-09-29 00:12:30 +000051void TServerSocket::setSendTimeout(int sendTimeout) {
52 sendTimeout_ = sendTimeout;
53}
54
55void TServerSocket::setRecvTimeout(int recvTimeout) {
56 recvTimeout_ = recvTimeout;
57}
58
boz1ea81ce2007-05-14 23:04:33 +000059void TServerSocket::setRetryLimit(int retryLimit) {
60 retryLimit_ = retryLimit;
61}
62
63void TServerSocket::setRetryDelay(int retryDelay) {
64 retryDelay_ = retryDelay;
65}
66
Mark Slee8d7e1f62006-06-07 06:48:56 +000067void TServerSocket::listen() {
Mark Slee561b5362007-03-09 19:26:29 +000068 int sv[2];
69 if (-1 == socketpair(AF_LOCAL, SOCK_STREAM, 0, sv)) {
boz6ded7752007-06-05 22:41:18 +000070 GlobalOutput("TServerSocket::init()");
Mark Slee561b5362007-03-09 19:26:29 +000071 intSock1_ = -1;
72 intSock2_ = -1;
73 } else {
Mark Sleee02385b2007-06-09 01:21:16 +000074 intSock1_ = sv[1];
75 intSock2_ = sv[0];
Mark Slee561b5362007-03-09 19:26:29 +000076 }
77
Mark Slee6d56eb92007-07-06 22:28:15 +000078 struct addrinfo hints, *res, *res0;
79 int error;
80 char port[sizeof("65536") + 1];
81 memset(&hints, 0, sizeof(hints));
82 hints.ai_family = PF_UNSPEC;
83 hints.ai_socktype = SOCK_STREAM;
Mark Slee256bdc42007-11-27 08:42:19 +000084 hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
Mark Slee6d56eb92007-07-06 22:28:15 +000085 sprintf(port, "%d", port_);
86
87 // Wildcard address
88 error = getaddrinfo(NULL, port, &hints, &res0);
89 if (error) {
90 fprintf(stderr, "getaddrinfo %d: %s\n", error, gai_strerror(error));
91 close();
92 throw TTransportException(TTransportException::NOT_OPEN, "Could not resolve host for server socket.");
93 }
94
95 // Pick the ipv6 address first since ipv4 addresses can be mapped
96 // into ipv6 space.
97 for (res = res0; res; res = res->ai_next) {
98 if (res->ai_family == AF_INET6 || res->ai_next == NULL)
99 break;
100 }
Mark Slee256bdc42007-11-27 08:42:19 +0000101
Mark Slee6d56eb92007-07-06 22:28:15 +0000102 serverSocket_ = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
Mark Sleee8540632006-05-30 09:24:40 +0000103 if (serverSocket_ == -1) {
boz6ded7752007-06-05 22:41:18 +0000104 GlobalOutput("TServerSocket::listen() socket");
Mark Sleee8540632006-05-30 09:24:40 +0000105 close();
Mark Sleef9831082007-02-20 20:59:21 +0000106 throw TTransportException(TTransportException::NOT_OPEN, "Could not create server socket.");
Mark Sleee8540632006-05-30 09:24:40 +0000107 }
108
109 // Set reusaddress to prevent 2MSL delay on accept
110 int one = 1;
111 if (-1 == setsockopt(serverSocket_, SOL_SOCKET, SO_REUSEADDR,
112 &one, sizeof(one))) {
boz6ded7752007-06-05 22:41:18 +0000113 GlobalOutput("TServerSocket::listen() SO_REUSEADDR");
Mark Sleee8540632006-05-30 09:24:40 +0000114 close();
Mark Sleef9831082007-02-20 20:59:21 +0000115 throw TTransportException(TTransportException::NOT_OPEN, "Could not set SO_REUSEADDR");
Mark Sleee8540632006-05-30 09:24:40 +0000116 }
117
Mark Slee29050782006-09-29 00:12:30 +0000118 // Defer accept
119 #ifdef TCP_DEFER_ACCEPT
120 if (-1 == setsockopt(serverSocket_, SOL_SOCKET, TCP_DEFER_ACCEPT,
121 &one, sizeof(one))) {
boz6ded7752007-06-05 22:41:18 +0000122 GlobalOutput("TServerSocket::listen() TCP_DEFER_ACCEPT");
Mark Slee29050782006-09-29 00:12:30 +0000123 close();
Mark Sleef9831082007-02-20 20:59:21 +0000124 throw TTransportException(TTransportException::NOT_OPEN, "Could not set TCP_DEFER_ACCEPT");
Mark Slee29050782006-09-29 00:12:30 +0000125 }
126 #endif // #ifdef TCP_DEFER_ACCEPT
127
Mark Sleee8540632006-05-30 09:24:40 +0000128 // Turn linger off, don't want to block on calls to close
129 struct linger ling = {0, 0};
130 if (-1 == setsockopt(serverSocket_, SOL_SOCKET, SO_LINGER,
131 &ling, sizeof(ling))) {
Mark Sleee8540632006-05-30 09:24:40 +0000132 close();
boz6ded7752007-06-05 22:41:18 +0000133 GlobalOutput("TServerSocket::listen() SO_LINGER");
Mark Sleef9831082007-02-20 20:59:21 +0000134 throw TTransportException(TTransportException::NOT_OPEN, "Could not set SO_LINGER");
Mark Sleee8540632006-05-30 09:24:40 +0000135 }
136
Mark Slee29050782006-09-29 00:12:30 +0000137 // TCP Nodelay, speed over bandwidth
138 if (-1 == setsockopt(serverSocket_, IPPROTO_TCP, TCP_NODELAY,
139 &one, sizeof(one))) {
140 close();
boz6ded7752007-06-05 22:41:18 +0000141 GlobalOutput("setsockopt TCP_NODELAY");
Mark Sleef9831082007-02-20 20:59:21 +0000142 throw TTransportException(TTransportException::NOT_OPEN, "Could not set TCP_NODELAY");
Mark Slee29050782006-09-29 00:12:30 +0000143 }
144
Mark Sleea5a783f2007-03-02 19:41:08 +0000145 // Set NONBLOCK on the accept socket
146 int flags = fcntl(serverSocket_, F_GETFL, 0);
147 if (flags == -1) {
148 throw TTransportException(TTransportException::NOT_OPEN, "fcntl() failed");
149 }
Mark Slee561b5362007-03-09 19:26:29 +0000150
Mark Sleea5a783f2007-03-02 19:41:08 +0000151 if (-1 == fcntl(serverSocket_, F_SETFL, flags | O_NONBLOCK)) {
152 throw TTransportException(TTransportException::NOT_OPEN, "fcntl() failed");
153 }
154
boz1ea81ce2007-05-14 23:04:33 +0000155 // prepare the port information
Mark Slee256bdc42007-11-27 08:42:19 +0000156 // we may want to try to bind more than once, since SO_REUSEADDR doesn't
boz1ea81ce2007-05-14 23:04:33 +0000157 // always seem to work. The client can configure the retry variables.
158 int retries = 0;
159 do {
Mark Slee6d56eb92007-07-06 22:28:15 +0000160 if (0 == bind(serverSocket_, res->ai_addr, res->ai_addrlen)) {
boz1ea81ce2007-05-14 23:04:33 +0000161 break;
162 }
163
164 // use short circuit evaluation here to only sleep if we need to
165 } while ((retries++ < retryLimit_) && (sleep(retryDelay_) == 0));
166
Mark Slee6d56eb92007-07-06 22:28:15 +0000167 // free addrinfo
168 freeaddrinfo(res0);
Mark Slee256bdc42007-11-27 08:42:19 +0000169
boz1ea81ce2007-05-14 23:04:33 +0000170 // throw an error if we failed to bind properly
171 if (retries > retryLimit_) {
Mark Sleee8540632006-05-30 09:24:40 +0000172 char errbuf[1024];
173 sprintf(errbuf, "TServerSocket::listen() BIND %d", port_);
boz6ded7752007-06-05 22:41:18 +0000174 GlobalOutput(errbuf);
Mark Sleee8540632006-05-30 09:24:40 +0000175 close();
Mark Sleef9831082007-02-20 20:59:21 +0000176 throw TTransportException(TTransportException::NOT_OPEN, "Could not bind");
Mark Sleee8540632006-05-30 09:24:40 +0000177 }
178
179 // Call listen
180 if (-1 == ::listen(serverSocket_, acceptBacklog_)) {
boz6ded7752007-06-05 22:41:18 +0000181 GlobalOutput("TServerSocket::listen() LISTEN");
Mark Sleee8540632006-05-30 09:24:40 +0000182 close();
Mark Sleef9831082007-02-20 20:59:21 +0000183 throw TTransportException(TTransportException::NOT_OPEN, "Could not listen");
Mark Sleee8540632006-05-30 09:24:40 +0000184 }
185
186 // The socket is now listening!
Mark Sleee8540632006-05-30 09:24:40 +0000187}
188
Marc Slemko16698852006-08-04 03:16:10 +0000189shared_ptr<TTransport> TServerSocket::acceptImpl() {
Martin Kraemer10640d82007-02-03 01:59:12 +0000190 if (serverSocket_ < 0) {
Mark Sleef9831082007-02-20 20:59:21 +0000191 throw TTransportException(TTransportException::NOT_OPEN, "TServerSocket not listening");
Mark Sleee8540632006-05-30 09:24:40 +0000192 }
193
Mark Sleea5a783f2007-03-02 19:41:08 +0000194 fd_set fds;
195
Aditya Agarwal7859a572007-05-31 01:33:07 +0000196 int maxEintrs = 5;
197 int numEintrs = 0;
198
Mark Sleea5a783f2007-03-02 19:41:08 +0000199 while (true) {
200 FD_ZERO(&fds);
201 FD_SET(serverSocket_, &fds);
Mark Slee561b5362007-03-09 19:26:29 +0000202 if (intSock2_ >= 0) {
203 FD_SET(intSock2_, &fds);
Mark Sleea5a783f2007-03-02 19:41:08 +0000204 }
Mark Slee561b5362007-03-09 19:26:29 +0000205 int ret = select(serverSocket_+1, &fds, NULL, NULL, NULL);
Mark Sleea5a783f2007-03-02 19:41:08 +0000206
Mark Slee561b5362007-03-09 19:26:29 +0000207 if (ret < 0) {
Aditya Agarwal7859a572007-05-31 01:33:07 +0000208 // error cases
bozf83c9db2007-05-31 23:38:37 +0000209 if (errno == EINTR && (numEintrs++ < maxEintrs)) {
Mark Slee256bdc42007-11-27 08:42:19 +0000210 // EINTR needs to be handled manually and we can tolerate
Aditya Agarwal7859a572007-05-31 01:33:07 +0000211 // a certain number
212 continue;
213 }
boz6ded7752007-06-05 22:41:18 +0000214 GlobalOutput("TServerSocket::acceptImpl() select -1");
Mark Sleea5a783f2007-03-02 19:41:08 +0000215 throw TTransportException(TTransportException::UNKNOWN);
Mark Slee561b5362007-03-09 19:26:29 +0000216 } else if (ret > 0) {
217 // Check for an interrupt signal
Mark Slee256bdc42007-11-27 08:42:19 +0000218 if (intSock2_ >= 0 && FD_ISSET(intSock2_, &fds)) {
Mark Slee561b5362007-03-09 19:26:29 +0000219 int8_t buf;
220 if (-1 == recv(intSock2_, &buf, sizeof(int8_t), 0)) {
boz6ded7752007-06-05 22:41:18 +0000221 GlobalOutput("TServerSocket::acceptImpl() interrupt receive");
Mark Slee561b5362007-03-09 19:26:29 +0000222 }
223 throw TTransportException(TTransportException::INTERRUPTED);
224 }
225 // Check for the actual server socket being ready
226 if (FD_ISSET(serverSocket_, &fds)) {
227 break;
228 }
229 } else {
boz6ded7752007-06-05 22:41:18 +0000230 GlobalOutput("TServerSocket::acceptImpl() select 0");
Mark Slee256bdc42007-11-27 08:42:19 +0000231 throw TTransportException(TTransportException::UNKNOWN);
Mark Sleea5a783f2007-03-02 19:41:08 +0000232 }
233 }
234
Mark Slee6d56eb92007-07-06 22:28:15 +0000235 struct sockaddr_storage clientAddress;
Mark Sleee8540632006-05-30 09:24:40 +0000236 int size = sizeof(clientAddress);
237 int clientSocket = ::accept(serverSocket_,
238 (struct sockaddr *) &clientAddress,
239 (socklen_t *) &size);
Mark Slee256bdc42007-11-27 08:42:19 +0000240
Martin Kraemeree341cb2007-02-05 21:40:38 +0000241 if (clientSocket < 0) {
David Reissbc3dddb2007-08-22 23:20:24 +0000242 int errno_copy = errno;
boz6ded7752007-06-05 22:41:18 +0000243 GlobalOutput("TServerSocket::accept()");
David Reissbc3dddb2007-08-22 23:20:24 +0000244 throw TTransportException(TTransportException::UNKNOWN, "accept()", errno_copy);
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) {
David Reissbc3dddb2007-08-22 23:20:24 +0000250 int errno_copy = errno;
boz6ded7752007-06-05 22:41:18 +0000251 GlobalOutput("TServerSocket::select() fcntl GETFL");
David Reissbc3dddb2007-08-22 23:20:24 +0000252 throw TTransportException(TTransportException::UNKNOWN, "fcntl(F_GETFL)", errno_copy);
Mark Sleea5a783f2007-03-02 19:41:08 +0000253 }
254 if (-1 == fcntl(clientSocket, F_SETFL, flags & ~O_NONBLOCK)) {
David Reissbc3dddb2007-08-22 23:20:24 +0000255 int errno_copy = errno;
boz6ded7752007-06-05 22:41:18 +0000256 GlobalOutput("TServerSocket::select() fcntl SETFL");
David Reissbc3dddb2007-08-22 23:20:24 +0000257 throw TTransportException(TTransportException::UNKNOWN, "fcntl(F_SETFL)", errno_copy);
Mark Sleea5a783f2007-03-02 19:41:08 +0000258 }
Mark Slee256bdc42007-11-27 08:42:19 +0000259
Mark Slee29050782006-09-29 00:12:30 +0000260 shared_ptr<TSocket> client(new TSocket(clientSocket));
261 if (sendTimeout_ > 0) {
262 client->setSendTimeout(sendTimeout_);
263 }
264 if (recvTimeout_ > 0) {
265 client->setRecvTimeout(recvTimeout_);
Mark Sleea5a783f2007-03-02 19:41:08 +0000266 }
Mark Slee256bdc42007-11-27 08:42:19 +0000267
Mark Slee29050782006-09-29 00:12:30 +0000268 return client;
Mark Sleee8540632006-05-30 09:24:40 +0000269}
270
Mark Slee561b5362007-03-09 19:26:29 +0000271void TServerSocket::interrupt() {
272 if (intSock1_ >= 0) {
273 int8_t byte = 0;
274 if (-1 == send(intSock1_, &byte, sizeof(int8_t), 0)) {
boz6ded7752007-06-05 22:41:18 +0000275 GlobalOutput("TServerSocket::interrupt()");
Mark Slee561b5362007-03-09 19:26:29 +0000276 }
277 }
278}
279
Mark Sleee8540632006-05-30 09:24:40 +0000280void TServerSocket::close() {
Martin Kraemeree341cb2007-02-05 21:40:38 +0000281 if (serverSocket_ >= 0) {
Mark Sleee8540632006-05-30 09:24:40 +0000282 shutdown(serverSocket_, SHUT_RDWR);
283 ::close(serverSocket_);
284 }
Mark Slee561b5362007-03-09 19:26:29 +0000285 if (intSock1_ >= 0) {
286 ::close(intSock1_);
287 }
288 if (intSock2_ >= 0) {
289 ::close(intSock2_);
290 }
Martin Kraemeree341cb2007-02-05 21:40:38 +0000291 serverSocket_ = -1;
Mark Slee561b5362007-03-09 19:26:29 +0000292 intSock1_ = -1;
293 intSock2_ = -1;
Mark Sleee8540632006-05-30 09:24:40 +0000294}
Marc Slemko6f038a72006-08-03 18:58:09 +0000295
296}}} // facebook::thrift::transport