gnash-commit
[Top][All Lists]
Advanced

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

[Gnash-commit] /srv/bzr/gnash/rtmp r10026: big merge from experimental b


From: rob
Subject: [Gnash-commit] /srv/bzr/gnash/rtmp r10026: big merge from experimental branch, rewritten RTMT support for Gnash, with
Date: Thu, 19 Mar 2009 18:00:54 -0600
User-agent: Bazaar (1.5)

------------------------------------------------------------
revno: 10026
committer: address@hidden
branch nick: rtmp
timestamp: Thu 2009-03-19 18:00:54 -0600
message:
  big merge from experimental branch, rewritten RTMT support for Gnash, with
  RTMP coming next. Cygnal handles Red5 'echo' tests for RTMPT and RTMP as
  well. Also new and greatly improved test case for 'echo' types of tests,
  ie... bouncing packets off a server and decoding the result.
  
  your mileage may vary...
modified:
  cygnal/http_server.cpp
  cygnal/http_server.h
  libamf/amf.cpp
  libamf/amf_msg.cpp
  libbase/GC.h
  libcore/as_value.cpp
  libcore/as_value.h
  libcore/asobj/NetConnection_as.cpp
  libcore/asobj/NetConnection_as.h
  libcore/asobj/NetStream_as.cpp
  libnet/http.cpp
  libnet/http.h
  libnet/network.cpp
  libnet/network.h
  libnet/rtmp.cpp
  libnet/rtmp.h
  libnet/rtmp_client.cpp
  libnet/rtmp_client.h
  testsuite/libnet.all/test_http.cpp
  testsuite/misc-ming.all/red5test.as
  utilities/processor.cpp
    ------------------------------------------------------------
    revno: 9483.276.1
    committer: address@hidden
    branch nick: remoting
    timestamp: Wed 2009-03-11 21:46:26 -0600
    message:
      stripped out remoting stuff, sreaming video still works.
    modified:
      libcore/asobj/NetConnection_as.cpp
      libcore/asobj/NetConnection_as.h
    ------------------------------------------------------------
    revno: 9483.276.2
    committer: address@hidden
    branch nick: remoting
    timestamp: Wed 2009-03-11 21:49:13 -0600
    message:
      stripped out remoting stuff, sreaming video still works.
    modified:
      libbase/GC.h
      libcore/as_value.cpp
      libcore/as_value.h
      libcore/asobj/NetStream_as.cpp
      libnet/http.cpp
      libnet/http.h
      libnet/network.h
      utilities/processor.cpp
    ------------------------------------------------------------
    revno: 9483.276.3
    committer: address@hidden
    branch nick: remoting
    timestamp: Thu 2009-03-12 18:44:41 -0600
    message:
      merge in from branch, partial RTMP support, using libnet HTTP support for 
remoting.
    modified:
      libcore/asobj/NetConnection_as.cpp
      libcore/asobj/NetConnection_as.h
    ------------------------------------------------------------
    revno: 9483.276.4
    committer: address@hidden
    branch nick: remoting
    timestamp: Thu 2009-03-12 18:45:21 -0600
    message:
      store the path from the URL as well.
    modified:
      libnet/rtmp_client.h
    ------------------------------------------------------------
    revno: 9483.276.5
    committer: address@hidden
    branch nick: remoting
    timestamp: Thu 2009-03-12 19:44:38 -0600
    message:
      toggle a flag when connected.
    modified:
      libnet/rtmp_client.cpp
    ------------------------------------------------------------
    revno: 9483.276.6
    committer: address@hidden
    branch nick: remoting
    timestamp: Thu 2009-03-12 20:06:31 -0600
    message:
      pass the right Network connection to the thread handler.
    modified:
      libcore/asobj/NetConnection_as.cpp
      libcore/asobj/NetConnection_as.h
    ------------------------------------------------------------
    revno: 9483.276.7
    committer: address@hidden
    branch nick: remoting
    timestamp: Fri 2009-03-13 18:32:20 -0600
    message:
      don't encode strict arrays as sparse arrays.
    modified:
      libamf/amf.cpp
    ------------------------------------------------------------
    revno: 9483.276.8
    committer: address@hidden
    branch nick: remoting
    timestamp: Fri 2009-03-13 18:33:42 -0600
    message:
      add versions of sendMsg that use the priate setting for the file 
descriptor.
    modified:
      libnet/rtmp.cpp
      libnet/rtmp.h
    ------------------------------------------------------------
    revno: 9483.276.9
    committer: address@hidden
    branch nick: remoting
    timestamp: Fri 2009-03-13 18:35:23 -0600
    message:
      greatly expanded version of the test case.
    modified:
      testsuite/misc-ming.all/red5test.as
    ------------------------------------------------------------
    revno: 9483.276.10
    committer: address@hidden
    branch nick: remoting
    timestamp: Fri 2009-03-13 18:36:09 -0600
    message:
      also run single threaded, since the VM isn't thread safe.
    modified:
      libcore/asobj/NetConnection_as.cpp
      libcore/asobj/NetConnection_as.h
=== modified file 'cygnal/http_server.cpp'
--- a/cygnal/http_server.cpp    2009-03-16 23:34:13 +0000
+++ b/cygnal/http_server.cpp    2009-03-20 00:00:54 +0000
@@ -658,6 +658,76 @@
 }
 #endif
 
+#if 0
+/// These methods extract data from an RTMPT message. RTMP is an
+/// extension to HTTP that adds commands to manipulate the
+/// connection's persistance.
+//
+/// The URL to be opened has the following form:
+/// http://server/<comand>/[<client>/]<index>
+/// <command>
+///    denotes the RTMPT request type, "OPEN", "SEND", "IDLE", "CLOSE")
+/// <client>
+///    specifies the id of the client that performs the requests
+///    (only sent for established sessions)
+/// <index>
+///    is a consecutive number that seems to be used to detect missing packages
+HTTP::rtmpt_cmd_e
+HTTP::extractRTMPT(boost::uint8_t *data)
+{
+    GNASH_REPORT_FUNCTION;
+
+    string body = reinterpret_cast<const char *>(data);
+    string tmp, cid, indx;
+    HTTP::rtmpt_cmd_e cmd;
+
+    // force the case to make comparisons easier
+    std::transform(body.begin(), body.end(), body.begin(), 
+               (int(*)(int)) toupper);
+    string::size_type start, end;
+
+    // Extract the command first
+    start = body.find("OPEN", 0);
+    if (start != string::npos) {
+        cmd = HTTP::OPEN;
+    }
+    start = body.find("SEND", 0);
+    if (start != string::npos) {
+        cmd = HTTP::SEND;
+    }
+    start = body.find("IDLE", 0);
+    if (start != string::npos) {
+        cmd = HTTP::IDLE;
+    }
+    start = body.find("CLOSE", 0);
+    if (start != string::npos) {
+        cmd = HTTP::CLOSE;
+    }
+
+    // Extract the optional client id
+    start = body.find("/", start+1);
+    if (start != string::npos) {
+       end = body.find("/", start+1);
+       if (end != string::npos) {
+           indx = body.substr(end, body.size());
+           cid = body.substr(start, (end-start));
+       } else {
+           cid = body.substr(start, body.size());
+       }
+    }
+
+    _index = strtol(indx.c_str(), NULL, 0);
+    _clientid = strtol(cid.c_str(), NULL, 0);
+    end =  body.find("\r\n", start);
+//     if (end != string::npos) {
+//         cmd = HTTP::CLOSE;
+//     }
+
+    return cmd;
+}
+
+#endif
+
 /// These methods extract data from an RTMPT message. RTMP is an
 /// extension to HTTPServer that adds commands to manipulate the
 /// connection's persistance.
@@ -725,6 +795,7 @@
     return cmd;
 }
 
+#if 0
 HTTPServer::http_method_e
 HTTPServer::extractCommand(boost::uint8_t *data)
 {
@@ -879,6 +950,7 @@
     
     return buf.reference() + end + 4;
 }
+#endif
 
 void
 HTTPServer::dump()

=== modified file 'cygnal/http_server.h'
--- a/cygnal/http_server.h      2009-03-16 23:34:13 +0000
+++ b/cygnal/http_server.h      2009-03-20 00:00:54 +0000
@@ -71,17 +71,19 @@
     rtmpt_cmd_e extractRTMPT(amf::Buffer &data)
        { return extractRTMPT(data.reference()); };    
 
+#if 0
     // Examine the beginning of the data for an HTTP request command
     // like GET or POST, etc...
     http_method_e extractCommand(boost::uint8_t *data);
     http_method_e extractCommand(amf::Buffer &data)
        { return extractCommand(data.reference()); };    
-    
+
     // process all the header fields in the Buffer, storing them internally
     // in _fields. The address returned is the address where the Content data
     // starts, and is "Content-Length" bytes long, of "Content-Type" data.
     boost::uint8_t *processHeaderFields(amf::Buffer &buf);
-
+#endif
+    
 #if 0
     // Parse an Echo Request message coming from the Red5 echo_test.
     std::vector<boost::shared_ptr<amf::Element > > 
parseEchoRequest(gnash::amf::Buffer &buf) { return 
parseEchoRequest(buf.reference(), buf.size()); };

=== modified file 'libamf/amf.cpp'
--- a/libamf/amf.cpp    2009-02-25 22:33:03 +0000
+++ b/libamf/amf.cpp    2009-03-14 00:32:20 +0000
@@ -561,6 +561,9 @@
        for (ait = props.begin(); ait != props.end(); ait++) {
            counter++;
            boost::shared_ptr<amf::Element> el = (*(ait));
+#if 0
+           // FIXME: Red5's echo tests like to turn strict array's into ecma
+           // arrays, but we shouldn't do that in the core.
            // If we see an undefined data item, then switch to an ECMA
            // array which is more compact. At least this is what Red5 does.
            if (el->getType() == Element::UNDEFINED_AMF0) {
@@ -578,10 +581,11 @@
                }
                continue;
            } else {
+#endif
                if (sparse) {
                    sparse = false;
-            std::ostringstream os;
-            os << counter;
+                   std::ostringstream os;
+                   os << counter;
                    amf::Element elnum(os.str().c_str(), el->to_number());
                    *buf += AMF::encodeElement(elnum);
                    double nodes = items;
@@ -597,7 +601,7 @@
                        break;
                    }
                }
-           }
+//         }
 //         el->dump();
        }
     }

=== modified file 'libamf/amf_msg.cpp'
--- a/libamf/amf_msg.cpp        2009-03-03 22:20:40 +0000
+++ b/libamf/amf_msg.cpp        2009-03-20 00:00:54 +0000
@@ -66,8 +66,8 @@
 {
 //    GNASH_REPORT_FUNCTION;
     // The size of the buffer are the two strings, their lenght fields, and 
the integer.
-    size_t size = head->target.size() + head->response.size() + 
sizeof(boost::uint32_t)
-        + (sizeof(boost::uint16_t) * 2);
+//     size_t size = head->target.size() + head->response.size() + 
sizeof(boost::uint32_t)
+//         + (sizeof(boost::uint16_t) * 2);
     boost::shared_ptr<amf::Buffer> buf (new 
amf::Buffer(sizeof(AMF_msg::message_header_t)));
 
     // Encode the target URI, which usually looks something like ."getway"
@@ -95,7 +95,7 @@
 }
 
 boost::shared_ptr<AMF_msg::context_header_t>
