gnash-commit
[Top][All Lists]
Advanced

[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
 


reply via email to

[Prev in Thread] Current Thread [Next in Thread]