| Mark Slee | 9f0c651 | 2007-02-28 23:58:26 +0000 | [diff] [blame] | 1 | // 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 |  | 
| Marc Slemko | e03da18 | 2006-07-21 21:32:36 +0000 | [diff] [blame] | 7 | #include <config.h> | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 8 | #include <sys/socket.h> | 
 | 9 | #include <arpa/inet.h> | 
 | 10 | #include <netinet/in.h> | 
 | 11 | #include <netinet/tcp.h> | 
 | 12 | #include <netdb.h> | 
 | 13 | #include <unistd.h> | 
 | 14 | #include <errno.h> | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 15 | #include <fcntl.h> | 
 | 16 | #include <sys/select.h> | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 17 |  | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 18 | #include "concurrency/Monitor.h" | 
| Marc Slemko | d42a2c2 | 2006-08-10 03:30:18 +0000 | [diff] [blame] | 19 | #include "TSocket.h" | 
 | 20 | #include "TTransportException.h" | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 21 |  | 
| Marc Slemko | 6f038a7 | 2006-08-03 18:58:09 +0000 | [diff] [blame] | 22 | namespace facebook { namespace thrift { namespace transport {  | 
 | 23 |  | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 24 | using namespace std; | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 25 | using namespace facebook::thrift::concurrency; | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 26 |  | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 27 | // Global var to track total socket sys calls | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 28 | uint32_t g_socket_syscalls = 0; | 
 | 29 |  | 
 | 30 | /** | 
 | 31 |  * TSocket implementation. | 
 | 32 |  * | 
 | 33 |  * @author Mark Slee <mcslee@facebook.com> | 
 | 34 |  */ | 
 | 35 |  | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 36 | // Mutex to protect syscalls to netdb | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 37 | static Monitor s_netdb_monitor; | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 38 |  | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 39 | TSocket::TSocket(string host, int port) :  | 
 | 40 |   host_(host), | 
 | 41 |   port_(port), | 
| Martin Kraemer | ee341cb | 2007-02-05 21:40:38 +0000 | [diff] [blame] | 42 |   socket_(-1), | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 43 |   connTimeout_(0), | 
 | 44 |   sendTimeout_(0), | 
 | 45 |   recvTimeout_(0), | 
 | 46 |   lingerOn_(1), | 
 | 47 |   lingerVal_(0), | 
| Aditya Agarwal | e04475b | 2007-05-23 02:14:58 +0000 | [diff] [blame] | 48 |   noDelay_(1), | 
 | 49 |   maxRecvRetries_(5) { | 
| Mark Slee | b9ff32a | 2006-11-16 01:00:24 +0000 | [diff] [blame] | 50 |   recvTimeval_.tv_sec = (int)(recvTimeout_/1000); | 
 | 51 |   recvTimeval_.tv_usec = (int)((recvTimeout_%1000)*1000); | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 52 | } | 
 | 53 |  | 
| Aditya Agarwal | ebc99e0 | 2007-01-15 23:14:58 +0000 | [diff] [blame] | 54 | TSocket::TSocket() :  | 
 | 55 |   host_(""), | 
 | 56 |   port_(0), | 
| Martin Kraemer | ee341cb | 2007-02-05 21:40:38 +0000 | [diff] [blame] | 57 |   socket_(-1), | 
| Aditya Agarwal | ebc99e0 | 2007-01-15 23:14:58 +0000 | [diff] [blame] | 58 |   connTimeout_(0), | 
 | 59 |   sendTimeout_(0), | 
 | 60 |   recvTimeout_(0), | 
 | 61 |   lingerOn_(1), | 
 | 62 |   lingerVal_(0), | 
| Aditya Agarwal | e04475b | 2007-05-23 02:14:58 +0000 | [diff] [blame] | 63 |   noDelay_(1), | 
 | 64 |   maxRecvRetries_(5) { | 
| Aditya Agarwal | ebc99e0 | 2007-01-15 23:14:58 +0000 | [diff] [blame] | 65 |   recvTimeval_.tv_sec = (int)(recvTimeout_/1000); | 
 | 66 |   recvTimeval_.tv_usec = (int)((recvTimeout_%1000)*1000); | 
 | 67 | } | 
 | 68 |  | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 69 | TSocket::TSocket(int socket) : | 
 | 70 |   host_(""), | 
 | 71 |   port_(0), | 
 | 72 |   socket_(socket), | 
 | 73 |   connTimeout_(0), | 
 | 74 |   sendTimeout_(0), | 
 | 75 |   recvTimeout_(0), | 
 | 76 |   lingerOn_(1), | 
 | 77 |   lingerVal_(0), | 
| Aditya Agarwal | e04475b | 2007-05-23 02:14:58 +0000 | [diff] [blame] | 78 |   noDelay_(1), | 
 | 79 |   maxRecvRetries_(5) { | 
| Mark Slee | b9ff32a | 2006-11-16 01:00:24 +0000 | [diff] [blame] | 80 |   recvTimeval_.tv_sec = (int)(recvTimeout_/1000); | 
 | 81 |   recvTimeval_.tv_usec = (int)((recvTimeout_%1000)*1000); | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 82 | } | 
 | 83 |    | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 84 | TSocket::~TSocket() { | 
 | 85 |   close(); | 
 | 86 | } | 
 | 87 |  | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 88 | bool TSocket::isOpen() { | 
| Martin Kraemer | ee341cb | 2007-02-05 21:40:38 +0000 | [diff] [blame] | 89 |   return (socket_ >= 0);  | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 90 | } | 
 | 91 |  | 
| Mark Slee | b9ff32a | 2006-11-16 01:00:24 +0000 | [diff] [blame] | 92 | bool TSocket::peek() { | 
 | 93 |   if (!isOpen()) { | 
 | 94 |     return false; | 
 | 95 |   } | 
 | 96 |   uint8_t buf; | 
 | 97 |   int r = recv(socket_, &buf, 1, MSG_PEEK); | 
 | 98 |   if (r == -1) { | 
| boz | 6ded775 | 2007-06-05 22:41:18 +0000 | [diff] [blame] | 99 |     GlobalOutput("TSocket::peek()"); | 
| Mark Slee | b9ff32a | 2006-11-16 01:00:24 +0000 | [diff] [blame] | 100 |     close(); | 
| Martin Kraemer | e6c4fa6 | 2007-07-09 19:08:25 +0000 | [diff] [blame^] | 101 |     char b_error[1024]; | 
 | 102 |     strerror_r(errno, b_error, sizeof(b_error)); | 
 | 103 |     throw TTransportException(TTransportException::UNKNOWN, string("recv() ERROR:") + b_error); | 
| Mark Slee | b9ff32a | 2006-11-16 01:00:24 +0000 | [diff] [blame] | 104 |   } | 
 | 105 |   return (r > 0); | 
 | 106 | } | 
 | 107 |  | 
| Mark Slee | 6d56eb9 | 2007-07-06 22:28:15 +0000 | [diff] [blame] | 108 | void TSocket::openConnection(struct addrinfo *res) { | 
| Mark Slee | a9848d7 | 2007-02-21 04:54:05 +0000 | [diff] [blame] | 109 |   if (isOpen()) { | 
 | 110 |     throw TTransportException(TTransportException::ALREADY_OPEN); | 
 | 111 |   } | 
| Mark Slee | 6d56eb9 | 2007-07-06 22:28:15 +0000 | [diff] [blame] | 112 |    | 
 | 113 |   socket_ = socket(res->ai_family, res->ai_socktype, res->ai_protocol); | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 114 |   if (socket_ == -1) { | 
| boz | 6ded775 | 2007-06-05 22:41:18 +0000 | [diff] [blame] | 115 |     GlobalOutput("TSocket::open() socket"); | 
| Martin Kraemer | e6c4fa6 | 2007-07-09 19:08:25 +0000 | [diff] [blame^] | 116 |     char b_error[1024]; | 
 | 117 |     strerror_r(errno, b_error, sizeof(b_error)); | 
 | 118 |     throw TTransportException(TTransportException::NOT_OPEN, string("socket() ERROR:") + b_error); | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 119 |   } | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 120 |  | 
 | 121 |   // Send timeout | 
 | 122 |   if (sendTimeout_ > 0) { | 
 | 123 |     setSendTimeout(sendTimeout_); | 
 | 124 |   } | 
 | 125 |  | 
 | 126 |   // Recv timeout | 
 | 127 |   if (recvTimeout_ > 0) { | 
 | 128 |     setRecvTimeout(recvTimeout_); | 
 | 129 |   } | 
 | 130 |  | 
 | 131 |   // Linger | 
 | 132 |   setLinger(lingerOn_, lingerVal_); | 
 | 133 |  | 
 | 134 |   // No delay | 
 | 135 |   setNoDelay(noDelay_); | 
 | 136 |  | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 137 |   // Set the socket to be non blocking for connect if a timeout exists | 
 | 138 |   int flags = fcntl(socket_, F_GETFL, 0);  | 
 | 139 |   if (connTimeout_ > 0) { | 
| Mark Slee | a5a783f | 2007-03-02 19:41:08 +0000 | [diff] [blame] | 140 |     if (-1 == fcntl(socket_, F_SETFL, flags | O_NONBLOCK)) { | 
 | 141 |       throw TTransportException(TTransportException::NOT_OPEN, "fcntl() failed"); | 
 | 142 |     } | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 143 |   } else { | 
| Mark Slee | a5a783f | 2007-03-02 19:41:08 +0000 | [diff] [blame] | 144 |     if (-1 == fcntl(socket_, F_SETFL, flags & ~O_NONBLOCK)) { | 
 | 145 |       throw TTransportException(TTransportException::NOT_OPEN, "fcntl() failed"); | 
 | 146 |     } | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 147 |   } | 
 | 148 |  | 
 | 149 |   // Conn timeout | 
 | 150 |   struct timeval c = {(int)(connTimeout_/1000), | 
 | 151 |                       (int)((connTimeout_%1000)*1000)}; | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 152 |     | 
 | 153 |   // Connect the socket | 
| Mark Slee | 6d56eb9 | 2007-07-06 22:28:15 +0000 | [diff] [blame] | 154 |   int ret = connect(socket_, res->ai_addr, res->ai_addrlen); | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 155 |    | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 156 |   if (ret == 0) { | 
 | 157 |     goto done; | 
 | 158 |   } | 
 | 159 |  | 
 | 160 |   if (errno != EINPROGRESS) { | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 161 |     char buff[1024]; | 
| boz | 6ded775 | 2007-06-05 22:41:18 +0000 | [diff] [blame] | 162 |     GlobalOutput(buff); | 
| Martin Kraemer | e6c4fa6 | 2007-07-09 19:08:25 +0000 | [diff] [blame^] | 163 |     sprintf(buff, "TSocket::open() connect %s %d", host_.c_str(), port_); | 
 | 164 |      | 
 | 165 |     char b_error[1024]; | 
 | 166 |     strerror_r(errno, b_error, sizeof(b_error)); | 
 | 167 |     throw TTransportException(TTransportException::NOT_OPEN, string("open() ERROR: ") + b_error); | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 168 |   } | 
 | 169 |  | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 170 |   fd_set fds; | 
 | 171 |   FD_ZERO(&fds); | 
 | 172 |   FD_SET(socket_, &fds); | 
 | 173 |   ret = select(socket_+1, NULL, &fds, NULL, &c); | 
 | 174 |  | 
 | 175 |   if (ret > 0) { | 
 | 176 |     // Ensure connected | 
 | 177 |     int val; | 
 | 178 |     socklen_t lon; | 
 | 179 |     lon = sizeof(int); | 
 | 180 |     int ret2 = getsockopt(socket_, SOL_SOCKET, SO_ERROR, (void *)&val, &lon); | 
 | 181 |     if (ret2 == -1) { | 
| boz | 6ded775 | 2007-06-05 22:41:18 +0000 | [diff] [blame] | 182 |       GlobalOutput("TSocket::open() getsockopt SO_ERROR"); | 
| Martin Kraemer | e6c4fa6 | 2007-07-09 19:08:25 +0000 | [diff] [blame^] | 183 |       char b_error[1024]; | 
 | 184 |       strerror_r(errno, b_error, sizeof(b_error)); | 
 | 185 |       throw TTransportException(TTransportException::NOT_OPEN, string("open() ERROR: ") + b_error); | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 186 |     } | 
 | 187 |     if (val == 0) { | 
 | 188 |       goto done; | 
 | 189 |     } | 
| boz | 6ded775 | 2007-06-05 22:41:18 +0000 | [diff] [blame] | 190 |     GlobalOutput("TSocket::open() SO_ERROR was set"); | 
| Martin Kraemer | e6c4fa6 | 2007-07-09 19:08:25 +0000 | [diff] [blame^] | 191 |     char b_error[1024]; | 
 | 192 |     strerror_r(errno, b_error, sizeof(b_error)); | 
 | 193 |     throw TTransportException(TTransportException::NOT_OPEN, string("open() ERROR: ") + b_error); | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 194 |   } else if (ret == 0) { | 
| boz | 6ded775 | 2007-06-05 22:41:18 +0000 | [diff] [blame] | 195 |     GlobalOutput("TSocket::open() timeed out"); | 
| Martin Kraemer | e6c4fa6 | 2007-07-09 19:08:25 +0000 | [diff] [blame^] | 196 |     char b_error[1024]; | 
 | 197 |     strerror_r(errno, b_error, sizeof(b_error)); | 
 | 198 |     throw TTransportException(TTransportException::NOT_OPEN, string("open() ERROR: ") + b_error);    | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 199 |   } else { | 
| boz | 6ded775 | 2007-06-05 22:41:18 +0000 | [diff] [blame] | 200 |     GlobalOutput("TSocket::open() select error"); | 
| Martin Kraemer | e6c4fa6 | 2007-07-09 19:08:25 +0000 | [diff] [blame^] | 201 |     char b_error[1024]; | 
 | 202 |     strerror_r(errno, b_error, sizeof(b_error)); | 
 | 203 |     throw TTransportException(TTransportException::NOT_OPEN, string("open() ERROR: ") + b_error); | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 204 |   } | 
 | 205 |  | 
 | 206 |  done: | 
 | 207 |   // Set socket back to normal mode (blocking) | 
 | 208 |   fcntl(socket_, F_SETFL, flags); | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 209 | } | 
 | 210 |  | 
| Mark Slee | 6d56eb9 | 2007-07-06 22:28:15 +0000 | [diff] [blame] | 211 | void TSocket::open() { | 
 | 212 |   if (isOpen()) { | 
 | 213 |     throw TTransportException(TTransportException::ALREADY_OPEN); | 
 | 214 |   } | 
 | 215 |  | 
 | 216 |   // Validate port number | 
 | 217 |   if (port_ < 0 || port_ > 65536) { | 
 | 218 |     throw TTransportException(TTransportException::NOT_OPEN, "Specified port is invalid"); | 
 | 219 |   } | 
 | 220 |  | 
 | 221 |   struct addrinfo hints, *res, *res0; | 
 | 222 |   int error; | 
 | 223 |   char port[sizeof("65536") + 1]; | 
 | 224 |   memset(&hints, 0, sizeof(hints)); | 
 | 225 |   hints.ai_family = PF_UNSPEC; | 
 | 226 |   hints.ai_socktype = SOCK_STREAM; | 
 | 227 |   hints.ai_flags = AI_PASSIVE; | 
 | 228 |   sprintf(port, "%d", port_); | 
 | 229 |    | 
 | 230 |   { | 
 | 231 |     // Scope lock on host entry lookup | 
 | 232 |     Synchronized s(s_netdb_monitor); | 
 | 233 |     error = getaddrinfo(host_.c_str(), port, &hints, &res0); | 
 | 234 |   } | 
 | 235 |   if (error) { | 
 | 236 |     fprintf(stderr, "getaddrinfo %d: %s\n", error, gai_strerror(error)); | 
 | 237 |     close(); | 
 | 238 |     throw TTransportException(TTransportException::NOT_OPEN, "Could not resolve host for client socket."); | 
 | 239 |   } | 
 | 240 |    | 
 | 241 |   // Cycle through all the returned addresses until one | 
 | 242 |   // connects or push the exception up. | 
 | 243 |   for (res = res0; res; res = res->ai_next) { | 
 | 244 |     try { | 
 | 245 |       openConnection(res); | 
 | 246 |       break; | 
 | 247 |     } catch (TTransportException& ttx) { | 
 | 248 |       if (res->ai_next) { | 
 | 249 |         close(); | 
 | 250 |       } else { | 
 | 251 |         close(); | 
 | 252 |         throw; | 
 | 253 |       } | 
 | 254 |     } | 
 | 255 |   } | 
 | 256 | } | 
 | 257 |  | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 258 | void TSocket::close() { | 
| Martin Kraemer | ee341cb | 2007-02-05 21:40:38 +0000 | [diff] [blame] | 259 |   if (socket_ >= 0) { | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 260 |     shutdown(socket_, SHUT_RDWR); | 
 | 261 |     ::close(socket_); | 
 | 262 |   } | 
| Martin Kraemer | ee341cb | 2007-02-05 21:40:38 +0000 | [diff] [blame] | 263 |   socket_ = -1; | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 264 | } | 
 | 265 |  | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 266 | uint32_t TSocket::read(uint8_t* buf, uint32_t len) { | 
| Martin Kraemer | ee341cb | 2007-02-05 21:40:38 +0000 | [diff] [blame] | 267 |   if (socket_ < 0) { | 
| Mark Slee | f983108 | 2007-02-20 20:59:21 +0000 | [diff] [blame] | 268 |     throw TTransportException(TTransportException::NOT_OPEN, "Called read on non-open socket"); | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 269 |   } | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 270 |  | 
| Aditya Agarwal | e04475b | 2007-05-23 02:14:58 +0000 | [diff] [blame] | 271 |   int32_t retries = 0; | 
 | 272 |  | 
 | 273 |   // EAGAIN can be signalled both when a timeout has occurred and when | 
 | 274 |   // the system is out of resources (an awesome undocumented feature). | 
 | 275 |   // The following is an approximation of the time interval under which | 
 | 276 |   // EAGAIN is taken to indicate an out of resources error. | 
 | 277 |   uint32_t eagainThresholdMicros = 0; | 
 | 278 |   if (recvTimeout_) { | 
 | 279 |     // if a readTimeout is specified along with a max number of recv retries, then  | 
 | 280 |     // the threshold will ensure that the read timeout is not exceeded even in the | 
 | 281 |     // case of resource errors | 
 | 282 |     eagainThresholdMicros = (recvTimeout_*1000)/ ((maxRecvRetries_>0) ? maxRecvRetries_ : 2); | 
 | 283 |   } | 
 | 284 |  | 
 | 285 |  try_again:   | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 286 |   // Read from the socket | 
| Aditya Agarwal | e04475b | 2007-05-23 02:14:58 +0000 | [diff] [blame] | 287 |   struct timeval begin; | 
 | 288 |   gettimeofday(&begin, NULL); | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 289 |   int got = recv(socket_, buf, len, 0); | 
| Aditya Agarwal | e04475b | 2007-05-23 02:14:58 +0000 | [diff] [blame] | 290 |   struct timeval end; | 
 | 291 |   gettimeofday(&end, NULL); | 
 | 292 |   uint32_t readElapsedMicros =  (((end.tv_sec - begin.tv_sec) * 1000 * 1000) | 
 | 293 |                                  + (((uint64_t)(end.tv_usec - begin.tv_usec)))); | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 294 |   ++g_socket_syscalls; | 
| Aditya Agarwal | e04475b | 2007-05-23 02:14:58 +0000 | [diff] [blame] | 295 |  | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 296 |   // Check for error on read | 
| Mark Slee | c425780 | 2007-01-24 23:14:30 +0000 | [diff] [blame] | 297 |   if (got < 0) {    | 
| Aditya Agarwal | e04475b | 2007-05-23 02:14:58 +0000 | [diff] [blame] | 298 |     if (errno == EAGAIN) { | 
 | 299 |       // check if this is the lack of resources or timeout case | 
 | 300 |       if (!eagainThresholdMicros || (readElapsedMicros < eagainThresholdMicros)) { | 
 | 301 |         if (retries++ < maxRecvRetries_) { | 
 | 302 |           usleep(50); | 
 | 303 |           goto try_again; | 
 | 304 |         } else { | 
 | 305 |           throw TTransportException(TTransportException::TIMED_OUT,  | 
 | 306 |                                     "EAGAIN (unavailable resources)"); | 
 | 307 |         } | 
 | 308 |       } else { | 
 | 309 |         // infer that timeout has been hit | 
 | 310 |         throw TTransportException(TTransportException::TIMED_OUT,  | 
 | 311 |                                   "EAGAIN (timed out)"); | 
 | 312 |       } | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 313 |     } | 
 | 314 |      | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 315 |     // If interrupted, try again | 
| Aditya Agarwal | e04475b | 2007-05-23 02:14:58 +0000 | [diff] [blame] | 316 |     if (errno == EINTR && retries++ < maxRecvRetries_) { | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 317 |       goto try_again; | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 318 |     } | 
 | 319 |      | 
| Mark Slee | c425780 | 2007-01-24 23:14:30 +0000 | [diff] [blame] | 320 |     // Now it's not a try again case, but a real probblez | 
| boz | 6ded775 | 2007-06-05 22:41:18 +0000 | [diff] [blame] | 321 |     GlobalOutput("TSocket::read()"); | 
| Mark Slee | c425780 | 2007-01-24 23:14:30 +0000 | [diff] [blame] | 322 |  | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 323 |     // If we disconnect with no linger time | 
 | 324 |     if (errno == ECONNRESET) { | 
| Mark Slee | f983108 | 2007-02-20 20:59:21 +0000 | [diff] [blame] | 325 |       throw TTransportException(TTransportException::NOT_OPEN, "ECONNRESET"); | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 326 |     } | 
 | 327 |      | 
 | 328 |     // This ish isn't open | 
 | 329 |     if (errno == ENOTCONN) { | 
| Mark Slee | f983108 | 2007-02-20 20:59:21 +0000 | [diff] [blame] | 330 |       throw TTransportException(TTransportException::NOT_OPEN, "ENOTCONN"); | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 331 |     } | 
 | 332 |      | 
 | 333 |     // Timed out! | 
 | 334 |     if (errno == ETIMEDOUT) { | 
| Mark Slee | f983108 | 2007-02-20 20:59:21 +0000 | [diff] [blame] | 335 |       throw TTransportException(TTransportException::TIMED_OUT, "ETIMEDOUT"); | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 336 |     } | 
 | 337 |      | 
 | 338 |     // Some other error, whatevz | 
| Mark Slee | 5f68c71 | 2007-05-11 17:58:54 +0000 | [diff] [blame] | 339 |     char buff[1024]; | 
 | 340 |     sprintf(buff, "ERROR errno: %d", errno); | 
 | 341 |     throw TTransportException(TTransportException::UNKNOWN, buff); | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 342 |   } | 
 | 343 |    | 
 | 344 |   // The remote host has closed the socket | 
 | 345 |   if (got == 0) { | 
 | 346 |     close(); | 
 | 347 |     return 0; | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 348 |   } | 
 | 349 |    | 
 | 350 |   // Pack data into string | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 351 |   return got; | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 352 | } | 
 | 353 |  | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 354 | void TSocket::write(const uint8_t* buf, uint32_t len) { | 
| Martin Kraemer | ee341cb | 2007-02-05 21:40:38 +0000 | [diff] [blame] | 355 |   if (socket_ < 0) { | 
| Mark Slee | f983108 | 2007-02-20 20:59:21 +0000 | [diff] [blame] | 356 |     throw TTransportException(TTransportException::NOT_OPEN, "Called write on non-open socket"); | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 357 |   } | 
 | 358 |  | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 359 |   uint32_t sent = 0; | 
 | 360 |      | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 361 |   while (sent < len) { | 
| Marc Slemko | 9d4a3e2 | 2006-07-21 19:53:48 +0000 | [diff] [blame] | 362 |  | 
 | 363 |     int flags = 0; | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 364 |     #ifdef MSG_NOSIGNAL | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 365 |     // Note the use of MSG_NOSIGNAL to suppress SIGPIPE errors, instead we | 
 | 366 |     // check for the EPIPE return condition and close the socket in that case | 
| Marc Slemko | 9d4a3e2 | 2006-07-21 19:53:48 +0000 | [diff] [blame] | 367 |     flags |= MSG_NOSIGNAL; | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 368 |     #endif // ifdef MSG_NOSIGNAL | 
| Marc Slemko | 9d4a3e2 | 2006-07-21 19:53:48 +0000 | [diff] [blame] | 369 |  | 
 | 370 |     int b = send(socket_, buf + sent, len - sent, flags); | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 371 |     ++g_socket_syscalls; | 
 | 372 |  | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 373 |     // Fail on a send error | 
 | 374 |     if (b < 0) { | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 375 |       if (errno == EPIPE) { | 
 | 376 |         close(); | 
| Mark Slee | f983108 | 2007-02-20 20:59:21 +0000 | [diff] [blame] | 377 |         throw TTransportException(TTransportException::NOT_OPEN, "EPIPE"); | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 378 |       } | 
 | 379 |  | 
 | 380 |       if (errno == ECONNRESET) { | 
 | 381 |         close(); | 
| Mark Slee | f983108 | 2007-02-20 20:59:21 +0000 | [diff] [blame] | 382 |         throw TTransportException(TTransportException::NOT_OPEN, "ECONNRESET"); | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 383 |       } | 
 | 384 |  | 
 | 385 |       if (errno == ENOTCONN) { | 
 | 386 |         close(); | 
| Mark Slee | f983108 | 2007-02-20 20:59:21 +0000 | [diff] [blame] | 387 |         throw TTransportException(TTransportException::NOT_OPEN, "ENOTCONN"); | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 388 |       } | 
 | 389 |  | 
| boz | 6ded775 | 2007-06-05 22:41:18 +0000 | [diff] [blame] | 390 |       GlobalOutput("TSocket::write() send < 0"); | 
| Martin Kraemer | e6c4fa6 | 2007-07-09 19:08:25 +0000 | [diff] [blame^] | 391 |       char b_error[1024]; | 
 | 392 |       strerror_r(errno, b_error, sizeof(b_error)); | 
 | 393 |       throw TTransportException(TTransportException::UNKNOWN, string("ERROR:") + b_error); | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 394 |     } | 
 | 395 |      | 
 | 396 |     // Fail on blocked send | 
 | 397 |     if (b == 0) { | 
| Mark Slee | f983108 | 2007-02-20 20:59:21 +0000 | [diff] [blame] | 398 |       throw TTransportException(TTransportException::NOT_OPEN, "Socket send returned 0."); | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 399 |     } | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 400 |     sent += b; | 
 | 401 |   } | 
 | 402 | } | 
 | 403 |  | 
| Aditya Agarwal | ebc99e0 | 2007-01-15 23:14:58 +0000 | [diff] [blame] | 404 | void TSocket::setHost(string host) { | 
 | 405 |   host_ = host; | 
 | 406 | } | 
 | 407 |  | 
 | 408 | void TSocket::setPort(int port) { | 
 | 409 |   port_ = port; | 
 | 410 | } | 
 | 411 |  | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 412 | void TSocket::setLinger(bool on, int linger) { | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 413 |   lingerOn_ = on; | 
 | 414 |   lingerVal_ = linger; | 
| Martin Kraemer | ee341cb | 2007-02-05 21:40:38 +0000 | [diff] [blame] | 415 |   if (socket_ < 0) { | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 416 |     return; | 
 | 417 |   } | 
 | 418 |  | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 419 |   struct linger l = {(lingerOn_ ? 1 : 0), lingerVal_}; | 
 | 420 |   int ret = setsockopt(socket_, SOL_SOCKET, SO_LINGER, &l, sizeof(l)); | 
 | 421 |   if (ret == -1) { | 
| boz | 6ded775 | 2007-06-05 22:41:18 +0000 | [diff] [blame] | 422 |     GlobalOutput("TSocket::setLinger()"); | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 423 |   } | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 424 | } | 
 | 425 |  | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 426 | void TSocket::setNoDelay(bool noDelay) { | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 427 |   noDelay_ = noDelay; | 
| Martin Kraemer | ee341cb | 2007-02-05 21:40:38 +0000 | [diff] [blame] | 428 |   if (socket_ < 0) { | 
| Mark Slee | 8d7e1f6 | 2006-06-07 06:48:56 +0000 | [diff] [blame] | 429 |     return; | 
 | 430 |   } | 
 | 431 |  | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 432 |   // Set socket to NODELAY | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 433 |   int v = noDelay_ ? 1 : 0; | 
 | 434 |   int ret = setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v)); | 
 | 435 |   if (ret == -1) { | 
| boz | 6ded775 | 2007-06-05 22:41:18 +0000 | [diff] [blame] | 436 |     GlobalOutput("TSocket::setNoDelay()"); | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 437 |   } | 
| Mark Slee | e854063 | 2006-05-30 09:24:40 +0000 | [diff] [blame] | 438 | } | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 439 |  | 
 | 440 | void TSocket::setConnTimeout(int ms) { | 
 | 441 |   connTimeout_ = ms; | 
 | 442 | } | 
 | 443 |  | 
 | 444 | void TSocket::setRecvTimeout(int ms) { | 
 | 445 |   recvTimeout_ = ms; | 
| Mark Slee | b9ff32a | 2006-11-16 01:00:24 +0000 | [diff] [blame] | 446 |   recvTimeval_.tv_sec = (int)(recvTimeout_/1000); | 
 | 447 |   recvTimeval_.tv_usec = (int)((recvTimeout_%1000)*1000); | 
| Martin Kraemer | ee341cb | 2007-02-05 21:40:38 +0000 | [diff] [blame] | 448 |   if (socket_ < 0) { | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 449 |     return; | 
 | 450 |   } | 
 | 451 |  | 
| Mark Slee | b9ff32a | 2006-11-16 01:00:24 +0000 | [diff] [blame] | 452 |   // Copy because select may modify | 
 | 453 |   struct timeval r = recvTimeval_; | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 454 |   int ret = setsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, &r, sizeof(r)); | 
 | 455 |   if (ret == -1) { | 
| boz | 6ded775 | 2007-06-05 22:41:18 +0000 | [diff] [blame] | 456 |     GlobalOutput("TSocket::setRecvTimeout()"); | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 457 |   } | 
 | 458 | } | 
 | 459 |  | 
 | 460 | void TSocket::setSendTimeout(int ms) { | 
 | 461 |   sendTimeout_ = ms; | 
| Martin Kraemer | ee341cb | 2007-02-05 21:40:38 +0000 | [diff] [blame] | 462 |   if (socket_ < 0) { | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 463 |     return; | 
 | 464 |   } | 
 | 465 |     | 
 | 466 |   struct timeval s = {(int)(sendTimeout_/1000), | 
 | 467 |                       (int)((sendTimeout_%1000)*1000)}; | 
 | 468 |   int ret = setsockopt(socket_, SOL_SOCKET, SO_SNDTIMEO, &s, sizeof(s)); | 
 | 469 |   if (ret == -1) { | 
| boz | 6ded775 | 2007-06-05 22:41:18 +0000 | [diff] [blame] | 470 |     GlobalOutput("TSocket::setSendTimeout()"); | 
| Mark Slee | 2905078 | 2006-09-29 00:12:30 +0000 | [diff] [blame] | 471 |   } | 
 | 472 | } | 
 | 473 |  | 
| Aditya Agarwal | e04475b | 2007-05-23 02:14:58 +0000 | [diff] [blame] | 474 | void TSocket::setMaxRecvRetries(int maxRecvRetries) { | 
 | 475 |   maxRecvRetries_ = maxRecvRetries; | 
 | 476 | } | 
 | 477 |  | 
| Marc Slemko | 6f038a7 | 2006-08-03 18:58:09 +0000 | [diff] [blame] | 478 | }}} // facebook::thrift::transport |