gnash-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Gnash-commit] /srv/bzr/gnash/trunk r10379: Send NetConnection status co


From: Benjamin Wolsey
Subject: [Gnash-commit] /srv/bzr/gnash/trunk r10379: Send NetConnection status codes on connect() failure or success. Implement
Date: Wed, 03 Dec 2008 12:38:50 +0100
User-agent: Bazaar (1.5)

------------------------------------------------------------
revno: 10379
committer: Benjamin Wolsey <address@hidden>
branch nick: trunk
timestamp: Wed 2008-12-03 12:38:50 +0100
message:
  Send NetConnection status codes on connect() failure or success. Implement
  NetConnection.isConnected. 
modified:
  libcore/asobj/NetConnection.cpp
  libcore/asobj/NetConnection.h
  libcore/asobj/NetStream_as.cpp
  libcore/asobj/NetStream_as.h
  testsuite/actionscript.all/NetConnection.as
  testsuite/misc-ming.all/NetStream-SquareTest.c
  testsuite/swfdec/PASSING
    ------------------------------------------------------------
    revno: 10375.1.1
    committer: Benjamin Wolsey <address@hidden>
    branch nick: work
    timestamp: Wed 2008-12-03 12:03:34 +0100
    message:
      Test NetConnection.connect and onStatus event.
      
      Rearrange NetConnection class so it's a bit more organized (but still
      something of a mess).
      
      Make NetStream status codes use std::strings, implement some NetConnection
      codes the same way (but without queueing). Currently only the connect
      status code is properly implemented, but this fixes video playback 
      in bug #19844.
    modified:
      libcore/asobj/NetConnection.cpp
      libcore/asobj/NetConnection.h
      libcore/asobj/NetStream_as.cpp
      libcore/asobj/NetStream_as.h
      testsuite/actionscript.all/NetConnection.as
    ------------------------------------------------------------
    revno: 10375.1.2
    committer: Benjamin Wolsey <address@hidden>
    branch nick: work
    timestamp: Wed 2008-12-03 12:25:09 +0100
    message:
      Minor cleanups, documentation.
    modified:
      libcore/asobj/NetStream_as.cpp
      libcore/asobj/NetStream_as.h
    ------------------------------------------------------------
    revno: 10375.1.3
    committer: Benjamin Wolsey <address@hidden>
    branch nick: work
    timestamp: Wed 2008-12-03 12:25:33 +0100
    message:
      Test the NetConnection info object and implement correctly.
      
      Passing tests in swfdec and misc-ming.all as well as the actionscript.all
      ones already committed.
    modified:
      libcore/asobj/NetConnection.cpp
      libcore/asobj/NetConnection.h
      testsuite/actionscript.all/NetConnection.as
      testsuite/misc-ming.all/NetStream-SquareTest.c
      testsuite/swfdec/PASSING
=== modified file 'libcore/asobj/NetConnection.cpp'
--- a/libcore/asobj/NetConnection.cpp   2008-10-28 15:32:20 +0000
+++ b/libcore/asobj/NetConnection.cpp   2008-12-03 11:25:33 +0000
@@ -55,165 +55,30 @@
 
 // #define GNASH_DEBUG_REMOTING
 
