gnash-commit
[Top][All Lists]
Advanced

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

[Gnash-commit] /srv/bzr/gnash/trunk r12011: Fix XMLSocket mess up.


From: Benjamin Wolsey
Subject: [Gnash-commit] /srv/bzr/gnash/trunk r12011: Fix XMLSocket mess up.
Date: Wed, 10 Mar 2010 20:21:04 +0100
User-agent: Bazaar (2.0.3)

------------------------------------------------------------
revno: 12011 [merge]
committer: Benjamin Wolsey <address@hidden>
branch nick: trunk
timestamp: Wed 2010-03-10 20:21:04 +0100
message:
  Fix XMLSocket mess up.
  
  Use non-blocking sockets for improved compatibility. Also fixes bug #28638 now
  networking is done properly.
  
  Implement XMLSocket.onClose.
  
  Make the XMLSocket test automatic so no one else can break it.
  
  Adapt RTMP to use the non-blocking interface. There are minor but
  important changes to the way the RTMP class behaves. These are reflected
  in utilities/rtmpget.cpp.
removed:
  testsuite/misc-ming.all/XMLSocketTest.c
added:
  testsuite/misc-ming.all/XMLSocketTest.as
  testsuite/misc-ming.all/XMLSocketTester.sh
modified:
  libbase/RTMP.cpp
  libbase/RTMP.h
  libbase/Socket.cpp
  libbase/Socket.h
  libcore/asobj/flash/net/XMLSocket_as.cpp
  testsuite/XmlSocketServer.pl
  testsuite/misc-ming.all/Makefile.am
  utilities/rtmpget.cpp
=== modified file 'libbase/RTMP.cpp'
--- a/libbase/RTMP.cpp  2010-02-24 18:00:03 +0000
+++ b/libbase/RTMP.cpp  2010-03-10 13:37:51 +0000
@@ -79,6 +79,45 @@
 
 }
 
+/// A utility functor for carrying out the handshake.
+class HandShaker
+{
+public:
+
+    static const int sigSize = 1536;
+
+    HandShaker(Socket& s);
+
+    /// Calls the next stage in the handshake process.
+    void call();
+
+    bool success() const {
+        return _complete;
+    }
+
+    bool error() const {
+        return _error || _socket.bad();
+    }
+
+private:
+
+    /// These are the stages of the handshake.
+    //
+    /// If the socket is not ready, they will return false. If the socket
+    /// is in error, they will set _error.
+    bool stage0();
+    bool stage1();
+    bool stage2();
+    bool stage3();
+
+    Socket _socket;
+    std::vector<boost::uint8_t> _sendBuf;
+    std::vector<boost::uint8_t> _recvBuf;
+    bool _error;
+    bool _complete;
+    size_t _stage;
+};
+
 RTMPPacket::RTMPPacket(size_t reserve)
     :
     header(),
@@ -106,7 +145,13 @@
     _bytesInSent(0),
     _serverBandwidth(2500000),
     _bandwidth(2500000),
-    _outChunkSize(RTMP_DEFAULT_CHUNKSIZE)
+    _outChunkSize(RTMP_DEFAULT_CHUNKSIZE),
+    _connected(false),
+    _error(false)
+{
+}
+
+RTMP::~RTMP()
 {
 }
 
@@ -133,12 +178,6 @@
     return stored;
 }
 
