OLD | NEW |
| (Empty) |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "net/base/tcp_client_socket_libevent.h" | |
6 | |
7 #include <errno.h> | |
8 #include <fcntl.h> | |
9 #include <netdb.h> | |
10 #include <sys/socket.h> | |
11 | |
12 #include "base/eintr_wrapper.h" | |
13 #include "base/message_loop.h" | |
14 #include "base/string_util.h" | |
15 #include "base/trace_event.h" | |
16 #include "net/base/io_buffer.h" | |
17 #include "net/base/net_errors.h" | |
18 #include "third_party/libevent/event.h" | |
19 | |
20 | |
21 namespace net { | |
22 | |
23 namespace { | |
24 | |
25 const int kInvalidSocket = -1; | |
26 | |
27 // Return 0 on success, -1 on failure. | |
28 // Too small a function to bother putting in a library? | |
29 int SetNonBlocking(int fd) { | |
30 int flags = fcntl(fd, F_GETFL, 0); | |
31 if (-1 == flags) | |
32 return flags; | |
33 return fcntl(fd, F_SETFL, flags | O_NONBLOCK); | |
34 } | |
35 | |
36 // Convert values from <errno.h> to values from "net/base/net_errors.h" | |
37 int MapPosixError(int err) { | |
38 // There are numerous posix error codes, but these are the ones we thus far | |
39 // find interesting. | |
40 switch (err) { | |
41 case EAGAIN: | |
42 #if EWOULDBLOCK != EAGAIN | |
43 case EWOULDBLOCK: | |
44 #endif | |
45 return ERR_IO_PENDING; | |
46 case ENETDOWN: | |
47 return ERR_INTERNET_DISCONNECTED; | |
48 case ETIMEDOUT: | |
49 return ERR_TIMED_OUT; | |
50 case ECONNRESET: | |
51 case ENETRESET: // Related to keep-alive | |
52 return ERR_CONNECTION_RESET; | |
53 case ECONNABORTED: | |
54 return ERR_CONNECTION_ABORTED; | |
55 case ECONNREFUSED: | |
56 return ERR_CONNECTION_REFUSED; | |
57 case EHOSTUNREACH: | |
58 case ENETUNREACH: | |
59 return ERR_ADDRESS_UNREACHABLE; | |
60 case EADDRNOTAVAIL: | |
61 return ERR_ADDRESS_INVALID; | |
62 case 0: | |
63 return OK; | |
64 default: | |
65 LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED"; | |
66 return ERR_FAILED; | |
67 } | |
68 } | |
69 | |
70 } // namespace | |
71 | |
72 //----------------------------------------------------------------------------- | |
73 | |
74 TCPClientSocketLibevent::TCPClientSocketLibevent(const AddressList& addresses) | |
75 : socket_(kInvalidSocket), | |
76 addresses_(addresses), | |
77 current_ai_(addresses_.head()), | |
78 waiting_connect_(false), | |
79 read_watcher_(this), | |
80 write_watcher_(this), | |
81 read_callback_(NULL), | |
82 write_callback_(NULL) { | |
83 } | |
84 | |
85 TCPClientSocketLibevent::~TCPClientSocketLibevent() { | |
86 Disconnect(); | |
87 } | |
88 | |
89 int TCPClientSocketLibevent::Connect(CompletionCallback* callback) { | |
90 // If already connected, then just return OK. | |
91 if (socket_ != kInvalidSocket) | |
92 return OK; | |
93 | |
94 DCHECK(!waiting_connect_); | |
95 | |
96 TRACE_EVENT_BEGIN("socket.connect", this, ""); | |
97 const addrinfo* ai = current_ai_; | |
98 DCHECK(ai); | |
99 | |
100 int rv = CreateSocket(ai); | |
101 if (rv != OK) | |
102 return rv; | |
103 | |
104 if (!HANDLE_EINTR(connect(socket_, ai->ai_addr, | |
105 static_cast<int>(ai->ai_addrlen)))) { | |
106 TRACE_EVENT_END("socket.connect", this, ""); | |
107 // Connected without waiting! | |
108 return OK; | |
109 } | |
110 | |
111 // Synchronous operation not supported | |
112 DCHECK(callback); | |
113 | |
114 if (errno != EINPROGRESS) { | |
115 DLOG(INFO) << "connect failed: " << errno; | |
116 close(socket_); | |
117 socket_ = kInvalidSocket; | |
118 return MapPosixError(errno); | |
119 } | |
120 | |
121 // Initialize write_socket_watcher_ and link it to our MessagePump. | |
122 // POLLOUT is set if the connection is established. | |
123 // POLLIN is set if the connection fails. | |
124 if (!MessageLoopForIO::current()->WatchFileDescriptor( | |
125 socket_, true, MessageLoopForIO::WATCH_WRITE, &write_socket_watcher_, | |
126 &write_watcher_)) { | |
127 DLOG(INFO) << "WatchFileDescriptor failed: " << errno; | |
128 close(socket_); | |
129 socket_ = kInvalidSocket; | |
130 return MapPosixError(errno); | |
131 } | |
132 | |
133 waiting_connect_ = true; | |
134 write_callback_ = callback; | |
135 return ERR_IO_PENDING; | |
136 } | |
137 | |
138 void TCPClientSocketLibevent::Disconnect() { | |
139 if (socket_ == kInvalidSocket) | |
140 return; | |
141 | |
142 TRACE_EVENT_INSTANT("socket.disconnect", this, ""); | |
143 | |
144 bool ok = read_socket_watcher_.StopWatchingFileDescriptor(); | |
145 DCHECK(ok); | |
146 ok = write_socket_watcher_.StopWatchingFileDescriptor(); | |
147 DCHECK(ok); | |
148 close(socket_); | |
149 socket_ = kInvalidSocket; | |
150 waiting_connect_ = false; | |
151 | |
152 // Reset for next time. | |
153 current_ai_ = addresses_.head(); | |
154 } | |
155 | |
156 bool TCPClientSocketLibevent::IsConnected() const { | |
157 if (socket_ == kInvalidSocket || waiting_connect_) | |
158 return false; | |
159 | |
160 // Check if connection is alive. | |
161 char c; | |
162 int rv = HANDLE_EINTR(recv(socket_, &c, 1, MSG_PEEK)); | |
163 if (rv == 0) | |
164 return false; | |
165 if (rv == -1 && errno != EAGAIN && errno != EWOULDBLOCK) | |
166 return false; | |
167 | |
168 return true; | |
169 } | |
170 | |
171 bool TCPClientSocketLibevent::IsConnectedAndIdle() const { | |
172 if (socket_ == kInvalidSocket || waiting_connect_) | |
173 return false; | |
174 | |
175 // Check if connection is alive and we haven't received any data | |
176 // unexpectedly. | |
177 char c; | |
178 int rv = HANDLE_EINTR(recv(socket_, &c, 1, MSG_PEEK)); | |
179 if (rv >= 0) | |
180 return false; | |
181 if (errno != EAGAIN && errno != EWOULDBLOCK) | |
182 return false; | |
183 | |
184 return true; | |
185 } | |
186 | |
187 int TCPClientSocketLibevent::Read(IOBuffer* buf, | |
188 int buf_len, | |
189 CompletionCallback* callback) { | |
190 DCHECK_NE(kInvalidSocket, socket_); | |
191 DCHECK(!waiting_connect_); | |
192 DCHECK(!read_callback_); | |
193 // Synchronous operation not supported | |
194 DCHECK(callback); | |
195 DCHECK_GT(buf_len, 0); | |
196 | |
197 TRACE_EVENT_BEGIN("socket.read", this, ""); | |
198 int nread = HANDLE_EINTR(read(socket_, buf->data(), buf_len)); | |
199 if (nread >= 0) { | |
200 TRACE_EVENT_END("socket.read", this, StringPrintf("%d bytes", nread)); | |
201 return nread; | |
202 } | |
203 if (errno != EAGAIN && errno != EWOULDBLOCK) { | |
204 DLOG(INFO) << "read failed, errno " << errno; | |
205 return MapPosixError(errno); | |
206 } | |
207 | |
208 if (!MessageLoopForIO::current()->WatchFileDescriptor( | |
209 socket_, true, MessageLoopForIO::WATCH_READ, | |
210 &read_socket_watcher_, &read_watcher_)) { | |
211 DLOG(INFO) << "WatchFileDescriptor failed on read, errno " << errno; | |
212 return MapPosixError(errno); | |
213 } | |
214 | |
215 read_buf_ = buf; | |
216 read_buf_len_ = buf_len; | |
217 read_callback_ = callback; | |
218 return ERR_IO_PENDING; | |
219 } | |
220 | |
221 int TCPClientSocketLibevent::Write(IOBuffer* buf, | |
222 int buf_len, | |
223 CompletionCallback* callback) { | |
224 DCHECK_NE(kInvalidSocket, socket_); | |
225 DCHECK(!waiting_connect_); | |
226 DCHECK(!write_callback_); | |
227 // Synchronous operation not supported | |
228 DCHECK(callback); | |
229 DCHECK_GT(buf_len, 0); | |
230 | |
231 TRACE_EVENT_BEGIN("socket.write", this, ""); | |
232 int nwrite = HANDLE_EINTR(write(socket_, buf->data(), buf_len)); | |
233 if (nwrite >= 0) { | |
234 TRACE_EVENT_END("socket.write", this, StringPrintf("%d bytes", nwrite)); | |
235 return nwrite; | |
236 } | |
237 if (errno != EAGAIN && errno != EWOULDBLOCK) | |
238 return MapPosixError(errno); | |
239 | |
240 if (!MessageLoopForIO::current()->WatchFileDescriptor( | |
241 socket_, true, MessageLoopForIO::WATCH_WRITE, | |
242 &write_socket_watcher_, &write_watcher_)) { | |
243 DLOG(INFO) << "WatchFileDescriptor failed on write, errno " << errno; | |
244 return MapPosixError(errno); | |
245 } | |
246 | |
247 | |
248 write_buf_ = buf; | |
249 write_buf_len_ = buf_len; | |
250 write_callback_ = callback; | |
251 return ERR_IO_PENDING; | |
252 } | |
253 | |
254 int TCPClientSocketLibevent::CreateSocket(const addrinfo* ai) { | |
255 socket_ = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); | |
256 if (socket_ == kInvalidSocket) | |
257 return MapPosixError(errno); | |
258 | |
259 if (SetNonBlocking(socket_)) | |
260 return MapPosixError(errno); | |
261 | |
262 return OK; | |
263 } | |
264 | |
265 void TCPClientSocketLibevent::DoReadCallback(int rv) { | |
266 DCHECK_NE(rv, ERR_IO_PENDING); | |
267 DCHECK(read_callback_); | |
268 | |
269 // since Run may result in Read being called, clear read_callback_ up front. | |
270 CompletionCallback* c = read_callback_; | |
271 read_callback_ = NULL; | |
272 c->Run(rv); | |
273 } | |
274 | |
275 void TCPClientSocketLibevent::DoWriteCallback(int rv) { | |
276 DCHECK_NE(rv, ERR_IO_PENDING); | |
277 DCHECK(write_callback_); | |
278 | |
279 // since Run may result in Write being called, clear write_callback_ up front. | |
280 CompletionCallback* c = write_callback_; | |
281 write_callback_ = NULL; | |
282 c->Run(rv); | |
283 } | |
284 | |
285 void TCPClientSocketLibevent::DidCompleteConnect() { | |
286 int result = ERR_UNEXPECTED; | |
287 | |
288 TRACE_EVENT_END("socket.connect", this, ""); | |
289 | |
290 // Check to see if connect succeeded | |
291 int error_code = 0; | |
292 socklen_t len = sizeof(error_code); | |
293 if (getsockopt(socket_, SOL_SOCKET, SO_ERROR, &error_code, &len) < 0) | |
294 error_code = errno; | |
295 | |
296 if (error_code == EINPROGRESS || error_code == EALREADY) { | |
297 NOTREACHED(); // This indicates a bug in libevent or our code. | |
298 result = ERR_IO_PENDING; | |
299 } else if (current_ai_->ai_next && ( | |
300 error_code == EADDRNOTAVAIL || | |
301 error_code == EAFNOSUPPORT || | |
302 error_code == ECONNREFUSED || | |
303 error_code == ENETUNREACH || | |
304 error_code == EHOSTUNREACH || | |
305 error_code == ETIMEDOUT)) { | |
306 // This address failed, try next one in list. | |
307 const addrinfo* next = current_ai_->ai_next; | |
308 Disconnect(); | |
309 current_ai_ = next; | |
310 result = Connect(write_callback_); | |
311 } else { | |
312 result = MapPosixError(error_code); | |
313 bool ok = write_socket_watcher_.StopWatchingFileDescriptor(); | |
314 DCHECK(ok); | |
315 waiting_connect_ = false; | |
316 } | |
317 | |
318 if (result != ERR_IO_PENDING) { | |
319 DoWriteCallback(result); | |
320 } | |
321 } | |
322 | |
323 void TCPClientSocketLibevent::DidCompleteRead() { | |
324 int bytes_transferred; | |
325 bytes_transferred = HANDLE_EINTR(read(socket_, read_buf_->data(), | |
326 read_buf_len_)); | |
327 | |
328 int result; | |
329 if (bytes_transferred >= 0) { | |
330 TRACE_EVENT_END("socket.read", this, | |
331 StringPrintf("%d bytes", bytes_transferred)); | |
332 result = bytes_transferred; | |
333 } else { | |
334 result = MapPosixError(errno); | |
335 } | |
336 | |
337 if (result != ERR_IO_PENDING) { | |
338 read_buf_ = NULL; | |
339 read_buf_len_ = 0; | |
340 bool ok = read_socket_watcher_.StopWatchingFileDescriptor(); | |
341 DCHECK(ok); | |
342 DoReadCallback(result); | |
343 } | |
344 } | |
345 | |
346 void TCPClientSocketLibevent::DidCompleteWrite() { | |
347 int bytes_transferred; | |
348 bytes_transferred = HANDLE_EINTR(write(socket_, write_buf_->data(), | |
349 write_buf_len_)); | |
350 | |
351 int result; | |
352 if (bytes_transferred >= 0) { | |
353 result = bytes_transferred; | |
354 TRACE_EVENT_END("socket.write", this, | |
355 StringPrintf("%d bytes", bytes_transferred)); | |
356 } else { | |
357 result = MapPosixError(errno); | |
358 } | |
359 | |
360 if (result != ERR_IO_PENDING) { | |
361 write_buf_ = NULL; | |
362 write_buf_len_ = 0; | |
363 write_socket_watcher_.StopWatchingFileDescriptor(); | |
364 DoWriteCallback(result); | |
365 } | |
366 } | |
367 | |
368 int TCPClientSocketLibevent::GetPeerName(struct sockaddr *name, | |
369 socklen_t *namelen) { | |
370 return ::getpeername(socket_, name, namelen); | |
371 } | |
372 | |
373 } // namespace net | |
OLD | NEW |