Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(246)

Side by Side Diff: net/base/tcp_client_socket_libevent.cc

Issue 3202: Make tcp_client_socket_unittest pass on Linux.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 12 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « net/base/tcp_client_socket.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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.h"
6 #include "net/base/net_errors.h"
7 #include "third_party/libevent/event.h"
8 #include "base/message_loop.h"
9
10 #include <sys/socket.h>
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <netdb.h>
14
15 namespace net {
16
17 #ifndef INVALID_SOCKET
18 #define INVALID_SOCKET (unsigned)(-1)
19 #endif
20
21 static int MapPosixError(int err) {
22 // There are numerous posix error codes, but these are the ones we thus far
23 // find interesting.
24 switch (err) {
25 default:
26 return ERR_FAILED;
27 }
28 }
29
30 //-----------------------------------------------------------------------------
31
32 TCPClientSocket::TCPClientSocket(const AddressList& addresses)
33 : socket_(INVALID_SOCKET),
34 addresses_(addresses),
35 current_ai_(addresses_.head()),
36 wait_state_(NOT_WAITING) {
37 event_ = new struct event;
38 }
39
40 TCPClientSocket::~TCPClientSocket() {
41 Disconnect();
42 delete event_;
43 }
44
45 int TCPClientSocket::Connect(CompletionCallback* callback) {
46 // If already connected, then just return OK.
47 if (socket_ != INVALID_SOCKET)
48 return OK;
49
50 const struct addrinfo* ai = current_ai_;
51 DCHECK(ai);
52
53 int rv = CreateSocket(ai);
54 if (rv != OK)
55 return rv;
56
57 if (!connect(socket_, ai->ai_addr, static_cast<int>(ai->ai_addrlen))) {
58 // Connected without waiting!
59 return OK;
60 }
61
62 if (errno != EINPROGRESS && errno != EWOULDBLOCK) {
63 LOG(ERROR) << "connect failed: " << errno;
64 return MapPosixError(errno);
65 }
66
67 // Initialize event_ and link it to our MessagePump.
68 // POLLOUT is set if the connection is established. POLLIN is set if the conne ction fails,
69 // so select for both read and write.
70 ::event_set(event_, socket_, EV_READ|EV_WRITE|EV_PERSIST, TCPClientSocket_libe vent_cb, this);
71 ::event_base_set(MessageLoop::current()->pump_libevent()->getEventbase(), even t_);
72 if (::event_add(event_, NULL))
73 return ERR_FAILED;
74
75 wait_state_ = WAITING_CONNECT;
76 callback_ = callback;
77 return ERR_IO_PENDING;
78 }
79
80 int TCPClientSocket::ReconnectIgnoringLastError(CompletionCallback* callback) {
81 // No ignorable errors!
82 return ERR_FAILED;
83 }
84
85 void TCPClientSocket::Disconnect() {
86 if (socket_ == INVALID_SOCKET)
87 return;
88
89 ::event_del(event_);
90 close(socket_);
91 socket_ = INVALID_SOCKET;
92
93 // Reset for next time.
94 current_ai_ = addresses_.head();
95 }
96
97 bool TCPClientSocket::IsConnected() const {
98 if (socket_ == INVALID_SOCKET || wait_state_ == WAITING_CONNECT)
99 return false;
100
101 // Check if connection is alive.
102 char c;
103 int rv = recv(socket_, &c, 1, MSG_PEEK);
104 if (rv == 0)
105 return false;
106
107 return true;
108 }
109
110 int TCPClientSocket::Read(char* buf,
111 int buf_len,
112 CompletionCallback* callback) {
113 DCHECK(socket_ != INVALID_SOCKET);
114 DCHECK(wait_state_ == NOT_WAITING);
115 DCHECK(!callback_);
116 DCHECK(callback); // null callback not allowed?
117 DCHECK(buf_len > 0);
118
119 int nread = read(socket_, buf, buf_len);
120 if (nread > 0) {
121 return nread;
122 }
123 if (nread == -1 && errno != EWOULDBLOCK)
124 return MapPosixError(errno);
125
126 ::event_set(event_, socket_, EV_READ|EV_PERSIST, TCPClientSocket_libevent_cb, this);
127 ::event_base_set(MessageLoop::current()->pump_libevent()->getEventbase(), even t_);
128 if (::event_add(event_, NULL))
129 return ERR_FAILED;
130 buf_ = buf;
131 buf_len_ = buf_len;
132 wait_state_ = WAITING_READ;
133 callback_ = callback;
134 return ERR_IO_PENDING;
135 }
136
137 int TCPClientSocket::Write(const char* buf,
138 int buf_len,
139 CompletionCallback* callback) {
140 DCHECK(socket_ != INVALID_SOCKET);
141 DCHECK(wait_state_ == NOT_WAITING);
142 DCHECK(!callback_);
143 DCHECK(buf_len > 0);
144
145 int nwrite = write(socket_, buf, buf_len);
146 if (nwrite > 0) {
147 return nwrite;
148 }
149 if (nwrite == -1 && errno != EWOULDBLOCK)
150 return MapPosixError(errno);
151
152 ::event_set(event_, socket_, EV_WRITE|EV_PERSIST, TCPClientSocket_libevent_cb, this);
153 ::event_base_set(MessageLoop::current()->pump_libevent()->getEventbase(), even t_);
154 if (::event_add(event_, NULL))
155 return ERR_FAILED;
156 buf_ = const_cast<char *>(buf);
157 buf_len_ = buf_len;
158 wait_state_ = WAITING_WRITE;
159 callback_ = callback;
160 return ERR_IO_PENDING;
161 }
162
163 int TCPClientSocket::CreateSocket(const struct addrinfo* ai) {
164 socket_ = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
165 if (socket_ == INVALID_SOCKET)
166 return MapPosixError(errno);
167
168 // All our socket I/O is nonblocking
169 int flags = fcntl(socket_, F_GETFL, 0);
170 if (-1 == flags)
171 flags = 0;
172 if (fcntl(socket_, F_SETFL, flags | O_NONBLOCK))
173 return MapPosixError(errno);
174
175 return OK;
176 }
177
178 void TCPClientSocket::DoCallback(int rv) {
179 DCHECK(rv != ERR_IO_PENDING);
180 DCHECK(callback_);
181
182 // since Run may result in Read being called, clear callback_ up front.
183 CompletionCallback* c = callback_;
184 callback_ = NULL;
185 c->Run(rv);
186 }
187
188 void TCPClientSocket::DidCompleteConnect() {
189 int result = ERR_UNEXPECTED;
190
191 wait_state_ = NOT_WAITING;
192
193 /* check to see if connect succeeded */
194 int error_code = -1;
195 socklen_t len = sizeof(error_code);
196 if (getsockopt(socket_, SOL_SOCKET, SO_ERROR, (char *)&error_code, &len) < 0) {
197 result = MapPosixError(errno);
198 } else if (error_code == EINPROGRESS) {
199 result = ERR_IO_PENDING;
200 } else if (current_ai_->ai_next && (
201 error_code == EADDRNOTAVAIL ||
202 error_code == EAFNOSUPPORT ||
203 error_code == ECONNREFUSED ||
204 error_code == ENETUNREACH ||
205 error_code == EHOSTUNREACH ||
206 error_code == ETIMEDOUT)) {
207 const struct addrinfo* next = current_ai_->ai_next;
208 Disconnect();
209 current_ai_ = next;
210 result = Connect(callback_);
211 } else if (error_code) {
212 result = MapPosixError(error_code);
213 } else {
214 result = 0;
215 ::event_del(event_);
216 }
217
218 if (result != ERR_IO_PENDING)
219 DoCallback(result);
220 }
221
222 void TCPClientSocket::DidCompleteIO() {
223 int result;
224 int n;
225
226 switch (wait_state_) {
227 case WAITING_READ:
228 n = read(socket_, buf_, buf_len_);
229 break;
230 case WAITING_WRITE:
231 n = write(socket_, buf_, buf_len_);
232 break;
233 }
234
235 if (n > 0)
236 result = n;
237 else if (n == 0) {
238 // TODO: can we tell why it was closed, and return a more informative messag e?
239 // And why does the unit test want to see zero?
240 //result = ERR_CONNECTION_CLOSED;
241 result = 0;
242 } else
243 result = MapPosixError(errno);
244
245 if (result != ERR_IO_PENDING) {
246 wait_state_ = NOT_WAITING;
247 ::event_del(event_);
248 DoCallback(result);
249 }
250 }
251
252 void TCPClientSocket::OnLibeventNotification(short flags) {
253
254 switch (wait_state_) {
255 case WAITING_CONNECT:
256 DidCompleteConnect();
257 break;
258 case WAITING_READ:
259 case WAITING_WRITE:
260 DidCompleteIO();
261 break;
262 default:
263 NOTREACHED();
264 break;
265 }
266 }
267
268 extern "C" void TCPClientSocket_libevent_cb(int socket, short flags, void *conte xt) {
269 TCPClientSocket* that = static_cast<TCPClientSocket*>(context);
270 DCHECK(that->socket_ == socket);
271 that->OnLibeventNotification(flags);
272 }
273
274 } // namespace net
275
OLDNEW
« no previous file with comments | « net/base/tcp_client_socket.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698