gnash-commit
[Top][All Lists]
Advanced

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

[Gnash-commit] /srv/bzr/gnash/trunk r9580: Initial remoting support. Nee


From: Sandro Santilli
Subject: [Gnash-commit] /srv/bzr/gnash/trunk r9580: Initial remoting support. Needs further cleanup, test and improvement .
Date: Tue, 12 Aug 2008 12:42:11 +0200
User-agent: Bazaar (1.5)

------------------------------------------------------------
revno: 9580
committer: Sandro Santilli <address@hidden>
branch nick: trunk
timestamp: Tue 2008-08-12 12:42:11 +0200
message:
  Initial remoting support. Needs further cleanup, test and improvement .
modified:
  libcore/asobj/NetConnection.cpp
  libcore/asobj/NetConnection.h
    ------------------------------------------------------------
    revno: 9577.2.1
    author: Jason Woofenden <address@hidden>
    committer: Sandro Santilli <address@hidden>
    branch nick: mybranch
    timestamp: Tue 2008-08-12 10:21:53 +0200
    message:
      Initial remoting draft
    modified:
      libcore/asobj/NetConnection.cpp
      libcore/asobj/NetConnection.h
    ------------------------------------------------------------
    revno: 9577.2.2
    committer: Sandro Santilli <address@hidden>
    branch nick: mybranch
    timestamp: Tue 2008-08-12 10:40:27 +0200
    message:
      Use proper log channels 
    modified:
      libcore/asobj/NetConnection.cpp
    ------------------------------------------------------------
    revno: 9577.2.3
    author: Jason Woofenden <address@hidden>
    committer: Sandro Santilli <address@hidden>
    branch nick: mybranch
    timestamp: Tue 2008-08-12 10:45:51 +0200
    message:
      Fix handling of amf response headers. Fixes simple amfphp service.
    modified:
      libcore/asobj/NetConnection.cpp
    ------------------------------------------------------------
    revno: 9577.2.4
    committer: Sandro Santilli <address@hidden>
    branch nick: mybranch
    timestamp: Tue 2008-08-12 10:57:20 +0200
    message:
      Keep AMFQueue::IOChannel by scoped_ptr
    modified:
      libcore/asobj/NetConnection.cpp
    ------------------------------------------------------------
    revno: 9577.2.5
    committer: Sandro Santilli <address@hidden>
    branch nick: mybranch
    timestamp: Tue 2008-08-12 11:10:03 +0200
    message:
      fix doxygen dox, use a const static for NCCALLREPLYMAX
    modified:
      libcore/asobj/NetConnection.cpp
    ------------------------------------------------------------
    revno: 9577.2.6
    committer: Sandro Santilli <address@hidden>
    branch nick: mybranch
    timestamp: Tue 2008-08-12 11:44:44 +0200
    message:
      Make amf->value decoder stack-based
    modified:
      libcore/asobj/NetConnection.cpp
    ------------------------------------------------------------
    revno: 9577.2.7
    committer: Sandro Santilli <address@hidden>
    branch nick: mybranch
    timestamp: Tue 2008-08-12 11:59:57 +0200
    message:
      make AMFQueue a concrete class (don't derive from as_object)
    modified:
      libcore/asobj/NetConnection.cpp
      libcore/asobj/NetConnection.h
=== modified file 'libcore/asobj/NetConnection.cpp'
--- a/libcore/asobj/NetConnection.cpp   2008-06-09 18:08:25 +0000
+++ b/libcore/asobj/NetConnection.cpp   2008-08-12 09:59:57 +0000
@@ -25,6 +25,8 @@
 #include <iostream>
 #include <string>
 #include <new>
+#include <boost/scoped_ptr.hpp>
+
 #include "NetConnection.h"
 #include "log.h"
 #include "GnashException.h"
@@ -38,6 +40,14 @@
 
 #include "FLVParser.h"
 
+// for NetConnection.call()
+#include "VM.h"
+#include "array.h"
+#include "amf.h"
+#include "SimpleBuffer.h"
+#include "timers.h"
+using namespace amf;
+
 namespace gnash {
 
 static as_value netconnection_new(const fn_call& fn);
@@ -51,17 +61,13 @@
 
 NetConnection::NetConnection()
        :
-       as_object(getNetConnectionInterface())
+       as_object(getNetConnectionInterface()),
+       call_queue(0)
 {
        attachProperties();
 }
 
 
-NetConnection::~NetConnection()
-{
-}
-
-
 /*public*/
 bool NetConnection::openConnection(const std::string& url)
 {
@@ -132,7 +138,11 @@
 {
        std::string completeUrl;
        if (_prefixUrl.size() > 0) {
-               completeUrl += _prefixUrl + "/" + url;
+               if(url.size() > 0) {
+                       completeUrl += _prefixUrl + "/" + url;
+               } else {
+                       completeUrl += _prefixUrl;
+               }
        } else {
                completeUrl += url;
        }
@@ -308,6 +318,7 @@
        return as_value(true);
 }
 
+
 as_value
 NetConnection::addHeader_method(const fn_call& fn)
 {
@@ -318,13 +329,651 @@
        return as_value();
 }
 
+boost::uint16_t
+readNetworkShort(const boost::uint8_t* buf) {
+       boost::uint16_t s = buf[0] << 8 | buf[1];
+       return s;
+}
+
+boost::uint16_t
+readNetworkLong(const boost::uint8_t* buf) {
+       boost::uint32_t s = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
+       return s;
+}
+
+// Pass pointer to buffer and pointer to end of buffer. Buffer is raw AMF
+// encoded data. Must start with a type byte unless third parameter is set.
+//
+// On success, sets the given as_value and returns true.
+// On error (premature end of buffer, etc.) returns false and leaves the given
+// as_value untouched.
+//
+// IF you pass a fourth parameter, it WILL NOT READ A TYPE BYTE, but use what
+// you passed instead.
+//
+// The l-value you pass as the first parameter (buffer start) is updated to
+// point just past the last byte parsed
+//
+// TODO restore first parameter on parse errors
+//
+static bool
+amf0_read_value(boost::uint8_t *&b, boost::uint8_t *end, as_value& ret, int 
inType = -1)
+{
+       boost::uint16_t si;
+       boost::uint16_t li;
+       double dub;
+       int amf_type;
+
+       if(b > end) {
+               return false;
+       }
+       if(inType != -1) {
+               amf_type = inType;
+       } else {
+               if(b < end) {
+                       amf_type = *b; b += 1;
+               } else {
+                       return false;
+               }
+       }
+
+       switch(amf_type) {
+               case Element::NUMBER_AMF0:
+                       if(b + 8 > end) {
+                               log_error(_("NetConnection.call(): server sent 
us a number which goes past the end of the data sent"));
+                               return false;
+                       }
+                       dub = *(reinterpret_cast<double*>(b)); b += 8;
+                       swapBytes(&dub, 8);
+                       log_debug("nc read double: %e", dub);
+                       ret.set_double(dub);
+                       return true;
+               case Element::STRING_AMF0:
+                       if(b + 2 > end) {
+                               log_error(_("NetConnection.call(): server sent 
us a string which goes past the end of the data sent"));
+                               return false;
+                       }
+                       si = readNetworkShort(b); b += 2;
+                       if(b + si > end) {
+                               log_error(_("NetConnection.call(): server sent 
us a string which goes past the end of the data sent"));
+                               return false;
+                       }
+
+                       {
+                               std::string str(reinterpret_cast<char *>(b), 
si); b += si;
+                               log_debug("nc read string: %s", str);
+                               ret.set_string(str);
+                               return true;
+
+                       }
+                       break;
+               case Element::STRICT_ARRAY_AMF0:
+                       {
+                               boost::intrusive_ptr<as_array_object> array(new 
as_array_object());
+                               li = readNetworkLong(b); b += 4;
+                               log_debug("nc starting read of array with %i 
elements", li);
+                               as_value arrayElement;
+                               for(int i = 0; i < li; ++i)
+                               {
+                                       if ( ! amf0_read_value(b, end, 
arrayElement) )
+                                       {
+                                               return false;
+                                       }
+                                       array->push(arrayElement);
+                               }
+
+                               ret.set_as_object(array);
+                               return true;
+                       }
+               case Element::OBJECT_AMF0:
+                       {
+                               // need this? boost::intrusive_ptr<as_object> 
obj(new as_object(getObjectInterface()));
+                               boost::intrusive_ptr<as_object> obj(new 
as_object());
+                               log_debug("nc starting read of object");
+                               as_value tmp;
+                               std::string keyString;
+                               for(;;)
+                               {
+                                       if ( ! amf0_read_value(b, end, tmp, 
Element::STRING_AMF0) )
+                                       {
+                                               return false;
+                                       }
+                                       keyString = tmp.to_string();
+
+                                       if ( keyString.empty() )
+                                       {
+                                               if(b < end) {
+                                                       b += 1; // AMF0 has a 
redundant "object end" byte
+                                               } else {
+                                                       log_error("AMF buffer 
terminated just before object end byte. continueing anyway.");
+                                               }
+                                               ret.set_as_object(obj);
+                                               return true;
+                                       }
+
+                                       if ( ! amf0_read_value(b, end, tmp) )
+                                       {
+                                               return false;
+                                       }
+                                       obj->init_member(keyString, tmp);
+                               }
+                       }
+               case Element::UNDEFINED_AMF0:
+                       {
+                               ret.set_undefined();
+                               return true;
+                       }
+               case Element::NULL_AMF0:
+                       {
+                               ret.set_null();
+                               return true;
+                       }
+               // TODO define other types (function, sprite, etc)
+               default:
+                       log_unimpl("NetConnection.call(): server sent us a 
value of unsupported type: %i", amf_type);
+                       return false;
+       }
+
+       // this function was called with a zero-length buffer
+       return false;
+}
+
+
+/// 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 AMFQueue {
+private:
+       NetConnection& _nc;
+       static const int NCCALLREPLYMAX=200000;
+       std::map<std::string, boost::intrusive_ptr<as_object> > callbacks;
+       SimpleBuffer postdata;
+       URL url;
+       boost::scoped_ptr<IOChannel> connection;
+       SimpleBuffer reply;
+       int reply_start;
+       int reply_end;
+       int queued_count;
+       uint8_t am_ticking;
+       unsigned int ticker;
+public:
+       AMFQueue(NetConnection& nc, URL url)
+               :
+               _nc(nc),
+               postdata(),
+               url(url),
+               connection(0),
+               reply(NCCALLREPLYMAX),
+               reply_start(0),
+               reply_end(0),
+               queued_count(0),
+               am_ticking(0)
+       {
+               // leave space for header
+               postdata.append("\000\000\000\000\000\000", 6);
+       }
+
+       ~AMFQueue() {
+               stop_ticking();
+       }
+       
+       void enqueue(SimpleBuffer &amf, std::string identifier, 
boost::intrusive_ptr<as_object> callback) {
+               push_amf(amf);
+               push_callback(identifier, callback);
+               //log_aserror("NetConnection::call(): called with a non-object 
as the callback");
+       };
+       void enqueue(SimpleBuffer &amf) {
+               push_amf(amf);
+       };
+       
+       // tick is called automatically on intervals (hopefully only between
+       // actionscript frames)
+       //
+       // it handles all networking for NetConnection::call() and dispatches
+       // callbacks when needed
+       void tick() {
+               log_debug("tick running");
+               if(connection) {
+                       log_debug("have connection");
+                       int read = connection->readNonBlocking(reply.data() + 
reply_end, NCCALLREPLYMAX - reply_end);
+                       if(read > 0) {
+                               log_debug("read '%1%' bytes:", read);
+                               log_debug(_(hexify(reply.data() + reply_end, 
read, false).c_str()));
+                               reply_end += read;
+                       }
+
+                       // There is no way to tell if we have a whole amf reply 
without
+                       // parsing everything
+                       //
+                       // The reply format has a header field which specifies 
the
+                       // number of bytes in the reply, but potlatch sends 
0xffffffff
+                       // and works fine in the proprietary player
+                       //
+                       // For now we just wait until we have the full reply.
+                       //
+                       // FIXME make this parse on other conditions, 
including: 1) when
+                       // the buffer is full, 2) when we have a "length in 
bytes" value
+                       // thas is satisfied
+                       
+                       if(connection->eof() && reply_end > 8) {
+                               log_debug("hit eof");
+                               boost::int16_t si;
+                               boost::uint16_t li;
+                               boost::uint8_t *b = reply.data() + reply_start;
+                               boost::uint8_t *end = reply.data() + reply_end;
+
+                               // parse header
+                               b += 2; // skip version indicator and client id
+                               si = readNetworkShort(b); b += 2; // number of 
headers
+                               uint8_t headers_ok = 1;
+                               if(si != 0)
+                               {
+                                       log_debug("NetConnection::call(): amf 
headers section parsing");
+                                       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;
+                                               }
+                                               b += si;
+                                               if ( b + 5 > end ) {
+                                                       headers_ok = 0;
+                                                       break;
+                                               }
+                                               b += 5; // skip past bool and 
length long
+                                               if( !amf0_read_value(b, end, 
tmp) )
+                                               {
+                                                       headers_ok = 0;
+                                                       break;
+                                               }
+                                               log_debug("Header value %s", 
tmp);
+                                       }
+                               }
+
+                               if(headers_ok == 1) {
+
+                                       si = readNetworkShort(b); b += 2; // 
number of replies
+
+                                       // TODO consider counting number of 
replies we
+                                       // actually parse and doing something 
if it
+                                       // doesn't match this value (does it 
matter?
+                                       if(si > 0) {
+                                               // parse replies until we get a 
parse error or we reach the end of the buffer
+                                               while(b < end) {
+                                                       if(b + 2 > end) break;
+                                                       si = 
readNetworkShort(b); b += 2; // reply length
+                                                       if(si < 11) {
+                                                               
log_error("NetConnection::call(): reply message name too short");
+                                                               break;
+                                                       }
+                                                       if(b + si > end) break;
+                                                       // TODO check that the 
last 9 bytes are "/onResult"
+                                                       // this should either 
split on the 2nd / or require onResult or onStatus
+                                                       std::string 
id(reinterpret_cast<char*>(b), si - 9);
+                                                       b += si;
+
+                                                       // parse past unused 
string in header
+                                                       if(b + 2 > end) break;
+                                                       si = 
readNetworkShort(b); b += 2; // reply length
+                                                       if(b + si > end) break;
+                                                       b += si;
+
+                                                       // this field is 
supposed to hold the
+                                                       // total number of 
bytes in the rest of
+                                                       // this particular 
reply value, but
+                                                       // openstreetmap.org 
(which works great
+                                                       // in the adobe player) 
sends
+                                                       // 0xffffffff. So we 
just ignore it
+                                                       if(b + 4 > end) break;
+                                                       li = 
readNetworkLong(b); b += 4; // reply length
+
+                                                       log_debug("about to 
parse amf value");
+                                                       // this updates b to 
point to the next unparsed byte
+                                                       as_value reply_as_value;
+                                                       if ( ! 
amf0_read_value(b, end, reply_as_value) )
+                                                       {
+                                                               
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;
+                                                       }
+                                                       log_debug("parsed amf");
+
+                                                       // 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) {
+                                                               
log_debug("calling callback");
+                                                               
string_table::key callback_method = callback -> getVM() . getStringTable() . 
find(std::string("onResult"));
+                                                               // FIXME check 
if above line can fail and we have to react
+                                                               
callback->callMethod(callback_method, reply_as_value);
+                                                               
log_debug("callback called");
+                                                       } else {
+                                                               
log_debug("couldn't find callback object");
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+
+                       if(connection->eof()) {
+                               log_debug("deleting connection");
+                               connection.reset();
+                               reply_start = 0;
+                               reply_end = 0;
+
+                               // FIXME send onStatus callback to any callback 
objects from this batch (must change some code to distinguish somehow)
+                       }
+               }
+
+               if(connection == 0 && queued_count > 0) {
+                       log_debug("creating connection");
+                       // 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());
+                       log_debug("NetConnection.call(): encoded args from %1% 
calls:", queued_count);
+                       log_debug("%s", hexify(postdata.data(), 
postdata.size(), false));
+                       queued_count = 0;
+                       connection.reset( 
StreamProvider::getDefaultInstance().getStream(url, postdata_str) );
+                       postdata.resize(6);
+                       log_debug("connection created");
+               }
+
+               if(connection == 0 && queued_count == 0) {
+                       log_debug("stopping ticking");
+                       stop_ticking();
+                       log_debug("ticking stopped");
+               }
+       };
+
+       static as_value amfqueue_tick_wrapper(const fn_call& fn)
+       {
+               boost::intrusive_ptr<NetConnection> ptr = 
ensureType<NetConnection>(fn.this_ptr);
+               // FIXME check if it's possible for the URL of a NetConnection 
to change between call()s
+               ptr->call_queue->tick();
+               return as_value();
+       };
+
+private:
+       void start_ticking() {
+               if(am_ticking) {
+                       return;
+               }
+
+               boost::intrusive_ptr<builtin_function> ticker_as = \
+                       new builtin_function(&AMFQueue::amfqueue_tick_wrapper);
+               std::auto_ptr<Timer> timer(new Timer);
+               unsigned long delayMS = 500; // FIXME crank up to 50 or so
+               timer->setInterval(*ticker_as, delayMS, &_nc);
+               ticker = _nc.getVM().getRoot().add_interval_timer(timer, true);
+
+               am_ticking = 1;
+       }
+       void push_amf(SimpleBuffer &amf) {
+               log_debug("pushing amf");
+               postdata.append(amf.data(), amf.size());
+               queued_count++;
+               log_debug("pushed amf");
+               start_ticking();
+       }
+       void stop_ticking() {
+               if(!am_ticking) {
+                       return;
+               }
+               _nc.getVM().getRoot().clear_interval_timer(ticker);
+               am_ticking = 0;
+       }
+       void push_callback(std::string id, boost::intrusive_ptr<as_object> 
callback) {
+               callbacks.insert(std::pair<std::string, 
boost::intrusive_ptr<as_object> >(id, callback));
+       }
+       boost::intrusive_ptr<as_object> pop_callback(std::string id) {
+               std::map<std::string, boost::intrusive_ptr<as_object> 
>::iterator it = callbacks.find(id);
+               if(it != callbacks.end()) {
+                       boost::intrusive_ptr<as_object> callback = it->second;
+                       //boost::intrusive_ptr<as_object> callback;
+                       //callback = it.second;
+                       callbacks.erase(it);
+                       return callback;
+               } else {
+                       return 0;
+               }
+       }
+};
+
 as_value
 NetConnection::call_method(const fn_call& fn)
 {
+       static int call_number = 0;
        boost::intrusive_ptr<NetConnection> ptr = 
ensureType<NetConnection>(fn.this_ptr); 
-       UNUSED(ptr);
+
+       if (fn.nargs < 1)
+       {
+               IF_VERBOSE_ASCODING_ERRORS(
+               log_aserror(_("NetConnection.call(): needs at least one 
argument"));
+               );
+               return as_value(false); // FIXME should we return true anyway?
+       }
+
+       as_value& methodName_as = fn.arg(0);
+       if (!methodName_as.is_string()) {
+                IF_VERBOSE_ASCODING_ERRORS(
+                log_aserror(_("NetConnection.call(): first argument 
(methodName) must be a string"));
+                );
+               return as_value(false); // FIXME should we return true anyway?
+       }
+
+       // TODO: arg(1) is the response object. let it know when data comes back
+       boost::intrusive_ptr<as_object> asCallback = 0;
+       if(fn.nargs > 1) {
+               if(fn.arg(1).is_object()) {
+                       asCallback = (fn.arg(1).to_object());
+               } else {
+                       log_aserror("NetConnection::call(): called with a 
non-object as the callback");
+               }
+       }
+
+       SimpleBuffer *buf = new SimpleBuffer(32);
+
+       std::string methodName = methodName_as.to_string();
+
+       // junk at the top (version, client id, 0 headers, 1 body)
+       // this is done by AMFQueue now: 
buf->append("\000\000\000\000\000\001", 6);
+
+       // method name
+       buf->appendNetworkShort(methodName.size());
+       buf->append(methodName.c_str(), methodName.size());
+
+       // client id (result number) as counted string
+       // the convention seems to be / followed by a unique (ascending) number
+       call_number += 1;
+       // TODO is there a better way to do this number->string conversion?
+       std::string call_number_str((boost::format("/%1%") % 
call_number).str());
+       buf->appendNetworkShort(call_number_str.size());
+       buf->append(call_number_str.c_str(), call_number_str.size());
+
+       size_t total_size_offset = buf->size();
+       buf->append("\000\000\000\000", 4); // total size to be filled in later
+
+
+       // encode array of arguments to remote method
+       buf->appendByte(Element::STRICT_ARRAY_AMF0);
+       buf->appendNetworkLong(fn.nargs - 2);
+       if(fn.nargs > 2) {
+               for(unsigned int i = 2; i < fn.nargs; ++i) {
+                       if(fn.arg(i).is_string()) {
+                               buf->appendByte(Element::STRING_AMF0);
+                               std::string str = fn.arg(i).to_string();
+                               buf->appendNetworkShort(str.size());
+                               buf->append(str.c_str(), str.size());
+                       // FIXME implement this
+                       //} else if(fn.arg(i).is_function()) {
+                       //      as_function f = fn.arg(i).to_function();
+                       //      tmp = AMF::encodefunction(f);
+                       } else if(fn.arg(i).is_number()) {
+                               double d = fn.arg(i).to_number();
+                               buf->appendByte(Element::NUMBER_AMF0);
+                               swapBytes(&d, 8); // this actually only swapps 
on little-endian machines
+                               buf->append(&d, 8);
+                       // FIXME implement this
+                       //} else if(fn.arg(i).is_object()) {
+                       //      boost::intrusive_ptr<as_object> o = 
fn.arg(i).to_object();
+                       //      tmp = AMF::encodeObject(o);
+                       } else {
+                               log_error(_("NetConnection.call(): unknown 
argument type"));
+                               buf->appendByte(Element::UNDEFINED_AMF0);
+                       }
+               }
+       }
+
+       // Set the "total size" parameter.
+       *(reinterpret_cast<uint32_t*>(buf->data() + total_size_offset)) = 
htonl(buf->size() - 4 - total_size_offset);
+       
+
+       log_debug(_("NetConnection.call(): encoded args: "));
+       log_debug("%s", hexify(buf->data(), buf->size(), false));
+
+       // FIXME check that ptr->_prefixURL is valid
+       URL url(ptr->validateURL(std::string()));
+
+
+
+       // FIXME check if it's possible for the URL of a NetConnection to 
change between call()s
+       if(ptr->call_queue == 0) {
+               ptr->call_queue = new AMFQueue(*ptr, url);
+       }
+
+       if(asCallback) {
+               //boost::intrusive_ptr<as_object> 
intrusive_callback(asCallback);
+               log_debug("calling enqueue with callback");
+               ptr->call_queue->enqueue(*buf, call_number_str, asCallback);
+               //? delete asCallback;
+       } else {
+               log_debug("calling enqueue without callback");
+               ptr->call_queue->enqueue(*buf);
+       }
+       log_debug("called enqueue");
+
+#if 0
+       
+       std::string postdata(reinterpret_cast<char*>(buf->data()), buf->size());
+       std::auto_ptr<IOChannel> 
stream(StreamProvider::getDefaultInstance().getStream(url, postdata));
+       SimpleBuffer reply(256);
+       int count = 0;
+       int read_size;
+
+       // FIXME this needs to be in a thread, as does the getStream() above
+       //while(!stream->eof()) {
+       //      reply.reserve(count + 256);
+       //      read_size = stream->readNonBlocking(reply.data() + count, 256);
+       //      if(read_size > 0) { // currently readNonBlocking returns -1 on 
error
+       //              count += read_size;
+       //      }
+       //}
+       while(!stream->eof()) {
+               reply.reserve(count + 50000);
+               read_size = stream->read(reply.data() + count, 50000);
+               log_debug("stream->read() returned: %i", read_size);
+               if(read_size > 0) { // currently readNonBlocking returns -1 on 
error
+                       if(read_size == 50000) { // ?????
+                               log_error("stream->read() said that it read 
50000 characters... seems unlikely");
+                       } else {
+                               count += read_size;
+                       }
+               }
+       }
+       reply.resize(count);
+
+       // if they didn't pass an object to be notified of the reply, exit now
+       // ( ne need to parse the server response
+       if(asCallback == 0) {
+               // TODO check if there's any cleanup needed here
+               return as_value();
+       }
+       
+
+       log_debug(_("NetConnection.call(): response: "));
+       log_debug("%s", hexify(reply.data(), reply.size(), false));
+
+       // parse server reply
+       if(reply.size() < 21) {
+               log_error(_("NetConnection.call(): response from server too 
short to be valid"));
+               // TODO call onStatus callback
+       } else {
+               boost::uint8_t *b = reply.data();
+               boost::uint8_t *end = b + reply.size();
+               boost::uint16_t si;
+               boost::uint16_t li;
+
+               b += 2; // skip header bytes (version, client_id)
+               si = readNetworkShort(b); b += 2;
+               if(si != 0) {
+                       // FIXME just skip them. but make sure the buffer is 
long enough to at least have the type byte for the value in the body.
+                       log_unimpl("NetConnection.call(): server sent back 
headers");
+               } else {
+                       si = readNetworkShort(b); b += 2;
+                       if(si != 1) {
+                               // TODO decide what to do in this case
+                               log_error(_("NetConnection.call(): server sent 
back a weird number of bodies"));
+                       } else {
+                               // scan past response message (something like 
/1/onResult)
+                               si = readNetworkShort(b); b += 2;
+                               b += si; // TODO make sure this ends with 
onResult, not onStatus
+
+                               // scan past "null"
+                               si = readNetworkShort(b); b += 2;
+                               b += si; // FIXME check if this is bigger than 
4. if so check that the buffer is long enouggh for the rest of the header
+
+                               // read length
+                               li = readNetworkLong(b); b += 4;
+                               if(li != end - b) {
+                                       if(li < end - b) {
+                                               
log_error(_("NetConnection.call(): server sent us a length that's less than the 
number of bytes it sent. Continuing anyway."));
+                                       } else {
+                                               // note: potlatch (which works 
in the proprietary player) gets 0xffffffff here
+                                               
log_error(_("NetConnection.call(): server sent us a length that's more than the 
number of bytes it sent. Continuing anyway."));
+                                       }
+                               }
+       
+                               as_value response;
+                               if ( amf0_read_value(b, end, response) )
+                               {
+                                       string_table::key callbackMethod = 
asCallback -> getVM() . getStringTable() . find(std::string("onResult"));
+                                       asCallback->callMethod(callbackMethod, 
response);
+                               }
+                               else
+                               {
+                                       // TODO construct an object with info 
about the failure to pass to onStatus
+                                       // see: 
http://livedocs.adobe.com/fms/2/docs/00000742.html
+                                       //string_table::key callbackMethod = 
asCallback->getVM().getStringTable().find(std::string("onStatus"));
+                                       
//asCallback->callMethod(callbackMethod, TODO);
+                               }
+                       }
+               }
+       }
 
        log_unimpl("NetConnection.call()");
+#endif
        return as_value();
 }
 
@@ -347,7 +996,7 @@
        if ( fn.nargs == 0 ) // getter
        {
                log_unimpl("NetConnection.isConnected get");
-         return as_value();
+               return as_value();
        }
        else // setter
        {
@@ -442,6 +1091,13 @@
        NetConnection::registerConstructor(global);
 }
 
+// here to have AMFQueue definition available
+NetConnection::~NetConnection()
+{
+       delete call_queue;
+}
+
+
 
 } // end of gnash namespace
 

=== modified file 'libcore/asobj/NetConnection.h'
--- a/libcore/asobj/NetConnection.h     2008-06-09 13:31:51 +0000
+++ b/libcore/asobj/NetConnection.h     2008-08-12 09:59:57 +0000
@@ -37,6 +37,7 @@
 // Forward declarations
 namespace gnash {
        //class NetStream;
+       class AMFQueue;
 }
 
 namespace gnash {
@@ -155,6 +156,9 @@
        bool loadCompleted();
 
 private:
+       friend class AMFQueue;
+
+       AMFQueue *call_queue;
 
        /// Extend the URL to be used for playing
        void addToURL(const std::string& url);


reply via email to

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