[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Gnash-commit] /srv/bzr/gnash/trunk r11082: Migrating XMLSocket to new h
From: |
Sharad Desai |
Subject: |
[Gnash-commit] /srv/bzr/gnash/trunk r11082: Migrating XMLSocket to new hierarchy... |
Date: |
Thu, 11 Jun 2009 15:51:30 -0600 |
User-agent: |
Bazaar (1.13.1) |
------------------------------------------------------------
revno: 11082
committer: Sharad Desai <address@hidden>
branch nick: trunk
timestamp: Thu 2009-06-11 15:51:30 -0600
message:
Migrating XMLSocket to new hierarchy...
modified:
libcore/ClassHierarchy.cpp
libcore/asobj/Global.cpp
libcore/asobj/flash.am
libcore/asobj/flash/net/XMLSocket_as.cpp
libcore/asobj/flash/net/net.am
------------------------------------------------------------
revno: 11081.1.1
committer: Sharad Desai <address@hidden>
branch nick: desaiTst
timestamp: Thu 2009-06-11 15:50:12 -0600
message:
Migrating XMLSocket to new hierarchy...
modified:
libcore/ClassHierarchy.cpp
libcore/asobj/Global.cpp
libcore/asobj/flash.am
libcore/asobj/flash/net/XMLSocket_as.cpp
libcore/asobj/flash/net/net.am
=== modified file 'libcore/ClassHierarchy.cpp'
--- a/libcore/ClassHierarchy.cpp 2009-06-11 17:14:37 +0000
+++ b/libcore/ClassHierarchy.cpp 2009-06-11 21:50:12 +0000
@@ -62,7 +62,7 @@
#include "namedStrings.h"
#include "ClassHierarchy.h"
#include "builtin_function.h"
-#include "XMLSocket_as.h"
+#include "flash/net/XMLSocket_as.h"
#include "xml/XMLDocument_as.h"
#include "xml/XMLNode_as.h"
#include "asClass.h"
=== modified file 'libcore/asobj/Global.cpp'
--- a/libcore/asobj/Global.cpp 2009-06-11 15:34:07 +0000
+++ b/libcore/asobj/Global.cpp 2009-06-11 21:50:12 +0000
@@ -45,7 +45,7 @@
#include "Object.h"
#include "Math_as.h"
#include "flash/xml/XMLDocument_as.h"
-#include "XMLSocket_as.h"
+#include "flash/net/XMLSocket_as.h"
#include "flash/ui/Mouse_as.h"
#include "MovieClipLoader.h"
#include "movie_definition.h"
=== modified file 'libcore/asobj/flash.am'
--- a/libcore/asobj/flash.am 2009-06-11 15:34:07 +0000
+++ b/libcore/asobj/flash.am 2009-06-11 21:50:12 +0000
@@ -47,7 +47,6 @@
asobj/MovieClipLoader.cpp\
asobj/String_as.cpp \
asobj/Stage_as.cpp \
- asobj/XMLSocket_as.cpp \
asobj/LoadableObject.cpp \
asobj/Object.cpp
@@ -71,7 +70,6 @@
asobj/Stage_as.h \
asobj/TextFormat_as.h \
asobj/String_as.h \
- asobj/XMLSocket_as.h \
asobj/LoadableObject.h \
asobj/Namespace_as.h \
asobj/Object.h
=== modified file 'libcore/asobj/flash/net/XMLSocket_as.cpp'
--- a/libcore/asobj/flash/net/XMLSocket_as.cpp 2009-05-28 17:12:46 +0000
+++ b/libcore/asobj/flash/net/XMLSocket_as.cpp 2009-06-11 21:50:12 +0000
@@ -1,17 +1,17 @@
-// XMLSocket_as.cpp: ActionScript "XMLSocket" class, for Gnash.
-//
-// Copyright (C) 2009 Free Software Foundation, Inc.
-//
+// XMLSocket_as.cpp: Network socket for data (usually XML) transfer for Gnash.
+//
+// Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
-//
+//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
-//
+//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
@@ -21,159 +21,571 @@
#include "gnashconfig.h"
#endif
+#include "GnashSystemFDHeaders.h"
+#include "network.h"
+#include "utility.h"
+#include "xml/XMLDocument_as.h"
#include "net/XMLSocket_as.h"
+#include "as_function.h"
+#include "movie_root.h"
+#include "fn_call.h"
+#include "VM.h"
+#include "builtin_function.h" // for setting timer, should likely avoid that..
+#include "URLAccessManager.h"
+#include "Object.h" // for getObjectInterface
#include "log.h"
-#include "fn_call.h"
-#include "smart_ptr.h" // for boost intrusive_ptr
-#include "builtin_function.h" // need builtin_function
-#include "GnashException.h" // for ActionException
+
+#include <boost/thread.hpp>
+#include <boost/scoped_array.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <string>
+
+#undef GNASH_XMLSOCKET_DEBUG
namespace gnash {
-// Forward declarations
namespace {
as_value xmlsocket_connect(const fn_call& fn);
as_value xmlsocket_send(const fn_call& fn);
+ as_value xmlsocket_new(const fn_call& fn);
as_value xmlsocket_close(const fn_call& fn);
- as_value xmlsocket_data(const fn_call& fn);
- as_value xmlsocket_ioError(const fn_call& fn);
- as_value xmlsocket_securityError(const fn_call& fn);
- as_value xmlsocket_ctor(const fn_call& fn);
+
+ // These are the event handlers called for this object
+ as_value xmlsocket_onData(const fn_call& fn);
+
+ as_object* getXMLSocketInterface();
void attachXMLSocketInterface(as_object& o);
- void attachXMLSocketStaticInterface(as_object& o);
- as_object* getXMLSocketInterface();
-
}
-class XMLSocket_as : public as_object
+/// Connection object
+//
+/// A wrapper round a Network object that adds specific functions needed
+/// by XMLSocket.
+namespace {
+
+class SocketConnection
{
-
public:
- XMLSocket_as()
+ SocketConnection()
:
- as_object(getXMLSocketInterface())
+ _complete(false)
{}
-};
+
+ /// Initiate a connection.
+ void connect(const std::string& host, boost::uint16_t port) {
+ _start.reset(new boost::thread(
+ boost::bind(&SocketConnection::makeConnection, this, host, port)));
+ }
+
+ /// The state of the connection.
+ //
+ /// Until complete() is true, this may change.
+ bool connected() const {
+ return _socket.connected();
+ }
+
+ /// Whether an initiated connection is finished
+ //
+ /// @return true if a connection attempt is complete.
+ /// The connection attempt may have failed. Check
+ /// connected() to find out.
+ bool complete() const {
+ return _complete;
+ }
+
+ void setComplete() {
+ _complete = true;
+ }
+
+ size_t writeMessage(const std::string& str) {
+ // We have to write the null terminator as well.
+ return write(_socket.getFileFd(), str.c_str(), str.size() + 1);
+ }
+
+ /// Read from the socket.
+ void readMessages(std::vector<std::string>& msgs) {
+
+ assert(_socket.connected());
+
+ const int fd = _socket.getFileFd();
+ assert(fd > 0);
+
+ fd_set fdset;
+ struct timeval tval;
+ size_t retries = 10;
+
+ const int bufSize = 10000;
+ boost::scoped_array<char> buf(new char[bufSize]);
+
+ while (retries-- > 0) {
+ FD_ZERO(&fdset);
+ FD_SET(fd, &fdset);
+
+ tval.tv_sec = 0;
+ tval.tv_usec = 103;
+
+ const int ret = select(fd + 1, &fdset, NULL, NULL, &tval);
+
+ // If interupted by a system call, try again
+ if (ret == -1 && errno == EINTR) {
+ log_debug(_("The socket for fd #%d was interupted by a "
+ "system call"), fd);
+ continue;
+ }
+ if (ret == -1) {
+ log_error(_("XMLSocket: The socket for fd #%d was never "
+ "available"), fd);
+ return;
+ }
+
+ // Return if timed out.
+ if (ret == 0) return;
+
+ const size_t bytesRead = read(fd, buf.get(), bufSize - 1);
+
+ // Return if there's no data.
+ if (!bytesRead) return;
+
+ if (buf[bytesRead - 1] != 0) {
+ // We received a partial message, so bung
+ // a null-terminator on the end.
+ buf[bytesRead] = 0;
+ }
+
+ char* ptr = buf.get();
+ while (static_cast<size_t>(ptr - buf.get()) < bytesRead - 1) {
+
+#ifdef GNASH_XMLSOCKET_DEBUG
+ log_debug ("read: %d, this string ends: %d", bytesRead,
+ ptr + std::strlen(ptr) - buf.get());
+#endif
+
+ // If the string reaches to the final byte read, it's
+ // incomplete. Store it and continue. The buffer is
+ // NULL-terminated, so this cannot read past the end.
+ if (static_cast<size_t>(
+ ptr + std::strlen(ptr) - buf.get()) == bytesRead) {
+
+ _remainder += std::string(ptr);
+ break;
+ }
+
+ if (!_remainder.empty()) {
+ msgs.push_back(_remainder + std::string(ptr));
+ ptr += std::strlen(ptr) + 1;
+ _remainder.clear();
+ continue;
+ }
+
+ // Don't do anything if nothing is received.
+ msgs.push_back(ptr);
+
+ ptr += std::strlen(ptr) + 1;
+ }
+ }
+ }
+
+ /// Close the connection.
+ //
+ /// This also cancels any connection attempt in progress.
+ void close() {
+ if (_start) _start.reset();
+ _socket.closeNet();
+
+ // Reset for next connection.
+ _complete = false;
+
+ assert(_socket.getFileFd() <= 0);
+ assert(!_socket.connected());
+ }
+
+private:
+
+ void makeConnection(const std::string& host, boost::uint16_t port) {
+ _socket.createClient(host, port);
+ _complete = true;
+ }
+
+ Network _socket;
+
+ bool _complete;
+
+ std::string _remainder;
+
+ boost::scoped_ptr<boost::thread> _start;
+
+};
+
+}
+
+class XMLSocket_as : public as_object {
+
+public:
+
+ typedef std::vector<std::string> MessageList;
+
+ XMLSocket_as();
+ ~XMLSocket_as();
+
+ /// True when the XMLSocket is not trying to connect.
+ //
+ /// If this is true but the socket is not connected, the connection
+ /// has failed.
+ bool ready() const {
+ return _ready;
+ }
+
+ /// Whether a connection exists.
+ //
+ /// This is not final until ready() is true.
+ bool connected() const {
+ return _connection.connected();
+ }
+
+ bool connect(const std::string& host, boost::uint16_t port);
+
+ /// Send a string with a null-terminator to the socket.
+ //
+ /// Actionscript doesn't care about the result.
+ void send(std::string str);
+
+ /// Close the socket
+ //
+ /// Actionscript doesn't care about the result.
+ void close();
+
+ /// Called on advance() when socket is connected
+ virtual void advanceState();
+
+private:
+
+ void checkForIncomingData();
+
+ /// Return the as_function with given name.
+ boost::intrusive_ptr<as_function> getEventHandler(
+ const std::string& name);
+
+ /// The connection
+ SocketConnection _connection;
+
+ bool _ready;
+
+};
+
+
+XMLSocket_as::XMLSocket_as()
+ :
+ as_object(getXMLSocketInterface()),
+ _ready(false)
+{
+}
+
+
+XMLSocket_as::~XMLSocket_as()
+{
+ // Remove advance callback and close network connections.
+ close();
+}
+
+void
+XMLSocket_as::advanceState()
+{
+ // Wait until something has happened with the connection
+ if (!_connection.complete()) return;
+
+ // If this XMLSocket hadn't finished a connection, check whether it
+ // has now.
+ if (!ready()) {
+
+ if (!connected()) {
+
+ // If connection failed, notify onConnect and stop callback.
+ // This means advanceState() will not be called again until
+ // XMLSocket.connect() is invoked.
+ callMethod(NSV::PROP_ON_CONNECT, false);
+ _vm.getRoot().removeAdvanceCallback(this);
+ return;
+ }
+
+ // Connection succeeded.
+ callMethod(NSV::PROP_ON_CONNECT, true);
+ _ready = true;
+ }
+
+ // Now the connection is established we can receive data.
+ checkForIncomingData();
+}
+
+
+bool
+XMLSocket_as::connect(const std::string& host, boost::uint16_t port)
+{
+
+ if (!URLAccessManager::allowXMLSocket(host, port)) {
+ return false;
+ }
+
+ _connection.connect(host, port);
+
+ // Start callbacks on advance.
+ _vm.getRoot().addAdvanceCallback(this);
+
+ return true;
+}
+
+void
+XMLSocket_as::close()
+{
+ _vm.getRoot().removeAdvanceCallback(this);
+ _connection.close();
+ _ready = false;
+}
+
+
+boost::intrusive_ptr<as_function>
+XMLSocket_as::getEventHandler(const std::string& name)
+{
+ boost::intrusive_ptr<as_function> ret;
+
+ as_value tmp;
+ string_table& st = getVM().getStringTable();
+ if (!get_member(st.find(name), &tmp) ) return ret;
+ ret = tmp.to_as_function();
+ return ret;
+}
+
+void
+XMLSocket_as::checkForIncomingData()
+{
+ assert(ready() && connected());
+
+ std::vector<std::string> msgs;
+ _connection.readMessages(msgs);
+
+ if (msgs.empty()) return;
+
+ log_debug(_("Got %d messages: "), msgs.size());
+
+#ifdef GNASH_XMLSOCKET_DEBUG
+ for (size_t i = 0, e = msgs.size(); i != e; ++i) {
+ log_debug(_(" Message %d: %s "), i, msgs[i]);
+ }
+#endif
+
+ as_environment env(_vm);
+
+ for (XMLSocket_as::MessageList::const_iterator it=msgs.begin(),
+ itEnd=msgs.end(); it != itEnd; ++it) {
+
+ // This should be checked on every iteration in case one call
+ // changes the handler.
+ boost::intrusive_ptr<as_function> onDataHandler =
+ getEventHandler("onData");
+
+ if (!onDataHandler) break;
+
+ const std::string& s = *it;
+
+ std::auto_ptr<std::vector<as_value> > args(
+ new std::vector<as_value>);
+
+ args->push_back(s);
+
+ fn_call call(this, env, args);
+
+ onDataHandler->call(call);
+ }
+
+}
+
+
+// XMLSocket.send doesn't return anything, so we don't need
+// to here either.
+void
+XMLSocket_as::send(std::string str)
+{
+ if (!ready() || !connected()) {
+ log_error(_("XMLSocket.send(): socket not initialized"));
+ return;
+ }
+
+ _connection.writeMessage(str);
+
+ return;
+}
+
// extern (used by Global.cpp)
-void xmlsocket_class_init(as_object& global)
+void
+xmlsocket_class_init(as_object& global)
{
+ // This is the global XMLSocket class
static boost::intrusive_ptr<builtin_function> cl;
if (!cl) {
- cl = new builtin_function(&xmlsocket_ctor, getXMLSocketInterface());
- attachXMLSocketStaticInterface(*cl);
+ cl = new builtin_function(&xmlsocket_new, getXMLSocketInterface());
}
-
+
// Register _global.XMLSocket
global.init_member("XMLSocket", cl.get());
+
}
+
namespace {
-void
-attachXMLSocketInterface(as_object& o)
-{
- o.init_member("connect", new builtin_function(xmlsocket_connect));
- o.init_member("send", new builtin_function(xmlsocket_send));
- o.init_member("close", new builtin_function(xmlsocket_close));
- o.init_member("connect", new builtin_function(xmlsocket_connect));
- o.init_member("data", new builtin_function(xmlsocket_data));
- o.init_member("ioError", new builtin_function(xmlsocket_ioError));
- o.init_member("securityError", new
builtin_function(xmlsocket_securityError));
-}
-
-void
-attachXMLSocketStaticInterface(as_object& o)
-{
-
+// XMLSocket.connect() returns true if the initial connection was
+// successful, false if no connection was established.
+as_value
+xmlsocket_connect(const fn_call& fn)
+{
+
+#ifdef GNASH_XMLSOCKET_DEBUG
+ std::stringstream ss;
+ fn.dump_args(ss);
+ log_debug(_("XMLSocket.connect(%s) called"), ss.str());
+#endif
+
+ boost::intrusive_ptr<XMLSocket_as> ptr =
+ ensureType<XMLSocket_as>(fn.this_ptr);
+
+ if (ptr->ready()) {
+ log_error(_("XMLSocket.connect() called while already "
+ "connected, ignored"));
+ return as_value(false);
+ }
+
+ as_value hostval = fn.arg(0);
+ const std::string& host = hostval.to_string();
+ const double port = fn.arg(1).to_number();
+
+ // Port numbers above 65535 are rejected always, but not port numbers below
+ // 0. It's not clear what happens with them.
+ // TODO: find out.
+ // Other ports and hosts are checked against security policy before
+ // acceptance or rejection.
+ if (port > std::numeric_limits<boost::uint16_t>::max()) {
+ return as_value(false);
+ }
+
+ // XMLSocket.connect() returns false only if the connection is
+ // forbidden. The result of the real connection attempt is
+ // notified via onConnect().
+ const bool ret = ptr->connect(host, port);
+
+ if (!ret) {
+ log_error(_("XMLSocket.connect(): connection failed"));
+ }
+
+ return as_value(ret);
+}
+
+
+/// XMLSocket.send()
+//
+/// Does not return anything.
+as_value
+xmlsocket_send(const fn_call& fn)
+{
+ boost::intrusive_ptr<XMLSocket_as> ptr =
+ ensureType<XMLSocket_as>(fn.this_ptr);
+
+ const std::string& str = fn.arg(0).to_string();
+ ptr->send(str);
+ return as_value();
+}
+
+
+/// XMLSocket.close()
+//
+/// Always returns void
+as_value
+xmlsocket_close(const fn_call& fn)
+{
+ GNASH_REPORT_FUNCTION;
+
+ boost::intrusive_ptr<XMLSocket_as> ptr =
+ ensureType<XMLSocket_as>(fn.this_ptr);
+
+ ptr->close();
+ return as_value();
+}
+
+as_value
+xmlsocket_new(const fn_call& /*fn*/)
+{
+
+ boost::intrusive_ptr<as_object> xmlsock_obj = new XMLSocket_as;
+ return as_value(xmlsock_obj);
+}
+
+as_value
+xmlsocket_onData(const fn_call& fn)
+{
+ GNASH_REPORT_FUNCTION;
+
+ boost::intrusive_ptr<XMLSocket_as> ptr =
+ ensureType<XMLSocket_as>(fn.this_ptr);
+
+ if (!fn.nargs) {
+ IF_VERBOSE_ASCODING_ERRORS(
+ log_aserror(_("Builtin XMLSocket.onData() needs an argument"));
+ );
+ return as_value();
+ }
+
+ const std::string& xmlin = fn.arg(0).to_string();
+
+#ifdef GNASH_XMLSOCKET_DEBUG
+ log_debug("Arg: %s, val: %s", xmlin, fn.arg(0));
+#endif
+
+ if (xmlin.empty()) {
+ log_error(_("Builtin XMLSocket.onData() called with an argument "
+ "that resolves to an empty string: %s"), fn.arg(0));
+ return as_value();
+ }
+
+ boost::intrusive_ptr<as_object> xml = new XMLDocument_as(xmlin);
+ as_value arg(xml.get());
+
+ ptr->callMethod(NSV::PROP_ON_XML, arg);
+
+ return as_value();
}
as_object*
getXMLSocketInterface()
{
static boost::intrusive_ptr<as_object> o;
- if ( ! o ) {
- o = new as_object();
+ if (!o) {
+ o = new as_object(getObjectInterface());
attachXMLSocketInterface(*o);
}
return o.get();
}
-as_value
-xmlsocket_connect(const fn_call& fn)
-{
- boost::intrusive_ptr<XMLSocket_as> ptr =
- ensureType<XMLSocket_as>(fn.this_ptr);
- UNUSED(ptr);
- log_unimpl (__FUNCTION__);
- return as_value();
-}
-
-as_value
-xmlsocket_send(const fn_call& fn)
-{
- boost::intrusive_ptr<XMLSocket_as> ptr =
- ensureType<XMLSocket_as>(fn.this_ptr);
- UNUSED(ptr);
- log_unimpl (__FUNCTION__);
- return as_value();
-}
-
-as_value
-xmlsocket_close(const fn_call& fn)
-{
- boost::intrusive_ptr<XMLSocket_as> ptr =
- ensureType<XMLSocket_as>(fn.this_ptr);
- UNUSED(ptr);
- log_unimpl (__FUNCTION__);
- return as_value();
-}
-
-as_value
-xmlsocket_data(const fn_call& fn)
-{
- boost::intrusive_ptr<XMLSocket_as> ptr =
- ensureType<XMLSocket_as>(fn.this_ptr);
- UNUSED(ptr);
- log_unimpl (__FUNCTION__);
- return as_value();
-}
-
-as_value
-xmlsocket_ioError(const fn_call& fn)
-{
- boost::intrusive_ptr<XMLSocket_as> ptr =
- ensureType<XMLSocket_as>(fn.this_ptr);
- UNUSED(ptr);
- log_unimpl (__FUNCTION__);
- return as_value();
-}
-
-as_value
-xmlsocket_securityError(const fn_call& fn)
-{
- boost::intrusive_ptr<XMLSocket_as> ptr =
- ensureType<XMLSocket_as>(fn.this_ptr);
- UNUSED(ptr);
- log_unimpl (__FUNCTION__);
- return as_value();
-}
-
-as_value
-xmlsocket_ctor(const fn_call& fn)
-{
- boost::intrusive_ptr<as_object> obj = new XMLSocket_as;
-
- return as_value(obj.get()); // will keep alive
-}
-
-} // anonymous namespace
+void
+attachXMLSocketInterface(as_object& o)
+{
+ o.init_member("connect", new builtin_function(xmlsocket_connect));
+ o.init_member("send", new builtin_function(xmlsocket_send));
+ o.init_member("close", new builtin_function(xmlsocket_close));
+
+
+ // all this crap to satisfy swfdec testsuite... (xml-socket-properties*)
+ as_object* onDataIface = new as_object(getObjectInterface());
+ as_function* onDataFun = new builtin_function(xmlsocket_onData,
+ onDataIface);
+ o.init_member("onData", onDataFun);
+ onDataIface->init_member(NSV::PROP_CONSTRUCTOR, onDataFun);
+}
+
+} // anonymous namespace
} // gnash namespace
-// local Variables:
+// Local Variables:
// mode: C++
// indent-tabs-mode: t
// End:
-
=== modified file 'libcore/asobj/flash/net/net.am'
--- a/libcore/asobj/flash/net/net.am 2009-06-09 18:42:51 +0000
+++ b/libcore/asobj/flash/net/net.am 2009-06-11 21:50:12 +0000
@@ -126,7 +126,7 @@
# FIXME: already exists
if BUILD_XMLSOCKET_AS3
-# NET_SOURCES += asobj/flash/net/XMLSocket_as.cpp
+NET_SOURCES += asobj/flash/net/XMLSocket_as.cpp
NET_HEADERS += asobj/flash/net/XMLSocket_as.h
endif
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Gnash-commit] /srv/bzr/gnash/trunk r11082: Migrating XMLSocket to new hierarchy...,
Sharad Desai <=