blob: 23931347a7a0c27a38fa240a185fd622a262f9ee [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),
28 interrupt_(false) {}
Mark Slee29050782006-09-29 00:12:30 +000029
30TServerSocket::TServerSocket(int port, int sendTimeout, int recvTimeout) :
31 port_(port),
Martin Kraemeree341cb2007-02-05 21:40:38 +000032 serverSocket_(-1),
Mark Slee29050782006-09-29 00:12:30 +000033 acceptBacklog_(1024),
34 sendTimeout_(sendTimeout),
Mark Sleea5a783f2007-03-02 19:41:08 +000035 recvTimeout_(recvTimeout),
36 interrupt_(false) {}
Mark Sleee8540632006-05-30 09:24:40 +000037
38TServerSocket::~TServerSocket() {
39 close();
40}
41
Mark Slee29050782006-09-29 00:12:30 +000042void TServerSocket::setSendTimeout(int sendTimeout) {
43 sendTimeout_ = sendTimeout;
44}
45
46void TServerSocket::setRecvTimeout(int recvTimeout) {
47 recvTimeout_ = recvTimeout;
48}
49
Mark Slee8d7e1f62006-06-07 06:48:56 +000050void TServerSocket::listen() {
Mark Sleee8540632006-05-30 09:24:40 +000051 serverSocket_ = socket(AF_INET, SOCK_STREAM, 0);
52 if (serverSocket_ == -1) {
Mark Slee8d7e1f62006-06-07 06:48:56 +000053 perror("TServerSocket::listen() socket");
Mark Sleee8540632006-05-30 09:24:40 +000054 close();
Mark Sleef9831082007-02-20 20:59:21 +000055 throw TTransportException(TTransportException::NOT_OPEN, "Could not create server socket.");
Mark Sleee8540632006-05-30 09:24:40 +000056 }
57
58 // Set reusaddress to prevent 2MSL delay on accept
59 int one = 1;
60 if (-1 == setsockopt(serverSocket_, SOL_SOCKET, SO_REUSEADDR,
61 &one, sizeof(one))) {
62 perror("TServerSocket::listen() SO_REUSEADDR");
63 close();
Mark Sleef9831082007-02-20 20:59:21 +000064 throw TTransportException(TTransportException::NOT_OPEN, "Could not set SO_REUSEADDR");
Mark Sleee8540632006-05-30 09:24:40 +000065 }
66
Mark Slee29050782006-09-29 00:12:30 +000067 // Defer accept
68 #ifdef TCP_DEFER_ACCEPT
69 if (-1 == setsockopt(serverSocket_, SOL_SOCKET, TCP_DEFER_ACCEPT,
70 &one, sizeof(one))) {
71 perror("TServerSocket::listen() TCP_DEFER_ACCEPT");
72 close();
Mark Sleef9831082007-02-20 20:59:21 +000073 throw TTransportException(TTransportException::NOT_OPEN, "Could not set TCP_DEFER_ACCEPT");
Mark Slee29050782006-09-29 00:12:30 +000074 }
75 #endif // #ifdef TCP_DEFER_ACCEPT
76
Mark Sleee8540632006-05-30 09:24:40 +000077 // Turn linger off, don't want to block on calls to close
78 struct linger ling = {0, 0};
79 if (-1 == setsockopt(serverSocket_, SOL_SOCKET, SO_LINGER,
80 &ling, sizeof(ling))) {
Mark Sleee8540632006-05-30 09:24:40 +000081 close();
Mark Slee8d7e1f62006-06-07 06:48:56 +000082 perror("TServerSocket::listen() SO_LINGER");
Mark Sleef9831082007-02-20 20:59:21 +000083 throw TTransportException(TTransportException::NOT_OPEN, "Could not set SO_LINGER");
Mark Sleee8540632006-05-30 09:24:40 +000084 }
85
Mark Slee29050782006-09-29 00:12:30 +000086 // TCP Nodelay, speed over bandwidth
87 if (-1 == setsockopt(serverSocket_, IPPROTO_TCP, TCP_NODELAY,
88 &one, sizeof(one))) {
89 close();
90 perror("setsockopt TCP_NODELAY");
Mark Sleef9831082007-02-20 20:59:21 +000091 throw TTransportException(TTransportException::NOT_OPEN, "Could not set TCP_NODELAY");
Mark Slee29050782006-09-29 00:12:30 +000092 }
93
Mark Sleea5a783f2007-03-02 19:41:08 +000094 // Set NONBLOCK on the accept socket
95 int flags = fcntl(serverSocket_, F_GETFL, 0);
96 if (flags == -1) {
97 throw TTransportException(TTransportException::NOT_OPEN, "fcntl() failed");
98 }
99 if (-1 == fcntl(serverSocket_, F_SETFL, flags | O_NONBLOCK)) {
100 throw TTransportException(TTransportException::NOT_OPEN, "fcntl() failed");
101 }
102
Mark Sleee8540632006-05-30 09:24:40 +0000103 // Bind to a port
104 struct sockaddr_in addr;
105 memset(&addr, 0, sizeof(addr));
106 addr.sin_family = AF_INET;
107 addr.sin_port = htons(port_);
108 addr.sin_addr.s_addr = INADDR_ANY;
109 if (-1 == bind(serverSocket_, (struct sockaddr *)&addr, sizeof(addr))) {
110 char errbuf[1024];
111 sprintf(errbuf, "TServerSocket::listen() BIND %d", port_);
112 perror(errbuf);
113 close();
Mark Sleef9831082007-02-20 20:59:21 +0000114 throw TTransportException(TTransportException::NOT_OPEN, "Could not bind");
Mark Sleee8540632006-05-30 09:24:40 +0000115 }
116
117 // Call listen
118 if (-1 == ::listen(serverSocket_, acceptBacklog_)) {
119 perror("TServerSocket::listen() LISTEN");
120 close();
Mark Sleef9831082007-02-20 20:59:21 +0000121 throw TTransportException(TTransportException::NOT_OPEN, "Could not listen");
Mark Sleee8540632006-05-30 09:24:40 +0000122 }
123
124 // The socket is now listening!
Mark Sleee8540632006-05-30 09:24:40 +0000125}
126
Marc Slemko16698852006-08-04 03:16:10 +0000127shared_ptr<TTransport> TServerSocket::acceptImpl() {
Martin Kraemer10640d82007-02-03 01:59:12 +0000128 if (serverSocket_ < 0) {
Mark Sleef9831082007-02-20 20:59:21 +0000129 throw TTransportException(TTransportException::NOT_OPEN, "TServerSocket not listening");
Mark Sleee8540632006-05-30 09:24:40 +0000130 }
131
Mark Sleea5a783f2007-03-02 19:41:08 +0000132 // 200ms timeout on accept
133 struct timeval c = {0, 200000};
134 fd_set fds;
135
136 while (true) {
137 FD_ZERO(&fds);
138 FD_SET(serverSocket_, &fds);
139 int ret = select(serverSocket_+1, &fds, NULL, NULL, &c);
140
141 // Check for interrupt case
142 if (ret == 0 && interrupt_) {
143 interrupt_ = false;
144 throw TTransportException(TTransportException::INTERRUPTED);
145 }
146
147 // Reset interrupt flag no matter what
148 interrupt_ = false;
149
150 if (ret > 0) {
Mark Slee4e441e22007-03-02 21:23:30 +0000151 // Cool, ready to accept
Mark Sleea5a783f2007-03-02 19:41:08 +0000152 break;
153 } else if (ret == 0) {
Mark Slee4e441e22007-03-02 21:23:30 +0000154 // Timed out... keep going
155 continue;
Mark Sleea5a783f2007-03-02 19:41:08 +0000156 } else {
Mark Slee4e441e22007-03-02 21:23:30 +0000157 // Bogus, select messed up
Mark Sleea5a783f2007-03-02 19:41:08 +0000158 perror("TServerSocket::select() negret");
159 throw TTransportException(TTransportException::UNKNOWN);
160 }
161 }
162
Mark Sleee8540632006-05-30 09:24:40 +0000163 struct sockaddr_in clientAddress;
164 int size = sizeof(clientAddress);
165 int clientSocket = ::accept(serverSocket_,
166 (struct sockaddr *) &clientAddress,
167 (socklen_t *) &size);
168
Martin Kraemeree341cb2007-02-05 21:40:38 +0000169 if (clientSocket < 0) {
Mark Sleee8540632006-05-30 09:24:40 +0000170 perror("TServerSocket::accept()");
Mark Sleef9831082007-02-20 20:59:21 +0000171 throw TTransportException(TTransportException::UNKNOWN, "ERROR:" + errno);
Mark Sleee8540632006-05-30 09:24:40 +0000172 }
Mark Sleea5a783f2007-03-02 19:41:08 +0000173
174 // Make sure client socket is blocking
175 int flags = fcntl(clientSocket, F_GETFL, 0);
176 if (flags == -1) {
177 perror("TServerSocket::select() fcntl GETFL");
178 throw TTransportException(TTransportException::UNKNOWN, "ERROR:" + errno);
179 }
180 if (-1 == fcntl(clientSocket, F_SETFL, flags & ~O_NONBLOCK)) {
181 perror("TServerSocket::select() fcntl SETFL");
182 throw TTransportException(TTransportException::UNKNOWN, "ERROR:" + errno);
183 }
Martin Kraemer10640d82007-02-03 01:59:12 +0000184
Mark Slee29050782006-09-29 00:12:30 +0000185 shared_ptr<TSocket> client(new TSocket(clientSocket));
186 if (sendTimeout_ > 0) {
187 client->setSendTimeout(sendTimeout_);
188 }
189 if (recvTimeout_ > 0) {
190 client->setRecvTimeout(recvTimeout_);
Mark Sleea5a783f2007-03-02 19:41:08 +0000191 }
192
Mark Slee29050782006-09-29 00:12:30 +0000193 return client;
Mark Sleee8540632006-05-30 09:24:40 +0000194}
195
196void TServerSocket::close() {
Martin Kraemeree341cb2007-02-05 21:40:38 +0000197 if (serverSocket_ >= 0) {
Mark Sleee8540632006-05-30 09:24:40 +0000198 shutdown(serverSocket_, SHUT_RDWR);
199 ::close(serverSocket_);
200 }
Martin Kraemeree341cb2007-02-05 21:40:38 +0000201 serverSocket_ = -1;
Mark Sleee8540632006-05-30 09:24:40 +0000202}
Marc Slemko6f038a72006-08-03 18:58:09 +0000203
204}}} // facebook::thrift::transport