ExternalInterface methods are static now.
[gnash.git] / libbase / Socket.cpp
blob9f4d35d93a1ea22810f219b8719efc6dbdc37798
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
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <sys/times.h>
23 #include <netdb.h>
24 #include <arpa/inet.h>
25 #include <unistd.h>
26 #include <netinet/in.h>
27 #include <netinet/tcp.h>
28 #include <fcntl.h>
30 #include <boost/cstdint.hpp>
31 #include <cstring>
32 #include <cerrno>
33 #include <boost/lexical_cast.hpp>
35 #include "URL.h"
36 #include "Socket.h"
37 #include "log.h"
38 #include "GnashAlgorithm.h"
40 namespace gnash
43 Socket::Socket()
45 _connected(false),
46 _socket(0),
47 _size(0),
48 _pos(0),
49 _error(false)
52 bool
53 Socket::connected() const
55 if (_connected) return true;
56 if (!_socket) return false;
58 size_t retries = 10;
59 fd_set fdset;
60 struct timeval tval;
62 while (retries-- > 0) {
64 FD_ZERO(&fdset);
65 FD_SET(_socket, &fdset);
67 tval.tv_sec = 0;
68 tval.tv_usec = 103;
70 const int ret = select(_socket + 1, NULL, &fdset, NULL, &tval);
72 // Select timeout
73 if (ret == 0) continue;
75 if (ret > 0) {
76 boost::uint32_t val = 0;
77 socklen_t len = sizeof(val);
78 if (::getsockopt(_socket, SOL_SOCKET, SO_ERROR, &val, &len) < 0) {
79 log_debug("Error");
80 _error = true;
81 return false;
84 if (!val) {
85 _connected = true;
86 return true;
88 _error = true;
89 return false;
92 // If interrupted by a system call, try again
93 if (ret == -1) {
94 const int err = errno;
95 if (err == EINTR) {
96 log_debug(_("Socket interrupted by a system call"));
97 continue;
100 log_error(_("XMLSocket: The socket was never available"));
101 _error = true;
102 return false;
105 return false;
109 void
110 Socket::close()
112 if (_socket) ::close(_socket);
113 _socket = 0;
114 _size = 0;
115 _pos = 0;
116 _connected = false;
117 _error = false;
120 bool
121 Socket::connect(const std::string& hostname, boost::uint16_t port)
124 // We use _socket here because connected() or _connected might not
125 // be true if a connection attempt is underway but not completed.
126 if (_socket) {
127 log_error("Connection attempt while already connected");
128 return false;
131 // If _socket is 0, either there has been no connection, or close() has
132 // been called. There must not be an error in either case.
133 assert(!_error);
135 if (hostname.empty()) return false;
137 struct sockaddr_in addr;
138 std::memset(&addr, 0, sizeof(addr));
139 addr.sin_family = AF_INET;
141 addr.sin_addr.s_addr = ::inet_addr(hostname.c_str());
142 if (addr.sin_addr.s_addr == INADDR_NONE) {
143 struct hostent* host = ::gethostbyname(hostname.c_str());
144 if (!host || !host->h_addr) {
145 return false;
147 addr.sin_addr = *reinterpret_cast<in_addr*>(host->h_addr);
150 addr.sin_port = htons(port);
152 _socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
154 if (_socket < 0) {
155 const int err = errno;
156 log_debug("Socket creation failed: %s", std::strerror(err));
157 _socket = 0;
158 return false;
161 // Set non-blocking.
162 const int flag = ::fcntl(_socket, F_GETFL, 0);
163 ::fcntl(_socket, F_SETFL, flag | O_NONBLOCK);
165 const struct sockaddr* a = reinterpret_cast<struct sockaddr*>(&addr);
167 // Attempt connection
168 if (::connect(_socket, a, sizeof(struct sockaddr)) < 0) {
169 const int err = errno;
170 if (err != EINPROGRESS) {
171 log_error("Failed to connect socket: %s", std::strerror(err));
172 _socket = 0;
173 return false;
177 // Magic timeout number. Use rcfile ?
178 struct timeval tv = { 120, 0 };
180 if (::setsockopt(_socket, SOL_SOCKET, SO_RCVTIMEO,
181 reinterpret_cast<unsigned char*>(&tv), sizeof(tv))) {
182 log_error("Setting socket timeout failed");
185 const boost::int32_t on = 1;
186 ::setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
188 assert(_socket);
189 return true;
192 void
193 Socket::fillCache()
196 // Read position is always _pos + _size wrapped.
197 const size_t cacheSize = arraySize(_cache);
198 size_t start = (_pos + _size) % cacheSize;
200 // Try to fill the whole remaining buffer.
201 const size_t completeRead = cacheSize - _size;
203 // End is start + read size, wrapped.
204 size_t end = (start + completeRead) % cacheSize;
205 if (end == 0) end = cacheSize;
207 boost::uint8_t* startpos = _cache + start;
209 while (1) {
211 // The end pos is either the end of the cache or the first
212 // unprocessed byte.
213 boost::uint8_t* endpos = _cache + ((startpos < _cache + _pos) ?
214 _pos : cacheSize);
216 const int thisRead = endpos - startpos;
217 assert(thisRead >= 0);
219 const int bytesRead = ::recv(_socket, startpos, thisRead, 0);
221 if (bytesRead == -1) {
223 const int err = errno;
224 if (err == EWOULDBLOCK || err == EAGAIN) {
225 // Nothing to read. Carry on.
226 return;
228 log_error("Socket receive error %s", std::strerror(err));
229 _error = true;
233 _size += bytesRead;
235 // If there weren't enough bytes, that's it.
236 if (bytesRead < thisRead) break;
238 // If we wrote up to the end of the cache, try writing more to the
239 // beginning.
240 startpos = _cache;
246 // Do a single read and report how many bytes were read.
247 std::streamsize
248 Socket::read(void* dst, std::streamsize num)
251 if (num < 0) return 0;
253 if (_size < num && !_error) {
254 fillCache();
257 if (_size < num) return 0;
258 return readNonBlocking(dst, num);
262 std::streamsize
263 Socket::readNonBlocking(void* dst, std::streamsize num)
265 if (bad()) return 0;
267 boost::uint8_t* ptr = static_cast<boost::uint8_t*>(dst);
269 if (!_size && !_error) {
270 fillCache();
273 size_t cacheSize = arraySize(_cache);
275 // First read from pos to end
277 // Maximum bytes available to read.
278 const size_t canRead = std::min<size_t>(_size, num);
280 size_t toRead = canRead;
282 // Space to the end (for the first read).
283 const int thisRead = std::min<size_t>(canRead, cacheSize - _pos);
285 std::copy(_cache + _pos, _cache + _pos + thisRead, ptr);
286 _pos += thisRead;
287 _size -= thisRead;
288 toRead -= thisRead;
290 if (toRead) {
291 std::copy(_cache, _cache + toRead, ptr + thisRead);
292 _pos = toRead;
293 _size -= toRead;
294 toRead = 0;
297 return canRead - toRead;
300 std::streamsize
301 Socket::write(const void* src, std::streamsize num)
303 if (bad()) return 0;
305 int bytesSent = 0;
306 int toWrite = num;
308 const boost::uint8_t* buf = static_cast<const boost::uint8_t*>(src);
310 while (toWrite > 0) {
311 bytesSent = ::send(_socket, buf, toWrite, 0);
312 if (bytesSent < 0) {
313 const int err = errno;
314 log_error("Socket send error %s", std::strerror(err));
315 _error = true;
316 return 0;
319 if (!bytesSent) break;
320 toWrite -= bytesSent;
321 buf += bytesSent;
323 return num - toWrite;
326 std::streampos
327 Socket::tell() const
329 log_error("tell() called for Socket");
330 return static_cast<std::streamsize>(-1);
333 bool
334 Socket::seek(std::streampos)
336 log_error("seek() called for Socket");
337 return false;
340 void
341 Socket::go_to_end()
343 log_error("go_to_end() called for Socket");
346 bool
347 Socket::eof() const
349 log_error("eof() called for Socket");
350 return false;
353 } // namespace gnash