+// Forward declarations.
+
 namespace gnash {
 
-static as_value netconnection_new(const fn_call& fn);
-
-/// \class NetConnection
-/// \brief Opens a local connection through which you can play
-/// back video (FLV) files from an HTTP address or from the local file
-/// system, using curl.
-NetConnection::NetConnection()
-       :
-       as_object(getNetConnectionInterface()),
-       _callQueue(0)
-{
-       attachProperties();
-}
-
-
-/*public*/
-std::string
-NetConnection::validateURL(const std::string& url)
-{
-       std::string completeUrl;
-       if (_prefixUrl.size() > 0) {
-               if(url.size() > 0) {
-                       completeUrl += _prefixUrl + "/" + url;
-               } else {
-                       completeUrl += _prefixUrl;
-               }
-       } else {
-               completeUrl += url;
-       }
-
-    const movie_root& mr = _vm.getRoot();
-       URL uri(completeUrl, mr.runInfo().baseURL());
-
-       std::string uriStr(uri.str());
-       assert(uriStr.find("://") != std::string::npos);
-
-       // Check if we're allowed to open url
-       if (!URLAccessManager::allow(uri)) {
-               log_security(_("Gnash is not allowed to open this url: %s"), 
uriStr);
-               return "";
-       }
-
-       log_debug(_("Connection to movie: %s"), uriStr);
-
-       return uriStr;
-}
-
-/*private*/
-void
-NetConnection::addToURL(const std::string& url)
-{
-       // What is this ? It is NOT documented in the header !!
-       //if (url == "null" || url == "NULL") return;
-
-       // If there already is something in _prefixUrl, then we already have a 
url,
-       // so no need to renew it. This may not correct, needs some testing.
-       if (_prefixUrl.size() > 0) return;
-
-       _prefixUrl += url;
-}
-
-
-/// \brief callback to instantiate a new NetConnection object.
-/// \param fn the parameters from the Flash movie
-/// \return nothing from the function call.
-/// \note The return value is returned through the fn.result member.
-static as_value
-netconnection_new(const fn_call& /* fn */)
-{
-       GNASH_REPORT_FUNCTION;
-
-       NetConnection *netconnection_obj = new NetConnection;
-
-       return as_value(netconnection_obj);
-}
-
-as_value
-NetConnection::connect_method(const fn_call& fn)
-{
-       // NOTE:
-       //
-       // NetConnection::connect() is *documented*, I repeat, *documented*, to 
require the
-       // "url" argument to be NULL in AS <= 2. This is *legal* and 
*required*. Anything
-       // other than NULL is undocumented behaviour, and I would like to know 
if there
-       // are any movies out there relying on it. --bjacques.
-
-       GNASH_REPORT_FUNCTION;
-
-       boost::intrusive_ptr<NetConnection> ptr = 
ensureType<NetConnection>(fn.this_ptr); 
-    
-       if (fn.nargs < 1)
-       {
-               IF_VERBOSE_ASCODING_ERRORS(
-               log_aserror(_("NetConnection.connect(): needs at least one 
argument"));
-               );
-               return as_value(false);
-       }
-
-       const as_value& url_val = fn.arg(0);
-
-       // Check first arg for validity 
-       if ( url_val.is_null())
-       {
-               // Null URL was passed. This is expected. Of course, it also 
makes this
-               // function (and, this class) rather useless. We return true, 
even though
-               // returning true has no meaning.
-               
-               return as_value(true);
-       }
-
-       // The remainder of this function is undocumented.
-       
-       if (url_val.is_undefined()) {
-                IF_VERBOSE_ASCODING_ERRORS(
-                log_aserror(_("NetConnection.connect(): first argument 
shouldn't be undefined"));
-                );
-               return as_value(false);
-       }
-
-
-       /// .. TODO: checkme ... addToURL ?? shoudnl't we attempt a connection 
??
-       ptr->addToURL(url_val.to_string());
-
-       if ( fn.nargs > 1 )
-       {
-               std::stringstream ss; fn.dump_args(ss);
-               log_unimpl("NetConnection.connect(%s): args after the first are 
not supported", ss.str());
-       }
-
-
-       // TODO: FIXME: should return true *or false* for RTMP connections
-       return as_value(true);
-}
-
-
-as_value
-NetConnection::addHeader_method(const fn_call& fn)
-{
-       boost::intrusive_ptr<NetConnection> ptr = 
ensureType<NetConnection>(fn.this_ptr); 
-       UNUSED(ptr);
-
-       log_unimpl("NetConnection.addHeader()");
-       return as_value();
-}
-
-static boost::uint16_t
-readNetworkShort(const boost::uint8_t* buf) {
-       boost::uint16_t s = buf[0] << 8 | buf[1];
-       return s;
-}
-
-static boost::uint32_t
-readNetworkLong(const boost::uint8_t* buf) {
-       boost::uint32_t s = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
-       return s;
-}
-
+namespace {
+    void attachProperties(as_object& o);
+    void attachNetConnectionInterface(as_object& o);
+    as_object* getNetConnectionInterface();
+    as_value netconnection_isConnected(const fn_call& fn);
+    as_value netconnection_uri(const fn_call& fn);
+    as_value netconnection_connect(const fn_call& fn);
+    as_value netconnection_close(const fn_call& fn);
+    as_value netconnection_call(const fn_call& fn);
+    as_value netconnection_addHeader(const fn_call& fn);
+    as_value netconnection_new(const fn_call& fn);
+
+}
+
+namespace {
+
+    boost::uint16_t readNetworkShort(const boost::uint8_t* buf);
+    boost::uint32_t readNetworkLong(const boost::uint8_t* buf);
+
+}
 /// Queue of remoting calls 
 //
 /// This class in made to handle data and do defered processing for
@@ -227,613 +92,850 @@
 /// script specified a callback function, use the optional parameters to 
specify
 /// the identifier (which must be unique) and the callback object as an 
as_value
 ///
+/// @todo move this somewhere more appropriate, perhaps by merging with libamf.
 class AMFQueue {
 private:
-       NetConnection& _nc;
-       static const int NCCALLREPLYMAX=200000;
-
-       typedef std::map<std::string, boost::intrusive_ptr<as_object> > 
CallbacksMap;
-       CallbacksMap callbacks;
-
-       SimpleBuffer postdata;
-       URL url;
-       boost::scoped_ptr<IOChannel> _connection;
-       SimpleBuffer reply;
-       int reply_start;
-       int reply_end;
-       int queued_count;
-       unsigned int ticker;
+
+    NetConnection& _nc;
+    static const int NCCALLREPLYMAX=200000;
+
+    typedef std::map<std::string, boost::intrusive_ptr<as_object> > 
+        CallbacksMap;
+    CallbacksMap callbacks;
+
+    SimpleBuffer postdata;
+    URL url;
+    boost::scoped_ptr<IOChannel> _connection;
+    SimpleBuffer reply;
+    int reply_start;
+    int reply_end;
+    int queued_count;
+    unsigned int ticker;
 
 public:
-       AMFQueue(NetConnection& nc, URL url)
-               :
-               _nc(nc),
-               postdata(),
-               url(url),
-               _connection(0),
-               reply(NCCALLREPLYMAX),
-               reply_start(0),
-               reply_end(0),
-               queued_count(0),
-               ticker(0)
-       {
-               // leave space for header
-               postdata.append("\000\000\000\000\000\000", 6);
-       }
+    AMFQueue(NetConnection& nc, URL url)
+        :
+        _nc(nc),
+        postdata(),
+        url(url),
+        _connection(0),
+        reply(NCCALLREPLYMAX),
+        reply_start(0),
+        reply_end(0),
+        queued_count(0),
+        ticker(0)
+    {
+        // leave space for header
+        postdata.append("\000\000\000\000\000\000", 6);
+    }
 
-       ~AMFQueue() {
-               stop_ticking();
-       }
-       
-       void enqueue(const SimpleBuffer &amf, const std::string& identifier, 
boost::intrusive_ptr<as_object> callback) {
-               push_amf(amf);
-               push_callback(identifier, callback);
-               //log_aserror("NetConnection::call(): called with a non-object 
as the callback");
-       };
-       void enqueue(const SimpleBuffer &amf) {
-               push_amf(amf);
-       };
-       
-       // tick is called automatically on intervals (hopefully only between
-       // actionscript frames)
-       //
-       // it handles all networking for NetConnection::call() and dispatches
-       // callbacks when needed
-       void tick() {
+    ~AMFQueue() {
+        stop_ticking();
+    }
+    
+    void enqueue(const SimpleBuffer &amf, const std::string& identifier,
+            boost::intrusive_ptr<as_object> callback) {
+        push_amf(amf);
+        push_callback(identifier, callback);
+    };
+    void enqueue(const SimpleBuffer &amf) {
+        push_amf(amf);
+    };
+    
+    // tick is called automatically on intervals (hopefully only between
+    // actionscript frames)
+    //
+    // it handles all networking for NetConnection::call() and dispatches
+    // callbacks when needed
+    void tick() {
 
 #ifdef GNASH_DEBUG_REMOTING
-               log_debug("tick running");
+        log_debug("tick running");
 #endif
-               if(_connection)
-               {
+        if(_connection)
+        {
 
             VM& vm = _nc.getVM();
 
 #ifdef GNASH_DEBUG_REMOTING
-                       log_debug("have connection");
+            log_debug("have connection");
 #endif
-                       int read = _connection->readNonBlocking(reply.data() + 
reply_end, NCCALLREPLYMAX - reply_end);
-                       if(read > 0) {
+            int read = _connection->readNonBlocking(reply.data() + reply_end,
+                    NCCALLREPLYMAX - reply_end);
+            if(read > 0) {
 #ifdef GNASH_DEBUG_REMOTING
-                               log_debug("read '%1%' bytes: %2%", read, 
hexify(reply.data() + reply_end, read, false));
+                log_debug("read '%1%' bytes: %2%", read, 
+                        hexify(reply.data() + reply_end, read, false));
 #endif
-                               reply_end += read;
-                       }
-
-                       // There is no way to tell if we have a whole amf reply 
without
-                       // parsing everything
-                       //
-                       // The reply format has a header field which specifies 
the
-                       // number of bytes in the reply, but potlatch sends 
0xffffffff
-                       // and works fine in the proprietary player
-                       //
-                       // For now we just wait until we have the full reply.
-                       //
-                       // FIXME make this parse on other conditions, 
including: 1) when
-                       // the buffer is full, 2) when we have a "length in 
bytes" value
-                       // thas is satisfied
-
-                       if(_connection->get_error())
-                       {
-                               log_debug("connection is in error condition, 
calling NetConnection.onStatus");
-                               reply_start = 0;
-                               reply_end = 0;
-                               //log_debug("deleting connection");
-                               _connection.reset(); // reset connection before 
calling the callback
-
-                               // FIXME: should only call NetConnection's 
onStatus
-                               //        if the IOChannel is in error 
condition.
-                               //        (tipically 404).
-                               //        When the response is empty, just 
nothing happens.
-                               _nc.callMethod(NSV::PROP_ON_STATUS, as_value());
-
-                       }
-                       else if(_connection->eof() )
-                       {
-                               if ( reply_end > 8)
-                               {
+                reply_end += read;
+            }
+
+            // There is no way to tell if we have a whole amf reply without
+            // parsing everything
+            //
+            // The reply format has a header field which specifies the
+            // number of bytes in the reply, but potlatch sends 0xffffffff
+            // and works fine in the proprietary player
+            //
+            // For now we just wait until we have the full reply.
+            //
+            // FIXME make this parse on other conditions, including: 1) when
+            // the buffer is full, 2) when we have a "length in bytes" value
+            // thas is satisfied
+
+            if(_connection->get_error())
+            {
+                log_debug("connection is in error condition, calling "
+                        "NetConnection.onStatus");
+                reply_start = 0;
+                reply_end = 0;
+                // reset connection before calling the callback
+                _connection.reset(); 
+
+                // FIXME: should only call NetConnection's onStatus
+                //        if the IOChannel is in error condition.
+                //        (tipically 404).
+                //        When the response is empty, just nothing happens.
+                _nc.callMethod(NSV::PROP_ON_STATUS, as_value());
+
+            }
+            else if(_connection->eof() )
+            {
+                if ( reply_end > 8)
+                {
                     std::vector<as_object*> objRefs;
 
 #ifdef GNASH_DEBUG_REMOTING
-                                       log_debug("hit eof");
-#endif
-                                       boost::int16_t si;
-                                       boost::uint16_t li;
-                                       boost::uint8_t *b = reply.data() + 
reply_start;
-                                       boost::uint8_t *end = reply.data() + 
reply_end;
-
-                                       // parse header
-                                       b += 2; // skip version indicator and 
client id
-
-                                       // NOTE: this looks much like parsing 
of an OBJECT_AMF0
-                                       si = readNetworkShort(b); b += 2; // 
number of headers
-                                       uint8_t headers_ok = 1;
-                                       if(si != 0)
-                                       {
-#ifdef GNASH_DEBUG_REMOTING
-                                               
log_debug("NetConnection::call(): amf headers section parsing");
-#endif
-                                               as_value tmp;
-                                               for(int i = si; i > 0; --i)
-                                               {
-                                                       if(b + 2 > end) {
-                                                               headers_ok = 0;
-                                                               break;
-                                                       }
-                                                       si = 
readNetworkShort(b); b += 2; // name length
-                                                       if(b + si > end) {
-                                                               headers_ok = 0;
-                                                               break;
-                                                       }
-                                                       std::string 
headerName((char*)b, si); // end-b);
-#ifdef GNASH_DEBUG_REMOTING
-                                                       log_debug("Header name 
%s", headerName);
-#endif
-                                                       b += si;
-                                                       if ( b + 5 > end ) {
-                                                               headers_ok = 0;
-                                                               break;
-                                                       }
-                                                       b += 5; // skip past 
bool and length long
-                                                       if( !tmp.readAMF0(b, 
end, -1, objRefs, vm) )
-                                                       {
-                                                               headers_ok = 0;
-                                                               break;
-                                                       }
-#ifdef GNASH_DEBUG_REMOTING
-                                                       log_debug("Header value 
%s", tmp);
-#endif
-
-                                                       { // method call for 
each header
-                                                         // FIXME: it seems to 
me that the call should happen
-                                                               VM& vm = 
_nc.getVM();
-                                                               string_table& 
st = vm.getStringTable();
-                                                               
string_table::key key = st.find(headerName);
-#ifdef GNASH_DEBUG_REMOTING
-                                                               
log_debug("Calling NetConnection.%s(%s)", headerName, tmp);
-#endif
-                                                               
_nc.callMethod(key, tmp);
-                                                       }
-                                               }
-                                       }
-
-                                       if(headers_ok == 1) {
-
-                                               si = readNetworkShort(b); b += 
2; // number of replies
-
-                                               // TODO consider counting 
number of replies we
-                                               // actually parse and doing 
something if it
-                                               // doesn't match this value 
(does it matter?
-                                               if(si > 0) {
-                                                       // parse replies until 
we get a parse error or we reach the end of the buffer
-                                                       while(b < end) {
-                                                               if(b + 2 > end) 
break;
-                                                               si = 
readNetworkShort(b); b += 2; // reply length
-                                                               if(si < 11) {
-                                                                       
log_error("NetConnection::call(): reply message name too short");
-                                                                       break;
-                                                               }
-                                                               if(b + si > 
end) break;
-                                                               // TODO check 
that the last 9 bytes are "/onResult"
-                                                               // this should 
either split on the 2nd / or require onResult or onStatus
-                                                               std::string 
id(reinterpret_cast<char*>(b), si - 9);
-                                                               b += si;
-
-                                                               // parse past 
unused string in header
-                                                               if(b + 2 > end) 
break;
-                                                               si = 
readNetworkShort(b); b += 2; // reply length
-                                                               if(b + si > 
end) break;
-                                                               b += si;
-
-                                                               // this field 
is supposed to hold the
-                                                               // total number 
of bytes in the rest of
-                                                               // this 
particular reply value, but
-                                                               // 
openstreetmap.org (which works great
-                                                               // in the adobe 
player) sends
-                                                               // 0xffffffff. 
So we just ignore it
-                                                               if(b + 4 > end) 
break;
-                                                               li = 
readNetworkLong(b); b += 4; // reply length
-
-#ifdef GNASH_DEBUG_REMOTING
-                                                               
log_debug("about to parse amf value");
-#endif
-                                                               // this updates 
b to point to the next unparsed byte
-                                                               as_value 
reply_as_value;
-                                                               if ( ! 
reply_as_value.readAMF0(b, end, -1, objRefs, vm) )
-                                                               {
-                                                                       
log_error("parse amf failed");
-                                                                       // this 
will happen if we get
-                                                                       // 
bogus data, or if the data runs
-                                                                       // off 
the end of the buffer
-                                                                       // 
provided, or if we get data we
-                                                                       // 
don't know how to parse
-                                                                       break;
-                                                               }
-#ifdef GNASH_DEBUG_REMOTING
-                                                               
log_debug("parsed amf");
-#endif
-
-                                                               // update 
variable to show how much we've parsed
-                                                               reply_start = b 
- reply.data();
-
-                                                               // if 
actionscript specified a callback object, call it
-                                                               
boost::intrusive_ptr<as_object> callback = pop_callback(id);
-                                                               if(callback) {
-#ifdef GNASH_DEBUG_REMOTING
-                                                                       
log_debug("calling onResult callback");
-#endif
-                                                                       // 
FIXME check if above line can fail and we have to react
-                                                                       
callback->callMethod(NSV::PROP_ON_RESULT, reply_as_value);
-#ifdef GNASH_DEBUG_REMOTING
-                                                                       
log_debug("callback called");
-#endif
-                                                               } else {
-#ifdef GNASH_DEBUG_REMOTING
-                                                                       
log_debug("couldn't find callback object");
-#endif
-                                                               }
-                                                       }
-                                               }
-                                       }
-                               }
-                               else
-                               {
-                                       log_error("Response from remoting 
service < 8 bytes");
-                               }
-
-#ifdef GNASH_DEBUG_REMOTING
-                               log_debug("deleting connection");
-#endif
-                               _connection.reset();
-                               reply_start = 0;
-                               reply_end = 0;
-                       }
-               }
-
-               if(!_connection && queued_count > 0) {
-#ifdef GNASH_DEBUG_REMOTING
-                       log_debug("creating connection");
-#endif
-                       // set the "number of bodies" header
-                       (reinterpret_cast<boost::uint16_t*>(postdata.data() + 
4))[0] = htons(queued_count);
-                       std::string 
postdata_str(reinterpret_cast<char*>(postdata.data()), postdata.size());
-#ifdef GNASH_DEBUG_REMOTING
-                       log_debug("NetConnection.call(): encoded args from %1% 
calls: %2%", queued_count, hexify(postdata.data(), postdata.size(), false));
-#endif
-                       queued_count = 0;
-                       
_connection.reset(StreamProvider::getDefaultInstance().getStream(url, 
postdata_str).release());
-                       postdata.resize(6);
-#ifdef GNASH_DEBUG_REMOTING
-                       log_debug("connection created");
-#endif
-               }
-
-               if(_connection == 0 && queued_count == 0) {
-#ifdef GNASH_DEBUG_REMOTING
-                       log_debug("stopping ticking");
-#endif
-                       stop_ticking();
-#ifdef GNASH_DEBUG_REMOTING
-                       log_debug("ticking stopped");
-#endif
-               }
-       };
-
-       static as_value amfqueue_tick_wrapper(const fn_call& fn)
-       {
-               boost::intrusive_ptr<NetConnection> ptr = 
ensureType<NetConnection>(fn.this_ptr);
-               // FIXME check if it's possible for the URL of a NetConnection 
to change between call()s
-               ptr->_callQueue->tick();
-               return as_value();
-       };
-
-       void markReachableResources() const
-       {
-               for (CallbacksMap::const_iterator i=callbacks.begin(), 
e=callbacks.end(); i!=e; ++i)
-               {
-                       i->second->setReachable();
-               }
-       }
+                    log_debug("hit eof");
+#endif
+                    boost::int16_t si;
+                    boost::uint16_t li;
+                    boost::uint8_t *b = reply.data() + reply_start;
+                    boost::uint8_t *end = reply.data() + reply_end;
+
+                    // parse header
+                    b += 2; // skip version indicator and client id
+
+                    // NOTE: this looks much like parsing of an OBJECT_AMF0
+                    si = readNetworkShort(b); b += 2; // number of headers
+                    uint8_t headers_ok = 1;
+                    if (si != 0)
+                    {
+#ifdef GNASH_DEBUG_REMOTING
+                        log_debug("NetConnection::call(): amf headers "
+                                "section parsing");
+#endif
+                        as_value tmp;
+                        for(int i = si; i > 0; --i)
+                        {
+                            if(b + 2 > end) {
+                                headers_ok = 0;
+                                break;
+                            }
+                            si = readNetworkShort(b); b += 2; // name length
+                            if(b + si > end) {
+                                headers_ok = 0;
+                                break;
+                            }
+                            std::string headerName((char*)b, si); // end-b);
+#ifdef GNASH_DEBUG_REMOTING
+                            log_debug("Header name %s", headerName);
+#endif
+                            b += si;
+                            if ( b + 5 > end ) {
+                                headers_ok = 0;
+                                break;
+                            }
+                            b += 5; // skip past bool and length long
+                            if( !tmp.readAMF0(b, end, -1, objRefs, vm) )
+                            {
+                                headers_ok = 0;
+                                break;
+                            }
+#ifdef GNASH_DEBUG_REMOTING
+                            log_debug("Header value %s", tmp);
+#endif
+
+                            { // method call for each header
+                              // FIXME: it seems to me that the call should 
happen
+                                VM& vm = _nc.getVM();
+                                string_table& st = vm.getStringTable();
+                                string_table::key key = st.find(headerName);
+#ifdef GNASH_DEBUG_REMOTING
+                                log_debug("Calling NetConnection.%s(%s)",
+                                        headerName, tmp);
+#endif
+                                _nc.callMethod(key, tmp);
+                            }
+                        }
+                    }
+
+                    if(headers_ok == 1) {
+
+                        si = readNetworkShort(b); b += 2; // number of replies
+
+                        // TODO consider counting number of replies we
+                        // actually parse and doing something if it
+                        // doesn't match this value (does it matter?
+                        if(si > 0) {
+                            // parse replies until we get a parse error or we 
reach the end of the buffer
+                            while(b < end) {
+                                if(b + 2 > end) break;
+                                si = readNetworkShort(b); b += 2; // reply 
length
+                                if(si < 11) {
+                                    log_error("NetConnection::call(): reply 
message name too short");
+                                    break;
+                                }
+                                if(b + si > end) break;
+                                // TODO check that the last 9 bytes are 
"/onResult"
+                                // this should either split on the 2nd / or 
require onResult or onStatus
+                                std::string id(reinterpret_cast<char*>(b), si 
- 9);
+                                b += si;
+
+                                // parse past unused string in header
+                                if(b + 2 > end) break;
+                                si = readNetworkShort(b); b += 2; // reply 
length
+                                if(b + si > end) break;
+                                b += si;
+
+                                // this field is supposed to hold the
+                                // total number of bytes in the rest of
+                                // this particular reply value, but
+                                // openstreetmap.org (which works great
+                                // in the adobe player) sends
+                                // 0xffffffff. So we just ignore it
+                                if(b + 4 > end) break;
+                                li = readNetworkLong(b); b += 4; // reply 
length
+
+#ifdef GNASH_DEBUG_REMOTING
+                                log_debug("about to parse amf value");
+#endif
+                                // this updates b to point to the next 
unparsed byte
+                                as_value reply_as_value;
+                                if ( ! reply_as_value.readAMF0(b, end, -1, 
objRefs, vm) )
+                                {
+                                    log_error("parse amf failed");
+                                    // this will happen if we get
+                                    // bogus data, or if the data runs
+                                    // off the end of the buffer
+                                    // provided, or if we get data we
+                                    // don't know how to parse
+                                    break;
+                                }
+#ifdef GNASH_DEBUG_REMOTING
+                                log_debug("parsed amf");
+#endif
+
+                                // update variable to show how much we've 
parsed
+                                reply_start = b - reply.data();
+
+                                // if actionscript specified a callback 
object, call it
+                                boost::intrusive_ptr<as_object> callback = 
pop_callback(id);
+                                if(callback) {
+#ifdef GNASH_DEBUG_REMOTING
+                                    log_debug("calling onResult callback");
+#endif
+                                    // FIXME check if above line can fail and 
we have to react
+                                    callback->callMethod(NSV::PROP_ON_RESULT, 
reply_as_value);
+#ifdef GNASH_DEBUG_REMOTING
+                                    log_debug("callback called");
+#endif
+                                } else {
+#ifdef GNASH_DEBUG_REMOTING
+                                    log_debug("couldn't find callback object");
+#endif
+                                }
+                            }
+                        }
+                    }
+                }
+                else
+                {
+                    log_error("Response from remoting service < 8 bytes");
+                }
+
+#ifdef GNASH_DEBUG_REMOTING
+                log_debug("deleting connection");
+#endif
+                _connection.reset();
+                reply_start = 0;
+                reply_end = 0;
+            }
+        }
+
+        if(!_connection && queued_count > 0) {
+#ifdef GNASH_DEBUG_REMOTING
+            log_debug("creating connection");
+#endif
+            // set the "number of bodies" header
+            (reinterpret_cast<boost::uint16_t*>(postdata.data() + 4))[0] = 
htons(queued_count);
+            std::string postdata_str(reinterpret_cast<char*>(postdata.data()), 
postdata.size());
+#ifdef GNASH_DEBUG_REMOTING
+            log_debug("NetConnection.call(): encoded args from %1% calls: 
%2%", queued_count, hexify(postdata.data(), postdata.size(), false));
+#endif
+            queued_count = 0;
+            
_connection.reset(StreamProvider::getDefaultInstance().getStream(url, 
postdata_str).release());
+            postdata.resize(6);
+#ifdef GNASH_DEBUG_REMOTING
+            log_debug("connection created");
+#endif
+        }
+
+        if(_connection == 0 && queued_count == 0) {
+#ifdef GNASH_DEBUG_REMOTING
+            log_debug("stopping ticking");
+#endif
+            stop_ticking();
+#ifdef GNASH_DEBUG_REMOTING
+            log_debug("ticking stopped");
+#endif
+        }
+    };
+
+    static as_value amfqueue_tick_wrapper(const fn_call& fn)
+    {
+        boost::intrusive_ptr<NetConnection> ptr = 
+            ensureType<NetConnection>(fn.this_ptr);
+        // FIXME check if it's possible for the URL of a NetConnection to 
change between call()s
+        ptr->_callQueue->tick();
+        return as_value();
+    };
+
+    void markReachableResources() const
+    {
+        for (CallbacksMap::const_iterator i=callbacks.begin(),
+                e=callbacks.end(); i!=e; ++i)
+        {
+            i->second->setReachable();
+        }
+    }
 
 private:
-       void start_ticking() 
-       {
-
-               if (ticker) return;
-
-               boost::intrusive_ptr<builtin_function> ticker_as = 
-                       new builtin_function(&AMFQueue::amfqueue_tick_wrapper);
-
-               std::auto_ptr<Timer> timer(new Timer);
-               unsigned long delayMS = 50; // FIXME crank up to 50 or so
-               timer->setInterval(*ticker_as, delayMS, &_nc);
-               ticker = _nc.getVM().getRoot().add_interval_timer(timer, true);
-       }
-
-       void push_amf(const SimpleBuffer &amf) 
-       {
-               GNASH_REPORT_FUNCTION;
-
-               postdata.append(amf.data(), amf.size());
-               queued_count++;
-
-               start_ticking();
-       }
-
-       void stop_ticking() 
-       {
-               if (!ticker) return;
-               _nc.getVM().getRoot().clear_interval_timer(ticker);
-               ticker=0;
-       }
-
-       void push_callback(const std::string& id, 
boost::intrusive_ptr<as_object> callback) {
-               callbacks.insert(std::pair<std::string, 
boost::intrusive_ptr<as_object> >(id, callback));
-       }
-
-       boost::intrusive_ptr<as_object> pop_callback(std::string id)
-       {
-               CallbacksMap::iterator it = callbacks.find(id);
-               if (it != callbacks.end()) {
-                       boost::intrusive_ptr<as_object> callback = it->second;
-                       //boost::intrusive_ptr<as_object> callback;
-                       //callback = it.second;
-                       callbacks.erase(it);
-                       return callback;
-               }
-               else {
-                       return 0;
-               }
-       }
+    void start_ticking() 
+    {
+
+        if (ticker) return;
+
+        boost::intrusive_ptr<builtin_function> ticker_as = 
+                new builtin_function(&AMFQueue::amfqueue_tick_wrapper);
+
+        std::auto_ptr<Timer> timer(new Timer);
+        unsigned long delayMS = 50; // FIXME crank up to 50 or so
+        timer->setInterval(*ticker_as, delayMS, &_nc);
+        ticker = _nc.getVM().getRoot().add_interval_timer(timer, true);
+    }
+
+    void push_amf(const SimpleBuffer &amf) 
+    {
+        GNASH_REPORT_FUNCTION;
+
+        postdata.append(amf.data(), amf.size());
+        queued_count++;
+
+        start_ticking();
+    }
+
+    void stop_ticking() 
+    {
+        if (!ticker) return;
+        _nc.getVM().getRoot().clear_interval_timer(ticker);
+        ticker=0;
+    }
+
+    void push_callback(const std::string& id,
+            boost::intrusive_ptr<as_object> callback) {
+        callbacks.insert(std::pair<std::string,
+                boost::intrusive_ptr<as_object> >(id, callback));
+    }
+
+    boost::intrusive_ptr<as_object> pop_callback(std::string id)
+    {
+        CallbacksMap::iterator it = callbacks.find(id);
+        if (it != callbacks.end()) {
+            boost::intrusive_ptr<as_object> callback = it->second;
+            //boost::intrusive_ptr<as_object> callback;
+            //callback = it.second;
+            callbacks.erase(it);
+            return callback;
+        }
+        else {
+            return 0;
+        }
+    }
 };
 
+/// \class NetConnection
+/// \brief Opens a local connection through which you can play
+/// back video (FLV) files from an HTTP address or from the local file
+/// system, using curl.
+NetConnection::NetConnection()
+    :
+    as_object(getNetConnectionInterface()),
+    _callQueue(0),
+    _isConnected(false)
+{
+    attachProperties(*this);
+}
+
+// extern (used by Global.cpp)
+void
+netconnection_class_init(as_object& global)
+{
+    // This is going to be the global NetConnection "class"/"function"
+    static boost::intrusive_ptr<builtin_function> cl;
+
+    if ( cl == NULL )
+    {
+        cl=new builtin_function(&netconnection_new,
+                getNetConnectionInterface());
+        // replicate all interface to class, to be able to access
+        // all methods as static functions
+        attachNetConnectionInterface(*cl);
+             
+    }
+
+    // Register _global.String
+    global.init_member("NetConnection", cl.get());
+}
+
+// here to have AMFQueue definition available
+NetConnection::~NetConnection()
+{
+}
+
+void
+NetConnection::markReachableResources() const
+{
+    if ( _callQueue.get() ) _callQueue->markReachableResources();
+    markAsObjectReachable();
+}
+
+/*public*/
+std::string
+NetConnection::validateURL(const std::string& url)
+{
+    std::string completeUrl;
+    if (_prefixUrl.size() > 0) {
+        if(url.size() > 0) {
+            completeUrl += _prefixUrl + "/" + url;
+        } else {
+            completeUrl += _prefixUrl;
+        }
+    } else {
+        completeUrl += url;
+    }
+
+    const movie_root& mr = _vm.getRoot();
+    URL uri(completeUrl, mr.runInfo().baseURL());
+
+    std::string uriStr(uri.str());
+    assert(uriStr.find("://") != std::string::npos);
+
+    // Check if we're allowed to open url
+    if (!URLAccessManager::allow(uri)) {
+        log_security(_("Gnash is not allowed to open this url: %s"), uriStr);
+        return "";
+    }
+
+    log_debug(_("Connection to movie: %s"), uriStr);
+
+    return uriStr;
+}
+
+void
+NetConnection::addToURL(const std::string& url)
+{
+    // What is this ? It is NOT documented in the header !!
+    //if (url == "null" || url == "NULL") return;
+
+    // If there already is something in _prefixUrl, then we already have a url,
+    // so no need to renew it. This may not correct, needs some testing.
+    if (_prefixUrl.size() > 0) return;
+
+    _prefixUrl += url;
+}
+
+void
+NetConnection::notifyStatus(StatusCode code)
+{
+    std::pair<std::string, std::string> info;
+    getStatusCodeInfo(code, info);
+
+    /// This is a new normal object each time (see NetConnection.as)
+    as_object* o = new as_object(getObjectInterface());
+
+    const int flags = 0;
+
+    o->init_member("code", info.first, flags);
+    o->init_member("level", info.second, flags);
+
+    callMethod(NSV::PROP_ON_STATUS, o);
+
+}
+
+void
+NetConnection::getStatusCodeInfo(StatusCode code, NetConnectionStatus& info)
+{
+    /// The Call statuses do exist, but this implementation is a guess.
+    switch (code)
+    {
+        case CONNECT_SUCCESS:
+            info.first = "NetConnection.Connect.Success";
+            info.second = "status";
+            return;
+
+        case CONNECT_FAILED:
+            info.first = "NetConnection.Connect.Failed";
+            info.second = "error";
+            return;
+
+        case CALL_FAILED:
+            info.first = "NetConnection.Call.Failed";
+            info.second = "error";
+            return;
+
+        case CALL_SUCCESS:
+            info.first = "NetConnection.Call.Success";
+            info.second = "status";
+            return;
+    }
+
+}
+
+void
+NetConnection::call(as_object* asCallback, const std::string& callNumber,
+        const SimpleBuffer& buf)
+{
+
+#ifdef GNASH_DEBUG_REMOTING
+    log_debug(_("NetConnection.call(): encoded args: %s"),
+            hexify(buf->data(), buf->size(), false));
+#endif
+
+    // FIXME check that ptr->_prefixURL is valid
+    URL url(validateURL(""));
+
+    // FIXME check if it's possible for the URL of a NetConnection
+    // to change between call()s
+    if (!_callQueue.get()) {
+        _callQueue.reset(new AMFQueue(*this, url));
+    }
+
+    if (asCallback) {
+        //boost::intrusive_ptr<as_object> intrusive_callback(asCallback);
+#ifdef GNASH_DEBUG_REMOTING
+        log_debug("calling enqueue with callback");
+#endif
+        _callQueue->enqueue(buf, callNumber, asCallback);
+        //? delete asCallback;
+    }
+    
+    else {
+#ifdef GNASH_DEBUG_REMOTING
+        log_debug("calling enqueue without callback");
+#endif
+        _callQueue->enqueue(buf);
+    }
+#ifdef GNASH_DEBUG_REMOTING
+    log_debug("called enqueue");
+#endif
+
+}
+
+/// Anonymous namespace for NetConnection AMF-reading helper functions
+/// (shouldn't be here).
+
+namespace {
+
+boost::uint16_t
+readNetworkShort(const boost::uint8_t* buf) {
+    boost::uint16_t s = buf[0] << 8 | buf[1];
+    return s;
+}
+
+boost::uint32_t
+readNetworkLong(const boost::uint8_t* buf) {
+    boost::uint32_t s = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
+    return s;
+}
+
+}
+
+
+/// Anonymous namespace for NetConnection interface implementation.
+
+namespace {
+
 as_value
-NetConnection::call_method(const fn_call& fn)
+netconnection_call(const fn_call& fn)
 {
-       static int call_number = 0;
-       boost::intrusive_ptr<NetConnection> ptr = 
ensureType<NetConnection>(fn.this_ptr); 
-
-       if (fn.nargs < 1)
-       {
-               IF_VERBOSE_ASCODING_ERRORS(
-               log_aserror(_("NetConnection.call(): needs at least one 
argument"));
-               );
-               return as_value(false); // FIXME should we return true anyway?
-       }
-
-       const as_value& methodName_as = fn.arg(0);
-       if (!methodName_as.is_string()) {
-                IF_VERBOSE_ASCODING_ERRORS(
-               std::stringstream ss; fn.dump_args(ss);
-                log_aserror(_("NetConnection.call(%s): first argument 
(methodName) must be a string"), ss.str());
-                );
-               return as_value(false); // FIXME should we return true anyway?
-       }
-
-       std::stringstream ss; fn.dump_args(ss);
+    boost::intrusive_ptr<NetConnection> ptr = 
+        ensureType<NetConnection>(fn.this_ptr); 
+
+    if (fn.nargs < 1)
+    {
+        IF_VERBOSE_ASCODING_ERRORS(
+        log_aserror(_("NetConnection.call(): needs at least one argument"));
+        );
+        return as_value(false); // FIXME should we return true anyway?
+    }
+
+    const as_value& methodName_as = fn.arg(0);
+    if (!methodName_as.is_string()) {
+        IF_VERBOSE_ASCODING_ERRORS(
+        std::stringstream ss; fn.dump_args(ss);
+        log_aserror(_("NetConnection.call(%s): first argument "
+                "(methodName) must be a string"), ss.str());
+        );
+        return as_value(false); // FIXME should we return true anyway?
+    }
+
+    std::stringstream ss; fn.dump_args(ss);
 #ifdef GNASH_DEBUG_REMOTING
-        log_debug("NetConnection.call(%s)", ss.str());
+    log_debug("NetConnection.call(%s)", ss.str());
 #endif
 
-       // TODO: arg(1) is the response object. let it know when data comes back
-       boost::intrusive_ptr<as_object> asCallback = 0;
-       if(fn.nargs > 1) {
-
-               if(fn.arg(1).is_object()) {
-                       asCallback = (fn.arg(1).to_object());
-               }
-
-               else {
+    // TODO: arg(1) is the response object. let it know when data comes back
+    boost::intrusive_ptr<as_object> asCallback;
+    if (fn.nargs > 1) {
+
+        if (fn.arg(1).is_object()) {
+            asCallback = (fn.arg(1).to_object());
+        }
+        else {
             IF_VERBOSE_ASCODING_ERRORS(
-                       std::stringstream ss; fn.dump_args(ss);
-                       log_aserror("NetConnection::call(%s): second argument 
must be an object", ss.str());
-                       );
-               }
-       }
-
-       boost::scoped_ptr<SimpleBuffer> buf ( new SimpleBuffer(32) );
-
-       std::string methodName = methodName_as.to_string();
-
-       // junk at the top (version, client id, 0 headers, 1 body)
-       // this is done by AMFQueue now: 
buf->append("\000\000\000\000\000\001", 6);
-
-       // method name
-       buf->appendNetworkShort(methodName.size());
-       buf->append(methodName.c_str(), methodName.size());
-
-       // client id (result number) as counted string
-       // the convention seems to be / followed by a unique (ascending) number
-       ++call_number;
-
-       std::ostringstream os;
-       os << "/" << call_number;
-       const std::string callNumberString = os.str();
-
-       buf->appendNetworkShort(callNumberString.size());
-       buf->append(callNumberString.c_str(), callNumberString.size());
-
-       size_t total_size_offset = buf->size();
-       buf->append("\000\000\000\000", 4); // total size to be filled in later
+                std::stringstream ss; fn.dump_args(ss);
+                log_aserror("NetConnection.call(%s): second argument must be "
+                    "an object", ss.str());
+            );
+        }
+    }
+    std::string methodName = methodName_as.to_string();
+
+    static int call_number = 0;
+
+    boost::scoped_ptr<SimpleBuffer> buf ( new SimpleBuffer(32) );
+
+    // method name
+    buf->appendNetworkShort(methodName.size());
+    buf->append(methodName.c_str(), methodName.size());
+
+    // client id (result number) as counted string
+    // the convention seems to be / followed by a unique (ascending) number
+    ++call_number;
+
+    std::ostringstream os;
+    os << "/" << call_number;
+    const std::string callNumberString = os.str();
+
+    buf->appendNetworkShort(callNumberString.size());
+    buf->append(callNumberString.c_str(), callNumberString.size());
+
+    size_t total_size_offset = buf->size();
+    buf->append("\000\000\000\000", 4); // total size to be filled in later
 
     std::map<as_object*, size_t> offsetTable;
+
+    // encode array of arguments to remote method
+    buf->appendByte(amf::Element::STRICT_ARRAY_AMF0);
+    buf->appendNetworkLong(fn.nargs - 2);
+
     VM& vm = ptr->getVM();
 
-       // encode array of arguments to remote method
-       buf->appendByte(amf::Element::STRICT_ARRAY_AMF0);
-       buf->appendNetworkLong(fn.nargs - 2);
-       if (fn.nargs > 2)
+    if (fn.nargs > 2)
     {
-               for (unsigned int i = 2; i < fn.nargs; ++i)
-               {
-                       const as_value& arg = fn.arg(i);
+        for (unsigned int i = 2; i < fn.nargs; ++i)
+        {
+            const as_value& arg = fn.arg(i);
             if ( ! arg.writeAMF0(*buf, offsetTable, vm) )
             {
-                log_error("Could not serialize NetConnection.call argument 
%d", i);
+                log_error("Could not serialize NetConnection.call argument %d",
+                        i);
             }
-               }
-       }
-
-       // Set the "total size" parameter.
-       *(reinterpret_cast<uint32_t*>(buf->data() + total_size_offset)) = 
htonl(buf->size() - 4 - total_size_offset);
-       
-
-#ifdef GNASH_DEBUG_REMOTING
-       log_debug(_("NetConnection.call(): encoded args: %s"), 
hexify(buf->data(), buf->size(), false));
-#endif
-
-       // FIXME check that ptr->_prefixURL is valid
-       URL url(ptr->validateURL(std::string()));
-
-
-       // FIXME check if it's possible for the URL of a NetConnection to 
change between call()s
-       if (! ptr->_callQueue.get()) {
-               ptr->_callQueue.reset(new AMFQueue(*ptr, url));
-       }
-
-       if (asCallback) {
-               //boost::intrusive_ptr<as_object> 
intrusive_callback(asCallback);
-#ifdef GNASH_DEBUG_REMOTING
-               log_debug("calling enqueue with callback");
-#endif
-               ptr->_callQueue->enqueue(*buf, callNumberString, asCallback);
-               //? delete asCallback;
-       }
-       
-       else {
-#ifdef GNASH_DEBUG_REMOTING
-               log_debug("calling enqueue without callback");
-#endif
-               ptr->_callQueue->enqueue(*buf);
-       }
-#ifdef GNASH_DEBUG_REMOTING
-       log_debug("called enqueue");
-#endif
-
-       return as_value();
-}
-
-as_value
-NetConnection::close_method(const fn_call& fn)
-{
-       boost::intrusive_ptr<NetConnection> ptr = 
ensureType<NetConnection>(fn.this_ptr); 
-       UNUSED(ptr);
-
-       log_unimpl("NetConnection.close()");
-       return as_value();
-}
-
-as_value
-NetConnection::isConnected_getset(const fn_call& fn)
-{
-       boost::intrusive_ptr<NetConnection> ptr = 
ensureType<NetConnection>(fn.this_ptr); 
-       UNUSED(ptr);
-
-       if ( fn.nargs == 0 ) // getter
-       {
-               log_unimpl("NetConnection.isConnected get");
-               return as_value();
-       }
-       else // setter
-       {
-               IF_VERBOSE_ASCODING_ERRORS(
-               log_aserror("Tried to set read-only property 
NetConnection.isConnected");
-               );
-               return as_value();
-       }
-}
-
-as_value
-NetConnection::uri_getset(const fn_call& fn)
-{
-       boost::intrusive_ptr<NetConnection> ptr = 
ensureType<NetConnection>(fn.this_ptr); 
-       UNUSED(ptr);
-
-       if ( fn.nargs == 0 ) // getter
-       {
-               log_unimpl("NetConnection.uri get");
-               return as_value();
-       }
-       else // setter
-       {
-               log_unimpl("NetConnection.uri set");
-               return as_value();
-       }
-
-}
-
-void
-NetConnection::attachNetConnectionInterface(as_object& o)
-{
-       o.init_member("connect", new 
builtin_function(NetConnection::connect_method));
-       o.init_member("addHeader", new 
builtin_function(NetConnection::addHeader_method));
-       o.init_member("call", new builtin_function(NetConnection::call_method));
-       o.init_member("close", new 
builtin_function(NetConnection::close_method));
-
-}
-
-void
-NetConnection::attachProperties()
-{
-       init_property("isConnected", &NetConnection::isConnected_getset, 
&NetConnection::isConnected_getset);
-       init_property("uri", &NetConnection::uri_getset, 
&NetConnection::uri_getset);
+        }
+    }
+
+    // Set the "total size" parameter.
+    *(reinterpret_cast<uint32_t*>(buf->data() + total_size_offset)) = 
+        htonl(buf->size() - 4 - total_size_offset);
+
+    ptr->call(asCallback.get(), callNumberString, *buf);
+
+    // Why return undefined here?
+    return as_value();
+}
+
+as_value
+netconnection_close(const fn_call& fn)
+{
+    boost::intrusive_ptr<NetConnection> ptr =
+        ensureType<NetConnection>(fn.this_ptr); 
+    UNUSED(ptr);
+
+    log_unimpl("NetConnection.close()");
+    return as_value();
+}
+
+
+/// Read-only
+as_value
+netconnection_isConnected(const fn_call& fn)
+{
+    boost::intrusive_ptr<NetConnection> ptr =
+        ensureType<NetConnection>(fn.this_ptr); 
+
+    return as_value(ptr->isConnected());
+}
+
+as_value
+netconnection_uri(const fn_call& fn)
+{
+    boost::intrusive_ptr<NetConnection> ptr =
+        ensureType<NetConnection>(fn.this_ptr); 
+    UNUSED(ptr);
+
+    if ( fn.nargs == 0 ) // getter
+    {
+        log_unimpl("NetConnection.uri get");
+        return as_value();
+    }
+    else // setter
+    {
+        log_unimpl("NetConnection.uri set");
+        return as_value();
+    }
+
+}
+
+void
+attachNetConnectionInterface(as_object& o)
+{
+    o.init_member("connect", new builtin_function(netconnection_connect));
+    o.init_member("addHeader", new builtin_function(netconnection_addHeader));
+    o.init_member("call", new builtin_function(netconnection_call));
+    o.init_member("close", new builtin_function(netconnection_close));
+}
+
+void
+attachProperties(as_object& o)
+{
+    o.init_readonly_property("isConnected", &netconnection_isConnected);
+    o.init_property("uri", &netconnection_uri, &netconnection_uri);
 }
 
 as_object*
-NetConnection::getNetConnectionInterface()
-{
-
-       static boost::intrusive_ptr<as_object> o;
-       if ( o == NULL )
-       {
-               o = new as_object(getObjectInterface());
-               NetConnection::attachNetConnectionInterface(*o);
-       }
-
-       return o.get();
-}
-
-void
-NetConnection::registerConstructor(as_object& global)
-{
-
-       // This is going to be the global NetConnection "class"/"function"
-       static boost::intrusive_ptr<builtin_function> cl;
-
-       if ( cl == NULL )
-       {
-               cl=new builtin_function(&netconnection_new, 
getNetConnectionInterface());
-               // replicate all interface to class, to be able to access
-               // all methods as static functions
-               // TODO: this is probably wrong !
-               NetConnection::attachNetConnectionInterface(*cl);
-                    
-       }
-
-       // Register _global.String
-       global.init_member("NetConnection", cl.get());
-
-}
-
-// extern (used by Global.cpp)
-void netconnection_class_init(as_object& global)
-{
-       NetConnection::registerConstructor(global);
-}
-
-// here to have AMFQueue definition available
-NetConnection::~NetConnection()
-{
-}
-
-void
-NetConnection::markReachableResources() const
-{
-       if ( _callQueue.get() ) _callQueue->markReachableResources();
-       markAsObjectReachable();
-}
-
-
+getNetConnectionInterface()
+{
+
+    static boost::intrusive_ptr<as_object> o;
+    if ( o == NULL )
+    {
+        o = new as_object(getObjectInterface());
+        attachNetConnectionInterface(*o);
+    }
+
+    return o.get();
+}
+
+/// \brief callback to instantiate a new NetConnection object.
+/// \param fn the parameters from the Flash movie
+/// \return nothing from the function call.
+/// \note The return value is returned through the fn.result member.
+as_value
+netconnection_new(const fn_call& /* fn */)
+{
+    GNASH_REPORT_FUNCTION;
+
+    NetConnection* nc = new NetConnection;
+
+    return as_value(nc);
+}
+
+
+/// For rtmp, NetConnect.connect() takes an RTMP URL. For all other streams,
+/// it takes null or undefined.
+//
+/// RTMP is untested.
+//
+/// For non-rtmp streams:
+//
+/// Returns undefined if there are no arguments. Otherwise true if the first
+/// argument is null, false if it is anything else. Undefined is also valid
+/// for SWF7 and above.
+//
+/// The isConnected property is set to the result of connect().
+as_value
+netconnection_connect(const fn_call& fn)
+{
+
+    boost::intrusive_ptr<NetConnection> ptr =
+        ensureType<NetConnection>(fn.this_ptr); 
+    
+    if (fn.nargs < 1)
+    {
+        IF_VERBOSE_ASCODING_ERRORS(
+            log_aserror(_("NetConnection.connect(): needs at least "
+                    "one argument"));
+        );
+        return as_value();
+    }
+
+    const as_value& uri = fn.arg(0);
+
+    const VM& vm = ptr->getVM();
+
+    bool success = false;
+
+    // Check first arg for validity 
+    if (uri.is_null() || (vm.getSWFVersion() > 6 && uri.is_undefined()))
+    {
+        // Null URL was passed. This is expected. Of course, it also makes this
+        // function (and, this class) rather useless. We return true,
+        // even though returning true has no meaning.
+        
+        success = true;
+
+    }
+    else if (uri.is_string()) {
+
+        // FIXME: RTMP URLs should attempt a connection (warning: this seems
+        // to be different for SWF8). Would probably return true on success.
+        //
+        // URLs starting with "http://"; are invalid, and no connection is
+        // initiated.
+        ptr->addToURL(uri.to_string());
+
+        if ( fn.nargs > 1 )
+        {
+            std::stringstream ss; fn.dump_args(ss);
+            log_unimpl("NetConnection.connect(%s): args after the first are "
+                    "not supported", ss.str());
+        }
+
+        success = false;
+    }
+
+    ptr->notifyStatus(success ? NetConnection::CONNECT_SUCCESS :
+                                NetConnection::CONNECT_FAILED); 
+
+    ptr->setConnected(success);
+    return as_value(success);
+
+}
+
+
+as_value
+netconnection_addHeader(const fn_call& fn)
+{
+    boost::intrusive_ptr<NetConnection> ptr =
+        ensureType<NetConnection>(fn.this_ptr); 
+    UNUSED(ptr);
+
+    log_unimpl("NetConnection.addHeader()");
+    return as_value();
+}
+
+} // anonymous namespace
 
 } // end of gnash namespace
 

=== modified file 'libcore/asobj/NetConnection.h'
--- a/libcore/asobj/NetConnection.h     2008-10-19 19:13:59 +0000
+++ b/libcore/asobj/NetConnection.h     2008-12-03 11:25:33 +0000
@@ -21,9 +21,6 @@
 
 #include "IOChannel.h"
 #include <string>
-
-// TODO: port to new AS architecture
-//
 #include "as_object.h" // for inheritance
 #include "fn_call.h"
 
@@ -39,9 +36,18 @@
 //
 /// Provides interfaces to load data from an URL
 ///
-class NetConnection: public as_object {
+class NetConnection: public as_object
+{
 public:
 
+    enum StatusCode
+    {
+        CONNECT_FAILED,
+        CONNECT_SUCCESS,
+        CALL_FAILED,
+        CALL_SUCCESS
+    };
+
        NetConnection();
        ~NetConnection();
 
@@ -64,54 +70,43 @@
        ///
        std::string validateURL(const std::string& url);
 
-       /// Register the "NetConnection" constructor to the given global object
-       static void registerConstructor(as_object& global);
+    void call(as_object* asCallback, const std::string& callNumber, 
+            const SimpleBuffer& buf);
+
+    void setConnected(bool b) {
+        _isConnected = b;
+    }
+
+    bool isConnected() const {
+        return _isConnected;
+    }
+
+       /// Extend the URL to be used for playing
+       void addToURL(const std::string& url);
+
+    /// Notify the NetConnection onStatus handler of a change.
+    void notifyStatus(StatusCode code);
 
 protected:
 
        /// Mark responders associated with remoting calls
        void markReachableResources() const;
-
 private:
        friend class AMFQueue;
 
        std::auto_ptr<AMFQueue> _callQueue;
 
-       /// Extend the URL to be used for playing
-       void addToURL(const std::string& url);
-
        /// the url prefix optionally passed to connect()
        std::string _prefixUrl;
 
        /// the complete url of the file
        std::string _completeUrl;
 
-       /// Attach ActionScript instance properties
-       void attachProperties();
-
-       /// Attach ActionScript class interface
-       static void attachNetConnectionInterface(as_object& o);
-
-       /// Get ActionScript class interface
-       static as_object* getNetConnectionInterface();
-
-       /// NetConnection.isConnected ActionScript Property
-       static as_value isConnected_getset(const fn_call& fn);
-
-       /// NetConnection.uri ActionScript Property
-       static as_value uri_getset(const fn_call& fn);
-
-       /// NetConnection.connect() ActionScript Method
-       static as_value connect_method(const fn_call& fn);
-
-       /// NetConnection.close() ActionScript Method
-       static as_value close_method(const fn_call& fn);
-
-       /// NetConnection.call() ActionScript Method
-       static as_value call_method(const fn_call& fn);
-
-       /// NetConnection.addHeader() ActionScript Method
-       static as_value addHeader_method(const fn_call& fn);
+    bool _isConnected;
+
+    typedef std::pair<std::string, std::string> NetConnectionStatus;
+
+    void getStatusCodeInfo(StatusCode code, NetConnectionStatus& info);
 
 };
 
@@ -119,5 +114,4 @@
 
 } // end of gnash namespace
 
-// __NETCONNECTION_H__
 #endif

=== modified file 'libcore/asobj/NetStream_as.cpp'
--- a/libcore/asobj/NetStream_as.cpp    2008-11-27 10:20:02 +0000
+++ b/libcore/asobj/NetStream_as.cpp    2008-12-03 11:25:09 +0000
@@ -486,7 +486,7 @@
   log_debug(" Invoking onMetaData");
 #endif
 
-        string_table::key func = 
getVM().getStringTable().find(PROPNAME(funcname));
+    string_table::key func = getVM().getStringTable().find(PROPNAME(funcname));
 
     callMethod(func, as_value(info_obj));
 }
@@ -503,14 +503,10 @@
         code = popNextPendingStatusNotification();
         if ( code == invalidStatus ) break; // no more pending notifications
 
-#ifdef GNASH_DEBUG_STATUS
-        log_debug(" Invoking onStatus(%s)", getStatusCodeInfo(code).first);
-#endif
-
-        // TODO: optimize by reusing the same as_object ?
-        boost::intrusive_ptr<as_object> o = getStatusObject(code);
-
-        callMethod(NSV::PROP_ON_STATUS, as_value(o.get()));
+        // Must be a new object every time.
+        as_object* o = getStatusObject(code);
+
+        callMethod(NSV::PROP_ON_STATUS, o);
     }
 }
 
@@ -561,50 +557,69 @@
     return m_imageframe;    
 }
 
-std::pair<const char*, const char*>
-NetStream_as::getStatusCodeInfo(StatusCode code)
+void
+NetStream_as::getStatusCodeInfo(StatusCode code, NetStreamStatus& info)
 {
     switch (code)
     {
     
         case bufferEmpty:
-            return std::pair<const char*, const 
char*>("NetStream.Buffer.Empty", "status");
+            info.first = "NetStream.Buffer.Empty";
+            info.second = "status";
+            return;
 
         case bufferFull:
-            return std::pair<const char*, const 
char*>("NetStream.Buffer.Full", "status");
+            info.first = "NetStream.Buffer.Full";
+            info.second = "status";
+            return;
 
         case bufferFlush:
-            return std::pair<const char*, const 
char*>("NetStream.Buffer.Flush", "status");
+            info.first = "NetStream.Buffer.Flush";
+            info.second = "status";
+            return;
 
         case playStart:
-            return std::pair<const char*, const char*>("NetStream.Play.Start", 
"status");
+            info.first = "NetStream.Play.Start";
+            info.second = "status";
+            return;
 
         case playStop:
-            return std::pair<const char*, const char*>("NetStream.Play.Stop", 
"status");
+            info.first = "NetStream.Play.Stop";
+            info.second = "status";
+            return;
 
         case seekNotify:
-            return std::pair<const char*, const 
char*>("NetStream.Seek.Notify", "status");
+            info.first = "NetStream.Seek.Notify";
+            info.second = "status";
+            return;
 
         case streamNotFound:
-            return std::pair<const char*, const 
char*>("NetStream.Play.StreamNotFound", "error");
+            info.first = "NetStream.Play.StreamNotFound";
+            info.second = "error";
+            return;
 
         case invalidTime:
-            return std::pair<const char*, const 
char*>("NetStream.Seek.InvalidTime", "error");
-
+            info.first = "NetStream.Seek.InvalidTime";
+            info.second = "error";
+            return;
         default:
-            return std::pair<const char*, const char*>("","");
+            return;
     }
 }
 
-boost::intrusive_ptr<as_object>
+as_object* 
 NetStream_as::getStatusObject(StatusCode code)
 {
     // code, level
-    std::pair<const char*, const char*> info = getStatusCodeInfo(code);
-
-    boost::intrusive_ptr<as_object> o = new as_object(getObjectInterface());
-    o->init_member("code",  info.first,  0); // enumerable, deletable
-    o->init_member("level", info.second, 0); // enumerable, deletable
+    NetStreamStatus info;
+    getStatusCodeInfo(code, info);
+
+    // Enumerable and deletable.
+    const int flags = 0;
+
+    as_object* o = new as_object(getObjectInterface());
+    o->init_member("code",  info.first, flags);
+    o->init_member("level", info.second, flags);
 
     return o;
 }

=== modified file 'libcore/asobj/NetStream_as.h'
--- a/libcore/asobj/NetStream_as.h      2008-11-27 10:20:02 +0000
+++ b/libcore/asobj/NetStream_as.h      2008-12-03 11:25:09 +0000
@@ -169,7 +169,8 @@
 /// This class is responsible for handlign external
 /// media files. Provides interfaces for playback control.
 ///
-class NetStream_as : public as_object {
+class NetStream_as : public as_object
+{
 
 protected:
     
@@ -203,7 +204,6 @@
         /// NetStream.Seek.InvalidTime (level: error)
         invalidTime
     };
-    
 
     boost::intrusive_ptr<NetConnection> _netCon;
 
@@ -432,10 +432,8 @@
     ///
     /// It might be invoked by a separate thread (neither main, nor decoder 
thread).
     ///
-    static unsigned int audio_streamer(void *udata, boost::int16_t* samples, 
unsigned int nSamples, bool& eof);
-
-
-
+    static unsigned int audio_streamer(void *udata, boost::int16_t* samples,
+            unsigned int nSamples, bool& eof);
 
 
 private:
@@ -596,7 +594,7 @@
     ///
     void clearStatusQueue();
 
-    // TODO: change to a container with fast pop_front()
+    // Queue of status notifications.
     typedef std::deque<StatusCode> StatusQueue;
 
     /// List of status messages to be processed
@@ -608,16 +606,17 @@
     /// Last status code (to avoid consecutively notifying the same event)
     StatusCode _lastStatus;
 
+    typedef std::pair<std::string, std::string> NetStreamStatus;
+
     /// Get 'status' (first) and 'level' (second) strings for given status code
     //
-    /// The two members of the pair are ensured to be not-NULL
     /// Any invalid code, out of bound or explicitly invalid (invalidCode) 
-    /// returns two empty C strings.
+    /// returns two empty strings.
     ///
-    std::pair<const char*, const char*> getStatusCodeInfo(StatusCode code);
+    void getStatusCodeInfo(StatusCode code, NetStreamStatus& info);
 
     /// Return a newly allocated information object for the given status
-    boost::intrusive_ptr<as_object> getStatusObject(StatusCode code);
+    as_object* getStatusObject(StatusCode code);
 
     /// hack for using a Timer to drive ::advance calls
     static as_value advanceWrapper(const fn_call& fn);

=== modified file 'testsuite/actionscript.all/NetConnection.as'
--- a/testsuite/actionscript.all/NetConnection.as       2008-03-11 19:31:46 
+0000
+++ b/testsuite/actionscript.all/NetConnection.as       2008-12-03 11:25:33 
+0000
@@ -41,22 +41,107 @@
 check_equals(typeof(tmp), 'object');
 check_equals(tmp.__proto__, NetConnection.prototype);
 check(tmp instanceof NetConnection);
-xcheck_equals(typeof(tmp.isConnected), 'boolean');
-xcheck_equals(tmp.isConnected, false);
-// TODO: add tests for all properties
+check_equals(typeof(tmp.isConnected), 'boolean');
+check_equals(tmp.isConnected, false);
+
+tmp.isConnected = true;
+check_equals(tmp.isConnected, false);
+
+tmp.isConnected = 56;
+check_equals(tmp.isConnected, false);
 
 // test the NetConnection::connect method
 tmp.connect();
 if ( ! 
tmp.connect("rtmp://www.mediacollege.com/flash/media-player/testclip-4sec.flv") 
)
 {
        // FIXME: this would fail in the reference player too...
-       xfail("NetConnection::connect() didn't initialized correctly");
+       xfail("NetConnection::connect() didn't initialize correctly");
 }
 else
 {
        pass("NetConnection::connect() initialized correctly");
 }
 
-check_totals(10);
+tmp.onStatus = function(info) {
+    result = info.code;
+    level = info.level;
+};
+
+result = "";
+level = "";
+
+ret = tmp.connect();
+check_equals(ret, undefined);
+check_equals(tmp.isConnected, false);
+check_equals(result, "");
+check_equals(level, "");
+
+
+ret = tmp.connect(null, "another argument");
+check_equals(ret, true);
+check_equals(tmp.isConnected, true);
+check_equals(result, "NetConnection.Connect.Success");
+check_equals(level, "status");
+
+ret = tmp.connect(1);
+check_equals(ret, false);
+check_equals(tmp.isConnected, false);
+check_equals(result, "NetConnection.Connect.Failed");
+check_equals(level, "error");
+
+ret = tmp.connect("string");
+check_equals(ret, false);
+check_equals(tmp.isConnected, false);
+check_equals(result, "NetConnection.Connect.Failed");
+check_equals(level, "error");
+
+ret = tmp.connect(undefined);
+
+#if OUTPUT_VERSION > 6
+check_equals(ret, true);
+check_equals(tmp.isConnected, true);
+check_equals(result, "NetConnection.Connect.Success");
+check_equals(level, "status");
+#else
+check_equals(ret, false);
+check_equals(tmp.isConnected, false);
+check_equals(result, "NetConnection.Connect.Failed");
+check_equals(level, "error");
+#endif
+
+ret = tmp.connect(null);
+check_equals(ret, true);
+check_equals(tmp.isConnected, true);
+check_equals(level, "status");
+
+ret = tmp.connect("http://someserver";);
+check_equals(ret, false);
+check_equals(tmp.isConnected, false);
+check_equals(result, "NetConnection.Connect.Failed");
+check_equals(level, "error");
+
+// Check onStatus object.
+
+nc = new NetConnection;
+nc.onStatus = function(info) {
+    infoObj = info;
+};
+
+nc.connect(6);
+nc.onStatus = undefined;
+check_equals(infoObj.code, "NetConnection.Connect.Failed");
+
+// It is a full object
+check(infoObj instanceof Object);
+check_equals(infoObj.toString(), "[object Object]");
+
+// Check whether the original object is modified on a new connect attempt.
+nc.connect(null);
+check_equals(infoObj.code, "NetConnection.Connect.Failed");
+
+
+check_totals(43);
+
+
 
 #endif // OUTPUT_VERSION >= 7

=== modified file 'testsuite/misc-ming.all/NetStream-SquareTest.c'
--- a/testsuite/misc-ming.all/NetStream-SquareTest.c    2008-11-12 09:47:02 
+0000
+++ b/testsuite/misc-ming.all/NetStream-SquareTest.c    2008-12-03 11:25:33 
+0000
@@ -86,7 +86,7 @@
        "nc=new NetConnection();"
        "check(!nc.isConnected, 'newly created NetConnection is not 
connected');"
        "nc.connect(null);"
-       "xcheck(nc.isConnected, 'NetConnection is connected after 
.connect(null)');"
+       "check(nc.isConnected, 'NetConnection is connected after 
.connect(null)');"
        "check(!NetStream.prototype.hasOwnProperty('currentFPS'));" // version 
7 here
        "xcheck(!NetStream.prototype.hasOwnProperty('currentFps'));"
        "stream = new NetStream();"

=== modified file 'testsuite/swfdec/PASSING'
--- a/testsuite/swfdec/PASSING  2008-12-02 11:49:24 +0000
+++ b/testsuite/swfdec/PASSING  2008-12-03 11:25:33 +0000
@@ -623,6 +623,7 @@
 native-run-6.swf:fe6847111ffbbb1822c02af05448351b
 native-run-7.swf:597e61a37f11873f92e5f8147a186b92
 native-run-8.swf:4146625b4f8fef9926a324e8de306ead
+netconnection.swf:691d140eb395ca4094119c24ee9028d5
 netstream-fscommand-6.swf:7151ee0808f01d502171c4813d38d3fa
 netstream-fscommand-7.swf:ec9312835ba2e8a7347252bcaef7db0b
 netstream-fscommand-8.swf:38f05e4f8d3a4e2faae0ef9e18e7754b


reply via email to

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