-AMF_msg::parseContextHeader(boost::uint8_t *data, size_t size)
+AMF_msg::parseContextHeader(boost::uint8_t *data, size_t /* size */)
 {
 //    GNASH_REPORT_FUNCTION;
     boost::shared_ptr<AMF_msg::context_header_t> msg (new 
AMF_msg::context_header_t);
@@ -135,7 +135,7 @@
     tmpptr += sizeof(boost::uint16_t);
     string str1(reinterpret_cast<const char *>(tmpptr), length);
     msg->target = str1;
-    if ((tmpptr - data) > size) {
+    if ((tmpptr - data) > static_cast<int>(size)) {
         boost::format msg("Trying to read past the end of data! Wants %1% 
bytes, given %2% bytes");
         msg % length % size;
         throw GnashException(msg.str());
@@ -222,8 +222,8 @@
 }
 
 boost::shared_ptr<amf::Buffer>
-AMF_msg::encodeAMFPacket(const std::string &target,
-                         const std::string &response, size_t size)
+AMF_msg::encodeAMFPacket(const std::string & /* target */,
+                         const std::string & /*response */, size_t /* size */)
 {
 }
 

=== modified file 'libbase/GC.h'
--- a/libbase/GC.h      2009-02-25 22:33:03 +0000
+++ b/libbase/GC.h      2009-03-12 03:49:13 +0000
@@ -28,9 +28,7 @@
 #include <string>
 #include <algorithm> //for std::find
 
-#ifndef NDEBUG
-# include <boost/thread.hpp>
-#endif
+#include <boost/thread.hpp>
 
 #include "dsodefs.h"
 
@@ -227,8 +225,10 @@
        ///
        void addCollectable(const GcResource* item)
        {
+         boost::thread self;
+         // Don't add an object from a thread
+         if (self == mainThread) {       
 #ifndef NDEBUG
-               boost::thread self;
                assert(self == mainThread);
                assert(item);
                assert(! item->isReachable());
@@ -237,6 +237,7 @@
 #endif
 
                _resList.push_back(item);
+         }
 #if GNASH_GC_DEBUG > 1
                log_debug(_("GC %p: collectable %p added, num collectables: 
%d"), (void*)this, (void*)item, _resList.size());
 #endif
@@ -301,12 +302,10 @@
 
        static GC* _singleton;
 
-#ifndef NDEBUG
        /// The thread that initialized the GC is 
        /// the only one allowed to run the collector
        /// and to register collectable objects
        boost::thread mainThread;
-#endif
 
        /// Number of resources in collectable list at end of last
        /// collect() call.

=== modified file 'libcore/as_value.cpp'
--- a/libcore/as_value.cpp      2009-01-22 20:10:39 +0000
+++ b/libcore/as_value.cpp      2009-03-12 03:49:13 +0000
@@ -17,6 +17,16 @@
 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 //
 
+#include <boost/shared_ptr.hpp>
+#include <cmath> // std::fmod
+#include <boost/algorithm/string/case_conv.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/format.hpp>
+#include <locale>
+#include <sstream>
+#include <iomanip>
+#include <string>
+
 #include "smart_ptr.h" // GNASH_USE_GC
 #include "as_value.h"
 #include "as_object.h"
@@ -41,29 +51,20 @@
 #include "SimpleBuffer.h"
 #include "StringPredicates.h"
 
-#include <cmath> // std::fmod
-#include <boost/algorithm/string/case_conv.hpp>
-#include <boost/lexical_cast.hpp>
-#include <boost/format.hpp>
-#include <locale>
-#include <sstream>
-#include <iomanip>
-#include <string>
-
 // Define the macro below to make abstract equality operator verbose
 //#define GNASH_DEBUG_EQUALITY 1
 
 // Define the macro below to make to_primitive verbose
-//#define GNASH_DEBUG_CONVERSION_TO_PRIMITIVE 1
+// #define GNASH_DEBUG_CONVERSION_TO_PRIMITIVE 1
 
 // Define this macro to make soft references activity verbose
-//#define GNASH_DEBUG_SOFT_REFERENCES
+#define GNASH_DEBUG_SOFT_REFERENCES
 
 // Define this macro to make AMF parsing verbose
-//#define GNASH_DEBUG_AMF_DESERIALIZE
+#define GNASH_DEBUG_AMF_DESERIALIZE 1
 
 // Define this macto to make AMF writing verbose
-//#define GNASH_DEBUG_AMF_SERIALIZE
+#define GNASH_DEBUG_AMF_SERIALIZE 1
 
 namespace gnash {
 
@@ -195,14 +196,22 @@
                 str = val.to_string();
             }
             el.reset(new amf::Element(name, str));
-        }
-
-        else if (val.is_bool()) {
+        } else if (val.is_bool()) {
             bool flag = val.to_bool();
             el.reset(new amf::Element(name, flag));
-        }
-
-        else if (val.is_number()) { 
+        } else if (val.is_object()) {
+//            el.reset(new amf::Element(name, flag));
+        } else if (val.is_null()) {
+           boost::shared_ptr<amf::Element> tmpel(new amf::Element);
+           tmpel->setName(name);
+           tmpel->makeNull();
+            el = tmpel;
+        } else if (val.is_undefined()) {
+           boost::shared_ptr<amf::Element> tmpel(new amf::Element);
+           tmpel->setName(name);
+           tmpel->makeUndefined();
+            el = tmpel;
+        } else if (val.is_number()) { 
             double dub;
             if (val.is_undefined()) {
                 dub = 0.0;
@@ -281,13 +290,16 @@
         boost::uint16_t namelen = name.size();
         _buf.appendNetworkShort(namelen);
         _buf.append(name.c_str(), namelen);
+#if 0
         if ( ! val.writeAMF0(_buf, _offsetTable, _vm, _allowStrict) )
         {
             log_error("Problems serializing an object's member");
             _error=true;
         }
+#else
+    log_error("writeAMF0 disabled for now");
+#endif
     }
-
 private:
 
     bool _allowStrict;
@@ -846,24 +858,30 @@
     }
 }
 
-std::auto_ptr<amf::Element>
+boost::shared_ptr<amf::Element>
 as_value::to_element() const
 {
     VM& vm = VM::get();
     //int swfVersion = vm.getSWFVersion();
-    std::auto_ptr<amf::Element> el ( new amf::Element );
+    boost::shared_ptr<amf::Element> el ( new amf::Element );
     boost::intrusive_ptr<as_object> ptr = to_object();
 
     switch (m_type) {
+      case UNDEFINED:
+         el->makeUndefined();
+         break;
+      case NULLTYPE:
+         el->makeNull();
+         break;
+      case BOOLEAN:
+         el->makeBoolean(getBool());
+         break;
       case  STRING:
          el->makeString(getStr());
          break;
       case NUMBER:
          el->makeNumber(getNum());
          break;
-      case BOOLEAN:
-         el->makeBoolean(getBool());
-         break;
       case OBJECT:
       {
          el->makeObject();
@@ -1836,6 +1854,7 @@
        :
        m_type(UNDEFINED)
 {
+    el.dump();
     VM& vm = VM::get();
     string_table& st = vm.getStringTable();
     
@@ -1907,12 +1926,12 @@
           as_object* obj = new as_object(getObjectInterface());
           if (el.propertySize()) {
               for (size_t i=0; i < el.propertySize(); i++) {
-              const boost::shared_ptr<amf::Element> prop = el.getProperty(i);
-              if (prop == 0) {
-                  break;
-              } else {
-                  obj->set_member(st.find(prop->getName()), as_value(*prop));
-              }
+                 const boost::shared_ptr<amf::Element> prop = 
el.getProperty(i);
+                 if (prop == 0) {
+                     break;
+                 } else {
+                     obj->set_member(st.find(prop->getName()), 
as_value(*prop));
+                 }
               }
           }
           set_as_object(obj);

=== modified file 'libcore/as_value.h'
--- a/libcore/as_value.h        2009-02-10 15:38:43 +0000
+++ b/libcore/as_value.h        2009-03-20 00:00:54 +0000
@@ -36,6 +36,7 @@
 #include <boost/type_traits/is_floating_point.hpp>
 #include <boost/utility/enable_if.hpp>
 #include <boost/static_assert.hpp>
+#include <boost/shared_ptr.hpp>
 
 #include "utility.h" // UNUSED
 #include "string_table.h"
@@ -370,7 +371,7 @@
        double  to_number() const;
 
        /// Get an AMF element representation for this value
-       std::auto_ptr<amf::Element> to_element() const;
+        boost::shared_ptr<amf::Element> to_element() const;
 
        /// Conversion to 32bit integer
        //

=== modified file 'libcore/asobj/NetConnection_as.cpp'
--- a/libcore/asobj/NetConnection_as.cpp        2009-03-13 22:46:37 +0000
+++ b/libcore/asobj/NetConnection_as.cpp        2009-03-20 00:00:54 +0000
@@ -25,13 +25,10 @@
 #include <iostream>
 #include <string>
 #include <boost/scoped_ptr.hpp>
-
-// FIXME: Get rid of this crap.
-#if defined(HAVE_WINSOCK_H) && !defined(__OS2__)
-# include <winsock.h>
-#else
-#include <arpa/inet.h> // for htons
-#endif
+#include <boost/thread/thread.hpp>
+#include <boost/bind.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/condition.hpp>
 
 #include "NetConnection_as.h"
 #include "log.h"
@@ -39,22 +36,34 @@
 #include "builtin_function.h"
 #include "movie_root.h"
 #include "Object.h" // for getObjectInterface
+
 #include "StreamProvider.h"
 #include "URLAccessManager.h"
 #include "URL.h"
+
+// for NetConnection_as.call()
 #include "VM.h"
 #include "amf.h"
+#include "http.h"
 #include "SimpleBuffer.h"
-#include "timers.h"
+#include "amf_msg.h"
+#include "buffer.h"
 #include "namedStrings.h"
-#include "GnashAlgorithm.h"
-
-//#define GNASH_DEBUG_REMOTING
+#include "element.h"
+#include "network.h"
+#include "rtmp.h"
+#include "rtmp_client.h"
+
+using namespace std;
+
+#define GNASH_DEBUG_REMOTING 1
 
 // Forward declarations.
 
 namespace gnash {
 
+boost::mutex _nc_mutex;
+
 namespace {
     void attachProperties(as_object& o);
     void attachNetConnectionInterface(as_object& o);
@@ -69,624 +78,13 @@
 
 }
 
-namespace {
-
-    boost::uint16_t readNetworkShort(const boost::uint8_t* buf);
-    boost::uint32_t readNetworkLong(const boost::uint8_t* buf);
-
-}
-
-//---- ConnectionHandler 
--------------------------------------------------------------
-
-/// Abstract connection handler class
-//
-/// This class abstract operations on network connections,
-/// specifically RPC and streams fetching.
-///
-class ConnectionHandler
-{
-public:
-
-    /// @param methodName
-    ///     A string identifying the remote procedure to call
-    ///
-    /// @param responseHandler
-    ///     Object to invoke response methods on.
-    ///
-    /// @param args
-    ///     A vector of arguments
-    ///
-    /// @param firstArg
-    ///     Index of first argument in the args vector
-    ///
-    ///
-    /// @return true if the call is queued, false otherwise
-    ///
-    virtual void call(as_object* asCallback, const std::string& methodName,
-            const std::vector<as_value>& args, size_t firstArg)=0;
-
-    /// Get an stream by name
-    //
-    /// @param name
-    ///     Stream identifier
-    ///
-    virtual std::auto_ptr<IOChannel> getStream(const std::string& name);
-
-    /// Process pending traffic, out or in bound
-    //
-    /// Handles all networking for NetConnection::call() and dispatches
-    /// callbacks when needed. 
-    ///
-    /// Return true if wants to be advanced again, false otherwise.
-    ///
-    virtual bool advance()=0;
-
-    /// Return true if the connection has pending calls 
-    //
-    /// This will be used on NetConnection.close(): if current
-    /// connection has pending calls to process it will be
-    /// queued and only really dropped when advance returns
-    /// false
-    ///
-    virtual bool hasPendingCalls() const=0;
-
-    /// Mark reachable resources, if any. 
-    virtual void setReachable() const
-    {
-        // NOTE: usually this function gets
-        //       called *by* the _nc's setReachable
-        //       but we do this just to be safe
-        //       in case the _nc object is deleted
-        //       and doesn't properly drops us
-        //
-        _nc.setReachable();
-    }
-
-    virtual ~ConnectionHandler() {}
-
-protected:
-
-    /// Construct a connection handler bound to the given NetConnection object
-    //
-    /// The binding is used to notify statuses and errors
-    ///
-    ConnectionHandler(NetConnection_as& nc)
-        :
-        _nc(nc)
-    {}
-
-    // Object handling connection status messages
-    NetConnection_as& _nc;
-};
-
-std::auto_ptr<IOChannel>
-ConnectionHandler::getStream(const std::string&)
-{
-    log_unimpl("%s doesn't support fetching streams", typeName(*this));
-    return std::auto_ptr<IOChannel>(0);
-}
-
-//---- HTTPRemotingHandler (HTTPConnectionHandler) 
-----------------------------------------------
-
-/// Queue of remoting calls 
-//
-/// This class in made to handle data and do defered processing for
-/// NetConnection::call()
-///
-/// Usage:
-///
-/// pass a URL to the constructor
-///
-/// call enqueue with a SimpleBuffer containing an encoded AMF call. If action
-/// 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
-///
-class HTTPRemotingHandler : public ConnectionHandler {
-
-public:
-
-    /// Create an handler for HTTP remoting
-    //
-    /// @param nc
-    ///     The NetConnection AS object to send status/error events to
-    ///
-    /// @param url
-    ///     URL to post calls to
-    ///
-    HTTPRemotingHandler(NetConnection_as& nc, const URL& url);
-
-    // See dox in ConnectionHandler
-    virtual bool hasPendingCalls() const
-    {
-        return _connection || queued_count;
-    }
-
-    // See dox in ConnectionHandler
-    virtual bool advance();
-
-    // See dox in ConnectionHandler
-    virtual void setReachable() const
-    {
-        for (CallbacksMap::const_iterator i=callbacks.begin(),
-                e=callbacks.end(); i!=e; ++i)
-        {
-            i->second->setReachable();
-        }
-        ConnectionHandler::setReachable();
-    }
-
-    // See dox in NetworkHandler class
-    virtual void call(as_object* asCallback, const std::string& methodName,
-            const std::vector<as_value>& args, size_t firstArg);
-
-private:
-
-    static const int NCCALLREPLYCHUNK=1024*200;
-
-    typedef std::map<std::string, as_object* > CallbacksMap;
-    CallbacksMap callbacks;
-
-    SimpleBuffer _postdata;
-    URL _url;
-    boost::scoped_ptr<IOChannel> _connection;
-    SimpleBuffer reply;
-    int reply_start;
-    int queued_count;
-    unsigned int _numCalls; // === queued_count ?
-
-    // Quick hack to send Content-Type: application/x-amf
-    // TODO: check if we should take headers on a per-call basis
-    //       due to NetConnection.addHeader.
-    //
-    NetworkAdapter::RequestHeaders _headers;
-
-    void push_amf(const SimpleBuffer &amf) 
-    {
-        //GNASH_REPORT_FUNCTION;
-
-        _postdata.append(amf.data(), amf.size());
-        queued_count++;
-    }
-
-    void push_callback(const std::string& id, as_object* callback)
-    {
-        callbacks[id] = callback;
-    }
-
-    as_object* pop_callback(const std::string& id)
-    {
-        CallbacksMap::iterator it = callbacks.find(id);
-        if (it != callbacks.end()) {
-            as_object* callback = it->second;
-            callbacks.erase(it);
-            return callback;
-        }
-        else return 0;
-    }
-
-    void enqueue(const SimpleBuffer &amf, const std::string& identifier,
-                 as_object* callback)
-    {
-        push_amf(amf);
-        push_callback(identifier, callback);
-    };
-
-    void enqueue(const SimpleBuffer &amf)
-    {
-        push_amf(amf);
-    };
-    
-};
-
-HTTPRemotingHandler::HTTPRemotingHandler(NetConnection_as& nc, const URL& url)
-        :
-        ConnectionHandler(nc),
-        _postdata(),
-        _url(url),
-        _connection(0),
-        reply(),
-        reply_start(0),
-        queued_count(0),
-        _numCalls(0) // TODO: replace by queued count ?
-{
-    // leave space for header
-    _postdata.append("\000\000\000\000\000\000", 6);
-    assert(reply.size() == 0);
-
-    _headers["Content-Type"] = "application/x-amf";
-}
-
-bool
-HTTPRemotingHandler::advance()
-{
-
-#ifdef GNASH_DEBUG_REMOTING
-    log_debug("advancing HTTPRemotingHandler");
-#endif
-    if(_connection)
-    {
-
-        VM& vm = _nc.getVM();
-
-#ifdef GNASH_DEBUG_REMOTING
-        log_debug("have connection");
-#endif
-
-        // Fill last chunk before reading in the next
-        size_t toRead = reply.capacity()-reply.size();
-        if ( ! toRead ) toRead = NCCALLREPLYCHUNK;
-
-#ifdef GNASH_DEBUG_REMOTING
-        log_debug("Attempt to read %d bytes", toRead);
-#endif
-
-        //
-        // See if we need to allocate more bytes for the next
-        // read chunk
-        //
-        if ( reply.capacity() < reply.size()+toRead )
-        {
-            // if _connection->size() >= 0, reserve for it, so
-            // if HTTP Content-Length response header is correct
-            // we'll be allocating only once for all.
-            size_t newCapacity = reply.size()+toRead;
-
-#ifdef GNASH_DEBUG_REMOTING
-            log_debug("NetConnection.call: reply buffer capacity (%d) "
-                      "is too small to accept next %d bytes of chunk "
-                      "(current size is %d). Reserving %d bytes.",
-                reply.capacity(), toRead, reply.size(), newCapacity);
-#endif
-
-            reply.reserve(newCapacity);
-
-#ifdef GNASH_DEBUG_REMOTING
-            log_debug(" after reserve, new capacity is %d", reply.capacity());
-#endif
-        }
-
-        int read = _connection->readNonBlocking(reply.data() + reply.size(), 
toRead);
-        if(read > 0) {
-#ifdef GNASH_DEBUG_REMOTING
-            log_debug("read '%1%' bytes: %2%", read, 
-                    hexify(reply.data() + reply.size(), read, false));
-#endif
-            reply.resize(reply.size()+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->bad())
-        {
-            log_debug("connection is in error condition, calling "
-                    "NetConnection.onStatus");
-            reply.resize(0);
-            reply_start = 0;
-            // reset connection before calling the callback
-            _connection.reset();
-
-            // This is just a guess, but is better than sending
-            // 'undefined'
-            _nc.notifyStatus(NetConnection_as::CALL_FAILED);
-        }
-        else if(_connection->eof() )
-        {
-            if ( reply.size() > 8)
-            {
-                std::vector<as_object*> objRefs;
-
-#ifdef GNASH_DEBUG_REMOTING
-                log_debug("hit eof");
-#endif
-                boost::int16_t si;
-                boost::uint16_t li;
-                const boost::uint8_t *b = reply.data() + reply_start;
-                const boost::uint8_t *end = reply.data() + reply.size();
-
-                // 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 < 4) { // shorted valid response is '/1/a'
-                                log_error("NetConnection::call(): reply 
message name too short");
-                                break;
-                            }
-                            if(b + si > end) break;
-
-                            // Reply message is: '/id/methodName'
-
-                            int ns = 1; // next slash position
-                            while (ns<si-1 && *(b+ns) != '/') ++ns;
-                            if ( ns >= si-1 ) {
-                                std::string msg(
-                                        reinterpret_cast<const char*>(b), si);
-                                log_error("NetConnection::call(): invalid "
-                                        "reply message name (%s)", msg);
-                                break;
-                            }
-
-                            std::string id(reinterpret_cast<const char*>(b),
-                                    ns);
-
-                            std::string methodName(
-                                    reinterpret_cast<const char*>(b+ns+1),
-                                    si-ns-1);
-
-                            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) {
-
-                                string_table::key methodKey;
-                                if ( methodName == "onResult" ) {
-                                    methodKey = NSV::PROP_ON_RESULT;
-                                } else if ( methodName == "onStatus" ) {
-                                    methodKey = NSV::PROP_ON_STATUS;
-                                } else {
-                                    // NOTE: the pp is known to actually 
invoke the custom
-                                    //       method, but with 7 undefined 
arguments (?)
-                                    //methodKey = 
_nc.getVM().getStringTable().find(methodName);
-                                    log_error("Unsupported HTTP Remoting 
response callback: '%s' (size %d)", methodName, methodName.size());
-                                    continue;
-                                }
-
-#ifdef GNASH_DEBUG_REMOTING
-                                log_debug("calling onResult callback");
-#endif
-                                // FIXME check if above line can fail and we 
have to react
-                                callback->callMethod(methodKey, 
reply_as_value);
-#ifdef GNASH_DEBUG_REMOTING
-                                log_debug("callback called");
-#endif
-                            } else {
-                                log_error("Unknown HTTP Remoting response 
identifier '%s'", id);
-                            }
-                        }
-                    }
-                }
-            }
-            else
-            {
-                log_error("Response from remoting service < 8 bytes");
-            }
-
-#ifdef GNASH_DEBUG_REMOTING
-            log_debug("deleting connection");
-#endif
-            _connection.reset();
-            reply.resize(0);
-            reply_start = 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;
-
-        // TODO: it might be useful for a Remoting Handler to have a 
-        // StreamProvider member
-        const StreamProvider& sp =
-            _nc.getVM().getRoot().runInfo().streamProvider();
-
-        _connection.reset(sp.getStream(_url, postdata_str, 
_headers).release());
-
-        _postdata.resize(6);
-#ifdef GNASH_DEBUG_REMOTING
-        log_debug("connection created");
-#endif
-    }
-
-    if (_connection == 0) {
-        // nothing more to do
-        return false;
-    }
-
-    return true;
-};
-
-void
-HTTPRemotingHandler::call(as_object* asCallback, const std::string& methodName,
-            const std::vector<as_value>& args, size_t firstArg)
-{
-    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
-    std::ostringstream os;
-    os << "/";
-    // Call number is not used if the callback is undefined
-    if ( asCallback )
-    {
-        os << ++_numCalls; 
-    }
-    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(args.size()-firstArg);
-
-    VM& vm = _nc.getVM();
-
-    for (unsigned int i = firstArg; i < args.size(); ++i)
-    {
-        const as_value& arg = args[i];
-        // STRICT_ARRAY encoding is allowed for remoting
-        if ( ! arg.writeAMF0(*buf, offsetTable, vm, true) )
-        {
-            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
-
-    if (asCallback) {
-#ifdef GNASH_DEBUG_REMOTING
-        log_debug("calling enqueue with callback");
-#endif
-        enqueue(*buf, callNumberString, asCallback);
-    }
-    
-    else {
-#ifdef GNASH_DEBUG_REMOTING
-        log_debug("calling enqueue without callback");
-#endif
-        enqueue(*buf);
-    }
-}
-
 //----- NetConnection_as ----------------------------------------------------
 
 NetConnection_as::NetConnection_as()
     :
     as_object(getNetConnectionInterface()),
-    _queuedConnections(),
-    _currentConnection(0),
     _uri(),
-    _isConnected(false),
-    _advanceTimer(0)
+    _isConnected(false)
 {
     attachProperties(*this);
 }
@@ -698,8 +96,7 @@
     // This is going to be the global NetConnection "class"/"function"
     static boost::intrusive_ptr<builtin_function> cl;
 
-    if ( cl == NULL )
-    {
+    if (cl == NULL) {
         cl=new builtin_function(&netconnection_new,
                 getNetConnectionInterface());
         // replicate all interface to class, to be able to access
@@ -715,21 +112,11 @@
 // here to have HTTPRemotingHandler definition available
 NetConnection_as::~NetConnection_as()
 {
-    deleteAllChecked(_queuedConnections);
 }
 
-
 void
 NetConnection_as::markReachableResources() const
 {
-    if ( _currentConnection.get() ) _currentConnection->setReachable();
-    for (std::list<ConnectionHandler*>::const_iterator
-            i=_queuedConnections.begin(), e=_queuedConnections.end();
-            i!=e; ++i)
-    {
-        (*i)->setReachable();
-    }
-    markAsObjectReachable();
 }
 
 
@@ -761,6 +148,7 @@
 void
 NetConnection_as::notifyStatus(StatusCode code)
 {
+    GNASH_REPORT_FUNCTION;
     std::pair<std::string, std::string> info;
     getStatusCodeInfo(code, info);
 
@@ -826,6 +214,7 @@
 void
 NetConnection_as::connect()
 {
+    GNASH_REPORT_FUNCTION;
     // Close any current connections.
     close();
     _isConnected = true;
@@ -836,12 +225,12 @@
 void
 NetConnection_as::connect(const std::string& uri)
 {
+    GNASH_REPORT_FUNCTION;
     // Close any current connections. (why?) Because that's what happens.
     close();
 
     // TODO: check for other kind of invalidities here...
-    if ( uri.empty() )
-    {
+    if (uri.empty()) {
         _isConnected = false;
         notifyStatus(CONNECT_FAILED);
         return;
@@ -850,45 +239,13 @@
     const movie_root& mr = _vm.getRoot();
     URL url(uri, mr.runInfo().baseURL());
 
-    if ( url.protocol() == "rtmp" )
-    {
-        LOG_ONCE( log_unimpl("NetConnection.connect(%s): RTMP not yet 
supported", url) );
-        notifyStatus(CONNECT_FAILED);
-        return;
-    }
-
-    if ( url.protocol() != "http" )
-    {
-        IF_VERBOSE_ASCODING_ERRORS(
-        log_aserror("NetConnection.connect(%s): invalid connection protocol", 
url);
-        );
-        notifyStatus(CONNECT_FAILED);
-        return;
-    }
-
-    // This is for HTTP remoting
-
+    // This is for remoting
     if (!URLAccessManager::allow(url)) {
         log_security(_("Gnash is not allowed to NetConnection.connect to %s"), 
url);
         notifyStatus(CONNECT_FAILED);
         return;
     }
 
-    _currentConnection.reset(new HTTPRemotingHandler(*this, url));
-
-
-    // FIXME: We should attempt a connection here (this is called when an
-    // argument is passed to NetConnection.connect(url).
-    // Would probably return true on success and set isConnected.
-    //
-    // Under certain circumstances, an an immediate failure notification
-    // happens. These are:
-    // a) sandbox restriction
-    // b) invalid URL? NetConnection.connect(5) fails straight away, but
-    //    could be either because a URL has to be absolute, perhaps including
-    //    a protocol, or because the load is attempted from the filesystem
-    //    and fails immediately.
-    // TODO: modify validateURL for doing this.
     _isConnected = false;
 }
 
@@ -898,22 +255,13 @@
 void
 NetConnection_as::close()
 {
-    bool needSendClosedStatus = _currentConnection.get() || _isConnected;
-
-    /// Queue the current call queue if it has pending calls
-    if ( _currentConnection.get() && _currentConnection->hasPendingCalls() )
-    {
-        _queuedConnections.push_back(_currentConnection.release());
-    }
+    GNASH_REPORT_FUNCTION;
 
     /// TODO: what should actually happen here? Should an attached
     /// NetStream object be interrupted?
     _isConnected = false;
 
-    if ( needSendClosedStatus )
-    {
-        notifyStatus(CONNECT_CLOSED);
-    }
+    notifyStatus(CONNECT_CLOSED);
 }
 
 
@@ -924,23 +272,178 @@
     _uri = uri;
 }
 
+//
 void
 NetConnection_as::call(as_object* asCallback, const std::string& methodName,
         const std::vector<as_value>& args, size_t firstArg)
 {
-    if ( ! _currentConnection.get() )
-    {
-        log_aserror("NetConnection.call: can't call while not connected");
-        return;
-    }
-
-    _currentConnection->call(asCallback, methodName, args, firstArg);
-
-#ifdef GNASH_DEBUG_REMOTING
-    log_debug("called enqueue");
-#endif
-
-    startAdvanceTimer();
+    GNASH_REPORT_FUNCTION;
+
+    URL url(_uri);
+
+    // The values for the connect call were set in ::connect(), but according
+    // to documentation, the connection isn't actually started till the first
+    // ()call(). My guess is back in the days of non-persistant network
+    // connections, each ::call() made it's own connection.
+    if (_isConnected == false) {
+
+       // We're using RTMPT, which is AMF over HTTP
+       short port = strtol(url.port().c_str(), NULL, 0) & 0xffff;
+       if ((url.protocol() == "rtmpt")
+           || (url.protocol() == "http")) {
+           _http_client.reset(new HTTP);
+           _http_client->toggleDebug(true);
+           if (!_http_client->createClient(url.hostname(), port)) {
+               log_error("Can't connect to server %s on port %s",
+                         url.hostname(), url.port());
+               notifyStatus(CONNECT_FAILED);
+               return;
+           } else {
+               log_debug("Connected to server %s on port %s",
+                         url.hostname(), url.port());
+               notifyStatus(CONNECT_SUCCESS);
+               _isConnected = true;
+           }
+       // We're using RTMP, Connect via RTMP
+       } else if (url.protocol() == "rtmp") {
+           _rtmp_client.reset(new RTMPClient);
+           _rtmp_client->toggleDebug(true);
+//         if (!_rtmp_client.createClient(url.hostname(), port)) {
+//             log_error("Can't connect to RTMP server %s", url.hostname());
+//             notifyStatus(CONNECT_FAILED);
+//             return;
+//         }
+           if (!_rtmp_client->handShakeRequest()) {
+               log_error("RTMP handshake request failed");
+               notifyStatus(CONNECT_FAILED);
+               return;
+           }
+           
+           if (!_rtmp_client->clientFinish()) {
+               log_error("RTMP handshake completion failed");
+               notifyStatus(CONNECT_FAILED);
+               return;
+           }
+           string app;         // the application name
+           string path;        // the path to the file on the server
+           string tcUrl;       // the tcUrl field
+           string swfUrl;      // the swfUrl field
+           string filename;    // the filename to play
+           string pageUrl;     // the pageUrl field
+           tcUrl = url.protocol() + "://" + url.hostname();
+           if (!url.querystring().empty()) {
+               tcUrl += "/" + url.querystring();
+           } else {
+               tcUrl += "/" + url.path();
+           }
+           app = url.path();
+           // FIXME: this should be the name of the refering swf file,
+           // although the value appears to be ignored by the server.
+           swfUrl = "mediaplayer.swf";
+           // FIXME: This should be the URL for the referring web page
+           // although the value appears to be ignored by the server.
+           pageUrl = "http://gnashdev.org";;
+
+           // FIXME: replace the "magic numbers" with intelligently
+           // designed ones.
+           boost::shared_ptr<amf::Buffer> buf2 = 
_rtmp_client->encodeConnect(app.c_str(), swfUrl.c_str(), tcUrl.c_str(), 615, 
124, 1, pageUrl.c_str());
+//         size_t total_size = buf2->allocated();
+//         _rtmp_client->sendMsg(0x3, RTMP::HEADER_12, total_size, 
RTMP::INVOKE, RTMPMsg::FROM_CLIENT, *buf2);
+//         RTMPMsg *msg1 = _rtmp_client->recvMsg();
+
+           // the connectino process is complete
+//         if (msg1->getStatus() ==  RTMPMsg::NC_CONNECT_SUCCESS) {
+//             notifyStatus(CONNECT_SUCCESS);
+//         } else {
+//             notifyStatus(CONNECT_FAILED);
+//             return;
+//         }
+       } // end of 'if RTMP'
+#if 0
+       // FIXME: do a GET request for the crossdomain.xml file
+       // in a portable way!
+       log_debug("Requesting crossdomain.xml file...");
+       amf::Buffer &request = _http_client->formatRequest("/crossdomain.xml", 
HTTP::HTTP_GET);
+       _http_client->writeNet(request);
+#endif
+    }
+    boost::shared_ptr<NetConnection_as::thread_params_t> tdata(new 
NetConnection_as::thread_params_t);
+    tdata->callback = asCallback;
+
+    static int numCalls = 0;
+    amf::AMF_msg top;
+    
+    boost::shared_ptr<amf::Element> name(new amf::Element);
+    name->makeString(methodName);
+
+    // make the result
+    std::ostringstream os;
+    os << "/";
+    // Call number is not used if the callback is undefined
+    if ( asCallback ) {
+        os << ++numCalls; 
+    }
+    boost::shared_ptr<amf::Element> response(new amf::Element);
+    name->makeString(os.str());
+
+    boost::shared_ptr<amf::Element> data(new amf::Element);
+    data->makeStrictArray();
+    for (size_t i=firstArg; i<args.size(); i++) {
+       cerr << "FIXME: NetConnection::" << __FUNCTION__ << "(): " << 
args[i].to_string() << endl;
+       boost::shared_ptr<amf::Element> el = args[i].to_element();
+//     el->dump();
+       data->addProperty(el);
+    }
+//     data->dump();
+
+    boost::shared_ptr<amf::AMF_msg::amf_message_t> msg(new 
amf::AMF_msg::amf_message_t);
+    msg->header.target = methodName;
+    msg->header.response = os.str();
+    msg->header.size = data->calculateSize(*data);
+    msg->data = data;
+    top.addMessage(msg);
+    
+    boost::shared_ptr<amf::Buffer> buf = top.encodeAMFPacket();
+//     top.dump();
+//     buf->dump();
+
+    // Send the request via HTTP
+    if ((url.protocol() == "rtmpt")
+       || (url.protocol() == "http")) {
+       log_debug("Requesting echo response file...");
+       // "/echo/gateway"
+       amf::Buffer &request = _http_client->formatRequest(url.path(), 
HTTP::HTTP_POST);
+       _http_client->formatContentLength(buf->allocated());
+       // All HTTP messages are followed by a blank line.
+       _http_client->terminateHeader();
+       request += buf;
+       _http_client->writeNet(request);
+       tdata->network = reinterpret_cast<Network *>(_http_client.get());
+       VM& vm = asCallback->getVM();
+       tdata->st = &vm.getStringTable();
+//     tdata->vm = vm;
+    }
+
+    // Send the request via RTMP
+    if (url.protocol() == "rtmp") {
+       tdata->network = reinterpret_cast<Network *>(_rtmp_client.get());
+//     boost::shared_ptr<buf3> = _rtmp_client->encodeStream(0x2);
+//     //    buf3->dump();
+//     total_size = buf3->size();
+//     RTMPMsg *msg2 = _rtmp_client->sendRecvMsg(0x3, RTMP::HEADER_12, 
total_size, RTMP::INVOKE, RTMPMsg::FROM_CLIENT, buf3);
+    }
+
+
+    //    this->test();
+
+    // Start a thread to wait for the response
+#if 0
+    boost::thread process_thread(boost::bind(&net_handler, tdata.get()));
+#else
+    net_handler(tdata.get());
+#endif
+    
+//    _currentConnection.reset(new HTTPRemotingHandler(*this, url));
 
 }
 
@@ -964,112 +467,6 @@
 
 }
 
-as_value
-NetConnection_as::advanceWrapper(const fn_call& fn)
-{
-    boost::intrusive_ptr<NetConnection_as> ptr = 
-        ensureType<NetConnection_as>(fn.this_ptr);
-    
-    ptr->advance();
-    return as_value();
-};
-
-void
-NetConnection_as::startAdvanceTimer() 
-{
-
-    if (_advanceTimer)
-    {
-        //log_debug("startAdvanceTimer: already running %d", _advanceTimer);
-        return;
-    }
-
-    boost::intrusive_ptr<builtin_function> ticker_as = 
-            new builtin_function(&NetConnection_as::advanceWrapper);
-
-    std::auto_ptr<Timer> timer(new Timer);
-    unsigned long delayMS = 50; 
-    timer->setInterval(*ticker_as, delayMS, this);
-    _advanceTimer = getVM().getRoot().add_interval_timer(timer, true);
-
-    log_debug("startAdvanceTimer: registered advance timer %d", _advanceTimer);
-}
-
-void
-NetConnection_as::stopAdvanceTimer() 
-{
-    if (!_advanceTimer)
-    {
-        log_debug("stopAdvanceTimer: not running");
-        return;
-    }
-
-    getVM().getRoot().clear_interval_timer(_advanceTimer);
-    log_debug("stopAdvanceTimer: deregistered timer %d", _advanceTimer);
-    _advanceTimer=0;
-}
-
-void
-NetConnection_as::advance()
-{
-    // Advance
-
-#ifdef GNASH_DEBUG_REMOTING
-    log_debug("NetConnection_as::advance: %d calls to advance", 
_queuedConnections.size());
-#endif
-
-    while ( ! _queuedConnections.empty() )
-    {
-        ConnectionHandler* ch = _queuedConnections.front();
-        if ( ! ch->advance() )
-        {
-            log_debug("ConnectionHandler done, dropping");
-            _queuedConnections.pop_front();
-            delete ch;
-        }
-        else break; // queues handling is serialized
-    }
-
-    if ( _currentConnection.get() ) 
-    {
-        _currentConnection->advance();
-    }
-
-    // Advancement of a connection might trigger creation
-    // of a new connection, so we won't stop the advance
-    // timer in that case
-    if ( _queuedConnections.empty() && ! _currentConnection.get() )
-    {
-#ifdef GNASH_DEBUG_REMOTING
-        log_debug("stopping advance timer");
-#endif
-        stopAdvanceTimer();
-#ifdef GNASH_DEBUG_REMOTING
-        log_debug("advance timer stopped");
-#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 {
@@ -1082,6 +479,7 @@
 as_value
 netconnection_call(const fn_call& fn)
 {
+    GNASH_REPORT_FUNCTION;
     boost::intrusive_ptr<NetConnection_as> ptr = 
         ensureType<NetConnection_as>(fn.this_ptr); 
 
@@ -1126,6 +524,7 @@
 as_value
 netconnection_close(const fn_call& fn)
 {
+    GNASH_REPORT_FUNCTION;
     boost::intrusive_ptr<NetConnection_as> ptr =
         ensureType<NetConnection_as>(fn.this_ptr); 
 
@@ -1213,12 +612,12 @@
 as_value
 netconnection_connect(const fn_call& fn)
 {
+    GNASH_REPORT_FUNCTION;
 
     boost::intrusive_ptr<NetConnection_as> ptr =
         ensureType<NetConnection_as>(fn.this_ptr); 
     
-    if (fn.nargs < 1)
-    {
+    if (fn.nargs < 1) {
         IF_VERBOSE_ASCODING_ERRORS(
             log_aserror(_("NetConnection.connect(): needs at least "
                     "one argument"));
@@ -1237,10 +636,8 @@
     // Check first arg for validity 
     if (uri.is_null() || (vm.getSWFVersion() > 6 && uri.is_undefined())) {
         ptr->connect();
-    }
-    else {
-        if ( fn.nargs > 1 )
-        {
+    } else {
+        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());
@@ -1252,6 +649,122 @@
 
 }
 
+// This thread waits for data from the server, and executes the callback
+extern "C" {
+bool DSOEXPORT 
+net_handler(NetConnection_as::thread_params_t *args)
+{
+    GNASH_REPORT_FUNCTION;
+
+#ifdef USE_STATISTICS
+       struct timespec start;
+       clock_gettime (CLOCK_REALTIME, &start);
+#endif
+    bool result = false;
+    bool done = false;
+
+//    boost::mutex::scoped_lock lock(call_mutex);
+
+//    args->caller->test();
+
+    // Suck all the data waiting for us in the network
+    boost::shared_ptr<amf::Buffer> buf(new amf::Buffer);
+    do {
+       size_t ret = args->network->readNet(buf->reference() + 
buf->allocated(), 
+                                        buf->size(), 10);
+       // The timeout expired
+       if (ret == 0) {
+           log_debug("no data yet for fd #%d, continuing...",
+                     args->network->getFileFd());
+           result = false;
+           done = true;
+       }
+       // Something happened to the network connection
+       if ((ret == static_cast<size_t>(string::npos)) || (ret == 
static_cast<size_t>(-1))) {
+           log_debug("socket for fd #%d was closed...",
+                     args->network->getFileFd());
+           return false;
+       }
+       // We got data.
+       if (ret > 0) {
+           // If we got less data than we tried to read, then we got the
+           // whole packet most likely.
+           if (ret < buf->size()) {
+               done = true;
+               result = true;
+           }
+           if (ret == buf->size()) {
+               // become larger by another default block size.
+               buf->resize(buf->size() + amf::NETBUFSIZE);
+               log_debug("Got a full packet, making the buffer larger to %d",
+                         buf->size());
+               result = true;
+           }
+           // manually set the seek pointer in the buffer, as we read
+           // the data into the raw memory allocated to the buffer. We
+           // only want to do this if we got data of course.
+           buf->setSeekPointer(buf->end() + ret);
+       } else {
+           log_debug("no more data for fd #%d, exiting...", 
+                     args->network->getFileFd());
+           done = true;
+       }
+    } while(done != true);
+
+    // Now process the data
+    if (result) {
+       HTTP *http = reinterpret_cast<HTTP *>(args->network);;
+       amf::AMF amf;
+       boost::uint8_t *data = http->processHeaderFields(*buf);
+//     http->dump();
+       boost::shared_ptr<std::vector<std::string> > encoding = 
http->getFieldItem("transfer-encoding");
+       size_t length = http->getContentLength();
+       if (http->getField("content-type") == "application/x-amf") {
+           amf::AMF_msg amsg;
+           boost::shared_ptr<amf::AMF_msg::context_header_t> head =
+               amsg.parseAMFPacket(data, length);
+           
+//         amsg.dump();
+           log_debug("%d messages in AMF packet", amsg.messageCount());
+           for (size_t i=0; i<amsg.messageCount(); i++) {
+//             amsg.getMessage(i)->data->dump();
+               boost::shared_ptr<amf::Element> el = amsg.getMessage(i)->data;
+               as_value tmp(*el);
+//             NetConnection_as *obj = (NetConnection_as *)args->network;
+               log_debug("Calling NetConnection %s(%s)",
+                         amsg.getMessage(i)->header.target, tmp);
+               // The method name looks something like this: /17/onResult
+               // the first field is a sequence number so each response can
+               // be matched to the request that made it. We only want the
+               // name part, so we can call the method.
+               string::size_type pos = 
amsg.getMessage(i)->header.target.find('/', 1);
+               string methodName;
+               if (pos != string::npos) {
+                   methodName = 
amsg.getMessage(i)->header.target.substr(pos+1,  
amsg.getMessage(i)->header.target.size());
+               }
+//             VM& vm = args->callback->getVM();
+//             string_table& st = vm.getStringTable();
+               string_table::key methodKey;
+               boost::mutex::scoped_lock lock(_nc_mutex);
+               methodKey = args->st->find(methodName);
+               args->callback->callMethod(methodKey, tmp);
+           }
+       } else {// not AMF data
+           log_debug("Content type is: %d", http->getField("content-type"));
+           if ((http->getField("content-type") == "application/xml")
+               || (http->getField("content-type") == "text/html")) {
+               log_debug("Textual Data is: %s", reinterpret_cast<char 
*>(data));
+           } else {
+               log_debug("Binary Data is: %s", hexify(data, length, true));
+           }
+       }
+    }
+
+    log_debug("net_handler all done...");
+
+    return result;
+}
+} // end of extern C
 
 as_value
 netconnection_addHeader(const fn_call& fn)
@@ -1268,3 +781,7 @@
 
 } // end of gnash namespace
 
+// local Variables:
+// mode: C++
+// indent-tabs-mode: t
+// End:

=== modified file 'libcore/asobj/NetConnection_as.h'
--- a/libcore/asobj/NetConnection_as.h  2009-02-25 22:33:03 +0000
+++ b/libcore/asobj/NetConnection_as.h  2009-03-20 00:00:54 +0000
@@ -19,17 +19,24 @@
 #ifndef GNASH_NETCONNECTION_H
 #define GNASH_NETCONNECTION_H
 
+
+#include <string>
+#include <list>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/thread/mutex.hpp>
+
 #include "IOChannel.h"
 #include "as_object.h" // for inheritance
 #include "fn_call.h"
-
-#include <string>
-#include <list>
-
-// Forward declarations
-namespace gnash {
-       class ConnectionHandler;
-}
+#include "VM.h"
+
+// Internal headers from libnet
+#include "network.h"
+#include "http.h"              // RTMPT & HTTP client side support
+#include "rtmp_client.h"       // RTMP client side support
+
 
 namespace gnash {
 
@@ -41,6 +48,14 @@
 {
 public:
 
+   // This is used to pass parameters to a thread using boost::bind
+    typedef struct {
+       as_object        *callback;
+       Network          *network;
+       VM               *vm;
+       string_table     *st;
+    } thread_params_t;
+
     enum StatusCode
     {
         CONNECT_FAILED,
@@ -55,15 +70,11 @@
        NetConnection_as();
        ~NetConnection_as();
 
-    /// Process connection stuff
-    virtual void advance();
-
-    static as_value advanceWrapper(const fn_call& fn);
-
     /// Make the stored URI into a valid and checked URL.
-       std::string validateURL() const;
+    std::string validateURL() const;
 
-    void call(as_object* asCallback, const std::string& methodName, const 
std::vector<as_value>& args, size_t firstArg);
+    void call(as_object* asCallback, const std::string& methodName,
+              const std::vector<as_value>& args, size_t firstArg);
 
     /// Process the close() method.
     void close();
@@ -79,10 +90,7 @@
     }
 
     void setURI(const std::string& uri);
-
-    const std::string& getURI() const {
-        return _uri;
-    }
+    const std::string& getURI() const { return _uri; }
 
     /// Notify the NetConnection onStatus handler of a change.
     void notifyStatus(StatusCode code);
@@ -96,43 +104,34 @@
        void markReachableResources() const;
 
 private:
-
+    
     typedef std::pair<std::string, std::string> NetConnectionStatus;
 
     void getStatusCodeInfo(StatusCode code, NetConnectionStatus& info);
 
-       /// Extend the URL to be used for playing
-       void addToURL(const std::string& url);
-
-    /// Queue of call groups
-    //
-    /// For HTTP based remoting, each element on this list
-    /// will perform a POST request containing all calls
-    /// to the same uri and dispatch results.
-    ///
-       std::list<ConnectionHandler*> _queuedConnections;
-
-    /// Queue of calls gathered during a single movie advancement
-    //
-    /// For HTTP based remoting, these calls will be performed
-    /// by a single POST operation.
-    ///
-    std::auto_ptr<ConnectionHandler> _currentConnection; 
-
-       /// the url prefix optionally passed to connect()
-       std::string _uri;
-
-    bool _isConnected;
-
-    void startAdvanceTimer();
-
-    void stopAdvanceTimer();
-
-    int _advanceTimer;
+    /// Extend the URL to be used for playing
+    void addToURL(const std::string& url);
+
+    /// the url prefix optionally passed to connect()
+    std::string                _uri;
+    bool               _isConnected;
+    unsigned int       _numCalls;
+    boost::scoped_ptr<HTTP> _http_client;
+    boost::scoped_ptr<RTMPClient> _rtmp_client;
 };
 
+// This thread waits for data from the server, and executes the callback
+extern "C" {
+bool DSOEXPORT net_handler(NetConnection_as::thread_params_t *args);
+}
+
 void netconnection_class_init(as_object& global);
 
 } // end of gnash namespace
 
-#endif
+#endif  // GNASH_NETCONNECTION_AS_H
+
+// local Variables:
+// mode: C++
+// indent-tabs-mode: t
+// End:

=== modified file 'libcore/asobj/NetStream_as.cpp'
--- a/libcore/asobj/NetStream_as.cpp    2009-03-13 22:46:37 +0000
+++ b/libcore/asobj/NetStream_as.cpp    2009-03-20 00:00:54 +0000
@@ -1621,6 +1621,7 @@
 as_value
 netstream_new(const fn_call& fn)
 {
+    GNASH_REPORT_FUNCTION;
 
     boost::intrusive_ptr<NetStream_as> netstream_obj = new NetStream_as;
 
@@ -1650,6 +1651,7 @@
 as_value
 netstream_close(const fn_call& fn)
 {
+    GNASH_REPORT_FUNCTION;
     boost::intrusive_ptr<NetStream_as> ns =
         ensureType<NetStream_as>(fn.this_ptr);
     ns->close();
@@ -1659,6 +1661,7 @@
 as_value
 netstream_pause(const fn_call& fn)
 {
+    GNASH_REPORT_FUNCTION;
     boost::intrusive_ptr<NetStream_as> ns = 
         ensureType<NetStream_as>(fn.this_ptr);
     
@@ -1678,6 +1681,7 @@
 as_value
 netstream_play(const fn_call& fn)
 {
+    GNASH_REPORT_FUNCTION;
     boost::intrusive_ptr<NetStream_as> ns =
         ensureType<NetStream_as>(fn.this_ptr);
 
@@ -1706,6 +1710,7 @@
 as_value
 netstream_seek(const fn_call& fn)
 {
+    GNASH_REPORT_FUNCTION;
     boost::intrusive_ptr<NetStream_as> ns = 
         ensureType<NetStream_as>(fn.this_ptr);
 
@@ -1770,6 +1775,7 @@
 as_value
 netstream_publish(const fn_call& fn)
 {
+    GNASH_REPORT_FUNCTION;
     boost::intrusive_ptr<NetStream_as> ns = 
         ensureType<NetStream_as>(fn.this_ptr);
     UNUSED(ns);
@@ -1806,6 +1812,7 @@
 as_value
 netstream_send(const fn_call& fn)
 {
+    GNASH_REPORT_FUNCTION;
     boost::intrusive_ptr<NetStream_as> ns =
         ensureType<NetStream_as>(fn.this_ptr);
     UNUSED(ns);

=== modified file 'libnet/http.cpp'
--- a/libnet/http.cpp   2009-03-16 23:34:13 +0000
+++ b/libnet/http.cpp   2009-03-20 00:00:54 +0000
@@ -133,7 +133,220 @@
     return *this; 
 }
 
+
+boost::uint8_t *
+HTTP::processHeaderFields(amf::Buffer &buf)
+{
+  //    GNASH_REPORT_FUNCTION;
+    string head(reinterpret_cast<const char *>(buf.reference()));
+
+    // The end of the header block is always followed by a blank line
+    string::size_type end = head.find("\r\n\r\n", 0);
+//    head.erase(end, buf.size()-end);
+    Tok t(head, Sep("\r\n"));
+    for (Tok::iterator i = t.begin(); i != t.end(); ++i) {
+       string::size_type pos = i->find(":", 0);
+       if (pos != string::npos) {
+           string name = i->substr(0, pos);
+           string value = i->substr(pos+2, i->size());
+           std::transform(name.begin(), name.end(), name.begin(), 
+                          (int(*)(int)) tolower);
+           std::transform(value.begin(), value.end(), value.begin(), 
+                          (int(*)(int)) tolower);
+           _fields[name] = value;
+           if (name == "keep-alive") {
+               _keepalive = true;
+               if ((value != "on") && (value != "off")) {
+                   _max_requests = strtol(value.c_str(), NULL, 0);
+                   log_debug("Setting Max Requests for Keep-Alive to %d", 
_max_requests);
+               }
+           }
+           if (name == "connection") {
+               if (value.find("keep-alive", 0) != string::npos) {
+                   _keepalive = true;
+               }
+           }
+           if (name == "content-length") {
+               _filesize = strtol(value.c_str(), NULL, 0);
+               log_debug("Setting Content Length to %d", _filesize);
+           }
+           if (name == "content-type") {
+               // This is the type used by flash when sending a AMF data via 
POST
+               if (value == "application/x-amf") {
+//                 log_debug("Got AMF data in the POST request!");
+                   _filetype = DiskStream::FILETYPE_AMF;
+               }
+               // This is the type used by wget when sending a file via POST
+               if (value == "application/x-www-form-urlencoded") {
+//                 log_debug("Got file data in the POST request");
+                   _filetype = DiskStream::FILETYPE_ENCODED;
+               }
+               log_debug("Setting Content Type to %d", _filetype);
+           }
+           
+//         cerr << "FIXME: " << (void *)i << " : " << dec <<  end << endl;
+       } else {
+           const boost::uint8_t *cmd = reinterpret_cast<const boost::uint8_t 
*>(i->c_str());
+           if (extractCommand(const_cast<boost::uint8_t *>(cmd)) == 
HTTP::HTTP_NONE) {
+               break;
+#if 1
+           } else {
+               log_debug("Got a request, parsing \"%s\"", *i);
+               string::size_type start = i->find(" ");
+               string::size_type params = i->find("?");
+               string::size_type pos = i->find("HTTP/");
+               if (pos != string::npos) {
+                   // The version is the last field and is the protocol name
+                   // followed by a slash, and the version number. Note that
+                   // the version is not a double, even though it has a dot
+                   // in it. It's actually two separate integers.
+                   _version.major = i->at(pos+5) - '0';
+                   _version.minor = i->at(pos+7) - '0';
+                   log_debug (_("Version: %d.%d"), _version.major, 
_version.minor);
+                   // the filespec in the request is the middle field, 
deliminated
+                   // by a space on each end.
+                   if (params != string::npos) {
+                       _params = i->substr(params+1, end);
+                       _filespec = i->substr(start+1, params);
+                       log_debug("Parameters for file: \"%s\"", _params);
+                   } else {
+                       _filespec = i->substr(start+1, pos-start-2);
+                   }
+                   log_debug("Requesting file: \"%s\"", _filespec);
+
+                   // HTTP 1.1 enables persistant network connections
+                   // by default.
+                   if (_version.minor > 0) {
+                       log_debug("Enabling Keep Alive by default for HTTP > 
1.0");
+                       _keepalive = true;
+                   }
+               }
+           }
+#endif
+       }
+    }
+    
+    return buf.reference() + end + 4;
+}
+
+// // Parse an Echo Request message coming from the Red5 echo_test. This
+// // method should only be used for testing purposes.
+// vector<boost::shared_ptr<amf::Element > >
+// HTTP::parseEchoRequest(boost::uint8_t *data, size_t size)
+// {
+// //    GNASH_REPORT_FUNCTION;
+    
+//     vector<boost::shared_ptr<amf::Element > > headers;
+       
+//     // skip past the header bytes, we don't care about them.
+//     boost::uint8_t *tmpptr = data + 6;
+    
+//     boost::uint16_t length;
+//     length = ntohs((*(boost::uint16_t *)tmpptr) & 0xffff);
+//     tmpptr += sizeof(boost::uint16_t);
+
+//     // Get the first name, which is a raw string, and not preceded by
+//     // a type byte.
+//     boost::shared_ptr<amf::Element > el1(new amf::Element);
+    
+//     // If the length of the name field is corrupted, then we get out of
+//     // range quick, and corrupt memory. This is a bit of a hack, but
+//     // reduces memory errors caused by some of the corrupted tes cases.
+//     boost::uint8_t *endstr = std::find(tmpptr, tmpptr+length, '\0');
+//     if (endstr != tmpptr+length) {
+//     log_debug("Caught corrupted string! length was %d, null at %d",
+//               length,  endstr-tmpptr);
+//     length = endstr-tmpptr;
+//     }
+//     el1->setName(tmpptr, length);
+//     tmpptr += length;
+//     headers.push_back(el1);
+    
+//     // Get the second name, which is a raw string, and not preceded by
+//     // a type byte.
+//     length = ntohs((*(boost::uint16_t *)tmpptr) & 0xffff);
+//     tmpptr += sizeof(boost::uint16_t);
+//     boost::shared_ptr<amf::Element > el2(new amf::Element);
+
+// //     std::string name2(reinterpret_cast<const char *>(tmpptr), length);
+// //     el2->setName(name2.c_str(), name2.size());
+//     // If the length of the name field is corrupted, then we get out of
+//     // range quick, and corrupt memory. This is a bit of a hack, but
+//     // reduces memory errors caused by some of the corrupted tes cases.
+//     endstr = std::find(tmpptr, tmpptr+length, '\0');
+//     if (endstr != tmpptr+length) {
+//     log_debug("Caught corrupted string! length was %d, null at %d",
+//               length,  endstr-tmpptr);
+//     length = endstr-tmpptr;
+//     }
+//     el2->setName(tmpptr, length);
+//     headers.push_back(el2);
+//     tmpptr += length;
+
+//     // Get the last two pieces of data, which are both AMF encoded
+//     // with a type byte.
+//     amf::AMF amf;
+//     boost::shared_ptr<amf::Element> el3 = amf.extractAMF(tmpptr, tmpptr + 
size);
+//     headers.push_back(el3);
+//     tmpptr += amf.totalsize();
+    
+//     boost::shared_ptr<amf::Element> el4 = amf.extractAMF(tmpptr, tmpptr + 
size);
+//     headers.push_back(el4);
+
+//      return headers;
+// }
+
+// // format a response to the 'echo' test used for testing Gnash. This
+// // is only used for testing by developers. The format appears to be
+// // two strings, followed by a double, followed by the "onResult".
+// amf::Buffer &
+// HTTP::formatEchoResponse(const std::string &num, amf::Element &el)
+// {
+// //    GNASH_REPORT_FUNCTION;
+//     boost::shared_ptr<amf::Buffer> data;
+
+//     amf::Element nel;
+//     if (el.getType() == amf::Element::TYPED_OBJECT_AMF0) {
+//     nel.makeTypedObject();
+//     string name = el.getName();
+//     nel.setName(name);
+//     if (el.propertySize()) {
+//         // FIXME: see about using std::reverse() instead.
+//         for (int i=el.propertySize()-1; i>=0; i--) {
+// //      for (int i=0 ; i<el.propertySize(); i++) {
+//             boost::shared_ptr<amf::Element> child = el.getProperty(i);
+//             nel.addProperty(child);
+//         }
+//         data = nel.encode();
+//     } else {
+//         data = el.encode();
+//     }
+//     } else {
+//     data = el.encode();
+//     }
+
+//     return formatEchoResponse(num, data->reference(), data->allocated());
+// }
+
 #if 0                          // FIXME:
+// Client side parsing of response message codes
+boost::shared_ptr<HTTP::http_response_t> 
+HTTP::parseStatus(const std::string &line)
+{
+//    GNASH_REPORT_FUNCTION;
+
+    boost::shared_ptr<http_response_t> status;
+    // The respnse is a number followed by the error message.
+    string::size_type pos = line.find(" ", 0);
+    if (pos != string::npos) {
+       status.reset(new http_response_t);
+       status->code = static_cast<HTTP::http_status_e>(strtol(line.substr(0, 
pos).c_str(), NULL, 0));
+       status->msg = line.substr(pos+1, line.size());
+    }
+
+    return status;
+}
+
 HTTP::http_method_e
 HTTP::processClientRequest(int fd)
 {
@@ -420,6 +633,20 @@
 }
 #endif
 
+// Convert the Content-Length field to a number we can use
+size_t
+HTTP::getContentLength()
+{
+    //    GNASH_REPORT_FUNCTION;
+    boost::shared_ptr<std::vector<std::string> > length = 
getFieldItem("content-length");
+    if (length->size() > 0) {
+       return static_cast<size_t>(strtol(length->at(0).c_str(), NULL, 0));
+    }
+
+    return 0;
+}
+
+
 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5 (5.3 Request 
Header Fields)
 bool
 HTTP::checkRequestFields(amf::Buffer & /* buf */)
@@ -896,104 +1123,6 @@
     return formatLastModified(date.str());
 }
 
-// Parse an Echo Request message coming from the Red5 echo_test. This
-// method should only be used for testing purposes.
-vector<boost::shared_ptr<amf::Element > >
-HTTP::parseEchoRequest(boost::uint8_t *data, size_t size)
-{
-//    GNASH_REPORT_FUNCTION;
-    
-    vector<boost::shared_ptr<amf::Element > > headers;
-       
-    // skip past the header bytes, we don't care about them.
-    boost::uint8_t *tmpptr = data + 6;
-    
-    boost::uint16_t length;
-    length = ntohs((*(boost::uint16_t *)tmpptr) & 0xffff);
-    tmpptr += sizeof(boost::uint16_t);
-
-    // Get the first name, which is a raw string, and not preceded by
-    // a type byte.
-    boost::shared_ptr<amf::Element > el1(new amf::Element);
-    
-    // If the length of the name field is corrupted, then we get out of
-    // range quick, and corrupt memory. This is a bit of a hack, but
-    // reduces memory errors caused by some of the corrupted tes cases.
-    boost::uint8_t *endstr = std::find(tmpptr, tmpptr+length, '\0');
-    if (endstr != tmpptr+length) {
-       log_debug("Caught corrupted string! length was %d, null at %d",
-                 length,  endstr-tmpptr);
-       length = endstr-tmpptr;
-    }
-    el1->setName(tmpptr, length);
-    tmpptr += length;
-    headers.push_back(el1);
-    
-    // Get the second name, which is a raw string, and not preceded by
-    // a type byte.
-    length = ntohs((*(boost::uint16_t *)tmpptr) & 0xffff);
-    tmpptr += sizeof(boost::uint16_t);
-    boost::shared_ptr<amf::Element > el2(new amf::Element);
-
-//     std::string name2(reinterpret_cast<const char *>(tmpptr), length);
-//     el2->setName(name2.c_str(), name2.size());
-    // If the length of the name field is corrupted, then we get out of
-    // range quick, and corrupt memory. This is a bit of a hack, but
-    // reduces memory errors caused by some of the corrupted tes cases.
-    endstr = std::find(tmpptr, tmpptr+length, '\0');
-    if (endstr != tmpptr+length) {
-       log_debug("Caught corrupted string! length was %d, null at %d",
-                 length,  endstr-tmpptr);
-       length = endstr-tmpptr;
-    }
-    el2->setName(tmpptr, length);
-    headers.push_back(el2);
-    tmpptr += length;
-
-    // Get the last two pieces of data, which are both AMF encoded
-    // with a type byte.
-    amf::AMF amf;
-    boost::shared_ptr<amf::Element> el3 = amf.extractAMF(tmpptr, tmpptr + 
size);
-    headers.push_back(el3);
-    tmpptr += amf.totalsize();
-    
-    boost::shared_ptr<amf::Element> el4 = amf.extractAMF(tmpptr, tmpptr + 
size);
-    headers.push_back(el4);
-
-     return headers;
-}
-
-// format a response to the 'echo' test used for testing Gnash. This
-// is only used for testing by developers. The format appears to be
-// two strings, followed by a double, followed by the "onResult".
-amf::Buffer &
-HTTP::formatEchoResponse(const std::string &num, amf::Element &el)
-{
-//    GNASH_REPORT_FUNCTION;
-    boost::shared_ptr<amf::Buffer> data;
-
-    amf::Element nel;
-    if (el.getType() == amf::Element::TYPED_OBJECT_AMF0) {
-       nel.makeTypedObject();
-       string name = el.getName();
-       nel.setName(name);
-       if (el.propertySize()) {
-           // FIXME: see about using std::reverse() instead.
-           for (int i=el.propertySize()-1; i>=0; i--) {
-//         for (int i=0 ; i<el.propertySize(); i++) {
-               boost::shared_ptr<amf::Element> child = el.getProperty(i);
-               nel.addProperty(child);
-           }
-           data = nel.encode();
-       } else {
-           data = el.encode();
-       }
-    } else {
-       data = el.encode();
-    }
-
-    return formatEchoResponse(num, data->reference(), data->allocated());
-}
 
 amf::Buffer &
 HTTP::formatEchoResponse(const std::string &num, amf::Buffer &data)
@@ -1060,29 +1189,121 @@
 }
 
 amf::Buffer &
-HTTP::formatRequest(const string & /* url */, http_method_e /* req */)
+HTTP::formatRequest(const string &url, http_method_e cmd)
 {
 //    GNASH_REPORT_FUNCTION;
-
-#if 0
-    _header.str("");
-
-    _header << req << " " << url << "HTTP/1.1" << "\r\n";
-    _header << "User-Agent: Opera/9.01 (X11; Linux i686; U; en)" << "\r\n";
-    _header << "Accept: text/html, application/xml;q=0.9, 
application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, 
*/*;q=0.1" << "\r\n";
-
-    _header << "Accept-Language: en" << "\r\n";
-    _header << "Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1" << "\r\n";
-    
-    _header << "Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0" << 
"\r\n";
-    _header << "Referer: " << url << "\r\n";
-
-    _header << "TE: deflate, gzip, chunked, identity, trailers" << "\r\n";
-#endif
-    
+    
+    clearHeader();
+
+    switch (cmd) {
+    case HTTP::HTTP_GET:
+       _buffer = "GET ";
+       break;
+    case HTTP::HTTP_POST:
+       _buffer = "POST ";
+       break;
+    case HTTP::HTTP_HEAD:
+       _buffer = "HEAD ";
+       break;
+    case HTTP::HTTP_CONNECT:
+       _buffer = "CONNECT ";
+       break;
+    case HTTP::HTTP_TRACE:
+       _buffer = "TRACE ";
+       break;
+    case HTTP::HTTP_OPTIONS:
+       _buffer = "OPTIONS ";
+       break;
+    default:
+       break;
+    }
+    _buffer += url;
+
+    _buffer += " HTTP/1.1";
+//     sprintf(num, "%d.%d", _version.major, _version.minor);
+//     _buffer += num;
+    // end the line
+    _buffer += "\r\n";
+
+    formatHost("localhost");
+    formatAgent("Gnash");
+
+    // Post messages want a bunch more fields than a GET request
+    if (cmd == HTTP::HTTP_POST) {
+       formatContentType(DiskStream::FILETYPE_AMF);
+       formatEncoding("deflate, gzip, x-gzip, identity, *;q=0");
+       formatConnection("Keep-Alive");
+    }
+
+//     // All HTTP messages are followed by a blank line.
+//     terminateHeader();
+
     return _buffer;
 }
 
+
+HTTP::http_method_e
+HTTP::extractCommand(boost::uint8_t *data)
+{
+    GNASH_REPORT_FUNCTION;
+
+//    string body = reinterpret_cast<const char *>(data);
+    HTTP::http_method_e cmd = HTTP::HTTP_NONE;
+
+    // force the case to make comparisons easier
+//     std::transform(body.begin(), body.end(), body.begin(), 
+//                (int(*)(int)) toupper);
+
+    // Extract the command
+    if (memcmp(data, "GET", 3) == 0) {
+        cmd = HTTP::HTTP_GET;
+    } else if (memcmp(data, "POST", 4) == 0) {
+        cmd = HTTP::HTTP_POST;
+    } else if (memcmp(data, "HEAD", 4) == 0) {
+        cmd = HTTP::HTTP_HEAD;
+    } else if (memcmp(data, "CONNECT", 7) == 0) {
+        cmd = HTTP::HTTP_CONNECT;
+    } else if (memcmp(data, "TRACE", 5) == 0) {
+        cmd = HTTP::HTTP_TRACE;
+    } else if (memcmp(data, "PUT", 3) == 0) {
+        cmd = HTTP::HTTP_PUT;
+    } else if (memcmp(data, "OPTIONS", 4) == 0) {
+        cmd = HTTP::HTTP_OPTIONS;
+    } else if (memcmp(data, "DELETE", 4) == 0) {
+        cmd = HTTP::HTTP_DELETE;
+    } else if (memcmp(data, "HTTP", 4) == 0) {
+        cmd = HTTP::HTTP_RESPONSE;
+    }
+
+    // For valid requests, the second argument, delimited by spaces
+    // is the filespec of the file being requested or transmitted.
+    if (cmd != HTTP::HTTP_NONE) {
+       boost::uint8_t *start = std::find(data, data+7, ' ') + 1;
+       boost::uint8_t *end   = std::find(start + 2, data+PATH_MAX, ' ');
+       boost::uint8_t *params = std::find(start, end, '?');
+       if (params != end) {
+           _params = std::string(params+1, end);
+           _filespec = std::string(start, params);
+           log_debug("Parameters for file: \"%s\"", _params);
+       } else {
+           // This is fine as long as end is within the buffer.
+           _filespec = std::string(start, end);
+       }
+       log_debug("Requesting file: \"%s\"", _filespec);
+
+       // The third field is always the HTTP version
+       // The version is the last field and is the protocol name
+       // followed by a slash, and the version number. Note that
+       // the version is not a double, even though it has a dot
+       // in it. It's actually two separate integers.
+       _version.major = *(end+6) - '0';
+       _version.minor = *(end+8) - '0';
+       log_debug (_("Version: %d.%d"), _version.major, _version.minor);
+    }
+
+    return cmd;
+}
+
 /// \brief Send a message to the other end of the network connection.
 ///`   Sends the contents of the _header and _body private data to
 ///    the already opened network connection.
@@ -1186,6 +1407,7 @@
     return ret;
 }
 
+
 void
 HTTP::dump() {
 //    GNASH_REPORT_FUNCTION;

=== modified file 'libnet/http.h'
--- a/libnet/http.h     2009-03-16 23:34:13 +0000
+++ b/libnet/http.h     2009-03-20 00:00:54 +0000
@@ -108,7 +108,8 @@
         HTTP_PUT,
         HTTP_DELETE,
         HTTP_TRACE,
-        HTTP_CONNECT
+        HTTP_CONNECT,
+       HTTP_RESPONSE           // unique to gnash
     } http_method_e;
     typedef enum {
        OPEN,
@@ -116,10 +117,11 @@
        IDLE,
        CLOSE
     } rtmpt_cmd_e;
-    struct status_codes {
-        const char *code;
-        const char *msg;
-    };
+    // A response from an FTTP request has a code an an error message
+    typedef struct {
+       http_status_e code;
+       std::string   msg;
+    } http_response_t;
     typedef struct {
        int major;
        int minor;
@@ -137,6 +139,14 @@
     std::vector<boost::shared_ptr<amf::Element > > 
parseEchoRequest(amf::Buffer &buf) { return parseEchoRequest(buf.reference(), 
buf.size()); };
     std::vector<boost::shared_ptr<amf::Element > > 
parseEchoRequest(boost::uint8_t *buf, size_t size);
     
+    // Convert the Content-Length field to a number we can use
+    size_t getContentLength();
+
+    // process all the header fields in the Buffer, storing them internally
+    // in _fields. The address returned is the address where the Content data
+    // starts, and is "Content-Length" bytes long, of "Content-Type" data.
+    boost::uint8_t *processHeaderFields(amf::Buffer &buf);
+    
     // Get the field for header 'name' that was stored by processHeaderFields()
     std::string &getField(const std::string &name) { return _fields[name]; };
     size_t NumOfFields() { return _fields.size(); };
@@ -145,6 +155,16 @@
     // Get an array of values for header field 'name'.
     boost::shared_ptr<std::vector<std::string> > getFieldItem(const 
std::string &name);
 
+    // Client side parsing of response message codes
+    boost::shared_ptr<http_response_t> parseStatus(const std::string &line);
+
+    // Handle the response for the request.
+    boost::shared_ptr<amf::Buffer> formatServerReply(http_status_e code);
+    amf::Buffer &formatGetReply(DiskStream::filetype_e type, size_t size, 
http_status_e code); 
+    amf::Buffer &formatGetReply(size_t size, http_status_e code); 
+    amf::Buffer &formatGetReply(http_status_e code); 
+    amf::Buffer &formatPostReply(rtmpt_cmd_e code);
+
     // Make copies of ourself
     HTTP &operator = (HTTP &obj);
 
@@ -271,6 +291,12 @@
     std::string &getDocRoot() { return _docroot; };
     
 protected:
+    // Examine the beginning of the data for an HTTP request command
+    // like GET or POST, etc...
+    http_method_e extractCommand(boost::uint8_t *data);
+    http_method_e extractCommand(amf::Buffer &data)
+       { return extractCommand(data.reference()); };    
+
     typedef boost::char_separator<char> Sep;
     typedef boost::tokenizer<Sep> Tok;
     http_method_e      _cmd;

=== modified file 'libnet/network.cpp'
--- a/libnet/network.cpp        2009-03-16 23:34:13 +0000
+++ b/libnet/network.cpp        2009-03-20 00:00:54 +0000
@@ -828,6 +828,8 @@
     fd_set              fdset;
     int                 ret = -1;
 
+//     boost::mutex::scoped_lock lock(_net_mutex);
+
     if (_debug) {
        log_debug (_("Trying to read %d bytes from fd #%d"), nbytes, fd);
     }
@@ -1005,6 +1007,8 @@
     fd_set              fdset;
     int                 ret = -1;
 
+    boost::mutex::scoped_lock lock(_net_mutex);
+    
     // We need a writable, and not const point for byte arithmetic.
     byte_t *bufptr = const_cast<byte_t *>(buffer);
 

=== modified file 'libnet/network.h'
--- a/libnet/network.h  2009-03-06 01:10:48 +0000
+++ b/libnet/network.h  2009-03-20 00:00:54 +0000
@@ -227,10 +227,20 @@
     int getListenFd() const { return _listenfd; };
     void setListenFd(int x) { _listenfd = x; };
     const std::string& getURL() const { return _url; }
+    void setURL(const std::string& url) { _url = url; }
+
     const std::string& getProtocol() const  { return _protocol; }
+    void setProtocol(const std::string& proto) { _protocol = proto; }
+
     const std::string& getHost() const { return _host; }
+    void setHost(const std::string& host) { _host = host; }
+
     const std::string& getPortStr() const { return _portstr; }
+    void setPortStr(const std::string& port) { _portstr = port; }
+
     const std::string& getPath() const { return _path; }
+    void setPath(const std::string& path) { _path = path; }
+
     void setTimeout(int x) { _timeout = x; }
     int getTimeout() const { return _timeout; }
 
@@ -270,6 +280,7 @@
     bool        _connected;
     bool        _debug;
     int         _timeout;
+    size_t     _bytes_loaded;
     /// \var Handler::_handlers
     ///                Keep a list of all active network connections
     std::map<int, entry_t *> _handlers;
@@ -278,6 +289,7 @@
     // This is the mutex that controls access to the que.
     boost::mutex       _poll_mutex;
 #endif
+    boost::mutex       _net_mutex;
 };
 
 } // end of gnash namespace

=== modified file 'libnet/rtmp.cpp'
--- a/libnet/rtmp.cpp   2009-02-25 22:33:03 +0000
+++ b/libnet/rtmp.cpp   2009-03-14 00:33:42 +0000
@@ -842,6 +842,15 @@
 // interval. The byte boundary defaults to 128 bytes (video data), but can
 // be changed by the ChunkSize() command.
 bool
+RTMP::sendMsg(int channel, rtmp_headersize_e head_size,
+             size_t total_size, content_types_e type,
+             RTMPMsg::rtmp_source_e routing, amf::Buffer &data)
+{
+//    GNASH_REPORT_FUNCTION;
+    return sendMsg(getFileFd(), channel, head_size, total_size, type, routing, 
data.reference(), data.allocated());
+}
+
+bool
 RTMP::sendMsg(int fd, int channel, rtmp_headersize_e head_size,
              size_t total_size, content_types_e type,
              RTMPMsg::rtmp_source_e routing, amf::Buffer &data)
@@ -850,6 +859,16 @@
     return sendMsg(fd, channel, head_size, total_size, type, routing, 
data.reference(), data.allocated());
 }
 
+
+bool
+RTMP::sendMsg(int channel, rtmp_headersize_e head_size,
+             size_t total_size, content_types_e type,
+             RTMPMsg::rtmp_source_e routing, boost::uint8_t *data, size_t size)
+{
+//  GNASH_REPORT_FUNCTION;
+    return sendMsg(getFileFd(), channel, head_size, total_size, type, routing, 
data, size);
+}
+
 bool
 RTMP::sendMsg(int fd, int channel, rtmp_headersize_e head_size,
              size_t total_size, content_types_e type,

=== modified file 'libnet/rtmp.h'
--- a/libnet/rtmp.h     2009-03-16 23:34:13 +0000
+++ b/libnet/rtmp.h     2009-03-20 00:00:54 +0000
@@ -280,11 +280,17 @@
     // interval. (128 bytes for video data by default). Each message main
     // contain multiple packets.
     bool sendMsg(amf::Buffer &data);
-    bool sendMsg(int fd, int channel, rtmp_headersize_e head_size,
-             size_t total_size, content_types_e type,
-             RTMPMsg::rtmp_source_e routing, amf::Buffer &data);
-    bool sendMsg(int fd, int channel, rtmp_headersize_e head_size,
-             size_t total_size, content_types_e type,
+    bool sendMsg(int channel, rtmp_headersize_e head_size,
+             size_t total_size, content_types_e type,
+             RTMPMsg::rtmp_source_e routing, amf::Buffer &data);
+    bool sendMsg(int fd, int channel, rtmp_headersize_e head_size,
+             size_t total_size, content_types_e type,
+             RTMPMsg::rtmp_source_e routing, amf::Buffer &data);
+    bool sendMsg(int channel, rtmp_headersize_e head_size,
+                size_t total_size, content_types_e type,
+                RTMPMsg::rtmp_source_e routing, boost::uint8_t *data, size_t 
size);
+    bool sendMsg(int fd, int channel, rtmp_headersize_e head_size,
+                size_t total_size, content_types_e type,
                 RTMPMsg::rtmp_source_e routing, boost::uint8_t *data, size_t 
size);
     
 #if 0

=== modified file 'libnet/rtmp_client.cpp'
--- a/libnet/rtmp_client.cpp    2009-02-25 22:33:03 +0000
+++ b/libnet/rtmp_client.cpp    2009-03-13 01:44:38 +0000
@@ -57,7 +57,8 @@
 extern map<int, Handler *> handlers;
 
 RTMPClient::RTMPClient()
-    : _connections(0)
+    : _connected(false),
+      _connections(0)
 {
 //    GNASH_REPORT_FUNCTION;
 }
@@ -65,6 +66,8 @@
 RTMPClient::~RTMPClient()
 {
 //    GNASH_REPORT_FUNCTION;
+    _connected = false;
+
     _properties.clear();
 //    delete _body;
 }
@@ -396,7 +399,12 @@
     }
 
     ret = writeNet(_handshake->reference(), RTMP_HANDSHAKE_SIZE);
-    if ( ret <= 0 ) return false;
+    if ( ret <= 0 ) {
+       return false;
+    }
+
+    // Since the handshake completed sucessfully, we're connected.
+    _connected == true;
 
     return true;
 }

=== modified file 'libnet/rtmp_client.h'
--- a/libnet/rtmp_client.h      2009-03-16 23:34:13 +0000
+++ b/libnet/rtmp_client.h      2009-03-20 00:00:54 +0000
@@ -58,8 +58,15 @@
     boost::shared_ptr<amf::Buffer> encodeStreamOp(double id, rtmp_op_e op, 
bool flag, const std::string &name);
     boost::shared_ptr<amf::Buffer> encodeStreamOp(double id, rtmp_op_e op, 
bool flag, const std::string &name, double pos);
 
+    bool isConnected() { return _connected; };
+
+    std::string &getPath() { return _path; };
+    void setPath(std::string &x) { _path = x; };
+
     void dump();
   private:
+    std::string _path;
+    bool   _connected;
     double _connections;
 };
 

=== modified file 'testsuite/libnet.all/test_http.cpp'
--- a/testsuite/libnet.all/test_http.cpp        2009-03-16 23:34:13 +0000
+++ b/testsuite/libnet.all/test_http.cpp        2009-03-20 00:00:54 +0000
@@ -597,7 +597,7 @@
     ptr1 += *encstr;
     ptr1.resize();              // shrink the buffer to be the exact size of 
the data
 
-#if 0
+#if 1
     // FIXME: should be moved to server side only test case
     // Check the Server field
     AMF amf;
@@ -687,7 +687,6 @@
         runtest.fail("HTTP::formatEchoResponse()");
     }
 #endif
-    
     if (dbglogfile.getVerbosity() > 0) {
         http.dump();
     }

=== modified file 'testsuite/misc-ming.all/red5test.as'
--- a/testsuite/misc-ming.all/red5test.as       2008-12-20 02:03:30 +0000
+++ b/testsuite/misc-ming.all/red5test.as       2009-03-14 00:35:23 +0000
@@ -21,18 +21,20 @@
 
 #include "../actionscript.all/check.as"
 #include "../actionscript.all/utils.as"
+#include "../actionscript.all/dejagnu.as"
 
 stop();
 
 endOfTest = function()
 {
        //note("END OF TEST");
-       check_totals(9);
-       play();
+//        check_totals(9);
+    totals();
+    play();
 };
 
 if ( ! _root.hasOwnProperty('host') ) {
-    host=RED5_HOST;
+    host="localhost";
 }
 
 nc = new NetConnection;
@@ -40,22 +42,28 @@
 nc.onStatus = function()
 {
     this.statuses.push(arguments);
-       note('NetConnection.onStatus called with args: '+dumpObject(arguments));
+    note('NetConnection.onStatus called with args: '+dumpObject(arguments));
+};
+
+nc.onResult = function()
+{
+    this.statuses.push(arguments);
+    note('NetConnection.onResult called with args: '+dumpObject(arguments));
 };
 
 function ResultHandler() {
     this.onResult = function(result) {
         note('default onResult called with args: '+dumpObject(arguments));
     };
-    this.onCustom = function(result) {
-        note('default onCustom called with args: '+dumpObject(arguments));
-    };
-    this.onDebugEvents = function(result) {
-        note('default onDebugEvents called with args: '+dumpObject(arguments));
-    };
-    this.onStatus = function(result) {
-       note("default onStatus called with args: "+dumpObject(arguments));
-    };
+//     this.onCustom = function(result) {
+//         note('default onCustom called with args: '+dumpObject(arguments));
+//     };
+//     this.onDebugEvents = function(result) {
+//         note('default onDebugEvents called with args: 
'+dumpObject(arguments));
+//     };
+//     this.onStatus = function(result) {
+//     note("default onStatus called with args: "+dumpObject(arguments));
+//     };
 };
 
 // nc.onStatus: level:error, code:NetConnection.Connect.InvalidApp
@@ -63,35 +71,467 @@
 //nc.connect("rtmp://localhost/");
 
 // nc.onStatus: level:status, code:NetConnection.Connect.Success
-rtmpuri = "rtmp://"+host+"/echo";
+rtmpuri = "http://"+host+":5080/echo/gateway";;
 note("Connecting to "+rtmpuri);
 nc.connect(rtmpuri);
-check_equals(nc.isConnected, false); // not yet
+// The network connection is not opened at connect() time, but when
+// the first call() is made.
+check_equals(nc.isConnected, false);
 check_equals(nc.statuses.length, 0);
 
 o=new ResultHandler();
 o.onResult = function()
 {
+    note("Got a result back from the server.");
     check_equals(nc.isConnected, true); // now it is connected
     check_equals(nc.statuses.length, 1);
     lastStatusArgs = nc.statuses[nc.statuses.length-1];
     check_equals(lastStatusArgs[0].level, 'status');
     check_equals(lastStatusArgs[0].code, 'NetConnection.Connect.Success');
-       check_equals(arguments.toString(), '1');
+};
+
+
+//
+// The Red5 echo tests Null, Undefined, Boolean True, Boolean False,
+// String, Number, Array, Object, Date, Custom Class Remote Class
+//
+
+// This call starts the actual network connection
+result1=false;;
+o.onResult = function()
+{
+    note("Got a null result back from the server."+dumpObject(arguments));
+    note(arguments[0]);
+    if (arguments.length == 1) {
+       if (arguments[0] == null) {
+           result1=true;
+       }
+    }
+};
+nc.call("echo", o, null);
+
+result2=false;
+o=new ResultHandler();
+o.onResult = function()
+{
+    note("Got an undefined result back from the 
server."+dumpObject(arguments));
+    if (arguments.length == 1) {
+       if (arguments[0] == undefined) {
+           result2=true;
+       }
+    }
+};
+nc.call("echo", o, undefined);
+
+// bt=new Boolean(true);
+// o=new ResultHandler();
+// o.onResult = function()
+// {
+//     check_equals(arguments.toString(), trued);
+// };
+// nc.call("echo", o, bt);
+
+// bf=new Boolean(false);
+// nc.call("echo", o, bf);
+
+// Empty String
+result3=false;
+tstr = new String();
+o=new ResultHandler();
+o.onResult = function()
+{
+    note("Got a string result back from the server."+dumpObject(arguments));
+    if (arguments.length == 1) {
+       if (arguments[0].length == 0) {
+           result3 = true;
+       }
+    }
+};
+nc.call("echo", o, tstr);
+
+// Hello World!
+result4=false;
+tstr2 = "Hello World!";
+o=new ResultHandler();
+o.onResult = function()
+{
+    note("Got a string result back from the server."+dumpObject(arguments));
+//     note("ARG4 is: " +dumpObject(arguments[0]));
+    str = arguments[0].toString();
+    if (arguments.length == 1) {
+       if ((arguments[0].length == 12)
+           && (arguments[0].toString() == "Hello World!")) {
+           result4 = true;
+       }
+    }
+};
+nc.call("echo", o, tstr2);
+
+// test1,test2,test3,test4
+
+// Number 0
+result5=false;
+o=new ResultHandler();
+o.onResult = function()
+{
+    note("Got a numerical 0 result back from the 
server."+dumpObject(arguments));
+    if (arguments.length == 1) {
+       if (arguments[0] == 0) {
+           result5 = true;
+       }
+    }
+};
+nc.call("echo", o, 0);
+
+// Number 1
+result6=false;
+o=new ResultHandler();
+o.onResult = function()
+{
+    note("Got a numerical 1 result back from the 
server."+dumpObject(arguments));
+    if (arguments.length == 1) {
+       if (arguments[0] == 1) {
+           result6 = true;
+       }
+    }
 };
 nc.call("echo", o, 1);
 
-o=new ResultHandler();
-o.onResult = function()
-{
-       check_equals(arguments.toString(), '1,2,3');
-};
-nc.call("echo", o, 1, 2, 3);
-
-o=new ResultHandler();
-o.onResult = function()
-{
-       check_equals(arguments.toString(), '1,two,true,4,5,6');
-    endOfTest();
-};
-nc.call("echo", o, 1, 'two', true, [4,5,6]);
+// Number -1
+result7=false;
+o=new ResultHandler();
+o.onResult = function()
+{
+    note("Got a numerical -1 result back from the 
server."+dumpObject(arguments));
+    if ((arguments.length == 1)) {
+       note("FIXME: "+arguments[0].to_number());
+       result7 = true;
+    }
+};
+nc.call("echo", o, -1);
+
+// Number 256
+result8=false;
+o=new ResultHandler();
+o.onResult = function()
+{
+    note("Got a numerical 256 result back from the 
server."+dumpObject(arguments));
+    if (arguments.length == 1) {
+       if (arguments[0] == 256) {
+           result8 = true;
+       }
+    }
+};
+nc.call("echo", o, 256);
+
+// Number -256
+result9=false;
+o=new ResultHandler();
+o.onResult = function()
+{
+    note("Got a numerical -256 result back from the 
server."+dumpObject(arguments));
+    if (arguments.length == 1) {
+       if (arguments[0] == -256) {
+           result9 = true;
+       }
+    }
+};
+nc.call("echo", o, -256);
+
+// Number 65536
+result10=false;
+o=new ResultHandler();
+o.onResult = function()
+{
+    note("Got a numerical 65536 result back from the 
server."+dumpObject(arguments));
+    if (arguments.length == 1) {
+       if (arguments[0] == 65536) {
+           result10 = true;
+       }
+    }
+};
+nc.call("echo", o, 65536);
+
+// Number -65536
+result11=false;
+o=new ResultHandler();
+o.onResult = function()
+{
+    note("Got a numerical -65536 result back from the 
server."+dumpObject(arguments));
+    if (arguments.length == 1) {
+       if (arguments[0] == -65536) {
+           result11 = true;
+       }
+    }
+};
+nc.call("echo", o, -65536);
+
+// 1.5
+result12=false;
+o=new ResultHandler();
+o.onResult = function()
+{
+    note("Got a numerical 1.5 result back from the 
server."+dumpObject(arguments));
+    if (arguments.length == 1) {
+       if (arguments[0] == 1.5) {
+           result12 = true;
+       }
+    }
+};
+nc.call("echo", o, 1.5);
+
+// -1.5
+result13=false;
+o=new ResultHandler();
+o.onResult = function()
+{
+    note("Got a numerical -1.5 result back from the 
server."+dumpObject(arguments));
+    if (arguments.length == 1) {
+       if (arguments[0] == -1.5) {
+           result13 = true;
+       }
+    }
+};
+nc.call("echo", o, -1.5);
+
+// Number NaN
+result14=false;
+o=new ResultHandler();
+o.onResult = function()
+{
+    note("Got a numerical NaN result back from the 
server."+dumpObject(arguments));
+    if (arguments.length == 1) {
+       if (arguments[0] == NaN) {
+           result14 = true;
+       }
+    }
+};
+nc.call("echo", o, NaN);
+
+// Number Infinity
+result15=false;
+o=new ResultHandler();
+o.onResult = function()
+{
+    note("Got an numerical infinity result back from the 
server."+dumpObject(arguments));
+    if (arguments.length == 1) {
+       if (arguments[0] == infinity) {
+           result15 = true;
+       }
+    }
+};
+nc.call("echo", o, infinity);
+
+// Number -Infinity
+result16=false;
+o=new ResultHandler();
+o.onResult = function()
+{
+    note("Got a numerical -infinity result back from the 
server."+dumpObject(arguments));
+    if (arguments.length == 1) {
+       if (arguments[0] == -infinity) {
+           result16 = true;
+       }
+    }
+};
+nc.call("echo", o, -infinity);
+
+// o=new ResultHandler();
+// o.onResult = function()
+// {
+//     note("Got a result back from the server.");
+//     check_equals(arguments.toString(), '1,two,true,4,5,6');
+//     endOfTest();
+// };
+// nc.call("echo", o, 1, 'two', true, [4,5,6]);
+
+// o=new ResultHandler();
+// o.onResult = function()
+// {
+//     note("Got a result back from the server.");
+//     check_equals(arguments.toString(), '1,2,3');
+// };
+// nc.call("echo", o, 1, 2, 3);
+
+
+// Test empty array
+tar = new Array();
+result17=false;
+o=new ResultHandler();
+o.onResult = function()
+{
+    note("Got an empty array result back from the 
server."+dumpObject(arguments));
+    if (arguments.length == 1) {
+       if (arguments[0].length == 0) {
+           result17 = true;
+       }
+    }
+};
+nc.call("echo", o, tar);
+
+// Test array with only one item
+result18=false;
+tar = new Array();
+tar.push(1);
+o=new ResultHandler();
+o.onResult = function()
+{
+    note("Got a single item array result back from the 
server."+dumpObject(arguments));
+    if (arguments.length == 1) {
+       if (arguments[0][0] == 1) {
+           result18 = true;
+       }
+    }
+};
+nc.call("echo", o, tar);
+
+// Test array with multiple items
+result19=false;
+tar = new Array();
+tar.push(1);
+tar.push(2);
+tar.push(3);
+o=new ResultHandler();
+o.onResult = function()
+{
+    note("Got an 3 item array result back from the 
server."+dumpObject(arguments));
+    if ((arguments.length == 1) && (arguments[0].length == 3)) {
+       if ((arguments[0][0] == 1) && (arguments[0][1] == 2) && 
(arguments[0][2] == 3))  {
+           result19 = true;
+       }
+    }
+//    note(arguments[0].toString());
+//    check_equals(arguments[0].toString(), "1.2.3");
+};
+nc.call("echo", o, tar);
+
+// Test sparse array
+result20=false;
+tar2 = new Array();
+tar2.push(1);
+tar2.push();
+tar2.push();
+tar2.push();
+tar2.push(5);
+o=new ResultHandler();
+o.onResult = function()
+{
+    note("Got a sparse result back from the server."+dumpObject(arguments));
+    if ((arguments.length == 1) && (arguments[0].length == 2)) {
+       if ((arguments[0][0] == 1) && (arguments[0][1] == 5))  {
+           result20 = true;
+       }   
+    }
+//    check_equals(arguments.toString(), "1..,,5");
+};
+nc.call("echo", o, tar2);
+
+// Do the tests to see what happened last, to give the callbacks time
+// to be executed, as they're a background thread.
+if (result1) {
+    pass("Echo NULL Object");
+} else {
+    fail("Echo NULL Object");
+}
+
+if (result2) {
+    pass("Echo UNDEFINED Object");
+} else {
+    fail("Echo UNDEFINED Object");
+}
+
+if (result3) {
+    pass("Echo empty String");
+} else {
+    fail("Echo empty String");
+}
+
+if (result4) {
+    pass("Echo short String");
+} else {
+    fail("Echo short String");
+}
+
+if (result5) {
+    pass("Echo Number 0");
+} else {
+    fail("Echo Number 0");
+}
+
+if (result6) {
+    pass("Echo Number 1");
+} else {
+    fail("Echo Number 1");
+}
+
+if (result7) {
+    pass("Echo Number -1");
+} else {
+    fail("Echo Number -1");
+}
+if (result8) {
+    pass("Echo Number 256");
+} else {
+    fail("Echo Number 256");
+}
+if (result9) {
+    pass("Echo Number -256");
+} else {
+    fail("Echo Number -256");
+}
+if (result10) {
+    pass("Echo Number 65536");
+} else {
+    fail("Echo Number 65536");
+}
+if (result11) {
+    pass("Echo Number -65536");
+} else {
+    fail("Echo Number -65536");
+}
+if (result12) {
+    pass("Echo Number 1.5");
+} else {
+    fail("Echo Number 1.5");
+}
+if (result13) {
+    pass("Echo Number -1.5");
+} else {
+    fail("Echo Number -1.5");
+}
+if (result14) {
+    pass("Echo Number NaN");
+} else {
+    fail("Echo Number NaN");
+}
+if (result15) {
+    pass("Echo Number Infinity");
+} else {
+    fail("Echo Number Infinity");
+}
+if (result16) {
+    pass("Echo Number -Infinity");
+} else {
+    fail("Echo Number -Infinity");
+}
+
+if (result17) {
+    pass("Echo empty array");
+} else {
+    fail("Echo empty array");
+}
+if (result18) {
+    pass("Echo 1 item array");
+} else {
+    fail("Echo 1 item array");
+}
+if (result19) {
+    pass("Echo 3 item array");
+} else {
+    fail("Echo 3 item array");
+}
+if (result20) {
+    pass("Echo sparse array");
+} else {
+    fail("Echo sparse array");
+}

=== modified file 'utilities/processor.cpp'
--- a/utilities/processor.cpp   2009-02-25 22:33:03 +0000
+++ b/utilities/processor.cpp   2009-03-12 03:49:13 +0000
@@ -517,8 +517,7 @@
         // We reached the end, done !
         if (curr_frame >= md->get_frame_count() - 1 )
         {
-            if ( allowed_end_hits && ++end_hitcount >= allowed_end_hits )
-            {
+            if ( allowed_end_hits && ++end_hitcount >= allowed_end_hits ) {
                 log_debug("exiting after %d" 
                        " times last frame was reached", end_hitcount);
                     break;
@@ -528,6 +527,13 @@
         // We didn't advance 
         if (curr_frame == last_frame)
         {
+           // We also want to exit if allowed_end_hits is set, and
+           // we're not advancing.
+           if ( allowed_end_hits && ++end_hitcount >= allowed_end_hits ) {
+                log_debug("exiting after %d" 
+                         " times last frame was reached", end_hitcount);
+                    break;
+            }
             // Max stop counts reached, kick it
             if ( secondsSinceLastAdvance() > waitforadvance )
             {


reply via email to

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