blob: 3b11e9daf1a07bf9316cd1be2c82f780daf6927c [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),
Mark Slee561b5362007-03-09 19:26:29 +000028 intSock1_(-1),
29 intSock2_(-1) {}
Mark Slee29050782006-09-29 00:12:30 +000030
31TServerSocket::TServerSocket(int port, int sendTimeout, int recvTimeout) :
32 port_(port),
Martin Kraemeree341cb2007-02-05 21:40:38 +000033 serverSocket_(-1),
Mark Slee29050782006-09-29 00:12:30 +000034 acceptBacklog_(1024),
35 sendTimeout_(sendTimeout),
Mark Sleea5a783f2007-03-02 19:41:08 +000036 recvTimeout_(recvTimeout),
Mark Slee561b5362007-03-09 19:26:29 +000037 intSock1_(-1),
38 intSock2_(-1) {}
Mark Sleee8540632006-05-30 09:24:40 +000039
40TServerSocket::~TServerSocket() {
41 close();
42}
43
Mark Slee29050782006-09-29 00:12:30 +000044void TServerSocket::setSendTimeout(int sendTimeout) {
45 sendTimeout_ = sendTimeout;
46}
47
48void TServerSocket::setRecvTimeout(int recvTimeout) {
49 recvTimeout_ = recvTimeout;
50}
51
Mark Slee8d7e1f62006-06-07 06:48:56 +000052void TServerSocket::listen() {
Mark Slee561b5362007-03-09 19:26:29 +000053 int sv[2];
54 if (-1 == socketpair(AF_LOCAL, SOCK_STREAM, 0, sv)) {
55 perror("TServerSocket::init()");
56 intSock1_ = -1;
57 intSock2_ = -1;
58 } else {
59 intSock1_ = sv[0];
60 intSock2_ = sv[1];
61 }
62
Mark Sleee8540632006-05-30 09:24:40 +000063 serverSocket_ = socket(AF_INET, SOCK_STREAM, 0);
64 if (serverSocket_ == -1) {
Mark Slee8d7e1f62006-06-07 06:48:56 +000065 perror("TServerSocket::listen() socket");
Mark Sleee8540632006-05-30 09:24:40 +000066 close();
Mark Sleef9831082007-02-20 20:59:21 +000067 throw TTransportException(TTransportException::NOT_OPEN, "Could not create server socket.");
Mark Sleee8540632006-05-30 09:24:40 +000068 }
69
70 // Set reusaddress to prevent 2MSL delay on accept
71 int one = 1;
72 if (-1 == setsockopt(serverSocket_, SOL_SOCKET, SO_REUSEADDR,
73 &one, sizeof(one))) {
74 perror("TServerSocket::listen() SO_REUSEADDR");
75 close();
Mark Sleef9831082007-02-20 20:59:21 +000076 throw TTransportException(TTransportException::NOT_OPEN, "Could not set SO_REUSEADDR");
Mark Sleee8540632006-05-30 09:24:40 +000077 }
78
Mark Slee29050782006-09-29 00:12:30 +000079 // Defer accept
80 #ifdef TCP_DEFER_ACCEPT
81 if (-1 == setsockopt(serverSocket_, SOL_SOCKET, TCP_DEFER_ACCEPT,
82 &one, sizeof(one))) {
83 perror("TServerSocket::listen() TCP_DEFER_ACCEPT");
84 close();
Mark Sleef9831082007-02-20 20:59:21 +000085 throw TTransportException(TTransportException::NOT_OPEN, "Could not set TCP_DEFER_ACCEPT");
Mark Slee29050782006-09-29 00:12:30 +000086 }
87 #endif // #ifdef TCP_DEFER_ACCEPT
88
Mark Sleee8540632006-05-30 09:24:40 +000089 // Turn linger off, don't want to block on calls to close
90 struct linger ling = {0, 0};
91 if (-1 == setsockopt(serverSocket_, SOL_SOCKET, SO_LINGER,
92 &ling, sizeof(ling))) {
Mark Sleee8540632006-05-30 09:24:40 +000093 close();
Mark Slee8d7e1f62006-06-07 06:48:56 +000094 perror("TServerSocket::listen() SO_LINGER");
Mark Sleef9831082007-02-20 20:59:21 +000095 throw TTransportException(TTransportException::NOT_OPEN, "Could not set SO_LINGER");
Mark Sleee8540632006-05-30 09:24:40 +000096 }
97
Mark Slee29050782006-09-29 00:12:30 +000098 // TCP Nodelay, speed over bandwidth
99 if (-1 == setsockopt(serverSocket_, IPPROTO_TCP, TCP_NODELAY,
100 &one, sizeof(one))) {
101 close();
102 perror("setsockopt TCP_NODELAY");
Mark Sleef9831082007-02-20 20:59:21 +0000103 throw TTransportException(TTransportException::NOT_OPEN, "Could not set TCP_NODELAY");
Mark Slee29050782006-09-29 00:12:30 +0000104 }
105
Mark Sleea5a783f2007-03-02 19:41:08 +0000106 // Set NONBLOCK on the accept socket
107 int flags = fcntl(serverSocket_, F_GETFL, 0);
108 if (flags == -1) {
109 throw TTransportException(TTransportException::NOT_OPEN, "fcntl() failed");
110 }
Mark Slee561b5362007-03-09 19:26:29 +0000111
Mark Sleea5a783f2007-03-02 19:41:08 +0000112 if (-1 == fcntl(serverSocket_, F_SETFL, flags | O_NONBLOCK)) {
113 throw TTransportException(TTransportException::NOT_OPEN, "fcntl() failed");
114 }
115
Mark Sleee8540632006-05-30 09:24:40 +0000116 // Bind to a port
117 struct sockaddr_in addr;
118 memset(&addr, 0, sizeof(addr));
119 addr.sin_family = AF_INET;
120 addr.sin_port = htons(port_);
121 addr.sin_addr.s_addr = INADDR_ANY;
122 if (-1 == bind(serverSocket_, (struct sockaddr *)&addr, sizeof(addr))) {
123 char errbuf[1024];
124 sprintf(errbuf, "TServerSocket::listen() BIND %d", port_);
125 perror(errbuf);
126 close();
Mark Sleef9831082007-02-20 20:59:21 +0000127 throw TTransportException(TTransportException::NOT_OPEN, "Could not bind");
Mark Sleee8540632006-05-30 09:24:40 +0000128 }
129
130 // Call listen
131 if (-1 == ::listen(serverSocket_, acceptBacklog_)) {
132 perror("TServerSocket::listen() LISTEN");
133 close();
Mark Sleef9831082007-02-20 20:59:21 +0000134 throw TTransportException(TTransportException::NOT_OPEN, "Could not listen");
Mark Sleee8540632006-05-30 09:24:40 +0000135 }
136
137 // The socket is now listening!
Mark Sleee8540632006-05-30 09:24:40 +0000138}
139
Marc Slemko16698852006-08-04 03:16:10 +0000140shared_ptr<TTransport> TServerSocket::acceptImpl() {
Martin Kraemer10640d82007-02-03 01:59:12 +0000141 if (serverSocket_ < 0) {
Mark Sleef9831082007-02-20 20:59:21 +0000142 throw TTransportException(TTransportException::NOT_OPEN, "TServerSocket not listening");
Mark Sleee8540632006-05-30 09:24:40 +0000143 }
144
Mark Sleea5a783f2007-03-02 19:41:08 +0000145 fd_set fds;
146
147 while (true) {
148 FD_ZERO(&fds);
149 FD_SET(serverSocket_, &fds);
Mark Slee561b5362007-03-09 19:26:29 +0000150 if (intSock2_ >= 0) {
151 FD_SET(intSock2_, &fds);
Mark Sleea5a783f2007-03-02 19:41:08 +0000152 }
Mark Slee561b5362007-03-09 19:26:29 +0000153 int ret = select(serverSocket_+1, &fds, NULL, NULL, NULL);
Mark Sleea5a783f2007-03-02 19:41:08 +0000154
Mark Slee561b5362007-03-09 19:26:29 +0000155 if (ret < 0) {
156 perror("TServerSocket::acceptImpl() select -1");
Mark Sleea5a783f2007-03-02 19:41:08 +0000157 throw TTransportException(TTransportException::UNKNOWN);
Mark Slee561b5362007-03-09 19:26:29 +0000158 } else if (ret > 0) {
159 // Check for an interrupt signal
160 if (intSock2_ >= 0 && FD_ISSET(intSock2_, &fds)) {
161 int8_t buf;
162 if (-1 == recv(intSock2_, &buf, sizeof(int8_t), 0)) {
163 perror("TServerSocket::acceptImpl() interrupt receive");
164 }
165 throw TTransportException(TTransportException::INTERRUPTED);
166 }
167 // Check for the actual server socket being ready
168 if (FD_ISSET(serverSocket_, &fds)) {
169 break;
170 }
171 } else {
172 perror("TServerSocket::acceptImpl() select 0");
173 throw TTransportException(TTransportException::UNKNOWN);
Mark Sleea5a783f2007-03-02 19:41:08 +0000174 }
175 }
176
Mark Sleee8540632006-05-30 09:24:40 +0000177 struct sockaddr_in clientAddress;
178 int size = sizeof(clientAddress);
179 int clientSocket = ::accept(serverSocket_,
180 (struct sockaddr *) &clientAddress,
181 (socklen_t *) &size);
182
Martin Kraemeree341cb2007-02-05 21:40:38 +0000183 if (clientSocket < 0) {
Mark Sleee8540632006-05-30 09:24:40 +0000184 perror("TServerSocket::accept()");
Mark Sleef9831082007-02-20 20:59:21 +0000185 throw TTransportException(TTransportException::UNKNOWN, "ERROR:" + errno);
Mark Sleee8540632006-05-30 09:24:40 +0000186 }
Mark Sleea5a783f2007-03-02 19:41:08 +0000187
188 // Make sure client socket is blocking
189 int flags = fcntl(clientSocket, F_GETFL, 0);
190 if (flags == -1) {
191 perror("TServerSocket::select() fcntl GETFL");
192 throw TTransportException(TTransportException::UNKNOWN, "ERROR:" + errno);
193 }
194 if (-1 == fcntl(clientSocket, F_SETFL, flags & ~O_NONBLOCK)) {
195 perror("TServerSocket::select() fcntl SETFL");
196 throw TTransportException(TTransportException::UNKNOWN, "ERROR:" + errno);
197 }
Martin Kraemer10640d82007-02-03 01:59:12 +0000198
Mark Slee29050782006-09-29 00:12:30 +0000199 shared_ptr<TSocket> client(new TSocket(clientSocket));
200 if (sendTimeout_ > 0) {
201 client->setSendTimeout(sendTimeout_);
202 }
203 if (recvTimeout_ > 0) {
204 client->setRecvTimeout(recvTimeout_);
Mark Sleea5a783f2007-03-02 19:41:08 +0000205 }
206
Mark Slee29050782006-09-29 00:12:30 +0000207 return client;
Mark Sleee8540632006-05-30 09:24:40 +0000208}
209
Mark Slee561b5362007-03-09 19:26:29 +0000210void TServerSocket::interrupt() {
211 if (intSock1_ >= 0) {
212 int8_t byte = 0;
213 if (-1 == send(intSock1_, &byte, sizeof(int8_t), 0)) {
214 perror("TServerSocket::interrupt()");
215 }
216 }
217}
218
Mark Sleee8540632006-05-30 09:24:40 +0000219void TServerSocket::close() {
Martin Kraemeree341cb2007-02-05 21:40:38 +0000220 if (serverSocket_ >= 0) {
Mark Sleee8540632006-05-30 09:24:40 +0000221 shutdown(serverSocket_, SHUT_RDWR);
222 ::close(serverSocket_);
223 }
Mark Slee561b5362007-03-09 19:26:29 +0000224 if (intSock1_ >= 0) {
225 ::close(intSock1_);
226 }
227 if (intSock2_ >= 0) {
228 ::close(intSock2_);
229 }
Martin Kraemeree341cb2007-02-05 21:40:38 +0000230 serverSocket_ = -1;
Mark Slee561b5362007-03-09 19:26:29 +0000231 intSock1_ = -1;
232 intSock2_ = -1;
Mark Sleee8540632006-05-30 09:24:40 +0000233}
Marc Slemko6f038a72006-08-03 18:58:09 +0000234
235}}} // facebook::thrift::transport