blob: 22346bddbcb011522b6e972de86def93e44d9a20 [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 Sleea5a783f2007-03-02 19:41:08 +000011#include <fcntl.h>
Mark Slee8d7e1f62006-06-07 06:48:56 +000012#include <errno.h>
Mark Sleee8540632006-05-30 09:24:40 +000013
Marc Slemkod42a2c22006-08-10 03:30:18 +000014#include "TSocket.h"
15#include "TServerSocket.h"
Marc Slemko16698852006-08-04 03:16:10 +000016#include <boost/shared_ptr.hpp>
Mark Sleee8540632006-05-30 09:24:40 +000017
Marc Slemko6f038a72006-08-03 18:58:09 +000018namespace facebook { namespace thrift { namespace transport {
19
Marc Slemko16698852006-08-04 03:16:10 +000020using namespace boost;
21
Mark Sleee8540632006-05-30 09:24:40 +000022TServerSocket::TServerSocket(int port) :
Mark Slee29050782006-09-29 00:12:30 +000023 port_(port),
Martin Kraemeree341cb2007-02-05 21:40:38 +000024 serverSocket_(-1),
Mark Slee29050782006-09-29 00:12:30 +000025 acceptBacklog_(1024),
26 sendTimeout_(0),
Mark Sleea5a783f2007-03-02 19:41:08 +000027 recvTimeout_(0),
boz1ea81ce2007-05-14 23:04:33 +000028 retryLimit_(0),
29 retryDelay_(0),
Mark Slee561b5362007-03-09 19:26:29 +000030 intSock1_(-1),
31 intSock2_(-1) {}
Mark Slee29050782006-09-29 00:12:30 +000032
33TServerSocket::TServerSocket(int port, int sendTimeout, int recvTimeout) :
34 port_(port),
Martin Kraemeree341cb2007-02-05 21:40:38 +000035 serverSocket_(-1),
Mark Slee29050782006-09-29 00:12:30 +000036 acceptBacklog_(1024),
37 sendTimeout_(sendTimeout),
Mark Sleea5a783f2007-03-02 19:41:08 +000038 recvTimeout_(recvTimeout),
boz1ea81ce2007-05-14 23:04:33 +000039 retryLimit_(0),
40 retryDelay_(0),
Mark Slee561b5362007-03-09 19:26:29 +000041 intSock1_(-1),
42 intSock2_(-1) {}
Mark Sleee8540632006-05-30 09:24:40 +000043
44TServerSocket::~TServerSocket() {
45 close();
46}
47
Mark Slee29050782006-09-29 00:12:30 +000048void TServerSocket::setSendTimeout(int sendTimeout) {
49 sendTimeout_ = sendTimeout;
50}
51
52void TServerSocket::setRecvTimeout(int recvTimeout) {
53 recvTimeout_ = recvTimeout;
54}
55
boz1ea81ce2007-05-14 23:04:33 +000056void TServerSocket::setRetryLimit(int retryLimit) {
57 retryLimit_ = retryLimit;
58}
59
60void TServerSocket::setRetryDelay(int retryDelay) {
61 retryDelay_ = retryDelay;
62}
63
Mark Slee8d7e1f62006-06-07 06:48:56 +000064void TServerSocket::listen() {
Mark Slee561b5362007-03-09 19:26:29 +000065 int sv[2];
66 if (-1 == socketpair(AF_LOCAL, SOCK_STREAM, 0, sv)) {
67 perror("TServerSocket::init()");
68 intSock1_ = -1;
69 intSock2_ = -1;
70 } else {
71 intSock1_ = sv[0];
72 intSock2_ = sv[1];
73 }
74
Mark Sleee8540632006-05-30 09:24:40 +000075 serverSocket_ = socket(AF_INET, SOCK_STREAM, 0);
76 if (serverSocket_ == -1) {
Mark Slee8d7e1f62006-06-07 06:48:56 +000077 perror("TServerSocket::listen() socket");
Mark Sleee8540632006-05-30 09:24:40 +000078 close();
Mark Sleef9831082007-02-20 20:59:21 +000079 throw TTransportException(TTransportException::NOT_OPEN, "Could not create server socket.");
Mark Sleee8540632006-05-30 09:24:40 +000080 }
81
82 // Set reusaddress to prevent 2MSL delay on accept
83 int one = 1;
84 if (-1 == setsockopt(serverSocket_, SOL_SOCKET, SO_REUSEADDR,
85 &one, sizeof(one))) {
86 perror("TServerSocket::listen() SO_REUSEADDR");
87 close();
Mark Sleef9831082007-02-20 20:59:21 +000088 throw TTransportException(TTransportException::NOT_OPEN, "Could not set SO_REUSEADDR");
Mark Sleee8540632006-05-30 09:24:40 +000089 }
90
Mark Slee29050782006-09-29 00:12:30 +000091 // Defer accept
92 #ifdef TCP_DEFER_ACCEPT
93 if (-1 == setsockopt(serverSocket_, SOL_SOCKET, TCP_DEFER_ACCEPT,
94 &one, sizeof(one))) {
95 perror("TServerSocket::listen() TCP_DEFER_ACCEPT");
96 close();
Mark Sleef9831082007-02-20 20:59:21 +000097 throw TTransportException(TTransportException::NOT_OPEN, "Could not set TCP_DEFER_ACCEPT");
Mark Slee29050782006-09-29 00:12:30 +000098 }
99 #endif // #ifdef TCP_DEFER_ACCEPT
100
Mark Sleee8540632006-05-30 09:24:40 +0000101 // Turn linger off, don't want to block on calls to close
102 struct linger ling = {0, 0};
103 if (-1 == setsockopt(serverSocket_, SOL_SOCKET, SO_LINGER,
104 &ling, sizeof(ling))) {
Mark Sleee8540632006-05-30 09:24:40 +0000105 close();
Mark Slee8d7e1f62006-06-07 06:48:56 +0000106 perror("TServerSocket::listen() SO_LINGER");
Mark Sleef9831082007-02-20 20:59:21 +0000107 throw TTransportException(TTransportException::NOT_OPEN, "Could not set SO_LINGER");
Mark Sleee8540632006-05-30 09:24:40 +0000108 }
109
Mark Slee29050782006-09-29 00:12:30 +0000110 // TCP Nodelay, speed over bandwidth
111 if (-1 == setsockopt(serverSocket_, IPPROTO_TCP, TCP_NODELAY,
112 &one, sizeof(one))) {
113 close();
114 perror("setsockopt TCP_NODELAY");
Mark Sleef9831082007-02-20 20:59:21 +0000115 throw TTransportException(TTransportException::NOT_OPEN, "Could not set TCP_NODELAY");
Mark Slee29050782006-09-29 00:12:30 +0000116 }
117
Mark Sleea5a783f2007-03-02 19:41:08 +0000118 // Set NONBLOCK on the accept socket
119 int flags = fcntl(serverSocket_, F_GETFL, 0);
120 if (flags == -1) {
121 throw TTransportException(TTransportException::NOT_OPEN, "fcntl() failed");
122 }
Mark Slee561b5362007-03-09 19:26:29 +0000123
Mark Sleea5a783f2007-03-02 19:41:08 +0000124 if (-1 == fcntl(serverSocket_, F_SETFL, flags | O_NONBLOCK)) {
125 throw TTransportException(TTransportException::NOT_OPEN, "fcntl() failed");
126 }
127
boz1ea81ce2007-05-14 23:04:33 +0000128 // prepare the port information
Mark Sleee8540632006-05-30 09:24:40 +0000129 struct sockaddr_in addr;
130 memset(&addr, 0, sizeof(addr));
131 addr.sin_family = AF_INET;
132 addr.sin_port = htons(port_);
133 addr.sin_addr.s_addr = INADDR_ANY;
boz1ea81ce2007-05-14 23:04:33 +0000134
135 // we may want to try to bind more than once, since SO_REUSEADDR doesn't
136 // always seem to work. The client can configure the retry variables.
137 int retries = 0;
138 do {
139 if (0 == bind(serverSocket_, (struct sockaddr *)&addr, sizeof(addr))) {
140 break;
141 }
142
143 // use short circuit evaluation here to only sleep if we need to
144 } while ((retries++ < retryLimit_) && (sleep(retryDelay_) == 0));
145
146 // throw an error if we failed to bind properly
147 if (retries > retryLimit_) {
Mark Sleee8540632006-05-30 09:24:40 +0000148 char errbuf[1024];
149 sprintf(errbuf, "TServerSocket::listen() BIND %d", port_);
150 perror(errbuf);
151 close();
Mark Sleef9831082007-02-20 20:59:21 +0000152 throw TTransportException(TTransportException::NOT_OPEN, "Could not bind");
Mark Sleee8540632006-05-30 09:24:40 +0000153 }
154
155 // Call listen
156 if (-1 == ::listen(serverSocket_, acceptBacklog_)) {
157 perror("TServerSocket::listen() LISTEN");
158 close();
Mark Sleef9831082007-02-20 20:59:21 +0000159 throw TTransportException(TTransportException::NOT_OPEN, "Could not listen");
Mark Sleee8540632006-05-30 09:24:40 +0000160 }
161
162 // The socket is now listening!
Mark Sleee8540632006-05-30 09:24:40 +0000163}
164
Marc Slemko16698852006-08-04 03:16:10 +0000165shared_ptr<TTransport> TServerSocket::acceptImpl() {
Martin Kraemer10640d82007-02-03 01:59:12 +0000166 if (serverSocket_ < 0) {
Mark Sleef9831082007-02-20 20:59:21 +0000167 throw TTransportException(TTransportException::NOT_OPEN, "TServerSocket not listening");
Mark Sleee8540632006-05-30 09:24:40 +0000168 }
169
Mark Sleea5a783f2007-03-02 19:41:08 +0000170 fd_set fds;
171
Aditya Agarwal7859a572007-05-31 01:33:07 +0000172 int maxEintrs = 5;
173 int numEintrs = 0;
174
Mark Sleea5a783f2007-03-02 19:41:08 +0000175 while (true) {
176 FD_ZERO(&fds);
177 FD_SET(serverSocket_, &fds);
Mark Slee561b5362007-03-09 19:26:29 +0000178 if (intSock2_ >= 0) {
179 FD_SET(intSock2_, &fds);
Mark Sleea5a783f2007-03-02 19:41:08 +0000180 }
Mark Slee561b5362007-03-09 19:26:29 +0000181 int ret = select(serverSocket_+1, &fds, NULL, NULL, NULL);
Mark Sleea5a783f2007-03-02 19:41:08 +0000182
Mark Slee561b5362007-03-09 19:26:29 +0000183 if (ret < 0) {
Aditya Agarwal7859a572007-05-31 01:33:07 +0000184 // error cases
185 if (error == EINTR && (numEintrs++ < maxEintrs)) {
186 // EINTR needs to be handled manually and we can tolerate
187 // a certain number
188 continue;
189 }
Mark Slee561b5362007-03-09 19:26:29 +0000190 perror("TServerSocket::acceptImpl() select -1");
Mark Sleea5a783f2007-03-02 19:41:08 +0000191 throw TTransportException(TTransportException::UNKNOWN);
Mark Slee561b5362007-03-09 19:26:29 +0000192 } else if (ret > 0) {
193 // Check for an interrupt signal
194 if (intSock2_ >= 0 && FD_ISSET(intSock2_, &fds)) {
195 int8_t buf;
196 if (-1 == recv(intSock2_, &buf, sizeof(int8_t), 0)) {
197 perror("TServerSocket::acceptImpl() interrupt receive");
198 }
199 throw TTransportException(TTransportException::INTERRUPTED);
200 }
201 // Check for the actual server socket being ready
202 if (FD_ISSET(serverSocket_, &fds)) {
203 break;
204 }
205 } else {
206 perror("TServerSocket::acceptImpl() select 0");
207 throw TTransportException(TTransportException::UNKNOWN);
Mark Sleea5a783f2007-03-02 19:41:08 +0000208 }
209 }
210
Mark Sleee8540632006-05-30 09:24:40 +0000211 struct sockaddr_in clientAddress;
212 int size = sizeof(clientAddress);
213 int clientSocket = ::accept(serverSocket_,
214 (struct sockaddr *) &clientAddress,
215 (socklen_t *) &size);
216
Martin Kraemeree341cb2007-02-05 21:40:38 +0000217 if (clientSocket < 0) {
Mark Sleee8540632006-05-30 09:24:40 +0000218 perror("TServerSocket::accept()");
Mark Sleef9831082007-02-20 20:59:21 +0000219 throw TTransportException(TTransportException::UNKNOWN, "ERROR:" + errno);
Mark Sleee8540632006-05-30 09:24:40 +0000220 }
Mark Sleea5a783f2007-03-02 19:41:08 +0000221
222 // Make sure client socket is blocking
223 int flags = fcntl(clientSocket, F_GETFL, 0);
224 if (flags == -1) {
225 perror("TServerSocket::select() fcntl GETFL");
226 throw TTransportException(TTransportException::UNKNOWN, "ERROR:" + errno);
227 }
228 if (-1 == fcntl(clientSocket, F_SETFL, flags & ~O_NONBLOCK)) {
229 perror("TServerSocket::select() fcntl SETFL");
230 throw TTransportException(TTransportException::UNKNOWN, "ERROR:" + errno);
231 }
Martin Kraemer10640d82007-02-03 01:59:12 +0000232
Mark Slee29050782006-09-29 00:12:30 +0000233 shared_ptr<TSocket> client(new TSocket(clientSocket));
234 if (sendTimeout_ > 0) {
235 client->setSendTimeout(sendTimeout_);
236 }
237 if (recvTimeout_ > 0) {
238 client->setRecvTimeout(recvTimeout_);
Mark Sleea5a783f2007-03-02 19:41:08 +0000239 }
Aditya Agarwal7859a572007-05-31 01:33:07 +0000240
Mark Slee29050782006-09-29 00:12:30 +0000241 return client;
Mark Sleee8540632006-05-30 09:24:40 +0000242}
243
Mark Slee561b5362007-03-09 19:26:29 +0000244void TServerSocket::interrupt() {
245 if (intSock1_ >= 0) {
246 int8_t byte = 0;
247 if (-1 == send(intSock1_, &byte, sizeof(int8_t), 0)) {
248 perror("TServerSocket::interrupt()");
249 }
250 }
251}
252
Mark Sleee8540632006-05-30 09:24:40 +0000253void TServerSocket::close() {
Martin Kraemeree341cb2007-02-05 21:40:38 +0000254 if (serverSocket_ >= 0) {
Mark Sleee8540632006-05-30 09:24:40 +0000255 shutdown(serverSocket_, SHUT_RDWR);
256 ::close(serverSocket_);
257 }
Mark Slee561b5362007-03-09 19:26:29 +0000258 if (intSock1_ >= 0) {
259 ::close(intSock1_);
260 }
261 if (intSock2_ >= 0) {
262 ::close(intSock2_);
263 }
Martin Kraemeree341cb2007-02-05 21:40:38 +0000264 serverSocket_ = -1;
Mark Slee561b5362007-03-09 19:26:29 +0000265 intSock1_ = -1;
266 intSock2_ = -1;
Mark Sleee8540632006-05-30 09:24:40 +0000267}
Marc Slemko6f038a72006-08-03 18:58:09 +0000268
269}}} // facebook::thrift::transport