[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Gnash-commit] /srv/bzr/gnash/trunk r10379: Send NetConnection status co
From: |
Benjamin Wolsey |
Subject: |
[Gnash-commit] /srv/bzr/gnash/trunk r10379: Send NetConnection status codes on connect() failure or success. Implement |
Date: |
Wed, 03 Dec 2008 12:38:50 +0100 |
User-agent: |
Bazaar (1.5) |
------------------------------------------------------------
revno: 10379
committer: Benjamin Wolsey <address@hidden>
branch nick: trunk
timestamp: Wed 2008-12-03 12:38:50 +0100
message:
Send NetConnection status codes on connect() failure or success. Implement
NetConnection.isConnected.
modified:
libcore/asobj/NetConnection.cpp
libcore/asobj/NetConnection.h
libcore/asobj/NetStream_as.cpp
libcore/asobj/NetStream_as.h
testsuite/actionscript.all/NetConnection.as
testsuite/misc-ming.all/NetStream-SquareTest.c
testsuite/swfdec/PASSING
------------------------------------------------------------
revno: 10375.1.1
committer: Benjamin Wolsey <address@hidden>
branch nick: work
timestamp: Wed 2008-12-03 12:03:34 +0100
message:
Test NetConnection.connect and onStatus event.
Rearrange NetConnection class so it's a bit more organized (but still
something of a mess).
Make NetStream status codes use std::strings, implement some NetConnection
codes the same way (but without queueing). Currently only the connect
status code is properly implemented, but this fixes video playback
in bug #19844.
modified:
libcore/asobj/NetConnection.cpp
libcore/asobj/NetConnection.h
libcore/asobj/NetStream_as.cpp
libcore/asobj/NetStream_as.h
testsuite/actionscript.all/NetConnection.as
------------------------------------------------------------
revno: 10375.1.2
committer: Benjamin Wolsey <address@hidden>
branch nick: work
timestamp: Wed 2008-12-03 12:25:09 +0100
message:
Minor cleanups, documentation.
modified:
libcore/asobj/NetStream_as.cpp
libcore/asobj/NetStream_as.h
------------------------------------------------------------
revno: 10375.1.3
committer: Benjamin Wolsey <address@hidden>
branch nick: work
timestamp: Wed 2008-12-03 12:25:33 +0100
message:
Test the NetConnection info object and implement correctly.
Passing tests in swfdec and misc-ming.all as well as the actionscript.all
ones already committed.
modified:
libcore/asobj/NetConnection.cpp
libcore/asobj/NetConnection.h
testsuite/actionscript.all/NetConnection.as
testsuite/misc-ming.all/NetStream-SquareTest.c
testsuite/swfdec/PASSING
=== modified file 'libcore/asobj/NetConnection.cpp'
--- a/libcore/asobj/NetConnection.cpp 2008-10-28 15:32:20 +0000
+++ b/libcore/asobj/NetConnection.cpp 2008-12-03 11:25:33 +0000
@@ -55,165 +55,30 @@
// #define GNASH_DEBUG_REMOTING
+// Forward declarations.
+
namespace gnash {
-static as_value netconnection_new(const fn_call& fn);
-
-/// \class NetConnection
-/// \brief Opens a local connection through which you can play
-/// back video (FLV) files from an HTTP address or from the local file
-/// system, using curl.
-NetConnection::NetConnection()
- :
- as_object(getNetConnectionInterface()),
- _callQueue(0)
-{
- attachProperties();
-}
-
-
-/*public*/
-std::string
-NetConnection::validateURL(const std::string& url)
-{
- std::string completeUrl;
- if (_prefixUrl.size() > 0) {
- if(url.size() > 0) {
- completeUrl += _prefixUrl + "/" + url;
- } else {
- completeUrl += _prefixUrl;
- }
- } else {
- completeUrl += url;
- }
-
- const movie_root& mr = _vm.getRoot();
- URL uri(completeUrl, mr.runInfo().baseURL());
-
- std::string uriStr(uri.str());
- assert(uriStr.find("://") != std::string::npos);
-
- // Check if we're allowed to open url
- if (!URLAccessManager::allow(uri)) {
- log_security(_("Gnash is not allowed to open this url: %s"),
uriStr);
- return "";
- }
-
- log_debug(_("Connection to movie: %s"), uriStr);
-
- return uriStr;
-}
-
-/*private*/
-void
-NetConnection::addToURL(const std::string& url)
-{
- // What is this ? It is NOT documented in the header !!
- //if (url == "null" || url == "NULL") return;
-
- // If there already is something in _prefixUrl, then we already have a
url,
- // so no need to renew it. This may not correct, needs some testing.
- if (_prefixUrl.size() > 0) return;
-
- _prefixUrl += url;
-}
-
-
-/// \brief callback to instantiate a new NetConnection object.
-/// \param fn the parameters from the Flash movie
-/// \return nothing from the function call.
-/// \note The return value is returned through the fn.result member.
-static as_value
-netconnection_new(const fn_call& /* fn */)
-{
- GNASH_REPORT_FUNCTION;
-
- NetConnection *netconnection_obj = new NetConnection;
-
- return as_value(netconnection_obj);
-}
-
-as_value
-NetConnection::connect_method(const fn_call& fn)
-{
- // NOTE:
- //
- // NetConnection::connect() is *documented*, I repeat, *documented*, to
require the
- // "url" argument to be NULL in AS <= 2. This is *legal* and
*required*. Anything
- // other than NULL is undocumented behaviour, and I would like to know
if there
- // are any movies out there relying on it. --bjacques.
-
- GNASH_REPORT_FUNCTION;
-
- boost::intrusive_ptr<NetConnection> ptr =
ensureType<NetConnection>(fn.this_ptr);
-
- if (fn.nargs < 1)
- {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror(_("NetConnection.connect(): needs at least one
argument"));
- );
- return as_value(false);
- }
-
- const as_value& url_val = fn.arg(0);
-
- // Check first arg for validity
- if ( url_val.is_null())
- {
- // Null URL was passed. This is expected. Of course, it also
makes this
- // function (and, this class) rather useless. We return true,
even though
- // returning true has no meaning.
-
- return as_value(true);
- }
-
- // The remainder of this function is undocumented.
-
- if (url_val.is_undefined()) {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror(_("NetConnection.connect(): first argument
shouldn't be undefined"));
- );
- return as_value(false);
- }
-
-
- /// .. TODO: checkme ... addToURL ?? shoudnl't we attempt a connection
??
- ptr->addToURL(url_val.to_string());
-
- if ( fn.nargs > 1 )
- {
- std::stringstream ss; fn.dump_args(ss);
- log_unimpl("NetConnection.connect(%s): args after the first are
not supported", ss.str());
- }
-
-
- // TODO: FIXME: should return true *or false* for RTMP connections
- return as_value(true);
-}
-
-
-as_value
-NetConnection::addHeader_method(const fn_call& fn)
-{
- boost::intrusive_ptr<NetConnection> ptr =
ensureType<NetConnection>(fn.this_ptr);
- UNUSED(ptr);
-
- log_unimpl("NetConnection.addHeader()");
- return as_value();
-}
-
-static boost::uint16_t
-readNetworkShort(const boost::uint8_t* buf) {
- boost::uint16_t s = buf[0] << 8 | buf[1];
- return s;
-}
-
-static boost::uint32_t
-readNetworkLong(const boost::uint8_t* buf) {
- boost::uint32_t s = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
- return s;
-}
-
+namespace {
+ void attachProperties(as_object& o);
+ void attachNetConnectionInterface(as_object& o);
+ as_object* getNetConnectionInterface();
+ as_value netconnection_isConnected(const fn_call& fn);
+ as_value netconnection_uri(const fn_call& fn);
+ as_value netconnection_connect(const fn_call& fn);
+ as_value netconnection_close(const fn_call& fn);
+ as_value netconnection_call(const fn_call& fn);
+ as_value netconnection_addHeader(const fn_call& fn);
+ as_value netconnection_new(const fn_call& fn);
+
+}
+
+namespace {
+
+ boost::uint16_t readNetworkShort(const boost::uint8_t* buf);
+ boost::uint32_t readNetworkLong(const boost::uint8_t* buf);
+
+}
/// Queue of remoting calls
//
/// This class in made to handle data and do defered processing for
@@ -227,613 +92,850 @@
/// script specified a callback function, use the optional parameters to
specify
/// the identifier (which must be unique) and the callback object as an
as_value
///
+/// @todo move this somewhere more appropriate, perhaps by merging with libamf.
class AMFQueue {
private:
- NetConnection& _nc;
- static const int NCCALLREPLYMAX=200000;
-
- typedef std::map<std::string, boost::intrusive_ptr<as_object> >
CallbacksMap;
- CallbacksMap callbacks;
-
- SimpleBuffer postdata;
- URL url;
- boost::scoped_ptr<IOChannel> _connection;
- SimpleBuffer reply;
- int reply_start;
- int reply_end;
- int queued_count;
- unsigned int ticker;
+
+ NetConnection& _nc;
+ static const int NCCALLREPLYMAX=200000;
+
+ typedef std::map<std::string, boost::intrusive_ptr<as_object> >
+ CallbacksMap;
+ CallbacksMap callbacks;
+
+ SimpleBuffer postdata;
+ URL url;
+ boost::scoped_ptr<IOChannel> _connection;
+ SimpleBuffer reply;
+ int reply_start;
+ int reply_end;
+ int queued_count;
+ unsigned int ticker;
public:
- AMFQueue(NetConnection& nc, URL url)
- :
- _nc(nc),
- postdata(),
- url(url),
- _connection(0),
- reply(NCCALLREPLYMAX),
- reply_start(0),
- reply_end(0),
- queued_count(0),
- ticker(0)
- {
- // leave space for header
- postdata.append("\000\000\000\000\000\000", 6);
- }
+ AMFQueue(NetConnection& nc, URL url)
+ :
+ _nc(nc),
+ postdata(),
+ url(url),
+ _connection(0),
+ reply(NCCALLREPLYMAX),
+ reply_start(0),
+ reply_end(0),
+ queued_count(0),
+ ticker(0)
+ {
+ // leave space for header
+ postdata.append("\000\000\000\000\000\000", 6);
+ }
- ~AMFQueue() {
- stop_ticking();
- }
-
- void enqueue(const SimpleBuffer &amf, const std::string& identifier,
boost::intrusive_ptr<as_object> callback) {
- push_amf(amf);
- push_callback(identifier, callback);
- //log_aserror("NetConnection::call(): called with a non-object
as the callback");
- };
- void enqueue(const SimpleBuffer &amf) {
- push_amf(amf);
- };
-
- // tick is called automatically on intervals (hopefully only between
- // actionscript frames)
- //
- // it handles all networking for NetConnection::call() and dispatches
- // callbacks when needed
- void tick() {
+ ~AMFQueue() {
+ stop_ticking();
+ }
+
+ void enqueue(const SimpleBuffer &amf, const std::string& identifier,
+ boost::intrusive_ptr<as_object> callback) {
+ push_amf(amf);
+ push_callback(identifier, callback);
+ };
+ void enqueue(const SimpleBuffer &amf) {
+ push_amf(amf);
+ };
+
+ // tick is called automatically on intervals (hopefully only between
+ // actionscript frames)
+ //
+ // it handles all networking for NetConnection::call() and dispatches
+ // callbacks when needed
+ void tick() {
#ifdef GNASH_DEBUG_REMOTING
- log_debug("tick running");
+ log_debug("tick running");
#endif
- if(_connection)
- {
+ if(_connection)
+ {
VM& vm = _nc.getVM();
#ifdef GNASH_DEBUG_REMOTING
- log_debug("have connection");
+ log_debug("have connection");
#endif
- int read = _connection->readNonBlocking(reply.data() +
reply_end, NCCALLREPLYMAX - reply_end);
- if(read > 0) {
+ int read = _connection->readNonBlocking(reply.data() + reply_end,
+ NCCALLREPLYMAX - reply_end);
+ if(read > 0) {
#ifdef GNASH_DEBUG_REMOTING
- log_debug("read '%1%' bytes: %2%", read,
hexify(reply.data() + reply_end, read, false));
+ log_debug("read '%1%' bytes: %2%", read,
+ hexify(reply.data() + reply_end, read, false));
#endif
- reply_end += read;
- }
-
- // There is no way to tell if we have a whole amf reply
without
- // parsing everything
- //
- // The reply format has a header field which specifies
the
- // number of bytes in the reply, but potlatch sends
0xffffffff
- // and works fine in the proprietary player
- //
- // For now we just wait until we have the full reply.
- //
- // FIXME make this parse on other conditions,
including: 1) when
- // the buffer is full, 2) when we have a "length in
bytes" value
- // thas is satisfied
-
- if(_connection->get_error())
- {
- log_debug("connection is in error condition,
calling NetConnection.onStatus");
- reply_start = 0;
- reply_end = 0;
- //log_debug("deleting connection");
- _connection.reset(); // reset connection before
calling the callback
-
- // FIXME: should only call NetConnection's
onStatus
- // if the IOChannel is in error
condition.
- // (tipically 404).
- // When the response is empty, just
nothing happens.
- _nc.callMethod(NSV::PROP_ON_STATUS, as_value());
-
- }
- else if(_connection->eof() )
- {
- if ( reply_end > 8)
- {
+ reply_end += read;
+ }
+
+ // There is no way to tell if we have a whole amf reply without
+ // parsing everything
+ //
+ // The reply format has a header field which specifies the
+ // number of bytes in the reply, but potlatch sends 0xffffffff
+ // and works fine in the proprietary player
+ //
+ // For now we just wait until we have the full reply.
+ //
+ // FIXME make this parse on other conditions, including: 1) when
+ // the buffer is full, 2) when we have a "length in bytes" value
+ // thas is satisfied
+
+ if(_connection->get_error())
+ {
+ log_debug("connection is in error condition, calling "
+ "NetConnection.onStatus");
+ reply_start = 0;
+ reply_end = 0;
+ // reset connection before calling the callback
+ _connection.reset();
+
+ // FIXME: should only call NetConnection's onStatus
+ // if the IOChannel is in error condition.
+ // (tipically 404).
+ // When the response is empty, just nothing happens.
+ _nc.callMethod(NSV::PROP_ON_STATUS, as_value());
+
+ }
+ else if(_connection->eof() )
+ {
+ if ( reply_end > 8)
+ {
std::vector<as_object*> objRefs;
#ifdef GNASH_DEBUG_REMOTING
- log_debug("hit eof");
-#endif
- boost::int16_t si;
- boost::uint16_t li;
- boost::uint8_t *b = reply.data() +
reply_start;
- boost::uint8_t *end = reply.data() +
reply_end;
-
- // parse header
- b += 2; // skip version indicator and
client id
-
- // NOTE: this looks much like parsing
of an OBJECT_AMF0
- si = readNetworkShort(b); b += 2; //
number of headers
- uint8_t headers_ok = 1;
- if(si != 0)
- {
-#ifdef GNASH_DEBUG_REMOTING
-
log_debug("NetConnection::call(): amf headers section parsing");
-#endif
- as_value tmp;
- for(int i = si; i > 0; --i)
- {
- if(b + 2 > end) {
- headers_ok = 0;
- break;
- }
- si =
readNetworkShort(b); b += 2; // name length
- if(b + si > end) {
- headers_ok = 0;
- break;
- }
- std::string
headerName((char*)b, si); // end-b);
-#ifdef GNASH_DEBUG_REMOTING
- log_debug("Header name
%s", headerName);
-#endif
- b += si;
- if ( b + 5 > end ) {
- headers_ok = 0;
- break;
- }
- b += 5; // skip past
bool and length long
- if( !tmp.readAMF0(b,
end, -1, objRefs, vm) )
- {
- headers_ok = 0;
- break;
- }
-#ifdef GNASH_DEBUG_REMOTING
- log_debug("Header value
%s", tmp);
-#endif
-
- { // method call for
each header
- // FIXME: it seems to
me that the call should happen
- VM& vm =
_nc.getVM();
- string_table&
st = vm.getStringTable();
-
string_table::key key = st.find(headerName);
-#ifdef GNASH_DEBUG_REMOTING
-
log_debug("Calling NetConnection.%s(%s)", headerName, tmp);
-#endif
-
_nc.callMethod(key, tmp);
- }
- }
- }
-
- if(headers_ok == 1) {
-
- si = readNetworkShort(b); b +=
2; // number of replies
-
- // TODO consider counting
number of replies we
- // actually parse and doing
something if it
- // doesn't match this value
(does it matter?
- if(si > 0) {
- // parse replies until
we get a parse error or we reach the end of the buffer
- while(b < end) {
- if(b + 2 > end)
break;
- si =
readNetworkShort(b); b += 2; // reply length
- if(si < 11) {
-
log_error("NetConnection::call(): reply message name too short");
- break;
- }
- if(b + si >
end) break;
- // TODO check
that the last 9 bytes are "/onResult"
- // this should
either split on the 2nd / or require onResult or onStatus
- std::string
id(reinterpret_cast<char*>(b), si - 9);
- b += si;
-
- // parse past
unused string in header
- if(b + 2 > end)
break;
- si =
readNetworkShort(b); b += 2; // reply length
- if(b + si >
end) break;
- b += si;
-
- // this field
is supposed to hold the
- // total number
of bytes in the rest of
- // this
particular reply value, but
- //
openstreetmap.org (which works great
- // in the adobe
player) sends
- // 0xffffffff.
So we just ignore it
- if(b + 4 > end)
break;
- li =
readNetworkLong(b); b += 4; // reply length
-
-#ifdef GNASH_DEBUG_REMOTING
-
log_debug("about to parse amf value");
-#endif
- // this updates
b to point to the next unparsed byte
- as_value
reply_as_value;
- if ( !
reply_as_value.readAMF0(b, end, -1, objRefs, vm) )
- {
-
log_error("parse amf failed");
- // this
will happen if we get
- //
bogus data, or if the data runs
- // off
the end of the buffer
- //
provided, or if we get data we
- //
don't know how to parse
- break;
- }
-#ifdef GNASH_DEBUG_REMOTING
-
log_debug("parsed amf");
-#endif
-
- // update
variable to show how much we've parsed
- reply_start = b
- reply.data();
-
- // if
actionscript specified a callback object, call it
-
boost::intrusive_ptr<as_object> callback = pop_callback(id);
- if(callback) {
-#ifdef GNASH_DEBUG_REMOTING
-
log_debug("calling onResult callback");
-#endif
- //
FIXME check if above line can fail and we have to react
-
callback->callMethod(NSV::PROP_ON_RESULT, reply_as_value);
-#ifdef GNASH_DEBUG_REMOTING
-
log_debug("callback called");
-#endif
- } else {
-#ifdef GNASH_DEBUG_REMOTING
-
log_debug("couldn't find callback object");
-#endif
- }
- }
- }
- }
- }
- else
- {
- log_error("Response from remoting
service < 8 bytes");
- }
-
-#ifdef GNASH_DEBUG_REMOTING
- log_debug("deleting connection");
-#endif
- _connection.reset();
- reply_start = 0;
- reply_end = 0;
- }
- }
-
- if(!_connection && queued_count > 0) {
-#ifdef GNASH_DEBUG_REMOTING
- log_debug("creating connection");
-#endif
- // set the "number of bodies" header
- (reinterpret_cast<boost::uint16_t*>(postdata.data() +
4))[0] = htons(queued_count);
- std::string
postdata_str(reinterpret_cast<char*>(postdata.data()), postdata.size());
-#ifdef GNASH_DEBUG_REMOTING
- log_debug("NetConnection.call(): encoded args from %1%
calls: %2%", queued_count, hexify(postdata.data(), postdata.size(), false));
-#endif
- queued_count = 0;
-
_connection.reset(StreamProvider::getDefaultInstance().getStream(url,
postdata_str).release());
- postdata.resize(6);
-#ifdef GNASH_DEBUG_REMOTING
- log_debug("connection created");
-#endif
- }
-
- if(_connection == 0 && queued_count == 0) {
-#ifdef GNASH_DEBUG_REMOTING
- log_debug("stopping ticking");
-#endif
- stop_ticking();
-#ifdef GNASH_DEBUG_REMOTING
- log_debug("ticking stopped");
-#endif
- }
- };
-
- static as_value amfqueue_tick_wrapper(const fn_call& fn)
- {
- boost::intrusive_ptr<NetConnection> ptr =
ensureType<NetConnection>(fn.this_ptr);
- // FIXME check if it's possible for the URL of a NetConnection
to change between call()s
- ptr->_callQueue->tick();
- return as_value();
- };
-
- void markReachableResources() const
- {
- for (CallbacksMap::const_iterator i=callbacks.begin(),
e=callbacks.end(); i!=e; ++i)
- {
- i->second->setReachable();
- }
- }
+ log_debug("hit eof");
+#endif
+ boost::int16_t si;
+ boost::uint16_t li;
+ boost::uint8_t *b = reply.data() + reply_start;
+ boost::uint8_t *end = reply.data() + reply_end;
+
+ // parse header
+ b += 2; // skip version indicator and client id
+
+ // NOTE: this looks much like parsing of an OBJECT_AMF0
+ si = readNetworkShort(b); b += 2; // number of headers
+ uint8_t headers_ok = 1;
+ if (si != 0)
+ {
+#ifdef GNASH_DEBUG_REMOTING
+ log_debug("NetConnection::call(): amf headers "
+ "section parsing");
+#endif
+ as_value tmp;
+ for(int i = si; i > 0; --i)
+ {
+ if(b + 2 > end) {
+ headers_ok = 0;
+ break;
+ }
+ si = readNetworkShort(b); b += 2; // name length
+ if(b + si > end) {
+ headers_ok = 0;
+ break;
+ }
+ std::string headerName((char*)b, si); // end-b);
+#ifdef GNASH_DEBUG_REMOTING
+ log_debug("Header name %s", headerName);
+#endif
+ b += si;
+ if ( b + 5 > end ) {
+ headers_ok = 0;
+ break;
+ }
+ b += 5; // skip past bool and length long
+ if( !tmp.readAMF0(b, end, -1, objRefs, vm) )
+ {
+ headers_ok = 0;
+ break;
+ }
+#ifdef GNASH_DEBUG_REMOTING
+ log_debug("Header value %s", tmp);
+#endif
+
+ { // method call for each header
+ // FIXME: it seems to me that the call should
happen
+ VM& vm = _nc.getVM();
+ string_table& st = vm.getStringTable();
+ string_table::key key = st.find(headerName);
+#ifdef GNASH_DEBUG_REMOTING
+ log_debug("Calling NetConnection.%s(%s)",
+ headerName, tmp);
+#endif
+ _nc.callMethod(key, tmp);
+ }
+ }
+ }
+
+ if(headers_ok == 1) {
+
+ si = readNetworkShort(b); b += 2; // number of replies
+
+ // TODO consider counting number of replies we
+ // actually parse and doing something if it
+ // doesn't match this value (does it matter?
+ if(si > 0) {
+ // parse replies until we get a parse error or we
reach the end of the buffer
+ while(b < end) {
+ if(b + 2 > end) break;
+ si = readNetworkShort(b); b += 2; // reply
length
+ if(si < 11) {
+ log_error("NetConnection::call(): reply
message name too short");
+ break;
+ }
+ if(b + si > end) break;
+ // TODO check that the last 9 bytes are
"/onResult"
+ // this should either split on the 2nd / or
require onResult or onStatus
+ std::string id(reinterpret_cast<char*>(b), si
- 9);
+ b += si;
+
+ // parse past unused string in header
+ if(b + 2 > end) break;
+ si = readNetworkShort(b); b += 2; // reply
length
+ if(b + si > end) break;
+ b += si;
+
+ // this field is supposed to hold the
+ // total number of bytes in the rest of
+ // this particular reply value, but
+ // openstreetmap.org (which works great
+ // in the adobe player) sends
+ // 0xffffffff. So we just ignore it
+ if(b + 4 > end) break;
+ li = readNetworkLong(b); b += 4; // reply
length
+
+#ifdef GNASH_DEBUG_REMOTING
+ log_debug("about to parse amf value");
+#endif
+ // this updates b to point to the next
unparsed byte
+ as_value reply_as_value;
+ if ( ! reply_as_value.readAMF0(b, end, -1,
objRefs, vm) )
+ {
+ log_error("parse amf failed");
+ // this will happen if we get
+ // bogus data, or if the data runs
+ // off the end of the buffer
+ // provided, or if we get data we
+ // don't know how to parse
+ break;
+ }
+#ifdef GNASH_DEBUG_REMOTING
+ log_debug("parsed amf");
+#endif
+
+ // update variable to show how much we've
parsed
+ reply_start = b - reply.data();
+
+ // if actionscript specified a callback
object, call it
+ boost::intrusive_ptr<as_object> callback =
pop_callback(id);
+ if(callback) {
+#ifdef GNASH_DEBUG_REMOTING
+ log_debug("calling onResult callback");
+#endif
+ // FIXME check if above line can fail and
we have to react
+ callback->callMethod(NSV::PROP_ON_RESULT,
reply_as_value);
+#ifdef GNASH_DEBUG_REMOTING
+ log_debug("callback called");
+#endif
+ } else {
+#ifdef GNASH_DEBUG_REMOTING
+ log_debug("couldn't find callback object");
+#endif
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ log_error("Response from remoting service < 8 bytes");
+ }
+
+#ifdef GNASH_DEBUG_REMOTING
+ log_debug("deleting connection");
+#endif
+ _connection.reset();
+ reply_start = 0;
+ reply_end = 0;
+ }
+ }
+
+ if(!_connection && queued_count > 0) {
+#ifdef GNASH_DEBUG_REMOTING
+ log_debug("creating connection");
+#endif
+ // set the "number of bodies" header
+ (reinterpret_cast<boost::uint16_t*>(postdata.data() + 4))[0] =
htons(queued_count);
+ std::string postdata_str(reinterpret_cast<char*>(postdata.data()),
postdata.size());
+#ifdef GNASH_DEBUG_REMOTING
+ log_debug("NetConnection.call(): encoded args from %1% calls:
%2%", queued_count, hexify(postdata.data(), postdata.size(), false));
+#endif
+ queued_count = 0;
+
_connection.reset(StreamProvider::getDefaultInstance().getStream(url,
postdata_str).release());
+ postdata.resize(6);
+#ifdef GNASH_DEBUG_REMOTING
+ log_debug("connection created");
+#endif
+ }
+
+ if(_connection == 0 && queued_count == 0) {
+#ifdef GNASH_DEBUG_REMOTING
+ log_debug("stopping ticking");
+#endif
+ stop_ticking();
+#ifdef GNASH_DEBUG_REMOTING
+ log_debug("ticking stopped");
+#endif
+ }
+ };
+
+ static as_value amfqueue_tick_wrapper(const fn_call& fn)
+ {
+ boost::intrusive_ptr<NetConnection> ptr =
+ ensureType<NetConnection>(fn.this_ptr);
+ // FIXME check if it's possible for the URL of a NetConnection to
change between call()s
+ ptr->_callQueue->tick();
+ return as_value();
+ };
+
+ void markReachableResources() const
+ {
+ for (CallbacksMap::const_iterator i=callbacks.begin(),
+ e=callbacks.end(); i!=e; ++i)
+ {
+ i->second->setReachable();
+ }
+ }
private:
- void start_ticking()
- {
-
- if (ticker) return;
-
- boost::intrusive_ptr<builtin_function> ticker_as =
- new builtin_function(&AMFQueue::amfqueue_tick_wrapper);
-
- std::auto_ptr<Timer> timer(new Timer);
- unsigned long delayMS = 50; // FIXME crank up to 50 or so
- timer->setInterval(*ticker_as, delayMS, &_nc);
- ticker = _nc.getVM().getRoot().add_interval_timer(timer, true);
- }
-
- void push_amf(const SimpleBuffer &amf)
- {
- GNASH_REPORT_FUNCTION;
-
- postdata.append(amf.data(), amf.size());
- queued_count++;
-
- start_ticking();
- }
-
- void stop_ticking()
- {
- if (!ticker) return;
- _nc.getVM().getRoot().clear_interval_timer(ticker);
- ticker=0;
- }
-
- void push_callback(const std::string& id,
boost::intrusive_ptr<as_object> callback) {
- callbacks.insert(std::pair<std::string,
boost::intrusive_ptr<as_object> >(id, callback));
- }
-
- boost::intrusive_ptr<as_object> pop_callback(std::string id)
- {
- CallbacksMap::iterator it = callbacks.find(id);
- if (it != callbacks.end()) {
- boost::intrusive_ptr<as_object> callback = it->second;
- //boost::intrusive_ptr<as_object> callback;
- //callback = it.second;
- callbacks.erase(it);
- return callback;
- }
- else {
- return 0;
- }
- }
+ void start_ticking()
+ {
+
+ if (ticker) return;
+
+ boost::intrusive_ptr<builtin_function> ticker_as =
+ new builtin_function(&AMFQueue::amfqueue_tick_wrapper);
+
+ std::auto_ptr<Timer> timer(new Timer);
+ unsigned long delayMS = 50; // FIXME crank up to 50 or so
+ timer->setInterval(*ticker_as, delayMS, &_nc);
+ ticker = _nc.getVM().getRoot().add_interval_timer(timer, true);
+ }
+
+ void push_amf(const SimpleBuffer &amf)
+ {
+ GNASH_REPORT_FUNCTION;
+
+ postdata.append(amf.data(), amf.size());
+ queued_count++;
+
+ start_ticking();
+ }
+
+ void stop_ticking()
+ {
+ if (!ticker) return;
+ _nc.getVM().getRoot().clear_interval_timer(ticker);
+ ticker=0;
+ }
+
+ void push_callback(const std::string& id,
+ boost::intrusive_ptr<as_object> callback) {
+ callbacks.insert(std::pair<std::string,
+ boost::intrusive_ptr<as_object> >(id, callback));
+ }
+
+ boost::intrusive_ptr<as_object> pop_callback(std::string id)
+ {
+ CallbacksMap::iterator it = callbacks.find(id);
+ if (it != callbacks.end()) {
+ boost::intrusive_ptr<as_object> callback = it->second;
+ //boost::intrusive_ptr<as_object> callback;
+ //callback = it.second;
+ callbacks.erase(it);
+ return callback;
+ }
+ else {
+ return 0;
+ }
+ }
};
+/// \class NetConnection
+/// \brief Opens a local connection through which you can play
+/// back video (FLV) files from an HTTP address or from the local file
+/// system, using curl.
+NetConnection::NetConnection()
+ :
+ as_object(getNetConnectionInterface()),
+ _callQueue(0),
+ _isConnected(false)
+{
+ attachProperties(*this);
+}
+
+// extern (used by Global.cpp)
+void
+netconnection_class_init(as_object& global)
+{
+ // This is going to be the global NetConnection "class"/"function"
+ static boost::intrusive_ptr<builtin_function> cl;
+
+ if ( cl == NULL )
+ {
+ cl=new builtin_function(&netconnection_new,
+ getNetConnectionInterface());
+ // replicate all interface to class, to be able to access
+ // all methods as static functions
+ attachNetConnectionInterface(*cl);
+
+ }
+
+ // Register _global.String
+ global.init_member("NetConnection", cl.get());
+}
+
+// here to have AMFQueue definition available
+NetConnection::~NetConnection()
+{
+}
+
+void
+NetConnection::markReachableResources() const
+{
+ if ( _callQueue.get() ) _callQueue->markReachableResources();
+ markAsObjectReachable();
+}
+
+/*public*/
+std::string
+NetConnection::validateURL(const std::string& url)
+{
+ std::string completeUrl;
+ if (_prefixUrl.size() > 0) {
+ if(url.size() > 0) {
+ completeUrl += _prefixUrl + "/" + url;
+ } else {
+ completeUrl += _prefixUrl;
+ }
+ } else {
+ completeUrl += url;
+ }
+
+ const movie_root& mr = _vm.getRoot();
+ URL uri(completeUrl, mr.runInfo().baseURL());
+
+ std::string uriStr(uri.str());
+ assert(uriStr.find("://") != std::string::npos);
+
+ // Check if we're allowed to open url
+ if (!URLAccessManager::allow(uri)) {
+ log_security(_("Gnash is not allowed to open this url: %s"), uriStr);
+ return "";
+ }
+
+ log_debug(_("Connection to movie: %s"), uriStr);
+
+ return uriStr;
+}
+
+void
+NetConnection::addToURL(const std::string& url)
+{
+ // What is this ? It is NOT documented in the header !!
+ //if (url == "null" || url == "NULL") return;
+
+ // If there already is something in _prefixUrl, then we already have a url,
+ // so no need to renew it. This may not correct, needs some testing.
+ if (_prefixUrl.size() > 0) return;
+
+ _prefixUrl += url;
+}
+
+void
+NetConnection::notifyStatus(StatusCode code)
+{
+ std::pair<std::string, std::string> info;
+ getStatusCodeInfo(code, info);
+
+ /// This is a new normal object each time (see NetConnection.as)
+ as_object* o = new as_object(getObjectInterface());
+
+ const int flags = 0;
+
+ o->init_member("code", info.first, flags);
+ o->init_member("level", info.second, flags);
+
+ callMethod(NSV::PROP_ON_STATUS, o);
+
+}
+
+void
+NetConnection::getStatusCodeInfo(StatusCode code, NetConnectionStatus& info)
+{
+ /// The Call statuses do exist, but this implementation is a guess.
+ switch (code)
+ {
+ case CONNECT_SUCCESS:
+ info.first = "NetConnection.Connect.Success";
+ info.second = "status";
+ return;
+
+ case CONNECT_FAILED:
+ info.first = "NetConnection.Connect.Failed";
+ info.second = "error";
+ return;
+
+ case CALL_FAILED:
+ info.first = "NetConnection.Call.Failed";
+ info.second = "error";
+ return;
+
+ case CALL_SUCCESS:
+ info.first = "NetConnection.Call.Success";
+ info.second = "status";
+ return;
+ }
+
+}
+
+void
+NetConnection::call(as_object* asCallback, const std::string& callNumber,
+ const SimpleBuffer& buf)
+{
+
+#ifdef GNASH_DEBUG_REMOTING
+ log_debug(_("NetConnection.call(): encoded args: %s"),
+ hexify(buf->data(), buf->size(), false));
+#endif
+
+ // FIXME check that ptr->_prefixURL is valid
+ URL url(validateURL(""));
+
+ // FIXME check if it's possible for the URL of a NetConnection
+ // to change between call()s
+ if (!_callQueue.get()) {
+ _callQueue.reset(new AMFQueue(*this, url));
+ }
+
+ if (asCallback) {
+ //boost::intrusive_ptr<as_object> intrusive_callback(asCallback);
+#ifdef GNASH_DEBUG_REMOTING
+ log_debug("calling enqueue with callback");
+#endif
+ _callQueue->enqueue(buf, callNumber, asCallback);
+ //? delete asCallback;
+ }
+
+ else {
+#ifdef GNASH_DEBUG_REMOTING
+ log_debug("calling enqueue without callback");
+#endif
+ _callQueue->enqueue(buf);
+ }
+#ifdef GNASH_DEBUG_REMOTING
+ log_debug("called enqueue");
+#endif
+
+}
+
+/// Anonymous namespace for NetConnection AMF-reading helper functions
+/// (shouldn't be here).
+
+namespace {
+
+boost::uint16_t
+readNetworkShort(const boost::uint8_t* buf) {
+ boost::uint16_t s = buf[0] << 8 | buf[1];
+ return s;
+}
+
+boost::uint32_t
+readNetworkLong(const boost::uint8_t* buf) {
+ boost::uint32_t s = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
+ return s;
+}
+
+}
+
+
+/// Anonymous namespace for NetConnection interface implementation.
+
+namespace {
+
as_value
-NetConnection::call_method(const fn_call& fn)
+netconnection_call(const fn_call& fn)
{
- static int call_number = 0;
- boost::intrusive_ptr<NetConnection> ptr =
ensureType<NetConnection>(fn.this_ptr);
-
- if (fn.nargs < 1)
- {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror(_("NetConnection.call(): needs at least one
argument"));
- );
- return as_value(false); // FIXME should we return true anyway?
- }
-
- const as_value& methodName_as = fn.arg(0);
- if (!methodName_as.is_string()) {
- IF_VERBOSE_ASCODING_ERRORS(
- std::stringstream ss; fn.dump_args(ss);
- log_aserror(_("NetConnection.call(%s): first argument
(methodName) must be a string"), ss.str());
- );
- return as_value(false); // FIXME should we return true anyway?
- }
-
- std::stringstream ss; fn.dump_args(ss);
+ boost::intrusive_ptr<NetConnection> ptr =
+ ensureType<NetConnection>(fn.this_ptr);
+
+ if (fn.nargs < 1)
+ {
+ IF_VERBOSE_ASCODING_ERRORS(
+ log_aserror(_("NetConnection.call(): needs at least one argument"));
+ );
+ return as_value(false); // FIXME should we return true anyway?
+ }
+
+ const as_value& methodName_as = fn.arg(0);
+ if (!methodName_as.is_string()) {
+ IF_VERBOSE_ASCODING_ERRORS(
+ std::stringstream ss; fn.dump_args(ss);
+ log_aserror(_("NetConnection.call(%s): first argument "
+ "(methodName) must be a string"), ss.str());
+ );
+ return as_value(false); // FIXME should we return true anyway?
+ }
+
+ std::stringstream ss; fn.dump_args(ss);
#ifdef GNASH_DEBUG_REMOTING
- log_debug("NetConnection.call(%s)", ss.str());
+ log_debug("NetConnection.call(%s)", ss.str());
#endif
- // TODO: arg(1) is the response object. let it know when data comes back
- boost::intrusive_ptr<as_object> asCallback = 0;
- if(fn.nargs > 1) {
-
- if(fn.arg(1).is_object()) {
- asCallback = (fn.arg(1).to_object());
- }
-
- else {
+ // TODO: arg(1) is the response object. let it know when data comes back
+ boost::intrusive_ptr<as_object> asCallback;
+ if (fn.nargs > 1) {
+
+ if (fn.arg(1).is_object()) {
+ asCallback = (fn.arg(1).to_object());
+ }
+ else {
IF_VERBOSE_ASCODING_ERRORS(
- std::stringstream ss; fn.dump_args(ss);
- log_aserror("NetConnection::call(%s): second argument
must be an object", ss.str());
- );
- }
- }
-
- boost::scoped_ptr<SimpleBuffer> buf ( new SimpleBuffer(32) );
-
- std::string methodName = methodName_as.to_string();
-
- // junk at the top (version, client id, 0 headers, 1 body)
- // this is done by AMFQueue now:
buf->append("\000\000\000\000\000\001", 6);
-
- // method name
- buf->appendNetworkShort(methodName.size());
- buf->append(methodName.c_str(), methodName.size());
-
- // client id (result number) as counted string
- // the convention seems to be / followed by a unique (ascending) number
- ++call_number;
-
- std::ostringstream os;
- os << "/" << call_number;
- const std::string callNumberString = os.str();
-
- buf->appendNetworkShort(callNumberString.size());
- buf->append(callNumberString.c_str(), callNumberString.size());
-
- size_t total_size_offset = buf->size();
- buf->append("\000\000\000\000", 4); // total size to be filled in later
+ std::stringstream ss; fn.dump_args(ss);
+ log_aserror("NetConnection.call(%s): second argument must be "
+ "an object", ss.str());
+ );
+ }
+ }
+ std::string methodName = methodName_as.to_string();
+
+ static int call_number = 0;
+
+ boost::scoped_ptr<SimpleBuffer> buf ( new SimpleBuffer(32) );
+
+ // method name
+ buf->appendNetworkShort(methodName.size());
+ buf->append(methodName.c_str(), methodName.size());
+
+ // client id (result number) as counted string
+ // the convention seems to be / followed by a unique (ascending) number
+ ++call_number;
+
+ std::ostringstream os;
+ os << "/" << call_number;
+ const std::string callNumberString = os.str();
+
+ buf->appendNetworkShort(callNumberString.size());
+ buf->append(callNumberString.c_str(), callNumberString.size());
+
+ size_t total_size_offset = buf->size();
+ buf->append("\000\000\000\000", 4); // total size to be filled in later
std::map<as_object*, size_t> offsetTable;
+
+ // encode array of arguments to remote method
+ buf->appendByte(amf::Element::STRICT_ARRAY_AMF0);
+ buf->appendNetworkLong(fn.nargs - 2);
+
VM& vm = ptr->getVM();
- // encode array of arguments to remote method
- buf->appendByte(amf::Element::STRICT_ARRAY_AMF0);
- buf->appendNetworkLong(fn.nargs - 2);
- if (fn.nargs > 2)
+ if (fn.nargs > 2)
{
- for (unsigned int i = 2; i < fn.nargs; ++i)
- {
- const as_value& arg = fn.arg(i);
+ for (unsigned int i = 2; i < fn.nargs; ++i)
+ {
+ const as_value& arg = fn.arg(i);
if ( ! arg.writeAMF0(*buf, offsetTable, vm) )
{
- log_error("Could not serialize NetConnection.call argument
%d", i);
+ log_error("Could not serialize NetConnection.call argument %d",
+ i);
}
- }
- }
-
- // Set the "total size" parameter.
- *(reinterpret_cast<uint32_t*>(buf->data() + total_size_offset)) =
htonl(buf->size() - 4 - total_size_offset);
-
-
-#ifdef GNASH_DEBUG_REMOTING
- log_debug(_("NetConnection.call(): encoded args: %s"),
hexify(buf->data(), buf->size(), false));
-#endif
-
- // FIXME check that ptr->_prefixURL is valid
- URL url(ptr->validateURL(std::string()));
-
-
- // FIXME check if it's possible for the URL of a NetConnection to
change between call()s
- if (! ptr->_callQueue.get()) {
- ptr->_callQueue.reset(new AMFQueue(*ptr, url));
- }
-
- if (asCallback) {
- //boost::intrusive_ptr<as_object>
intrusive_callback(asCallback);
-#ifdef GNASH_DEBUG_REMOTING
- log_debug("calling enqueue with callback");
-#endif
- ptr->_callQueue->enqueue(*buf, callNumberString, asCallback);
- //? delete asCallback;
- }
-
- else {
-#ifdef GNASH_DEBUG_REMOTING
- log_debug("calling enqueue without callback");
-#endif
- ptr->_callQueue->enqueue(*buf);
- }
-#ifdef GNASH_DEBUG_REMOTING
- log_debug("called enqueue");
-#endif
-
- return as_value();
-}
-
-as_value
-NetConnection::close_method(const fn_call& fn)
-{
- boost::intrusive_ptr<NetConnection> ptr =
ensureType<NetConnection>(fn.this_ptr);
- UNUSED(ptr);
-
- log_unimpl("NetConnection.close()");
- return as_value();
-}
-
-as_value
-NetConnection::isConnected_getset(const fn_call& fn)
-{
- boost::intrusive_ptr<NetConnection> ptr =
ensureType<NetConnection>(fn.this_ptr);
- UNUSED(ptr);
-
- if ( fn.nargs == 0 ) // getter
- {
- log_unimpl("NetConnection.isConnected get");
- return as_value();
- }
- else // setter
- {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Tried to set read-only property
NetConnection.isConnected");
- );
- return as_value();
- }
-}
-
-as_value
-NetConnection::uri_getset(const fn_call& fn)
-{
- boost::intrusive_ptr<NetConnection> ptr =
ensureType<NetConnection>(fn.this_ptr);
- UNUSED(ptr);
-
- if ( fn.nargs == 0 ) // getter
- {
- log_unimpl("NetConnection.uri get");
- return as_value();
- }
- else // setter
- {
- log_unimpl("NetConnection.uri set");
- return as_value();
- }
-
-}
-
-void
-NetConnection::attachNetConnectionInterface(as_object& o)
-{
- o.init_member("connect", new
builtin_function(NetConnection::connect_method));
- o.init_member("addHeader", new
builtin_function(NetConnection::addHeader_method));
- o.init_member("call", new builtin_function(NetConnection::call_method));
- o.init_member("close", new
builtin_function(NetConnection::close_method));
-
-}
-
-void
-NetConnection::attachProperties()
-{
- init_property("isConnected", &NetConnection::isConnected_getset,
&NetConnection::isConnected_getset);
- init_property("uri", &NetConnection::uri_getset,
&NetConnection::uri_getset);
+ }
+ }
+
+ // Set the "total size" parameter.
+ *(reinterpret_cast<uint32_t*>(buf->data() + total_size_offset)) =
+ htonl(buf->size() - 4 - total_size_offset);
+
+ ptr->call(asCallback.get(), callNumberString, *buf);
+
+ // Why return undefined here?
+ return as_value();
+}
+
+as_value
+netconnection_close(const fn_call& fn)
+{
+ boost::intrusive_ptr<NetConnection> ptr =
+ ensureType<NetConnection>(fn.this_ptr);
+ UNUSED(ptr);
+
+ log_unimpl("NetConnection.close()");
+ return as_value();
+}
+
+
+/// Read-only
+as_value
+netconnection_isConnected(const fn_call& fn)
+{
+ boost::intrusive_ptr<NetConnection> ptr =
+ ensureType<NetConnection>(fn.this_ptr);
+
+ return as_value(ptr->isConnected());
+}
+
+as_value
+netconnection_uri(const fn_call& fn)
+{
+ boost::intrusive_ptr<NetConnection> ptr =
+ ensureType<NetConnection>(fn.this_ptr);
+ UNUSED(ptr);
+
+ if ( fn.nargs == 0 ) // getter
+ {
+ log_unimpl("NetConnection.uri get");
+ return as_value();
+ }
+ else // setter
+ {
+ log_unimpl("NetConnection.uri set");
+ return as_value();
+ }
+
+}
+
+void
+attachNetConnectionInterface(as_object& o)
+{
+ o.init_member("connect", new builtin_function(netconnection_connect));
+ o.init_member("addHeader", new builtin_function(netconnection_addHeader));
+ o.init_member("call", new builtin_function(netconnection_call));
+ o.init_member("close", new builtin_function(netconnection_close));
+}
+
+void
+attachProperties(as_object& o)
+{
+ o.init_readonly_property("isConnected", &netconnection_isConnected);
+ o.init_property("uri", &netconnection_uri, &netconnection_uri);
}
as_object*
-NetConnection::getNetConnectionInterface()
-{
-
- static boost::intrusive_ptr<as_object> o;
- if ( o == NULL )
- {
- o = new as_object(getObjectInterface());
- NetConnection::attachNetConnectionInterface(*o);
- }
-
- return o.get();
-}
-
-void
-NetConnection::registerConstructor(as_object& global)
-{
-
- // This is going to be the global NetConnection "class"/"function"
- static boost::intrusive_ptr<builtin_function> cl;
-
- if ( cl == NULL )
- {
- cl=new builtin_function(&netconnection_new,
getNetConnectionInterface());
- // replicate all interface to class, to be able to access
- // all methods as static functions
- // TODO: this is probably wrong !
- NetConnection::attachNetConnectionInterface(*cl);
-
- }
-
- // Register _global.String
- global.init_member("NetConnection", cl.get());
-
-}
-
-// extern (used by Global.cpp)
-void netconnection_class_init(as_object& global)
-{
- NetConnection::registerConstructor(global);
-}
-
-// here to have AMFQueue definition available
-NetConnection::~NetConnection()
-{
-}
-
-void
-NetConnection::markReachableResources() const
-{
- if ( _callQueue.get() ) _callQueue->markReachableResources();
- markAsObjectReachable();
-}
-
-
+getNetConnectionInterface()
+{
+
+ static boost::intrusive_ptr<as_object> o;
+ if ( o == NULL )
+ {
+ o = new as_object(getObjectInterface());
+ attachNetConnectionInterface(*o);
+ }
+
+ return o.get();
+}
+
+/// \brief callback to instantiate a new NetConnection object.
+/// \param fn the parameters from the Flash movie
+/// \return nothing from the function call.
+/// \note The return value is returned through the fn.result member.
+as_value
+netconnection_new(const fn_call& /* fn */)
+{
+ GNASH_REPORT_FUNCTION;
+
+ NetConnection* nc = new NetConnection;
+
+ return as_value(nc);
+}
+
+
+/// For rtmp, NetConnect.connect() takes an RTMP URL. For all other streams,
+/// it takes null or undefined.
+//
+/// RTMP is untested.
+//
+/// For non-rtmp streams:
+//
+/// Returns undefined if there are no arguments. Otherwise true if the first
+/// argument is null, false if it is anything else. Undefined is also valid
+/// for SWF7 and above.
+//
+/// The isConnected property is set to the result of connect().
+as_value
+netconnection_connect(const fn_call& fn)
+{
+
+ boost::intrusive_ptr<NetConnection> ptr =
+ ensureType<NetConnection>(fn.this_ptr);
+
+ if (fn.nargs < 1)
+ {
+ IF_VERBOSE_ASCODING_ERRORS(
+ log_aserror(_("NetConnection.connect(): needs at least "
+ "one argument"));
+ );
+ return as_value();
+ }
+
+ const as_value& uri = fn.arg(0);
+
+ const VM& vm = ptr->getVM();
+
+ bool success = false;
+
+ // Check first arg for validity
+ if (uri.is_null() || (vm.getSWFVersion() > 6 && uri.is_undefined()))
+ {
+ // Null URL was passed. This is expected. Of course, it also makes this
+ // function (and, this class) rather useless. We return true,
+ // even though returning true has no meaning.
+
+ success = true;
+
+ }
+ else if (uri.is_string()) {
+
+ // FIXME: RTMP URLs should attempt a connection (warning: this seems
+ // to be different for SWF8). Would probably return true on success.
+ //
+ // URLs starting with "http://" are invalid, and no connection is
+ // initiated.
+ ptr->addToURL(uri.to_string());
+
+ if ( fn.nargs > 1 )
+ {
+ std::stringstream ss; fn.dump_args(ss);
+ log_unimpl("NetConnection.connect(%s): args after the first are "
+ "not supported", ss.str());
+ }
+
+ success = false;
+ }
+
+ ptr->notifyStatus(success ? NetConnection::CONNECT_SUCCESS :
+ NetConnection::CONNECT_FAILED);
+
+ ptr->setConnected(success);
+ return as_value(success);
+
+}
+
+
+as_value
+netconnection_addHeader(const fn_call& fn)
+{
+ boost::intrusive_ptr<NetConnection> ptr =
+ ensureType<NetConnection>(fn.this_ptr);
+ UNUSED(ptr);
+
+ log_unimpl("NetConnection.addHeader()");
+ return as_value();
+}
+
+} // anonymous namespace
} // end of gnash namespace
=== modified file 'libcore/asobj/NetConnection.h'
--- a/libcore/asobj/NetConnection.h 2008-10-19 19:13:59 +0000
+++ b/libcore/asobj/NetConnection.h 2008-12-03 11:25:33 +0000
@@ -21,9 +21,6 @@
#include "IOChannel.h"
#include <string>
-
-// TODO: port to new AS architecture
-//
#include "as_object.h" // for inheritance
#include "fn_call.h"
@@ -39,9 +36,18 @@
//
/// Provides interfaces to load data from an URL
///
-class NetConnection: public as_object {
+class NetConnection: public as_object
+{
public:
+ enum StatusCode
+ {
+ CONNECT_FAILED,
+ CONNECT_SUCCESS,
+ CALL_FAILED,
+ CALL_SUCCESS
+ };
+
NetConnection();
~NetConnection();
@@ -64,54 +70,43 @@
///
std::string validateURL(const std::string& url);
- /// Register the "NetConnection" constructor to the given global object
- static void registerConstructor(as_object& global);
+ void call(as_object* asCallback, const std::string& callNumber,
+ const SimpleBuffer& buf);
+
+ void setConnected(bool b) {
+ _isConnected = b;
+ }
+
+ bool isConnected() const {
+ return _isConnected;
+ }
+
+ /// Extend the URL to be used for playing
+ void addToURL(const std::string& url);
+
+ /// Notify the NetConnection onStatus handler of a change.
+ void notifyStatus(StatusCode code);
protected:
/// Mark responders associated with remoting calls
void markReachableResources() const;
-
private:
friend class AMFQueue;
std::auto_ptr<AMFQueue> _callQueue;
- /// Extend the URL to be used for playing
- void addToURL(const std::string& url);
-
/// the url prefix optionally passed to connect()
std::string _prefixUrl;
/// the complete url of the file
std::string _completeUrl;
- /// Attach ActionScript instance properties
- void attachProperties();
-
- /// Attach ActionScript class interface
- static void attachNetConnectionInterface(as_object& o);
-
- /// Get ActionScript class interface
- static as_object* getNetConnectionInterface();
-
- /// NetConnection.isConnected ActionScript Property
- static as_value isConnected_getset(const fn_call& fn);
-
- /// NetConnection.uri ActionScript Property
- static as_value uri_getset(const fn_call& fn);
-
- /// NetConnection.connect() ActionScript Method
- static as_value connect_method(const fn_call& fn);
-
- /// NetConnection.close() ActionScript Method
- static as_value close_method(const fn_call& fn);
-
- /// NetConnection.call() ActionScript Method
- static as_value call_method(const fn_call& fn);
-
- /// NetConnection.addHeader() ActionScript Method
- static as_value addHeader_method(const fn_call& fn);
+ bool _isConnected;
+
+ typedef std::pair<std::string, std::string> NetConnectionStatus;
+
+ void getStatusCodeInfo(StatusCode code, NetConnectionStatus& info);
};
@@ -119,5 +114,4 @@
} // end of gnash namespace
-// __NETCONNECTION_H__
#endif
=== modified file 'libcore/asobj/NetStream_as.cpp'
--- a/libcore/asobj/NetStream_as.cpp 2008-11-27 10:20:02 +0000
+++ b/libcore/asobj/NetStream_as.cpp 2008-12-03 11:25:09 +0000
@@ -486,7 +486,7 @@
log_debug(" Invoking onMetaData");
#endif
- string_table::key func =
getVM().getStringTable().find(PROPNAME(funcname));
+ string_table::key func = getVM().getStringTable().find(PROPNAME(funcname));
callMethod(func, as_value(info_obj));
}
@@ -503,14 +503,10 @@
code = popNextPendingStatusNotification();
if ( code == invalidStatus ) break; // no more pending notifications
-#ifdef GNASH_DEBUG_STATUS
- log_debug(" Invoking onStatus(%s)", getStatusCodeInfo(code).first);
-#endif
-
- // TODO: optimize by reusing the same as_object ?
- boost::intrusive_ptr<as_object> o = getStatusObject(code);
-
- callMethod(NSV::PROP_ON_STATUS, as_value(o.get()));
+ // Must be a new object every time.
+ as_object* o = getStatusObject(code);
+
+ callMethod(NSV::PROP_ON_STATUS, o);
}
}
@@ -561,50 +557,69 @@
return m_imageframe;
}
-std::pair<const char*, const char*>
-NetStream_as::getStatusCodeInfo(StatusCode code)
+void
+NetStream_as::getStatusCodeInfo(StatusCode code, NetStreamStatus& info)
{
switch (code)
{
case bufferEmpty:
- return std::pair<const char*, const
char*>("NetStream.Buffer.Empty", "status");
+ info.first = "NetStream.Buffer.Empty";
+ info.second = "status";
+ return;
case bufferFull:
- return std::pair<const char*, const
char*>("NetStream.Buffer.Full", "status");
+ info.first = "NetStream.Buffer.Full";
+ info.second = "status";
+ return;
case bufferFlush:
- return std::pair<const char*, const
char*>("NetStream.Buffer.Flush", "status");
+ info.first = "NetStream.Buffer.Flush";
+ info.second = "status";
+ return;
case playStart:
- return std::pair<const char*, const char*>("NetStream.Play.Start",
"status");
+ info.first = "NetStream.Play.Start";
+ info.second = "status";
+ return;
case playStop:
- return std::pair<const char*, const char*>("NetStream.Play.Stop",
"status");
+ info.first = "NetStream.Play.Stop";
+ info.second = "status";
+ return;
case seekNotify:
- return std::pair<const char*, const
char*>("NetStream.Seek.Notify", "status");
+ info.first = "NetStream.Seek.Notify";
+ info.second = "status";
+ return;
case streamNotFound:
- return std::pair<const char*, const
char*>("NetStream.Play.StreamNotFound", "error");
+ info.first = "NetStream.Play.StreamNotFound";
+ info.second = "error";
+ return;
case invalidTime:
- return std::pair<const char*, const
char*>("NetStream.Seek.InvalidTime", "error");
-
+ info.first = "NetStream.Seek.InvalidTime";
+ info.second = "error";
+ return;
default:
- return std::pair<const char*, const char*>("","");
+ return;
}
}
-boost::intrusive_ptr<as_object>
+as_object*
NetStream_as::getStatusObject(StatusCode code)
{
// code, level
- std::pair<const char*, const char*> info = getStatusCodeInfo(code);
-
- boost::intrusive_ptr<as_object> o = new as_object(getObjectInterface());
- o->init_member("code", info.first, 0); // enumerable, deletable
- o->init_member("level", info.second, 0); // enumerable, deletable
+ NetStreamStatus info;
+ getStatusCodeInfo(code, info);
+
+ // Enumerable and deletable.
+ const int flags = 0;
+
+ as_object* o = new as_object(getObjectInterface());
+ o->init_member("code", info.first, flags);
+ o->init_member("level", info.second, flags);
return o;
}
=== modified file 'libcore/asobj/NetStream_as.h'
--- a/libcore/asobj/NetStream_as.h 2008-11-27 10:20:02 +0000
+++ b/libcore/asobj/NetStream_as.h 2008-12-03 11:25:09 +0000
@@ -169,7 +169,8 @@
/// This class is responsible for handlign external
/// media files. Provides interfaces for playback control.
///
-class NetStream_as : public as_object {
+class NetStream_as : public as_object
+{
protected:
@@ -203,7 +204,6 @@
/// NetStream.Seek.InvalidTime (level: error)
invalidTime
};
-
boost::intrusive_ptr<NetConnection> _netCon;
@@ -432,10 +432,8 @@
///
/// It might be invoked by a separate thread (neither main, nor decoder
thread).
///
- static unsigned int audio_streamer(void *udata, boost::int16_t* samples,
unsigned int nSamples, bool& eof);
-
-
-
+ static unsigned int audio_streamer(void *udata, boost::int16_t* samples,
+ unsigned int nSamples, bool& eof);
private:
@@ -596,7 +594,7 @@
///
void clearStatusQueue();
- // TODO: change to a container with fast pop_front()
+ // Queue of status notifications.
typedef std::deque<StatusCode> StatusQueue;
/// List of status messages to be processed
@@ -608,16 +606,17 @@
/// Last status code (to avoid consecutively notifying the same event)
StatusCode _lastStatus;
+ typedef std::pair<std::string, std::string> NetStreamStatus;
+
/// Get 'status' (first) and 'level' (second) strings for given status code
//
- /// The two members of the pair are ensured to be not-NULL
/// Any invalid code, out of bound or explicitly invalid (invalidCode)
- /// returns two empty C strings.
+ /// returns two empty strings.
///
- std::pair<const char*, const char*> getStatusCodeInfo(StatusCode code);
+ void getStatusCodeInfo(StatusCode code, NetStreamStatus& info);
/// Return a newly allocated information object for the given status
- boost::intrusive_ptr<as_object> getStatusObject(StatusCode code);
+ as_object* getStatusObject(StatusCode code);
/// hack for using a Timer to drive ::advance calls
static as_value advanceWrapper(const fn_call& fn);
=== modified file 'testsuite/actionscript.all/NetConnection.as'
--- a/testsuite/actionscript.all/NetConnection.as 2008-03-11 19:31:46
+0000
+++ b/testsuite/actionscript.all/NetConnection.as 2008-12-03 11:25:33
+0000
@@ -41,22 +41,107 @@
check_equals(typeof(tmp), 'object');
check_equals(tmp.__proto__, NetConnection.prototype);
check(tmp instanceof NetConnection);
-xcheck_equals(typeof(tmp.isConnected), 'boolean');
-xcheck_equals(tmp.isConnected, false);
-// TODO: add tests for all properties
+check_equals(typeof(tmp.isConnected), 'boolean');
+check_equals(tmp.isConnected, false);
+
+tmp.isConnected = true;
+check_equals(tmp.isConnected, false);
+
+tmp.isConnected = 56;
+check_equals(tmp.isConnected, false);
// test the NetConnection::connect method
tmp.connect();
if ( !
tmp.connect("rtmp://www.mediacollege.com/flash/media-player/testclip-4sec.flv")
)
{
// FIXME: this would fail in the reference player too...
- xfail("NetConnection::connect() didn't initialized correctly");
+ xfail("NetConnection::connect() didn't initialize correctly");
}
else
{
pass("NetConnection::connect() initialized correctly");
}
-check_totals(10);
+tmp.onStatus = function(info) {
+ result = info.code;
+ level = info.level;
+};
+
+result = "";
+level = "";
+
+ret = tmp.connect();
+check_equals(ret, undefined);
+check_equals(tmp.isConnected, false);
+check_equals(result, "");
+check_equals(level, "");
+
+
+ret = tmp.connect(null, "another argument");
+check_equals(ret, true);
+check_equals(tmp.isConnected, true);
+check_equals(result, "NetConnection.Connect.Success");
+check_equals(level, "status");
+
+ret = tmp.connect(1);
+check_equals(ret, false);
+check_equals(tmp.isConnected, false);
+check_equals(result, "NetConnection.Connect.Failed");
+check_equals(level, "error");
+
+ret = tmp.connect("string");
+check_equals(ret, false);
+check_equals(tmp.isConnected, false);
+check_equals(result, "NetConnection.Connect.Failed");
+check_equals(level, "error");
+
+ret = tmp.connect(undefined);
+
+#if OUTPUT_VERSION > 6
+check_equals(ret, true);
+check_equals(tmp.isConnected, true);
+check_equals(result, "NetConnection.Connect.Success");
+check_equals(level, "status");
+#else
+check_equals(ret, false);
+check_equals(tmp.isConnected, false);
+check_equals(result, "NetConnection.Connect.Failed");
+check_equals(level, "error");
+#endif
+
+ret = tmp.connect(null);
+check_equals(ret, true);
+check_equals(tmp.isConnected, true);
+check_equals(level, "status");
+
+ret = tmp.connect("http://someserver");
+check_equals(ret, false);
+check_equals(tmp.isConnected, false);
+check_equals(result, "NetConnection.Connect.Failed");
+check_equals(level, "error");
+
+// Check onStatus object.
+
+nc = new NetConnection;
+nc.onStatus = function(info) {
+ infoObj = info;
+};
+
+nc.connect(6);
+nc.onStatus = undefined;
+check_equals(infoObj.code, "NetConnection.Connect.Failed");
+
+// It is a full object
+check(infoObj instanceof Object);
+check_equals(infoObj.toString(), "[object Object]");
+
+// Check whether the original object is modified on a new connect attempt.
+nc.connect(null);
+check_equals(infoObj.code, "NetConnection.Connect.Failed");
+
+
+check_totals(43);
+
+
#endif // OUTPUT_VERSION >= 7
=== modified file 'testsuite/misc-ming.all/NetStream-SquareTest.c'
--- a/testsuite/misc-ming.all/NetStream-SquareTest.c 2008-11-12 09:47:02
+0000
+++ b/testsuite/misc-ming.all/NetStream-SquareTest.c 2008-12-03 11:25:33
+0000
@@ -86,7 +86,7 @@
"nc=new NetConnection();"
"check(!nc.isConnected, 'newly created NetConnection is not
connected');"
"nc.connect(null);"
- "xcheck(nc.isConnected, 'NetConnection is connected after
.connect(null)');"
+ "check(nc.isConnected, 'NetConnection is connected after
.connect(null)');"
"check(!NetStream.prototype.hasOwnProperty('currentFPS'));" // version
7 here
"xcheck(!NetStream.prototype.hasOwnProperty('currentFps'));"
"stream = new NetStream();"
=== modified file 'testsuite/swfdec/PASSING'
--- a/testsuite/swfdec/PASSING 2008-12-02 11:49:24 +0000
+++ b/testsuite/swfdec/PASSING 2008-12-03 11:25:33 +0000
@@ -623,6 +623,7 @@
native-run-6.swf:fe6847111ffbbb1822c02af05448351b
native-run-7.swf:597e61a37f11873f92e5f8147a186b92
native-run-8.swf:4146625b4f8fef9926a324e8de306ead
+netconnection.swf:691d140eb395ca4094119c24ee9028d5
netstream-fscommand-6.swf:7151ee0808f01d502171c4813d38d3fa
netstream-fscommand-7.swf:ec9312835ba2e8a7347252bcaef7db0b
netstream-fscommand-8.swf:38f05e4f8d3a4e2faae0ef9e18e7754b
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Gnash-commit] /srv/bzr/gnash/trunk r10379: Send NetConnection status codes on connect() failure or success. Implement,
Benjamin Wolsey <=