[Top][All Lists]
[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);
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Gnash-commit] /srv/bzr/gnash/trunk r9580: Initial remoting support. Needs further cleanup, test and improvement .,
Sandro Santilli <=