change bzr to git
[gnash.git] / libbase / Socket.cpp
blob4dd6e06b45aae370f4594970a9ff8b24b22266f9
1 // Socket.cpp - an IOChannel for sockets
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
4 // Foundation, Inc
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #include <boost/cstdint.hpp>
23 namespace {
26 #include "GnashSystemNetHeaders.h"
27 #include "GnashSystemFDHeaders.h"
29 #include <cstring>
30 #include <cerrno>
31 #include <boost/lexical_cast.hpp>
33 #include "URL.h"
34 #include "Socket.h"
35 #include "log.h"
36 #include "utility.h"
37 #include "GnashAlgorithm.h"
39 namespace gnash {
41 Socket::Socket()
43 _connected(false),
44 _socket(0),
45 _size(0),
46 _pos(0),
47 _error(false)
50 bool
51 Socket::connected() const
53 if (_connected) return true;
54 if (!_socket) return false;
56 size_t retries = 10;
57 fd_set fdset;
58 struct timeval tval;
60 while (retries-- > 0) {
62 FD_ZERO(&fdset);
63 FD_SET(_socket, &fdset);
65 tval.tv_sec = 0;
66 tval.tv_usec = 103;
68 const int ret = ::select(_socket + 1, NULL, &fdset, NULL, &tval);
70 // Select timeout
71 if (ret == 0) continue;
73 if (ret > 0) {
74 int val = 0;
75 socklen_t len = sizeof(val);
76 // NB: the cast to char* is needed for windows and is harmless
77 // for POSIX.
78 if (::getsockopt(_socket, SOL_SOCKET, SO_ERROR,
79 reinterpret_cast<char*>(&val), &len) < 0) {
80 log_debug("Error");
81 _error = true;
82 return false;
85 if (!val) {
86 _connected = true;
87 return true;
89 _error = true;
90 return false;
93 // If interrupted by a system call, try again
94 if (ret == -1) {
95 const int err = errno;
96 if (err == EINTR) {
97 log_debug(_("Socket interrupted by a system call"));
98 continue;
101 log_error(_("XMLSocket: The socket was never available"));
102 _error = true;
103 return false;
106 return false;
110 void
111 Socket::close()
113 if (_socket) ::close(_socket);
114 _socket = 0;
115 _size = 0;
116 _pos = 0;
117 _connected = false;
118 _error = false;
121 bool
122 Socket::connect(const std::string& hostname, boost::uint16_t port)
125 // We use _socket here because connected() or _connected might not
126 // be true if a connection attempt is underway but not completed.
127 if (_socket) {
128 log_error("Connection attempt while already connected");
129 return false;
132 // If _socket is 0, either there has been no connection, or close() has
133 // been called. There must not be an error in either case.
134 assert(!_error);
136 if (hostname.empty()) return false;
138 struct sockaddr_in addr;
139 std::memset(&addr, 0, sizeof(addr));
140 addr.sin_family = AF_INET;
142 addr.sin_addr.s_addr = ::inet_addr(hostname.c_str());
143 if (addr.sin_addr.s_addr == INADDR_NONE) {
144 struct hostent* host = ::gethostbyname(hostname.c_str());
145 if (!host || !host->h_addr) {
146 return false;
148 addr.sin_addr = *reinterpret_cast<in_addr*>(host->h_addr);
151 addr.sin_port = htons(port);
153 _socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
155 if (_socket < 0) {
156 const int err = errno;
157 log_debug("Socket creation failed: %s", std::strerror(err));
158 _socket = 0;
159 return false;
161 #ifndef _WIN32
162 // Set non-blocking.
163 const int flag = ::fcntl(_socket, F_GETFL, 0);
164 ::fcntl(_socket, F_SETFL, flag | O_NONBLOCK);
165 #endif
167 const struct sockaddr* a = reinterpret_cast<struct sockaddr*>(&addr);
169 // Attempt connection
170 if (::connect(_socket, a, sizeof(struct sockaddr)) < 0) {
171 const int err = errno;
172 #ifndef _WIN32
173 if (err != EINPROGRESS) {
174 log_error("Failed to connect socket: %s", std::strerror(err));
175 _socket = 0;
176 return false;
178 #else
179 return false;
180 #endif
183 // Magic timeout number. Use rcfile ?
184 const struct timeval tv = { 120, 0 };
186 // NB: the cast to const char* is needed for windows and is harmless
187 // for POSIX.
188 if (::setsockopt(_socket, SOL_SOCKET, SO_RCVTIMEO,
189 reinterpret_cast<const char*>(&tv), sizeof(tv))) {
190 log_error("Setting socket timeout failed");
193 const int on = 1;
194 // NB: the cast to const char* is needed for windows and is harmless
195 // for POSIX.
196 ::setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY,
197 reinterpret_cast<const char*>(&on), sizeof(on));
199 assert(_socket);
200 return true;
203 void
204 Socket::fillCache()
207 // Read position is always _pos + _size wrapped.
208 const size_t cacheSize = arraySize(_cache);
209 size_t start = (_pos + _size) % cacheSize;
211 // Try to fill the whole remaining buffer.
212 const size_t completeRead = cacheSize - _size;
214 // End is start + read size, wrapped.
215 size_t end = (start + completeRead) % cacheSize;
216 if (end == 0) end = cacheSize;
218 char* startpos = _cache + start;
220 while (1) {
222 // The end pos is either the end of the cache or the first
223 // unprocessed byte.
224 char* endpos = _cache + ((startpos < _cache + _pos) ?
225 _pos : cacheSize);
227 const int thisRead = endpos - startpos;
228 assert(thisRead >= 0);
230 const int bytesRead = ::recv(_socket, startpos, thisRead, 0);
232 if (bytesRead == -1) {
233 const int err = errno;
234 #ifndef _WIN32
235 if (err == EWOULDBLOCK || err == EAGAIN) {
236 // Nothing to read. Carry on.
237 return;
239 #endif
240 log_error("Socket receive error %s", std::strerror(err));
241 _error = true;
242 return;
246 _size += bytesRead;
248 // If there weren't enough bytes, that's it.
249 if (bytesRead < thisRead) break;
251 // If we wrote up to the end of the cache, try writing more to the
252 // beginning.
253 startpos = _cache;
259 // Do a single read and report how many bytes were read.
260 std::streamsize
261 Socket::read(void* dst, std::streamsize num)
264 if (num < 0) return 0;
266 if (_size < num && !_error) {
267 fillCache();
270 if (_size < num) return 0;
271 return readNonBlocking(dst, num);
275 std::streamsize
276 Socket::readNonBlocking(void* dst, std::streamsize num)
278 if (bad()) return 0;
280 char* ptr = static_cast<char*>(dst);
282 if (!_size && !_error) {
283 fillCache();
286 size_t cacheSize = arraySize(_cache);
288 // First read from pos to end
290 // Maximum bytes available to read.
291 const size_t canRead = std::min<size_t>(_size, num);
293 size_t toRead = canRead;
295 // Space to the end (for the first read).
296 const int thisRead = std::min<size_t>(canRead, cacheSize - _pos);
298 std::copy(_cache + _pos, _cache + _pos + thisRead, ptr);
299 _pos += thisRead;
300 _size -= thisRead;
301 toRead -= thisRead;
303 if (toRead) {
304 std::copy(_cache, _cache + toRead, ptr + thisRead);
305 _pos = toRead;
306 _size -= toRead;
307 toRead = 0;
310 return canRead - toRead;
313 std::streamsize
314 Socket::write(const void* src, std::streamsize num)
316 if (bad()) return 0;
318 int bytesSent = 0;
319 int toWrite = num;
321 const char* buf = static_cast<const char*>(src);
323 while (toWrite > 0) {
324 bytesSent = ::send(_socket, buf, toWrite, 0);
325 if (bytesSent < 0) {
326 const int err = errno;
327 log_error("Socket send error %s", std::strerror(err));
328 _error = true;
329 return 0;
332 if (!bytesSent) break;
333 toWrite -= bytesSent;
334 buf += bytesSent;
336 return num - toWrite;
339 std::streampos
340 Socket::tell() const
342 log_error("tell() called for Socket");
343 return static_cast<std::streamsize>(-1);
346 bool
347 Socket::seek(std::streampos)
349 log_error("seek() called for Socket");
350 return false;
353 void
354 Socket::go_to_end()
356 log_error("go_to_end() called for Socket");
359 bool
360 Socket::eof() const
362 log_error("eof() called for Socket");
363 return false;
366 } // namespace gnash