blob: a1e0327a1b7d5d80b8ee3d3ccf13559aae05e3f9 [file] [log] [blame]
Marc Slemkoe03da182006-07-21 21:32:36 +00001#include <config.h>
Mark Sleee8540632006-05-30 09:24:40 +00002#include <sys/socket.h>
3#include <arpa/inet.h>
4#include <netinet/in.h>
5#include <netinet/tcp.h>
6#include <netdb.h>
7#include <unistd.h>
8#include <errno.h>
9
10#include "transport/TSocket.h"
Mark Slee8d7e1f62006-06-07 06:48:56 +000011#include "transport/TTransportException.h"
Mark Sleee8540632006-05-30 09:24:40 +000012
Marc Slemko6f038a72006-08-03 18:58:09 +000013namespace facebook { namespace thrift { namespace transport {
14
Mark Sleee8540632006-05-30 09:24:40 +000015using namespace std;
16
Mark Slee8d7e1f62006-06-07 06:48:56 +000017uint32_t g_socket_syscalls = 0;
18
19/**
20 * TSocket implementation.
21 *
22 * @author Mark Slee <mcslee@facebook.com>
23 */
24
Mark Sleee8540632006-05-30 09:24:40 +000025// Mutex to protect syscalls to netdb
26pthread_mutex_t g_netdb_mutex = PTHREAD_MUTEX_INITIALIZER;
27
28// TODO(mcslee): Make this an option to the socket class
29#define MAX_RECV_RETRIES 20
30
31TSocket::TSocket(string host, int port) :
32 host_(host), port_(port), socket_(0) {}
33
34TSocket::TSocket(int socket) {
35 socket_ = socket;
36}
37
38TSocket::~TSocket() {
39 close();
40}
41
Mark Slee8d7e1f62006-06-07 06:48:56 +000042bool TSocket::isOpen() {
43 return (socket_ > 0);
44}
45
46void TSocket::open() {
Mark Sleee8540632006-05-30 09:24:40 +000047 // Create socket
48 socket_ = socket(AF_INET, SOCK_STREAM, 0);
49 if (socket_ == -1) {
Mark Slee8d7e1f62006-06-07 06:48:56 +000050 perror("TSocket::open() socket");
51 close();
52 throw TTransportException(TTX_NOT_OPEN, "socket() ERROR:" + errno);
Mark Sleee8540632006-05-30 09:24:40 +000053 }
54
Mark Slee8d7e1f62006-06-07 06:48:56 +000055 // Lookup the hostname
Mark Sleee8540632006-05-30 09:24:40 +000056 struct sockaddr_in addr;
57 addr.sin_family = AF_INET;
58 addr.sin_port = htons(port_);
59
60 /*
61 if (inet_pton(AF_INET, host_.c_str(), &addr.sin_addr) < 0) {
62 perror("TSocket::open() inet_pton");
63 }
64 */
65
66 {
67 // TODO(mcslee): Fix scope-locking here to protect hostname lookups
68 // scopelock sl(&netdb_mutex);
69 struct hostent *host_entry = gethostbyname(host_.c_str());
70
71 if (host_entry == NULL) {
72 // perror("dns error: failed call to gethostbyname.\n");
73 close();
Mark Slee8d7e1f62006-06-07 06:48:56 +000074 throw TTransportException(TTX_NOT_OPEN, "gethostbyname() failed");
Mark Sleee8540632006-05-30 09:24:40 +000075 }
76
77 addr.sin_port = htons(port_);
78 memcpy(&addr.sin_addr.s_addr,
79 host_entry->h_addr_list[0],
80 host_entry->h_length);
81 }
82
83 // Connect the socket
84 int ret = connect(socket_, (struct sockaddr *)&addr, sizeof(addr));
85
86 // Connect failed
87 if (ret < 0) {
88 perror("TSocket::open() connect");
89 close();
Mark Slee8d7e1f62006-06-07 06:48:56 +000090 throw TTransportException(TTX_NOT_OPEN, "open() ERROR: " + errno);
Mark Sleee8540632006-05-30 09:24:40 +000091 }
92
Mark Slee8d7e1f62006-06-07 06:48:56 +000093 // Connection was successful
Mark Sleee8540632006-05-30 09:24:40 +000094}
95
96void TSocket::close() {
97 if (socket_ > 0) {
98 shutdown(socket_, SHUT_RDWR);
99 ::close(socket_);
100 }
101 socket_ = 0;
102}
103
Mark Slee8d7e1f62006-06-07 06:48:56 +0000104uint32_t TSocket::read(uint8_t* buf, uint32_t len) {
105 if (socket_ <= 0) {
106 throw TTransportException(TTX_NOT_OPEN, "Called read on non-open socket");
107 }
Mark Sleee8540632006-05-30 09:24:40 +0000108
Mark Sleee8540632006-05-30 09:24:40 +0000109 uint32_t retries = 0;
Mark Slee8d7e1f62006-06-07 06:48:56 +0000110
111 try_again:
112 // Read from the socket
113 int got = recv(socket_, buf, len, 0);
114 ++g_socket_syscalls;
115
116 // Check for error on read
117 if (got < 0) {
118 perror("TSocket::read()");
119
120 // If temporarily out of resources, sleep a bit and try again
121 if (errno == EAGAIN && retries++ < MAX_RECV_RETRIES) {
122 usleep(50);
123 goto try_again;
Mark Sleee8540632006-05-30 09:24:40 +0000124 }
125
Mark Slee8d7e1f62006-06-07 06:48:56 +0000126 // If interrupted, try again
127 if (errno == EINTR && retries++ < MAX_RECV_RETRIES) {
128 goto try_again;
Mark Sleee8540632006-05-30 09:24:40 +0000129 }
130
Mark Slee8d7e1f62006-06-07 06:48:56 +0000131 // If we disconnect with no linger time
132 if (errno == ECONNRESET) {
133 throw TTransportException(TTX_NOT_OPEN, "ECONNRESET");
134 }
135
136 // This ish isn't open
137 if (errno == ENOTCONN) {
138 throw TTransportException(TTX_NOT_OPEN, "ENOTCONN");
139 }
140
141 // Timed out!
142 if (errno == ETIMEDOUT) {
143 throw TTransportException(TTX_TIMED_OUT, "ETIMEDOUT");
144 }
145
146 // Some other error, whatevz
147 throw TTransportException(TTX_UNKNOWN, "ERROR:" + errno);
148 }
149
150 // The remote host has closed the socket
151 if (got == 0) {
152 close();
153 return 0;
Mark Sleee8540632006-05-30 09:24:40 +0000154 }
155
156 // Pack data into string
Mark Slee8d7e1f62006-06-07 06:48:56 +0000157 return got;
Mark Sleee8540632006-05-30 09:24:40 +0000158}
159
Mark Slee8d7e1f62006-06-07 06:48:56 +0000160void TSocket::write(const uint8_t* buf, uint32_t len) {
161 if (socket_ <= 0) {
162 throw TTransportException(TTX_NOT_OPEN, "Called write on non-open socket");
163 }
164
Mark Sleee8540632006-05-30 09:24:40 +0000165 uint32_t sent = 0;
166
Mark Slee8d7e1f62006-06-07 06:48:56 +0000167 while (sent < len) {
Marc Slemko9d4a3e22006-07-21 19:53:48 +0000168
169 int flags = 0;
170
Marc Slemkoe03da182006-07-21 21:32:36 +0000171 #if defined(MSG_NOSIGNAL)
Mark Slee8d7e1f62006-06-07 06:48:56 +0000172 // Note the use of MSG_NOSIGNAL to suppress SIGPIPE errors, instead we
173 // check for the EPIPE return condition and close the socket in that case
Marc Slemko9d4a3e22006-07-21 19:53:48 +0000174 flags |= MSG_NOSIGNAL;
Marc Slemkoe03da182006-07-21 21:32:36 +0000175 #endif // defined(MSG_NOSIGNAL)
Marc Slemko9d4a3e22006-07-21 19:53:48 +0000176
177 int b = send(socket_, buf + sent, len - sent, flags);
Mark Slee8d7e1f62006-06-07 06:48:56 +0000178 ++g_socket_syscalls;
179
Mark Sleee8540632006-05-30 09:24:40 +0000180 // Fail on a send error
181 if (b < 0) {
Mark Slee8d7e1f62006-06-07 06:48:56 +0000182 if (errno == EPIPE) {
183 close();
184 throw TTransportException(TTX_NOT_OPEN, "EPIPE");
185 }
186
187 if (errno == ECONNRESET) {
188 close();
189 throw TTransportException(TTX_NOT_OPEN, "ECONNRESET");
190 }
191
192 if (errno == ENOTCONN) {
193 close();
194 throw TTransportException(TTX_NOT_OPEN, "ENOTCONN");
195 }
196
197 perror("TSocket::write() send < 0");
198 throw TTransportException(TTX_UNKNOWN, "ERROR:" + errno);
Mark Sleee8540632006-05-30 09:24:40 +0000199 }
200
201 // Fail on blocked send
202 if (b == 0) {
Mark Slee8d7e1f62006-06-07 06:48:56 +0000203 throw TTransportException(TTX_NOT_OPEN, "Socket send returned 0.");
Mark Sleee8540632006-05-30 09:24:40 +0000204 }
Mark Sleee8540632006-05-30 09:24:40 +0000205 sent += b;
206 }
207}
208
Mark Slee8d7e1f62006-06-07 06:48:56 +0000209void TSocket::setLinger(bool on, int linger) {
210 // TODO(mcslee): Store these options so they can be set pre-connect
211 if (socket_ <= 0) {
212 return;
213 }
214
Mark Sleee8540632006-05-30 09:24:40 +0000215 struct linger ling = {(on ? 1 : 0), linger};
216 if (-1 == setsockopt(socket_, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling))) {
217 close();
218 perror("TSocket::setLinger()");
Mark Sleee8540632006-05-30 09:24:40 +0000219 }
Mark Sleee8540632006-05-30 09:24:40 +0000220}
221
Mark Slee8d7e1f62006-06-07 06:48:56 +0000222void TSocket::setNoDelay(bool noDelay) {
223 // TODO(mcslee): Store these options so they can be set pre-connect
224 if (socket_ <= 0) {
225 return;
226 }
227
Mark Sleee8540632006-05-30 09:24:40 +0000228 // Set socket to NODELAY
229 int val = (noDelay ? 1 : 0);
230 if (-1 == setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val))) {
231 close();
232 perror("TSocket::setNoDelay()");
Mark Sleee8540632006-05-30 09:24:40 +0000233 }
Mark Sleee8540632006-05-30 09:24:40 +0000234}
Marc Slemko6f038a72006-08-03 18:58:09 +0000235}}} // facebook::thrift::transport