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

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
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <netdb.h>
10 #include <sys/socket.h>
11
12 #include "base/message_loop.h"
13 #include "net/base/net_errors.h"
14 #include "third_party/libevent/event.h"
15
16
17 namespace net {
18
19 const int kInvalidSocket = -1;
20
21 // Return 0 on success
22 // Too small a function to bother putting in a library?
23 static int SetNonBlocking(int fd)
24 {
25 int flags = fcntl(fd, F_GETFL, 0);
26 if (-1 == flags)
27 flags = 0;
28 return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
29 }
30
31 // Convert values from <errno.h> to values from "net/base/net_errors.h"
32 static int MapPosixError(int err) {
33 // There are numerous posix error codes, but these are the ones we thus far
34 // find interesting.
35 // TODO(port): fill this with a real conversion table
36 switch (err) {
37 case EWOULDBLOCK: return ERR_IO_PENDING;
38 default:
39 return ERR_FAILED;
40 }
41 }
42
43 //-----------------------------------------------------------------------------
44
45 TCPClientSocket::TCPClientSocket(const AddressList& addresses)
46 : socket_(kInvalidSocket),
47 addresses_(addresses),
48 current_ai_(addresses_.head()),
49 wait_state_(NOT_WAITING),
50 event_(new event) {
51 }
52
53 TCPClientSocket::~TCPClientSocket() {
54 Disconnect();
55 }
56
57 int TCPClientSocket::Connect(CompletionCallback* callback) {
58
59 // If already connected, then just return OK.
60 if (socket_ != kInvalidSocket)
61 return OK;
62
63 DCHECK(wait_state_ == NOT_WAITING);
64
65 const addrinfo* ai = current_ai_;
66 DCHECK(ai);
67
68 int rv = CreateSocket(ai);
69 if (rv != OK)
70 return rv;
71
72 if (!connect(socket_, ai->ai_addr, static_cast<int>(ai->ai_addrlen))) {
73 // Connected without waiting!
74 return OK;
75 }
76
77 // Synchronous operation not supported
78 DCHECK(callback);
79
80 if (errno != EINPROGRESS && errno != EWOULDBLOCK) {
81 LOG(ERROR) << "connect failed: " << errno;
82 return MapPosixError(errno);
83 }
84
85 // Initialize event_ and link it to our MessagePump.
86 // POLLOUT is set if the connection is established.
87 // POLLIN is set if the connection fails,
88 // so select for both read and write.
89 MessageLoopForIO::current()->WatchSocket(
90 socket_, EV_READ|EV_WRITE|EV_PERSIST, event_.get(), this);
91
92 wait_state_ = WAITING_CONNECT;
93 callback_ = callback;
94 return ERR_IO_PENDING;
95 }
96
97 int TCPClientSocket::ReconnectIgnoringLastError(CompletionCallback* callback) {
98 // No ignorable errors!
99 return ERR_FAILED;
100 }
101
102 void TCPClientSocket::Disconnect() {
103 if (socket_ == kInvalidSocket)
104 return;
105
106 MessageLoopForIO::current()->UnwatchSocket(event_.get());
107 close(socket_);
108 socket_ = kInvalidSocket;
109
110 // Reset for next time.
111 current_ai_ = addresses_.head();
112 }
113
114 bool TCPClientSocket::IsConnected() const {
115 if (socket_ == kInvalidSocket || wait_state_ == WAITING_CONNECT)
116 return false;
117
118 // Check if connection is alive.
119 char c;
120 int rv = recv(socket_, &c, 1, MSG_PEEK);
121 if (rv == 0)
122 return false;
123
124 return true;
125 }
126
127 int TCPClientSocket::Read(char* buf,
128 int buf_len,
129 CompletionCallback* callback) {
130 DCHECK(socket_ != kInvalidSocket);
131 DCHECK(wait_state_ == NOT_WAITING);
132 DCHECK(!callback_);
133 // Synchronous operation not supported
134 DCHECK(callback);
135 DCHECK(buf_len > 0);
136
137 int nread = read(socket_, buf, buf_len);
138 if (nread > 0) {
139 return nread;
140 }
141 if (nread == -1 && errno != EWOULDBLOCK)
142 return MapPosixError(errno);
143
144 MessageLoopForIO::current()->WatchSocket(
145 socket_, EV_READ|EV_PERSIST, event_.get(), this);
146
147 buf_ = buf;
148 buf_len_ = buf_len;
149 wait_state_ = WAITING_READ;
150 callback_ = callback;
151 return ERR_IO_PENDING;
152 }
153
154 int TCPClientSocket::Write(const char* buf,
155 int buf_len,
156 CompletionCallback* callback) {
157 DCHECK(socket_ != kInvalidSocket);
158 DCHECK(wait_state_ == NOT_WAITING);
159 DCHECK(!callback_);
160 // Synchronous operation not supported
161 DCHECK(callback);
162 DCHECK(buf_len > 0);
163
164 int nwrite = write(socket_, buf, buf_len);
165 if (nwrite > 0) {
166 return nwrite;
167 }
168 if (nwrite == -1 && errno != EWOULDBLOCK)
169 return MapPosixError(errno);
170
171 MessageLoopForIO::current()->WatchSocket(
172 socket_, EV_WRITE|EV_PERSIST, event_.get(), this);
173
174 buf_ = const_cast<char*>(buf);
175 buf_len_ = buf_len;
176 wait_state_ = WAITING_WRITE;
177 callback_ = callback;
178 return ERR_IO_PENDING;
179 }
180
181 int TCPClientSocket::CreateSocket(const addrinfo* ai) {
182 socket_ = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
183 if (socket_ == kInvalidSocket)
184 return MapPosixError(errno);
185
186 // All our socket I/O is nonblocking
187 if (SetNonBlocking(socket_))
188 return MapPosixError(errno);
189
190 return OK;
191 }
192
193 void TCPClientSocket::DoCallback(int rv) {
194 DCHECK(rv != ERR_IO_PENDING);
195 DCHECK(callback_);
196
197 // since Run may result in Read being called, clear callback_ up front.
198 CompletionCallback* c = callback_;
199 callback_ = NULL;
200 c->Run(rv);
201 }
202
203 void TCPClientSocket::DidCompleteConnect() {
204 int result = ERR_UNEXPECTED;
205
206 wait_state_ = NOT_WAITING;
207
208 // Check to see if connect succeeded
209 int error_code = -1;
210 socklen_t len = sizeof(error_code);
211 if (getsockopt(socket_, SOL_SOCKET, SO_ERROR,
212 reinterpret_cast<char*>(&error_code), &len) < 0) {
213 result = MapPosixError(errno);
214 } else if (error_code == EINPROGRESS) {
215 result = ERR_IO_PENDING;
216 // And await next callback. Haven't seen this case yet myself.
217 } else if (current_ai_->ai_next && (
218 error_code == EADDRNOTAVAIL ||
219 error_code == EAFNOSUPPORT ||
220 error_code == ECONNREFUSED ||
221 error_code == ENETUNREACH ||
222 error_code == EHOSTUNREACH ||
223 error_code == ETIMEDOUT)) {
224 // This address failed, try next one in list.
225 const addrinfo* next = current_ai_->ai_next;
226 Disconnect();
227 current_ai_ = next;
228 result = Connect(callback_);
229 } else if (error_code) {
230 result = MapPosixError(error_code);
231 } else {
232 result = 0;
233 MessageLoopForIO::current()->UnwatchSocket(event_.get());
234 }
235
236 if (result != ERR_IO_PENDING)
237 DoCallback(result);
238 }
239
240 void TCPClientSocket::DidCompleteIO() {
241 int bytes_transferred;
242 switch (wait_state_) {
243 case WAITING_READ:
244 bytes_transferred = read(socket_, buf_, buf_len_);
245 break;
246 case WAITING_WRITE:
247 bytes_transferred = write(socket_, buf_, buf_len_);
248 break;
249 default:
250 NOTREACHED();
251 }
252
253 int result;
254 if (bytes_transferred > 0) {
255 result = bytes_transferred;
256 } else if (bytes_transferred == 0) {
257 // TODO(port): can we tell why it closed, and return a more informative
258 // message? And why does the unit test want to see zero?
259 //result = ERR_CONNECTION_CLOSED;
260 result = 0;
261 } else {
262 result = MapPosixError(errno);
263 }
264
265 if (result != ERR_IO_PENDING) {
266 wait_state_ = NOT_WAITING;
267 MessageLoopForIO::current()->UnwatchSocket(event_.get());
268 DoCallback(result);
269 }
270 }
271
272 void TCPClientSocket::OnSocketReady(short flags) {
273 switch (wait_state_) {
274 case WAITING_CONNECT:
275 DidCompleteConnect();
276 break;
277 case WAITING_READ:
278 case WAITING_WRITE:
279 DidCompleteIO();
280 break;
281 default:
282 NOTREACHED();
283 break;
284 }
285 }
286
287 } // namespace net
288
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