-bool
-RTMP::connected() const
-{
-    return _socket.connected();
-}
-
 void
 RTMP::setBufferTime(size_t size, int streamID)
 {
@@ -161,18 +200,29 @@
 {
     log_debug("Connecting to %s", url.str());
 
+    const std::string& hostname = url.hostname();
+    const std::string& p = url.port();
+
+    // Default port.
+    boost::uint16_t port = 1935;
+    if (!p.empty()) {
+        try {
+            port = boost::lexical_cast<boost::uint16_t>(p);
+        }
+        catch (boost::bad_lexical_cast&) {}
+    }
+
     // Basic connection attempt.
-    if (!_socket.connect(url)) {
+    if (!_socket.connect(hostname, port)) {
         log_error("Initial connection failed");
         return false;
     }
     
-    if (!handShake()) {
-        log_error( "handshake failed.");
-        close();
-        return false;
-    }
-    
+    _handShaker.reset(new HandShaker(_socket));
+
+    // Start handshake attempt immediately.
+    _handShaker->call();
+
     return true;
 }
 
@@ -180,18 +230,50 @@
 RTMP::update()
 {
     if (!connected()) {
-        log_debug("Not connected!");
-        return;
+        _handShaker->call();
+        if (_handShaker->error()) {
+            _error = true;
+        }
+        if (!_handShaker->success()) return;
+        _connected = true;
     }
     
     const size_t reads = 10;
 
     for (size_t i = 0; i < reads; ++i) {
 
+        /// No need to continue reading (though it should do no harm).
+        if (error()) return;
+
         RTMPPacket p;
-        readPacket(p);
-    
+
+        // If we haven't finished reading a packet, retrieve it; otherwise
+        // use an empty one.
+        if (_incompletePacket.get()) {
+            log_debug("Doing incomplete packet");
+            p = *_incompletePacket;
+            _incompletePacket.reset();
+        }
+        else {
+            if (!readPacketHeader(p)) continue;
+        }
+
+        // Get the payload if possible.
+        if (hasPayload(p) && !readPacketPayload(p)) {
+            // If the payload is not completely readable, store it and
+            // continue.
+            _incompletePacket.reset(new RTMPPacket(p));
+            continue;
+        }
+        
+        // Store a copy of the packet for later additions and as a reference 
for
+        // future sends.
+        RTMPPacket& stored = storePacket(CHANNELS_IN, p.header.channel, p);
+      
+        // If the packet is complete, the stored packet no longer needs to
+        // keep the data alive.
         if (isReady(p)) {
+            clearPayload(stored);
             handlePacket(p);
             return;
         }
@@ -275,28 +357,29 @@
 int
 RTMP::readSocket(boost::uint8_t* buffer, int n)
 {
-    int toRead = n;
-    while (toRead) {
-        const std::streamsize bytesRead = _socket.read(buffer, toRead);
-
-        if (bytesRead < 0) return -1;
-        
-        if (!bytesRead) {
-            return -1;
-        }
-
-        _bytesIn += bytesRead;
-        toRead -= bytesRead;
-
-        // Report bytes recieved every time we reach half the bandwidth.
-        // Doesn't seem very likely to be the way the pp does it.
-        if (_bytesIn > _bytesInSent + _bandwidth / 2) {
-            sendBytesReceived(this);
-            log_debug("Sent bytes received");
-        }
-        buffer += bytesRead;
-    }
-    return n - toRead;
+
+    assert(n >= 0);
+
+    const std::streamsize bytesRead = _socket.read(buffer, n);
+    
+    if (_socket.bad()) {
+        _error = true;
+        return 0;
+    }
+
+    if (!bytesRead) return 0;
+
+    _bytesIn += bytesRead;
+
+    // Report bytes recieved every time we reach half the bandwidth.
+    // Doesn't seem very likely to be the way the pp does it.
+    if (_bytesIn > _bytesInSent + _bandwidth / 2) {
+        sendBytesReceived(this);
+        log_debug("Sent bytes received");
+    }
+
+    buffer += bytesRead;
+    return bytesRead;
 }
 
 void
@@ -322,7 +405,7 @@
 /// It seems as if new packets can add to the data of old ones if they have
 /// a minimal, small header.
 bool
-RTMP::readPacket(RTMPPacket& packet)
+RTMP::readPacketHeader(RTMPPacket& packet)
 {
       
     RTMPHeader& hr = packet.header;
@@ -330,8 +413,8 @@
     boost::uint8_t hbuf[RTMPHeader::headerSize] = { 0 };
     boost::uint8_t* header = hbuf;
   
+    // The first read may fail, but otherwise we expect a complete header.
     if (readSocket(hbuf, 1) == 0) {
-        log_error( "%s, failed to read RTMP packet header", __FUNCTION__);
         return false;
     }
 
@@ -457,6 +540,13 @@
     // Resize anyway. If it's different from what it was before, we should
     // already have cleared it.
     packet.buffer->resize(bufSize);
+    return true;
+}
+
+bool
+RTMP::readPacketPayload(RTMPPacket& packet)
+{
+    RTMPHeader& hr = packet.header;
 
     const size_t bytesRead = packet.bytesRead;
 
@@ -465,23 +555,13 @@
     const int nChunk = std::min<int>(nToRead, _inChunkSize);
     assert(nChunk >= 0);
 
+    // This is fine. We'll keep trying to read this payload until there
+    // is enough data.
     if (readSocket(payloadData(packet) + bytesRead, nChunk) != nChunk) {
-        log_error( "Failed to read RTMP packet payload. len: %s", bytesRead);
         return false;
     }
 
     packet.bytesRead += nChunk;
-
-    // Store a copy of the packet for later additions and as a reference for
-    // future sends.
-    RTMPPacket& storedpacket = storePacket(CHANNELS_IN, hr.channel, packet);
-  
-    // If the packet is complete, the stored packet no longer needs to
-    // keep the data alive.
-    if (isReady(packet)) {
-        // The timestamp should be absolute by this stage.
-        clearPayload(storedpacket);
-    }
         
     return true;
 }
@@ -766,7 +846,6 @@
     return true;
 }
 
-   
 void
 RTMP::close()
 {
@@ -782,6 +861,154 @@
     _serverBandwidth = 2500000;
 }
 
+
+/////////////////////////////////////
+/// HandShaker implementation
+/////////////////////////////////////
+
+HandShaker::HandShaker(Socket& s)
+    :
+    _socket(s),
+    _sendBuf(sigSize + 1),
+    _recvBuf(sigSize + 1),
+    _error(false),
+    _complete(false),
+    _stage(0)
+{
+    // Not encrypted
+    _sendBuf[0] = 0x03;
+    
+    // TODO: do this properly.
+    boost::uint32_t uptime = htonl(getUptime());
+
+    boost::uint8_t* ourSig = &_sendBuf.front() + 1;
+    std::memcpy(ourSig, &uptime, 4);
+    std::fill_n(ourSig + 4, 4, 0);
+
+    // Generate 1536 random bytes.
+    std::generate(ourSig + 8, ourSig + sigSize, RandomByte());
+
+}
+
+
+/// Calls the next stage in the handshake process.
+void
+HandShaker::call()
+{
+    if (error() || !_socket.connected()) return;
+
+    switch (_stage) {
+        case 0:
+            if (!stage0()) return;
+            _stage = 1;
+        case 1:
+            if (!stage1()) return;
+            _stage = 2;
+        case 2:
+            if (!stage2()) return;
+            _stage = 3;
+        case 3:
+            if (!stage3()) return;
+            log_debug("Handshake completed");
+            _complete = true;
+    }
+}
+
+bool
+HandShaker::stage0()
+{
+    std::streamsize sent = _socket.write(&_sendBuf.front(), sigSize + 1);
+
+    // This should probably not happen, but we can try again. An error will
+    // be signalled later if the socket is no longer usable.
+    if (!sent) {
+        log_error("Stage 1 socket not ready. This should not happen.");
+        return false;
+    }
+
+    /// If we sent the wrong amount of data, we can't recover.
+    if (sent != sigSize + 1) {
+        log_error("Could not send stage 1 data");
+        _error = true;
+        return false;
+    }
+    return true;
+}
+
+bool
+HandShaker::stage1()
+{
+
+    std::streamsize read = _socket.read(&_recvBuf.front(), sigSize + 1);
+
+    if (!read) {
+        // If we receive nothing, wait until the next try.
+        return false;
+    }
+
+    // The read should never return anything but 0 or what we asked for.
+    assert (read == sigSize + 1);
+
+    if (_recvBuf[0] != _sendBuf[0]) {
+        log_error( "Type mismatch: client sent %d, server answered %d",
+               _recvBuf[0], _sendBuf[0]);
+    }
+    
+    const boost::uint8_t* serverSig = &_recvBuf.front() + 1;
+
+    // decode server response
+    boost::uint32_t suptime;
+    std::memcpy(&suptime, serverSig, 4);
+    suptime = ntohl(suptime);
+
+    log_debug("Server Uptime : %d", suptime);
+    log_debug("FMS Version   : %d.%d.%d.%d",
+            +serverSig[4], +serverSig[5], +serverSig[6], +serverSig[7]);
+
+    return true;
+}
+
+bool
+HandShaker::stage2()
+{
+    
+    std::streamsize sent = _socket.write(&_recvBuf.front() + 1, sigSize);
+    
+    // This should probably not happen.
+    if (!sent) return false;
+
+    if (sent != sigSize) {
+        log_error("Could not send complete signature.");
+        _error = true;
+        return false;
+    }
+
+    return true;
+}
+
+bool
+HandShaker::stage3()
+{
+
+    // Expect it back again.
+    std::streamsize got = _socket.read(&_recvBuf.front(), sigSize);
+   
+    if (!got) return false;
+    
+    assert (got == sigSize);
+
+    const boost::uint8_t* serverSig = &_recvBuf.front();
+    const boost::uint8_t* ourSig = &_sendBuf.front() + 1;
+
+    const bool match = std::equal(serverSig, serverSig + sigSize, ourSig);
+
+    // Should we set an error here?
+    if (!match) {
+        log_error( "Signatures do not match during handshake!");
+    }
+    return true;
+}
+
 /// The type of Ping packet is 0x4 and contains two mandatory parameters
 /// and two optional parameters. The first parameter is
 /// the type of Ping and in short integer. The second parameter is the

=== modified file 'libbase/RTMP.h'
--- a/libbase/RTMP.h    2010-03-10 16:37:25 +0000
+++ b/libbase/RTMP.h    2010-03-10 19:21:04 +0000
@@ -20,6 +20,7 @@
 
 #include <boost/cstdint.hpp>
 #include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
 #include <deque>
 #include <map>
 
@@ -30,8 +31,14 @@
 
 #define RTMP_DEFAULT_CHUNKSIZE 128
 
+// Forward declarations.
 namespace gnash {
+    namespace rtmp {
+        class HandShaker;
+    }
+}
 
+namespace gnash {
 namespace rtmp {
 
 /// Known control / ping codes
@@ -294,27 +301,45 @@
 
 /// This class is for handling the RTMP protocol.
 //
-/// What should happen:
-//
-/// The RTMP object should connect to a server.
-//
-/// Once that is done the caller should call "play" on a stream. This (and
-/// other commands) can be called as long as the connection is connected.
-//
-/// It can be closed and reconnected.
+/// Only the RTMP protocol itself is handled in this class. An RTMP connection
+/// is valid only when connected() is true.
+//
+/// An RTMP object may be closed and reconnected. As soon as connect() returns
+/// true, callers are responsible for calling close().
+//
+/// RTMP has a set of channels for incoming and outgoing packets. Packets 
+/// are stored here for two reasons:
+/// 1. The payload size exceeds the chunk size, so a single payload requires
+///    several complete packets. A packet is not 'ready' unless it has a
+///    complete payload, or is the packet that completes the payload of
+///    previous packets.
+/// 2. Subsequent packets sent on the same channel can be compressed if they
+///    have the same header information. The stored packet header is used for
+///    comparison. For this case, the payload is no longer necessary.
+//
+/// A different case applies to incomplete packets. The payload of a single
+/// packet (whether the packet is 'ready' or not) is the smaller of (a) the
+/// advertised data size and (b) the chunk size. Until this much data has
+/// been read, the packet is incomplete.  Whereas Gnash always
+/// expects a complete header to be available or none at all, the payload
+/// can be read over several calls to update().
 struct DSOEXPORT RTMP
 {
 
-    /// Construct an RTMP handler.
+    /// Construct a non-connected RTMP handler.
     RTMP();
 
-    ~RTMP() {}
+    ~RTMP();
 
     /// Initiate a network connection.
     //
     /// Note that this only creates the TCP connection and carries out the
-    /// handshake. You must send a "connect" request before doing anything
-    /// else.
+    /// handshake. An active data connection needs an AMF connect request,
+    /// which is not part of the RTMP protocol.
+    //
+    /// @return     true if the connection attempt starts, otherwise false.
+    ///             A return of false means that the RTMP object is in a 
+    ///             closed state and can be reconnected.
     bool connect(const URL& url);
 
     /// This is used for sending call requests from the core.
@@ -344,31 +369,43 @@
 
     /// Whether we have a basic connection to a server.
     //
-    /// This does not mean we are ready to send or receive streams. It does
-    /// mean that messages sent via call() will be transmitted to the server.
-    bool connected() const;
-
-    /// Called to do receives.
-    //
-    /// TODO: this is the least satisfactory part. Currently sends are
-    /// executed immediately. This is probably correct. Data are only
-    /// received when update() is called, and then only one packet at a
-    /// time.
+    /// This only means that the handshake is complete and that AMF requests
+    /// can be sent to the server. It does not mean that was can send or
+    /// receive media streams.
+    //
+    /// You should ensure that connected() is true before attempting to send
+    /// or receive data.
+    bool connected() const {
+        return _connected;
+    }
+
+    /// Whether the RTMP connection is in error condition.
+    //
+    /// This is a fatal error.
+    bool error() const {
+        return _error;
+    }
+
+    /// This function handles reading incoming data and filling data queues.
+    //
+    /// You should call this function regularly once the initial connection
+    /// has been initiated.
+    //
+    /// Its tasks involve:
+    /// 1. completing the handshake
+    /// 2. checking for socket errors
+    /// 3. reading incoming data
+    /// 4. filling data queues.
+    //
+    /// None of those things should concern you. Just call the function
+    /// regularly and use connected(), error(), and check the message
+    /// queues.
     void update();
 
-    /// Handle an RTMPPacket.
-    void handlePacket(const RTMPPacket& packet);
-
     /// Close the connection.
     //
     /// A new connection may now be opened.
     void close();
-    
-    /// Read from the socket.
-    int readSocket(boost::uint8_t* dst, int num);
-
-    /// Send an RTMPPacket on the connection.
-    bool sendPacket(RTMPPacket& packet);
 
     /// Get an AMF message received from the server.
     //
@@ -394,6 +431,15 @@
         return b;
     }
 
+    /// Handle an RTMPPacket.
+    void handlePacket(const RTMPPacket& packet);
+    
+    /// Read from the socket.
+    int readSocket(boost::uint8_t* dst, int num);
+
+    /// Send an RTMPPacket on the connection.
+    bool sendPacket(RTMPPacket& packet);
+
     /// Store the server bandwidth
     //
     /// Not sure why we need this.
@@ -430,7 +476,9 @@
     };
     
     /// Read an RTMP packet from the connection.
-    bool readPacket(RTMPPacket& packet);
+    bool readPacketHeader(RTMPPacket& packet);
+
+    bool readPacketPayload(RTMPPacket& packet);
 
     /// Check whether a packet exists on a channel.
     bool hasPacket(ChannelType t, size_t channel) const;
@@ -481,6 +529,18 @@
     /// Chunk size for sending.
     size_t _outChunkSize;
 
+    boost::scoped_ptr<HandShaker> _handShaker;
+
+    bool _connected;
+
+    bool _error;
+
+    /// If a packet could not be read in one go, it is stored here.
+    //
+    /// This is not the same as a non-ready packet. It applies only to packets
+    /// waiting for payload data.
+    boost::scoped_ptr<RTMPPacket> _incompletePacket;
+
 };
 
 /// Send a control packet

=== modified file 'libbase/Socket.cpp'
--- a/libbase/Socket.cpp        2010-02-24 11:22:12 +0000
+++ b/libbase/Socket.cpp        2010-03-10 17:31:45 +0000
@@ -25,6 +25,7 @@
 #include <unistd.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
+#include <fcntl.h>
 
 #include <boost/cstdint.hpp>
 #include <cstring>
@@ -41,12 +42,69 @@
 
 Socket::Socket()
     :
+    _connected(false),
     _socket(0),
     _size(0),
     _pos(0),
-    _timedOut(false)
+    _error(false)
 {}
 
+bool
+Socket::connected() const
+{
+    if (_connected) return true;
+    if (!_socket) return false;
+
+    size_t retries = 10;
+    fd_set fdset;
+    struct timeval tval;
+
+    while (retries-- > 0) {
+
+        FD_ZERO(&fdset);
+        FD_SET(_socket, &fdset);
+            
+        tval.tv_sec = 0;
+        tval.tv_usec = 103;
+            
+        const int ret = select(_socket + 1, NULL, &fdset, NULL, &tval);
+        
+        // Select timeout
+        if (ret == 0) continue;
+           
+        if (ret > 0) {
+            boost::uint32_t val = 0;
+            socklen_t len = sizeof(val);
+            if (::getsockopt(_socket, SOL_SOCKET, SO_ERROR, &val, &len) < 0) {
+                log_debug("Error");
+                _error = true;
+                return false;
+            }
+
+            if (!val) {
+                _connected = true;
+                return true;
+            }
+            _error = true;
+            return false;
+        }
+
+        // If interrupted by a system call, try again
+        if (ret == -1) {
+            const int err = errno;
+            if (err == EINTR) {
+                log_debug(_("Socket interrupted by a system call"));
+                continue;
+            }
+
+            log_error(_("XMLSocket: The socket was never available"));
+            _error = true;
+            return false;
+        }
+    }
+    return false;
+} 
+    
 
 void
 Socket::close()
@@ -54,10 +112,11 @@
     if (_socket) ::close(_socket);
     _socket = 0;
     _size = 0;
+    _pos = 0;
 }
 
 bool
-Socket::connect(const URL& url)
+Socket::connect(const std::string& hostname, boost::uint16_t port)
 {
 
     if (connected()) {
@@ -65,88 +124,117 @@
         return false;
     }
 
-    const std::string& hostname = url.hostname();
+    assert(!_error);
+    assert(!_socket);
+
     if (hostname.empty()) return false;
 
     struct sockaddr_in addr;
     std::memset(&addr, 0, sizeof(addr));
     addr.sin_family = AF_INET;
 
-    addr.sin_addr.s_addr = inet_addr(hostname.c_str());
+    addr.sin_addr.s_addr = ::inet_addr(hostname.c_str());
     if (addr.sin_addr.s_addr == INADDR_NONE) {
-        struct hostent* host = gethostbyname(hostname.c_str());
+        struct hostent* host = ::gethostbyname(hostname.c_str());
         if (!host || !host->h_addr) {
             return false;
         }
         addr.sin_addr = *reinterpret_cast<in_addr*>(host->h_addr);
     }
 
-    const std::string& port = url.port();
-    const int p = port.empty() ? 0 : boost::lexical_cast<int>(port);
-    addr.sin_port = htons(p);
-
-    _timedOut = false;
-
-    _socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-    if (_socket != -1) {
-        const struct sockaddr* a = reinterpret_cast<struct sockaddr*>(&addr);
-
-        if (::connect(_socket, a, sizeof(struct sockaddr)) < 0) {
-            const int err = errno;
+    addr.sin_port = htons(port);
+
+    _socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    
+    if (_socket < 0) {
+        const int err = errno;
+        log_debug("Socket creation failed: %s", std::strerror(err));
+        _socket = 0;
+        return false;
+    }
+
+    // Set non-blocking.
+    const int flag = ::fcntl(_socket, F_GETFL, 0);
+    ::fcntl(_socket, F_SETFL, flag | O_NONBLOCK);
+
+    const struct sockaddr* a = reinterpret_cast<struct sockaddr*>(&addr);
+
+    // Attempt connection
+    if (::connect(_socket, a, sizeof(struct sockaddr)) < 0) {
+        const int err = errno;
+        if (err != EINPROGRESS) {
             log_error("Failed to connect socket: %s", std::strerror(err));
-            close();
+            _socket = 0;
             return false;
         }
-
     }
 
     // Magic timeout number. Use rcfile ?
     struct timeval tv = { 120, 0 };
 
-    if (setsockopt(_socket, SOL_SOCKET, SO_RCVTIMEO,
+    if (::setsockopt(_socket, SOL_SOCKET, SO_RCVTIMEO,
                 reinterpret_cast<unsigned char*>(&tv), sizeof(tv))) {
         log_error("Setting socket timeout failed");
     }
 
     const boost::int32_t on = 1;
-    setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
+    ::setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
 
     assert(_socket);
     return true;
 }
 
-
-std::streamsize
+void
 Socket::fillCache()
 {
 
-    // If there are no unprocessed bytes, start from the beginning.
-    if (!_size) {
-        _pos = _cache;
-    }
+    // Read position is always _pos + _size wrapped.
+    const size_t cacheSize = arraySize(_cache);
+    size_t start = (_pos + _size) % cacheSize;
+
+    // Try to fill the whole remaining buffer.
+    const size_t completeRead = cacheSize - _size;
+    
+    // End is start + read size, wrapped.
+    size_t end = (start + completeRead) % cacheSize;
+    if (end == 0) end = cacheSize;
+
+    boost::uint8_t* startpos = _cache + start;
 
     while (1) {
 
-
-        // Read up to the end of the cache if possible.
-        const int toRead = arraySize(_cache) - _size - (_pos - _cache);
-
-        const int bytesRead = recv(_socket, _pos + _size, toRead, 0);
+        // The end pos is either the end of the cache or the first 
+        // unprocessed byte.
+        boost::uint8_t* endpos = _cache + ((startpos < _cache + _pos) ?
+                _pos : cacheSize);
+
+        const int thisRead = endpos - startpos;
+        assert(thisRead >= 0);
+
+        const int bytesRead = ::recv(_socket, startpos, thisRead, 0);
         
         if (bytesRead == -1) {
+            
             const int err = errno;
-            log_debug("Socket receive error %s", std::strerror(err));
-            //if (err == EINTR) continue;
-            
             if (err == EWOULDBLOCK || err == EAGAIN) {
-                // Nothing read.
-                _timedOut = true;
-                return 0;
+                // Nothing to read. Carry on.
+                return;
             }
+            log_error("Socket receive error %s", std::strerror(err));
+            _error = true;
         }
+
+
         _size += bytesRead;
-        return bytesRead;
+
+        // If there weren't enough bytes, that's it.
+        if (bytesRead < thisRead) break;
+
+        // If we wrote up to the end of the cache, try writing more to the
+        // beginning.
+        startpos = _cache;
     }
+    
 }
 
 
@@ -154,39 +242,60 @@
 std::streamsize 
 Socket::read(void* dst, std::streamsize num)
 {
+ 
+    if (num < 0) return 0;
+
+    if (_size < num && !_error) {
+        fillCache();
+    }
+
+    if (_size < num) return 0;
+    return readNonBlocking(dst, num);
+
+}
+
+std::streamsize
+Socket::readNonBlocking(void* dst, std::streamsize num)
+{
+    if (bad()) return 0;
     
-    int toRead = num;
-    _timedOut = false;
-
     boost::uint8_t* ptr = static_cast<boost::uint8_t*>(dst);
-
-    if (!_size) {
-        if (fillCache() < 1) {
-            if (_timedOut) {
-                return -1;
-            }
-        }
-    }
-
-    const int thisRead = std::min(_size, toRead);
-    if (thisRead > 0) {
-        std::copy(_pos, _pos + thisRead, ptr);
-        _pos += thisRead;
-        _size -= thisRead;
-    }
-    return thisRead;
-}
-
-std::streamsize
-Socket::readNonBlocking(void* dst, std::streamsize num)
-{
-    return read(dst, num);
+    
+    if (!_size && !_error) {
+        fillCache();
+    }
+
+    size_t cacheSize = arraySize(_cache);
+
+    // First read from pos to end
+
+    // Maximum bytes available to read.
+    const size_t canRead = std::min<size_t>(_size, num);
+    
+    size_t toRead = canRead;
+
+    // Space to the end (for the first read).
+    const int thisRead = std::min<size_t>(canRead, cacheSize - _pos);
+
+    std::copy(_cache + _pos, _cache + _pos + thisRead, ptr);
+    _pos += thisRead;
+    _size -= thisRead;
+    toRead -= thisRead;
+
+    if (toRead) {
+        std::copy(_cache, _cache + toRead, ptr + thisRead);
+        _pos = toRead;
+        _size -= toRead;
+        toRead = 0;
+    }
+
+    return canRead - toRead;
 }
 
 std::streamsize
 Socket::write(const void* src, std::streamsize num)
 {
-    assert(!bad());
+    if (bad()) return 0;
 
     int bytesSent = 0;
     int toWrite = num;
@@ -195,15 +304,14 @@
 
     while (toWrite > 0) {
         bytesSent = ::send(_socket, buf, toWrite, 0);
-        //log_debug("Bytes sent %s", bytesSent);
         if (bytesSent < 0) {
             const int err = errno;
             log_error("Socket send error %s", std::strerror(err));
-            close();
-            return -1;
-            break;
+            _error = true;
+            return 0;
         }
-        if (bytesSent == 0) break;
+
+        if (!bytesSent) break;
         toWrite -= bytesSent;
         buf += bytesSent;
     }
@@ -237,10 +345,4 @@
     return false;
 }
 
-bool
-Socket::bad() const
-{
-    return !_socket || _timedOut;
-}
-
 } // namespace gnash

=== modified file 'libbase/Socket.h'
--- a/libbase/Socket.h  2010-02-23 15:28:45 +0000
+++ b/libbase/Socket.h  2010-03-10 13:05:38 +0000
@@ -33,36 +33,66 @@
 
 /// A simple IOChannel subclass for reading and writing sockets.
 //
-/// The only important functions here are read and write.
+/// The Socket class will give you years of satisfaction provided you observe
+/// the following points:
+/// 1. A socket is active only when both connected() and not bad().
+/// 2. The only accurate way of determining an error is to check bad().
+/// 3. read() and write() should not be called until connected() is true.
 class DSOEXPORT Socket : public IOChannel
 {
 public:
 
+    /// Create a non-connected socket.
     Socket();
 
     virtual ~Socket() {}
 
-    bool connect(const URL& url);
+    /// Initiate a connection
+    //
+    /// A return of true does not mean the connection has succeeded, only that
+    /// it did not fail. The connection attempt is only complete when
+    /// connected() returns true.
+    //
+    /// @return         false if the connection fails. In this case, the
+    ///                 Socket is still in a closed state and is ready for
+    ///                 a new connection attempt. Otherwise true.
+    bool connect(const std::string& hostname, boost::uint16_t port);
 
+    /// Close the Socket.
+    //
+    /// A closed Socket is in a state where another connection attempt can
+    /// be made. Any errors are reset.
     void close();
 
-    bool connected() const {
-        return (_socket);
-    }
-
-    bool timedOut() const {
-        return (_timedOut);
-    }
-
-    /// Read the given number of bytes from the stream
-    //
-    /// Return the number of bytes actually read. 
+    /// Whether a connection attempt is complete.
+    //
+    /// This is true as soon as the socket is ready for reading and writing.
+    /// But beware! This function may still return true if the Socket is
+    /// in error condition. Always check bad() if you care about this.
+    bool connected() const;
+    
+    /// True if the Socket is in an error condition.
+    //
+    /// An error condition is fatal and can only be reset when the Socket
+    /// is closed. Any read or write failure other than EAGAIN or EWOULDBLOCK
+    /// causes a fatal error.
+    virtual bool bad() const {
+        return _error;
+    }
+
+    /// Read exactly the given number of bytes from the Socket or none at all.
+    //
     virtual std::streamsize read(void* dst, std::streamsize num);
 
-    /// The same as read().
+    /// Read up to the given number of bytes from the Socket.
     virtual std::streamsize readNonBlocking(void* dst, std::streamsize num);
 
     /// Write the given number of bytes to the stream
+    //
+    /// If you call write() before connected() is true, it may put the Socket
+    /// into an error condition.
+    //
+    /// Calling write() when the Socket is bad has no effect.
     virtual std::streamsize write(const void* src, std::streamsize num);
     
     /// Return current stream position
@@ -84,14 +114,13 @@
     //
     /// Not implemented for Socket.
     virtual bool eof() const;
-    
-    /// Return true if the stream is in an error state
-    virtual bool bad() const;
 
 private:
 
+    mutable bool _connected;
+
     /// Fill the cache.
-    std::streamsize fillCache();
+    void fillCache();
 
     /// A cache for received data.
     boost::uint8_t _cache[16384];
@@ -103,9 +132,9 @@
     int _size;
 
     /// Current read position in cache.
-    boost::uint8_t* _pos;
+    size_t _pos;
 
-    bool _timedOut;
+    mutable bool _error;
 };
 
 } // namespace gnash

=== modified file 'libcore/asobj/flash/net/XMLSocket_as.cpp'
--- a/libcore/asobj/flash/net/XMLSocket_as.cpp  2010-03-09 21:22:09 +0000
+++ b/libcore/asobj/flash/net/XMLSocket_as.cpp  2010-03-10 17:32:34 +0000
@@ -55,172 +55,6 @@
     void attachXMLSocketInterface(as_object& o);
 }
 
-/// Connection object
-//
-/// A wrapper round a Network object that adds specific functions needed
-/// by XMLSocket.
-namespace {
-
-class SocketConnection
-{
-public:
-
-    SocketConnection()
-        :
-        _complete(false)
-    {}
-
-    /// Initiate a connection.
-    void connect(const std::string& host, boost::uint16_t port) {
-        _start.reset(new boost::thread(
-            boost::bind(&SocketConnection::makeConnection, this, host, port)));
-    }
-
-    /// The state of the connection.
-    //
-    /// Until complete() is true, this may change.
-    bool connected() const {
-        return _socket.connected();
-    }
-
-    /// Whether an initiated connection is finished
-    //
-    /// @return true if a connection attempt is complete.
-    ///         The connection attempt may have failed. Check
-    ///         connected() to find out.
-    bool complete() const {
-        return _complete;
-    }
-
-    void setComplete() {
-        _complete = true;
-    }
-
-    size_t writeMessage(const std::string& str) {
-        // We have to write the null terminator as well.
-        return write(_socket.connected(), str.c_str(), str.size() + 1);
-    }
-
-    /// Read from the socket.
-    void readMessages(std::vector<std::string>& msgs) {
-        
-        assert(_socket.connected());
-    
-        const int fd = _socket.connected();
-        assert(fd > 0);
-
-        fd_set fdset;
-        struct timeval tval;
-        size_t retries = 10;
-
-        const int bufSize = 10000;
-        boost::scoped_array<char> buf(new char[bufSize]);
-
-        while (retries-- > 0) {
-            FD_ZERO(&fdset);
-            FD_SET(fd, &fdset);
-            
-            tval.tv_sec = 0;
-            tval.tv_usec = 103;
-            
-            const int ret = select(fd + 1, &fdset, NULL, NULL, &tval);
-            
-            // If interupted by a system call, try again
-            if (ret == -1 && errno == EINTR) {
-                log_debug(_("The socket for fd #%d was interupted by a "
-                            "system call"), fd);
-                continue;
-            }
-            if (ret == -1) {
-                log_error(_("XMLSocket: The socket for fd #%d was never "
-                            "available"), fd);
-                return;
-            }
-     
-            // Return if timed out.
-            if (ret == 0) return;
-
-            const size_t bytesRead = read(fd, buf.get(), bufSize - 1);
-
-            // Return if there's no data.
-            if (!bytesRead) return;
-
-            if (buf[bytesRead - 1] != 0) {
-                // We received a partial message, so bung
-                // a null-terminator on the end.
-                buf[bytesRead] = 0;
-            }
-
-            char* ptr = buf.get();
-            while (static_cast<size_t>(ptr - buf.get()) < bytesRead - 1) {
-
-#ifdef GNASH_XMLSOCKET_DEBUG
-                log_debug ("read: %d, this string ends: %d", bytesRead,
-                        ptr + std::strlen(ptr) - buf.get());
-#endif
-
-                // If the string reaches to the final byte read, it's
-                // incomplete. Store it and continue. The buffer is 
-                // NULL-terminated, so this cannot read past the end.
-                if (static_cast<size_t>(
-                    ptr + std::strlen(ptr) - buf.get()) == bytesRead) {
-
-                    _remainder += std::string(ptr);
-                    break;
-                }
-
-                if (!_remainder.empty()) {
-                    msgs.push_back(_remainder + std::string(ptr));
-                    ptr += std::strlen(ptr) + 1;
-                    _remainder.clear();
-                    continue;
-                }
-                
-                // Don't do anything if nothing is received.
-                msgs.push_back(ptr);
-                
-                ptr += std::strlen(ptr) + 1;
-            }
-        }
-    }
-    
-    /// Close the connection.
-    //
-    /// This also cancels any connection attempt in progress.
-    void close() {
-        if (_start) {
-           _start.reset();
-       }
-       _socket.close();
-        
-        // Reset for next connection.
-        _complete = false;
-
-        assert(!_socket.connected());
-    }
-
-private:
-
-    void makeConnection(const std::string& host, boost::uint16_t port) {
-       std::string combined = host;
-       combined += ":";
-       combined += port;       
-       URL url(combined);
-        _socket.connect(url);
-        _complete = true;
-    }
-
-    Socket _socket;
-
-    bool _complete;
-
-    std::string _remainder;
-
-    boost::scoped_ptr<boost::thread> _start;
-
-};
-
-}
 
 class XMLSocket_as : public ActiveRelay
 {
@@ -231,21 +65,12 @@
     XMLSocket_as(as_object* owner);
     ~XMLSocket_as();
     
-    /// True when the XMLSocket is not trying to connect.
-    //
-    /// If this is true but the socket is not connected, the connection
-    /// has failed.
+    /// True only when the XMLSocket is ready for read/write.
     bool ready() const {
         return _ready;
     }
 
-    /// Whether a connection exists.
-    //
-    /// This is not final until ready() is true.
-    bool connected() const {
-        return _connection.connected();
-    }
-
+    /// Attempt a connection.
     bool connect(const std::string& host, boost::uint16_t port);
 
     /// Send a string with a null-terminator to the socket.
@@ -265,14 +90,12 @@
 
        void checkForIncomingData();
     
-       /// Return the as_function with given name.
-       boost::intrusive_ptr<as_function> getEventHandler(
-            const std::string& name);
-
     /// The connection
-    SocketConnection _connection;
+    Socket _socket;
 
     bool _ready;
+    
+    std::string _remainder;
 
 };
 
@@ -294,26 +117,29 @@
 void
 XMLSocket_as::update()
 {
-    // Wait until something has happened with the connection
-    if (!_connection.complete()) return;
-    
-    // If this XMLSocket hadn't finished a connection, check whether it
-    // has now.
+
+    // This function should never be called unless a connection is active 
+    // or a connection attempt is being made.
+
+    // If the connection was not complete, check to see if it is.
     if (!ready()) {
-
-        if (!connected()) {
-
-            // If connection failed, notify onConnect and stop callback.
-            // This means update() will not be called again until
-            // XMLSocket.connect() is invoked.
+        
+        if (_socket.bad()) {
+            // Connection failed during connect()
+            // Notify onConnect and stop callback. This means update()
+            // will not be called again until XMLSocket.connect() is invoked.
             callMethod(&owner(), NSV::PROP_ON_CONNECT, false);
             getRoot(owner()).removeAdvanceCallback(this);
-            return;    
+            return;
         }
 
+        // Not yet ready.
+        if (!_socket.connected()) return;
+
         // Connection succeeded.
         _ready = true;
         callMethod(&owner(), NSV::PROP_ON_CONNECT, true);
+
     }
 
     // Now the connection is established we can receive data.
@@ -329,7 +155,7 @@
            return false;
     }
 
-    _connection.connect(host, port);
+    _socket.connect(host, port);
     
     // Start callbacks on advance.
     getRoot(owner()).addAdvanceCallback(this);
@@ -341,62 +167,83 @@
 XMLSocket_as::close()
 {
     getRoot(owner()).removeAdvanceCallback(this);
-    _connection.close();
+    _socket.close();
     _ready = false;
 }
 
 
-boost::intrusive_ptr<as_function>
-XMLSocket_as::getEventHandler(const std::string& name)
-{
-       boost::intrusive_ptr<as_function> ret;
-
-       as_value tmp;
-       string_table& st = getStringTable(owner());
-       if (!owner().get_member(st.find(name), &tmp) ) return ret;
-       ret = tmp.to_function();
-       return ret;
-}
-
 void
 XMLSocket_as::checkForIncomingData()
 {
-    assert(ready() && connected());
+    assert(ready());
     
     std::vector<std::string> msgs;
-    _connection.readMessages(msgs);
+    
+    const int bufSize = 10000;
+    boost::scoped_array<char> buf(new char[bufSize]);
+
+    const size_t bytesRead = _socket.readNonBlocking(buf.get(), bufSize - 1);
+
+    // Return if there's no data.
+    if (!bytesRead) return;
+
+    if (buf[bytesRead - 1] != 0) {
+        // We received a partial message, so bung
+        // a null-terminator on the end.
+        buf[bytesRead] = 0;
+    }
+
+    char* ptr = buf.get();
+    while (static_cast<size_t>(ptr - buf.get()) < bytesRead) {
+
+#ifdef GNASH_XMLSOCKET_DEBUG
+        log_debug ("read: %d, this string ends: %d", bytesRead,
+                ptr + std::strlen(ptr) - buf.get());
+#endif
+
+        std::string msg(ptr);
+
+        // If the string reaches to the final byte read, it's
+        // incomplete. Store it and continue. The buffer is 
+        // NULL-terminated, so this cannot read past the end.
+        if (static_cast<size_t>(
+            ptr + std::strlen(ptr) - buf.get()) == bytesRead) {
+            _remainder += msg;
+            break;
+        }
+
+        if (!_remainder.empty()) {
+            msgs.push_back(_remainder + msg);
+            ptr += msg.size() + 1;
+            _remainder.clear();
+            continue;
+        }
+        
+        // Don't do anything if nothing is received.
+        msgs.push_back(msg);
+        
+        ptr += msg.size() + 1;
+    }
    
     if (msgs.empty()) return;
     
-    //log_debug(_("Got %d messages: "), msgs.size());
-
 #ifdef GNASH_XMLSOCKET_DEBUG
     for (size_t i = 0, e = msgs.size(); i != e; ++i) {
         log_debug(_(" Message %d: %s "), i, msgs[i]);
     }
 #endif
 
-    as_environment env(getVM(owner())); 
-
     for (XMLSocket_as::MessageList::const_iterator it=msgs.begin(),
                     itEnd=msgs.end(); it != itEnd; ++it) {
- 
-        // This should be checked on every iteration in case one call
-        // changes the handler.       
-        boost::intrusive_ptr<as_function> onDataHandler =
-            getEventHandler("onData");
-
-        if (!onDataHandler) break;
-
-        const std::string& s = *it;
-
-        fn_call::Args args;
-        args += s;
-        
-        fn_call call(&owner(), env, args);
-
-        onDataHandler->call(call);
-    }
+        callMethod(&owner(), NSV::PROP_ON_DATA, *it);
+    }
+    
+    if (_socket.bad()) {
+        callMethod(&owner(), NSV::PROP_ON_CLOSE);
+        getRoot(owner()).removeAdvanceCallback(this);
+        return;
+    }
+
 
 }
 
@@ -406,12 +253,12 @@
 void
 XMLSocket_as::send(std::string str)
 {
-    if (!ready() || !connected()) {
+    if (!_socket.connected()) {
         log_error(_("XMLSocket.send(): socket not initialized"));
            return;
     }
     
-    _connection.writeMessage(str);
+    _socket.write(str.c_str(), str.size() + 1);
     
     return;
 }

=== modified file 'testsuite/XmlSocketServer.pl'
--- a/testsuite/XmlSocketServer.pl      2008-08-20 16:15:55 +0000
+++ b/testsuite/XmlSocketServer.pl      2010-03-10 17:32:12 +0000
@@ -4,8 +4,8 @@
 
 $SIG{PIPE}='IGNORE';
 
-$m=new IO::Socket::INET(Listen=>1,LocalPort=>2229);
-$O=new IO::Select($m);
+my $m=new IO::Socket::INET(Listen=>1,LocalPort=>2229,Reuse=>1,Proto=>'tcp');
+my $O=new IO::Select($m);
 
 
 $/ = "\0";
@@ -22,6 +22,12 @@
             # Log message received:
             print "XmlSocketServer: received \"$i\"\n";
             
+            if ($i =~ m/closeNow/) {
+                print("Closing...\n");
+                close($m);
+                Time::HiRes::sleep(1);
+            }
+
             if ($R==0) {
                 $T=syswrite($_, "\n", 16000);
                 if ($T==undef) {

=== modified file 'testsuite/misc-ming.all/Makefile.am'
--- a/testsuite/misc-ming.all/Makefile.am       2010-02-11 16:08:00 +0000
+++ b/testsuite/misc-ming.all/Makefile.am       2010-03-10 17:32:12 +0000
@@ -143,7 +143,6 @@
        attachMovieLoopingTestRunner \
        registerClassTest \
        registerClassTestRunner \
-       XMLSocketTest \
        goto_frame_test \
        consecutive_goto_frame_test \
        matrix_test \
@@ -255,6 +254,7 @@
        DeviceFontTestRunner \
        EmbeddedFontTestRunner \
        TextSnapshotTest-Runner \
+       XMLSocketTester \
        LCTestRunner \
        timeline_var_test-Runner \
        place_object_testrunner \
@@ -1517,14 +1517,6 @@
        sh $< $(top_builddir) action_execution_order_extend_test.swf > $@
        chmod 755 $@
 
-XMLSocketTest_SOURCES =        \
-       XMLSocketTest.c \
-       $(NULL)
-XMLSocketTest_LDADD = libgnashmingutils.la
-
-XMLSocketTest.swf: XMLSocketTest
-       ./XMLSocketTest $(top_srcdir)/testsuite/media
-
 BitmapDataTest_SOURCES = \
        BitmapDataTest.c \
        $(NULL)
@@ -1827,6 +1819,14 @@
        sh $< -c "ENDOFTEST" $(top_builddir) LC-Receive.swf LC-Send.swf > $@
        chmod 755 $@
 
+XMLSocketTest.swf: $(srcdir)/XMLSocketTest.as 
+       $(MAKESWF) -r 1 -o $@  $(srcdir)/empty.as $(srcdir)/XMLSocketTest.as
+
+XMLSocketTester: $(srcdir)/XMLSocketTester.sh XMLSocketTest.swf
+       sh $< -c "ENDOFTEST" $(top_builddir) $(top_srcdir) $(PERL) \
+               XMLSocketTest.swf > $@
+       chmod 755 $@
+
 DrawingApiTest.swf: $(srcdir)/DrawingApiTest.as 
        $(MAKESWF) -r 1 -o $@  $(srcdir)/empty.as $(srcdir)/DrawingApiTest.as
 
@@ -2060,6 +2060,7 @@
        action_execution_order_extend_testrunner \
        simple_loop_testrunner \
        loadMovieTestRunner \
+       XMLSocketTester \
        LCTestRunner \
        DrawingApiTestRunner \
        TextSnapshotTest-Runner \

=== added file 'testsuite/misc-ming.all/XMLSocketTest.as'
--- a/testsuite/misc-ming.all/XMLSocketTest.as  1970-01-01 00:00:00 +0000
+++ b/testsuite/misc-ming.all/XMLSocketTest.as  2010-03-10 17:46:27 +0000
@@ -0,0 +1,109 @@
+
+#include "../actionscript.all/check.as"
+
+xmlArray = [];
+xmlArray[0] = 'Plain text';
+xmlArray[1] = 'Plain *NEWLINE* text';
+xmlArray[2] = 'Plain *NULL* text';
+xmlArray[3] = 'Plain *NULL**NEWLINE* text';
+xmlArray[4] = '<xml>Some XML</xml>';
+xmlArray[5] = '<xml>Some XML*NEWLINE*</xml>';
+xmlArray[6] = '<xml>Some XML*NULL*</xml>';
+xmlArray[7] = '<xml>Some XML*NEWLINE**NULL*</xml>';
+xmlArray[8] = undefined;
+xmlArray[9] = 9;
+xmlArray[10] = "";
+a = "";
+for (i = 0; i < 15000; ++i) { a += "a"; };
+xmlArray[11] = a;
+xmlArray[12] = 'Last Item';
+xmlArray[13] = 'closeNow';
+xmlArray[14] = 'moo';
+
+expectedArray = new Array();
+expectedArray[0] = 'Plain text';
+expectedArray[1] = 'Plain \n text';
+expectedArray[2] = 'Plain ';
+expectedArray[3] = ' text';
+expectedArray[4] = 'Plain ';
+expectedArray[5] = '\n text';
+expectedArray[6] = '<xml>Some XML</xml>';
+expectedArray[7] = '<xml>Some XML\n</xml>';
+expectedArray[8] = '<xml>Some XML';
+expectedArray[9] = '</xml>';
+expectedArray[10] = '<xml>Some XML\n';
+expectedArray[11] = '</xml>';
+expectedArray[12] = 'undefined';
+expectedArray[13] = 9;
+expectedArray[14] = '';
+// Don't check 15.
+expectedArray[16] = 'Last Item';
+
+ gc = 0;
+ wait = 0;
+ count = -1;
+ connected = false;
+
+ var myXML;
+
+function handleConnect(connectionStatus) {
+   check_equals(connectionStatus, true);
+   if (connectionStatus) {                                      
+       trace('Connected');                
+       connected = true;
+       if (gc < xmlArray.length) {
+           myXML.send(xmlArray[gc++]);
+       }
+   }                                      
+   else { trace('Initial connection failed!'); }  
+};                                         
+
+// Store data and send next lot.
+function handleData(data) {       
+    receivedArray.push(data);
+    str = xmlArray[gc++];
+    myXML.send(str);
+};                                         
+
+function handleDisconnect() {              
+    trace('Connection lost.');
+    checkResults();
+};                                     
+
+myXML = new XMLSocket;
+myXML.onConnect = handleConnect;
+myXML.onData = handleData;
+myXML.onClose = handleDisconnect;
+receivedArray = new Array();
+
+ ret = myXML.connect("localhost", 2229);
+ check_equals(ret, true);
+ stop();
+
+
+function checkResults() {
+
+    check_equals(receivedArray[0], expectedArray[0]);
+    check_equals(receivedArray[1], expectedArray[1]);
+    check_equals(receivedArray[2], expectedArray[2]);
+    check_equals(receivedArray[3], expectedArray[3]);
+    check_equals(receivedArray[4], expectedArray[4]);
+    check_equals(receivedArray[5], expectedArray[5]);
+    check_equals(receivedArray[6], expectedArray[6]);
+    check_equals(receivedArray[7], expectedArray[7]);
+    check_equals(receivedArray[8], expectedArray[8]);
+    check_equals(receivedArray[9], expectedArray[9]);
+    check_equals(receivedArray[10], expectedArray[10]);    
+    check_equals(receivedArray[11], expectedArray[11]); 
+    check_equals(receivedArray[12], expectedArray[12]); 
+    check_equals(receivedArray[13], expectedArray[13]);         
+    check_equals(receivedArray[14], expectedArray[14]);
+    check_equals(receivedArray[15].length, 15000);         
+    check_equals(receivedArray[15].charAt(0), 'a');         
+    check_equals(receivedArray[16], expectedArray[16]);
+
+    trace("ENDOFTEST");
+
+    loadMovie ("FSCommand:quit", "");
+};
+    

=== removed file 'testsuite/misc-ming.all/XMLSocketTest.c'
--- a/testsuite/misc-ming.all/XMLSocketTest.c   2010-01-01 17:48:26 +0000
+++ b/testsuite/misc-ming.all/XMLSocketTest.c   1970-01-01 00:00:00 +0000
@@ -1,264 +0,0 @@
-/*
- *   Copyright (C) 2005, 2006, 2009, 2010 Free Software Foundation, Inc.
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */ 
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <ming.h>
-
-#include "ming_utils.h"
-
-#define OUTPUT_VERSION 7
-#define OUTPUT_FILENAME "XMLSocketTest.swf"
-
-int
-main(int argc, char** argv)
-{
-
-    /* Basic setup */
-
-       SWFMovieClip dejagnuclip;
-    SWFMovie mo;
-       const char* srcdir = ".";
-
-    char longString[15001];
-
-       if ( argc>1 ) srcdir=argv[1];
-       else
-       {
-               fprintf(stderr, "Usage: %s\n", argv[0]);
-               return 1;
-       }
-
-    Ming_init();
-
-    mo = newSWFMovieWithVersion(OUTPUT_VERSION);
-    SWFMovie_setDimension(mo, 800, 600);
-    SWFMovie_setRate (mo, 10.0);
-
-    dejagnuclip = get_dejagnu_clip((SWFBlock)get_default_font(srcdir), 10, 0, 
0, 800, 600);
-    SWFMovie_add(mo, (SWFBlock)dejagnuclip);
-
-    /* Setup XMLsocket */
-
-    /* Advance to next frame on connection */
-    add_actions(mo,
-        "function handleConnect(connectionStatus) { "
-        "    if (connectionStatus)                  "
-        "    {                                      "
-        "        trace('Connected');                "
-        "        connected = true;                  "
-        "        play();                            "
-        "    }                                      "
-        "    else { trace('Connection failed.'); }  "
-        "};                                         "
-     );
-
-    /* Add any data recieved to an array, advance to next
-       frame */
-    add_actions(mo, 
-        "function handleData(data) {                "
-        "    play();                                "
-        "    trace('Data received: ' + data);       "
-        "    receivedArray.push(data);              "
-        "    trace ('Pushed data');"
-        "};                                         "
-    );
-
-    /* Advance to next frame on losing connection */
-    add_actions(mo,
-        "function handleDisconnect() {              "
-        "    trace('Connection lost.');             "
-        "    connected = false;                     "
-        "    play();                                "
-        "};                                         "
-    );
-
-    /* Wait for connection */
-    add_actions(mo, "stop();");
-
-    /* Check here that a successful XMLSocket.connect() returns
-       true */
-    add_actions(mo,
-        "wait = 0;"
-        "count = -1;"
-        "connected = false;"
-        "myXML = new XMLSocket;"
-        "myXML.onConnect = handleConnect;"
-        "myXML.onData = handleData;"
-        "myXML.onClose = handleDisconnect;"
-        "ret = myXML.connect(\"localhost\", 2229);"
-        "check_equals(ret, true);"
-    );
-
-    /* The data we're going to send */
-
-    add_actions(mo, 
-        "xmlArray = new Array();"
-        "xmlArray[0] = 'Plain text';"
-        "xmlArray[1] = 'Plain *NEWLINE* text';"
-        "xmlArray[2] = 'Plain *NULL* text';"
-        "xmlArray[3] = 'Plain *NULL**NEWLINE* text';"
-        "xmlArray[4] = '<xml>Some XML</xml>';"
-        "xmlArray[5] = '<xml>Some XML*NEWLINE*</xml>';"
-        "xmlArray[6] = '<xml>Some XML*NULL*</xml>';"
-        "xmlArray[7] = '<xml>Some XML*NEWLINE**NULL*</xml>';"
-        "xmlArray[8] = undefined;"
-        "xmlArray[9] = 9;"
-        "xmlArray[10] = '';"
-    );
-
-    memset(longString, 'a', 15000);
-    strncpy(longString, "xmlArray[11] = '", 16);
-    longString[14998] = '\'';
-    longString[14999] = ';';
-    longString[15000] = 0;
-
-    add_actions(mo, longString);
-
-    add_actions(mo, "xmlArray[12] = 'Last Item';");
-
-    /* The data we should get back */
-    add_actions(mo, 
-        "expectedArray = new Array();"
-        "expectedArray[0] = 'Plain text';"
-        "expectedArray[1] = 'Plain \n text';"
-        "expectedArray[2] = 'Plain ';"
-        "expectedArray[3] = ' text';"
-        "expectedArray[4] = 'Plain ';"
-        "expectedArray[5] = '\n text';"
-        "expectedArray[6] = '<xml>Some XML</xml>';"
-        "expectedArray[7] = '<xml>Some XML\n</xml>';"
-        "expectedArray[8] = '<xml>Some XML';"
-        "expectedArray[9] = '</xml>';"
-        "expectedArray[10] = '<xml>Some XML\n';"
-        "expectedArray[11] = '</xml>';"
-        "expectedArray[12] = 'undefined';"
-        "expectedArray[13] = 9;"
-        "expectedArray[14] = '';"
-        "expectedArray[15] = 'aaa...';" /* Don't test this, we'll check the 
length */
-        "expectedArray[16] = 'Last Item';"
-    );
-
-
-    /* Where we're going to put the data */
-    add_actions(mo, 
-        "receivedArray = new Array();"
-    );
-
-
-
-    /* Frame 2 */
-    SWFMovie_nextFrame(mo); 
-    /* We should be connected here */
-       check_equals(mo, "connected", "true");
-
-
-
-
-    /* Frame 3 */
-    /* This is where we send the socket data */
-    SWFMovie_nextFrame(mo); 
-
-    add_actions(mo,
-        "count++;"
-        "if (count >= xmlArray.length) { _root.gotoAndStop(4); };"
-        "trace('Frame 3 (iteration ' + count + '): sending ' + 
xmlArray[count]);"
-        "myXML.send(xmlArray[count]);"
-        "play();"
-    );
-    
-
-    /* Frame 4 */
-    /* This is here to make a small pause between each send 
-       (a quarter of a second) */
-    SWFMovie_nextFrame(mo);
-
-    add_actions(mo,
-        "trace ('Frame 4');"
-        "play();"
-    );
-
-    /* Frame 5 */
-    /* If we have sent all the data, continue. Otherwise send the next item */
-    SWFMovie_nextFrame(mo);
-
-    add_actions(mo,
-        "trace ('Frame 5');"
-        "if (count < xmlArray.length - 1 ) { _root.gotoAndStop(3); };"
-        "play();"
-    );
-
-    /* Frame 6 */
-    /* This is a bit of a hackish loop to wait for data */
-    SWFMovie_nextFrame(mo);
-
-    add_actions(mo,
-        "play();"
-    );
-
-    /* Frame 7 */
-    SWFMovie_nextFrame(mo);
-
-    add_actions(mo,
-        "if (receivedArray[receivedArray.length -1] != 'Last Item' && wait++ < 
100)"
-        "{ _root.gotoAndStop(6); };"
-        "play();"
-    );
-
-
-    /* Last frame (8) */
-    SWFMovie_nextFrame(mo);
-
-    /* Close the connection to make sure it has no evil effects */
-    add_actions(mo,
-        "myXML.close();"
-    );
-
-    check_equals(mo, "receivedArray.length", "expectedArray.length");
-    
-    check_equals(mo, "receivedArray[0]", "expectedArray[0]");
-    check_equals(mo, "receivedArray[1]", "expectedArray[1]");
-    check_equals(mo, "receivedArray[2]", "expectedArray[2]");
-    check_equals(mo, "receivedArray[3]", "expectedArray[3]");
-    check_equals(mo, "receivedArray[4]", "expectedArray[4]");
-    check_equals(mo, "receivedArray[5]", "expectedArray[5]");
-    check_equals(mo, "receivedArray[6]", "expectedArray[6]");
-    check_equals(mo, "receivedArray[7]", "expectedArray[7]");
-    check_equals(mo, "receivedArray[8]", "expectedArray[8]");
-    check_equals(mo, "receivedArray[9]", "expectedArray[9]");
-    check_equals(mo, "receivedArray[10]", "expectedArray[10]");    
-    check_equals(mo, "receivedArray[11]", "expectedArray[11]"); 
-    check_equals(mo, "receivedArray[12]", "expectedArray[12]"); 
-    check_equals(mo, "receivedArray[13]", "expectedArray[13]");         
-    check_equals(mo, "receivedArray[14]", "expectedArray[14]");
-    check_equals(mo, "receivedArray[15].length", "14982");         
-    
-             
-    check_equals(mo, "receivedArray[16]", "expectedArray[16]");         
-
-    
-
-    add_actions(mo, "totals(); stop();");
-
-    /* Output movie */
-    puts("Saving " OUTPUT_FILENAME );
-    SWFMovie_save(mo, OUTPUT_FILENAME);
-
-  return 0;
-}
-

=== added file 'testsuite/misc-ming.all/XMLSocketTester.sh'
--- a/testsuite/misc-ming.all/XMLSocketTester.sh        1970-01-01 00:00:00 
+0000
+++ b/testsuite/misc-ming.all/XMLSocketTester.sh        2010-03-10 18:33:52 
+0000
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+# 
+#   Copyright (C) 2005, 2006, 2009, 2010 Free Software Foundation, Inc.
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+# 
+
+while getopts c:C: name; do
+       case $name in
+               c) endtagpat="$OPTARG" ;;
+               C) endtagpat="$OPTARG"; endtagexp=X ;;
+               ?)
+                  {
+                  echo "Usage: $0 [-r <runs>] [-f <advances>] [-c <string>]  
<swf> ..." 
+                  echo "   -c <pattern>    : verify that the test ends with a 
trace "
+           echo "                     matching <pattern>, or print a failure" 
+                  echo "   -C <pattern>    : same as -c <pattern> but a 
failure is "
+           echo "                     expected" 
+                  } >&2
+                  exit 1;;
+       esac
+done
+shift $(($OPTIND - 1))
+
+
+top_builddir=$1
+shift
+top_srcdir=$1
+shift
+perl=$1
+shift
+swf=$1
+
+
+echo "#!/bin/sh"
+echo
+
+echo "# Environment"
+env | grep GNASH | while read REPLY; do
+       echo "export ${REPLY}"
+done
+
+timeout=40
+cat << EOF
+
+outlog=${top_builddir}/testoutlog.\$$
+(
+    echo "Running first process"
+    $perl ${top_srcdir}/testsuite/XmlSocketServer.pl
+) &
+(
+    echo "Running second process"
+    ${top_builddir}/gui/gnash -v -r0 ${swf} -t ${timeout} > \${outlog}
+    cat \${outlog}
+       if test "x${endtagpat}" != x; then
+               lasttrace=\`grep TRACE \${outlog} | tail -1 | sed 's/.*TRACE: 
//'\`
+               if ! expr "\${lasttrace}" : '${endtagpat}' > /dev/null; then
+                       echo "${endtagexp}FAILED: consistency check: last trace 
from run of test \${t} (\${lasttrace}) doesn't match pattern (${endtagpat})"
+               else
+                       echo "${endtagexp}PASSED: consistency check: last trace 
from run of test \${t} (\${lasttrace}) matches pattern (${endtagpat})"
+               fi
+       fi
+    rm \${outlog}
+)
+EOF

=== modified file 'utilities/rtmpget.cpp'
--- a/utilities/rtmpget.cpp     2010-02-24 18:00:03 +0000
+++ b/utilities/rtmpget.cpp     2010-03-10 10:08:21 +0000
@@ -24,6 +24,7 @@
 #include "SimpleBuffer.h"
 #include "AMF.h"
 #include "GnashAlgorithm.h"
+#include "GnashSleep.h"
 
 #include <boost/cstdint.hpp>
 #include <iomanip>
@@ -480,6 +481,16 @@
         std::exit(EXIT_FAILURE);
     }
 
+    do {
+        r.update();
+        gnashSleep(1000);
+    } while (!r.connected());
+    
+    if (r.error()) {
+        log_error("Connection attempt failed");
+        std::exit(EXIT_FAILURE);
+    }
+
     /// 1. connect.
     sendConnectPacket(r, nc, app, ver, swf, tc, page);
  
@@ -495,6 +506,10 @@
 
     while (1) {
         r.update();
+        if (r.error()) {
+            gnash::log_error("Connection error");
+            break;
+        }
 
         /// Retrieve messages.
         boost::shared_ptr<SimpleBuffer> b = r.getMessage();
@@ -514,8 +529,8 @@
             }
             f = r.getMessage();
         }
+        gnashSleep(1000);
 
-        if (!r.connected()) break;
     }
 
 }


reply via email to

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