[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Gnash-commit] gnash/libnet Makefile.am buffer.cpp buffer.h cq...
From: |
Rob Savoye |
Subject: |
[Gnash-commit] gnash/libnet Makefile.am buffer.cpp buffer.h cq... |
Date: |
Fri, 28 Mar 2008 03:30:24 +0000 |
CVSROOT: /sources/gnash
Module name: gnash
Changes by: Rob Savoye <rsavoye> 08/03/28 03:30:23
Added files:
libnet : Makefile.am buffer.cpp buffer.h cque.cpp cque.h
cqueue.cpp cqueue.h handler.cpp handler.h
http.cpp http.h netstats.cpp netstats.h
network.cpp network.h rtmp.cpp rtmp.h
statistics.cpp statistics.h
Log message:
* libnet: New directory for networking and protocols.
*
libnet/buffer.{h,cpp},cque.{h,cpp},handler.{h,cpp},http.{h,cpp},
network.{h,cpp},rtmp.{h,cpp},netstats.{h,cpp},statistics.{h,cpp},
cque.{h,cpp}: Moved from cygnal so they can be shared.
CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/gnash/libnet/Makefile.am?cvsroot=gnash&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libnet/buffer.cpp?cvsroot=gnash&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libnet/buffer.h?cvsroot=gnash&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libnet/cque.cpp?cvsroot=gnash&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libnet/cque.h?cvsroot=gnash&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libnet/cqueue.cpp?cvsroot=gnash&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libnet/cqueue.h?cvsroot=gnash&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libnet/handler.cpp?cvsroot=gnash&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libnet/handler.h?cvsroot=gnash&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libnet/http.cpp?cvsroot=gnash&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libnet/http.h?cvsroot=gnash&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libnet/netstats.cpp?cvsroot=gnash&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libnet/netstats.h?cvsroot=gnash&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libnet/network.cpp?cvsroot=gnash&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libnet/network.h?cvsroot=gnash&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libnet/rtmp.cpp?cvsroot=gnash&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libnet/rtmp.h?cvsroot=gnash&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libnet/statistics.cpp?cvsroot=gnash&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/gnash/libnet/statistics.h?cvsroot=gnash&rev=1.1
Patches:
Index: Makefile.am
===================================================================
RCS file: Makefile.am
diff -N Makefile.am
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Makefile.am 28 Mar 2008 03:30:21 -0000 1.1
@@ -0,0 +1,70 @@
+#
+# Copyright (C) 2005, 2006, 2007, 2008 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
+#
+
+## Process this file with automake to generate Makefile.in
+
+AUTOMAKE_OPTIONS = dejagnu
+
+pkglib_LTLIBRARIES = libgnashnet.la
+
+AM_LDFLAGS = \
+ ../libbase/libgnashbase.la \
+ ../libamf/libgnashamf.la \
+ $(LIBLTDL) \
+ $(GLIB_LIBS) \
+ $(LIBXML_LIBS) \
+ $(CURL_LIBS) \
+ $(BOOST_LIBS) \
+ $(PTHREAD_LIBS)
+
+localedir = $(datadir)/locale
+
+INCLUDES = -I.. \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libamf \
+ -I$(top_srcdir)/libbase \
+ -I$(top_srcdir)/server \
+ -DLOCALEDIR=\"$(localedir)\" \
+ $(LIBXML_CFLAGS) \
+ $(DMALLOC_CFLAGS) \
+ $(CURL_CFLAGS) \
+ $(BOOST_CFLAGS) \
+ $(PTHREAD_CFLAGS)
+
+noinst_HEADERS = \
+ buffer.h \
+ cque.h \
+ handler.h \
+ http.h \
+ network.h \
+ rtmp.h \
+ statistics.h
+
+libgnashnet_la_SOURCES = \
+ buffer.cpp \
+ cque.cpp \
+ handler.cpp \
+ http.cpp \
+ network.cpp \
+ rtmp.cpp \
+ statistics.cpp
+
+# Rebuild with GCC 4.x Mudflap support
+mudflap:
+ @echo "Rebuilding with GCC Mudflap support"
+ $(MAKE) CXXFLAGS="$(CXXFLAGS) $(MUDFLAP_OPT)" LIBS="$(LIBS)
$(MUDFLAP_LIB)"
Index: buffer.cpp
===================================================================
RCS file: buffer.cpp
diff -N buffer.cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ buffer.cpp 28 Mar 2008 03:30:21 -0000 1.1
@@ -0,0 +1,212 @@
+//
+// Copyright (C) 2008 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 <boost/cstdint.hpp>
+#include "buffer.h"
+#include "log.h"
+#include "network.h"
+
+using namespace std;
+using namespace gnash;
+
+namespace cygnal
+{
+
+void *
+Buffer::init(size_t nbytes)
+{
+// GNASH_REPORT_FUNCTION;
+ if (_ptr == 0) {
+ _ptr = new Network::byte_t[nbytes];
+ _nbytes = nbytes;
+ // this could be a performance hit, but for debugging we leave it in
so we get
+ // easier to ready hex dumps in GDB,
+ empty();
+ }
+
+#ifdef USE_STATS_BUFFERS
+ clock_gettime (CLOCK_REALTIME, &_stamp);
+#endif
+ return _ptr;
+}
+
+Buffer::Buffer()
+{
+// GNASH_REPORT_FUNCTION;
+ _ptr = 0;
+ _nbytes = gnash::NETBUFSIZE;
+ init(gnash::NETBUFSIZE);
+}
+
+// Create with a size other than the default
+Buffer::Buffer(size_t nbytes)
+{
+// GNASH_REPORT_FUNCTION;
+ _ptr = 0;
+ _nbytes = nbytes;
+ init(nbytes);
+}
+
+// Delete the allocate memory
+Buffer::~Buffer()
+{
+// GNASH_REPORT_FUNCTION;
+ if (_ptr) {
+#ifdef USE_STATS_BUFFERS
+ struct timespec now;
+ clock_gettime (CLOCK_REALTIME, &now);
+ log_debug("Buffer %x (%d) stayed in queue for %f seconds",
+ (void *)_ptr, _nbytes,
+ (float)((now.tv_sec - _stamp.tv_sec) + ((now.tv_nsec -
_stamp.tv_nsec)/1e9)));
+#endif
+ delete[] _ptr;
+ _ptr = 0;
+ _nbytes = 0;
+ }
+}
+
+Network::byte_t *
+Buffer::find(Network::byte_t b)
+{
+// GNASH_REPORT_FUNCTION;
+ for (size_t i=0; i< _nbytes; i++) {
+ if ( *(_ptr + i) == b) {
+ return _ptr + i;
+ }
+ }
+ return 0;
+}
+
+// Put data into the buffer
+void
+Buffer::copy(Network::byte_t *data, size_t nbytes)
+{
+// GNASH_REPORT_FUNCTION;
+ std::copy(data, data + nbytes, _ptr);
+}
+
+void
+Buffer::copy(string &str)
+{
+// GNASH_REPORT_FUNCTION;
+ std::copy(str.begin(), str.end(), _ptr);
+}
+
+// make ourselves be able to be copied.
+Buffer &
+Buffer::operator=(Buffer *buf)
+{
+// GNASH_REPORT_FUNCTION;
+ if (buf->size() != _nbytes) {
+ resize(buf->size());
+ }
+
+ std::copy(buf->reference(), buf->reference() + _nbytes, _ptr);
+
+ return *this;
+}
+
+Buffer &
+Buffer::operator=(Buffer &buf)
+{
+// GNASH_REPORT_FUNCTION;
+ if (buf.size() != _nbytes) {
+ resize(buf.size());
+ }
+
+ std::copy(buf.reference(), buf.reference() + _nbytes, _ptr);
+
+ return *this;
+}
+
+// Check to see if two Buffer objects are identical
+bool
+Buffer::operator==(Buffer *buf)
+{
+// GNASH_REPORT_FUNCTION;
+ if (buf->size() == _nbytes) {
+ if (memcmp(buf->reference(), _ptr, _nbytes) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+Buffer::operator==(Buffer &buf)
+{
+// GNASH_REPORT_FUNCTION;
+ if (buf.size() == _nbytes){
+ if (memcmp(buf.reference(), _ptr, _nbytes) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Just reset to having no data, but still having storage
+void
+Buffer::empty()
+{
+// GNASH_REPORT_FUNCTION;
+ if (_ptr) {
+ memset(_ptr, 0, _nbytes);
+ }
+}
+
+// Resize the buffer that holds the data
+void *
+Buffer::resize(size_t nbytes)
+{
+// GNASH_REPORT_FUNCTION;
+ // Allocate a new memory block
+ Network::byte_t *tmp = new Network::byte_t[nbytes];
+ // And copy ourselves into it
+ if (nbytes > _nbytes) {
+ std::copy(_ptr, _ptr + _nbytes, tmp);
+ }
+
+ if (nbytes < _nbytes) {
+ std::copy(_ptr, _ptr + nbytes, tmp);
+ }
+
+ _nbytes = nbytes;
+
+ // Delete the old block, it's unused now
+ delete[] _ptr;
+
+ // Make the memeory block use the new space
+ _ptr = tmp;
+
+ return tmp;
+}
+
+void
+Buffer::dump()
+{
+ cerr << "Buffer is " << _nbytes << " bytes at " << (void *)_ptr << endl;
+ cerr << gnash::hexify((unsigned char *)_ptr, _nbytes, true) << endl;
+}
+
+} // end of cygnal namespace
+
+
+// local Variables:
+// mode: C++
+// indent-tabs-mode: t
+// End:
Index: buffer.h
===================================================================
RCS file: buffer.h
diff -N buffer.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ buffer.h 28 Mar 2008 03:30:21 -0000 1.1
@@ -0,0 +1,87 @@
+//
+// Copyright (C) 2008 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
+//
+
+#ifndef __BUFFER_H__
+#define __BUFFER_H__ 1
+
+#include <boost/cstdint.hpp>
+#include <string>
+#include <time.h>
+
+#include "network.h"
+
+// _definst_ is the default instance name
+namespace cygnal
+{
+
+class Buffer
+{
+public:
+ Buffer();
+ // Create with a size other than the default
+ Buffer(size_t nbytes);
+
+ // Delete the allocate memory
+ ~Buffer();
+ void empty();
+
+ // Resize the buffer that holds the data
+ void *resize(size_t nbytes);
+
+ // Put data into the buffer
+ void copy(gnash::Network::byte_t *data, size_t nbytes);
+ void copy(gnash::Network::byte_t *data) { copy(data, _nbytes); };
+ void copy(std::string &str);
+
+ // Accessors
+ gnash::Network::byte_t *find(gnash::Network::byte_t b);
+ gnash::Network::byte_t *begin() { return _ptr ; };
+ gnash::Network::byte_t *end() { return _ptr + _nbytes; };
+ gnash::Network::byte_t *reference() { return _ptr; }
+ size_t size() { return _nbytes; }
+ void setSize(size_t nbytes) { _nbytes = nbytes; };
+
+ // make ourselves be able to be copied.
+ Buffer &operator=(Buffer *buf);
+ Buffer &operator=(Buffer &buf);
+
+ // Test against other buffers
+ bool operator==(Buffer *buf);
+ bool operator==(Buffer &buf);
+ gnash::Network::byte_t operator[](int x) { return *(_ptr + x); };
+
+ // debug stuff, not need for running Cygnal
+ void dump();
+private:
+ void *init(size_t nbytes);
+ gnash::Network::byte_t *_ptr;
+ int _nbytes;
+#ifdef USE_STATS_BUFFERS
+ struct timespec _stamp; // used for timing how long data stays in the
queue.
+#endif
+};
+
+
+} // end of cygnal namespace
+
+#endif // end of __BUFFER_H__
+
+// local Variables:
+// mode: C++
+// indent-tabs-mode: t
+// End:
Index: cque.cpp
===================================================================
RCS file: cque.cpp
diff -N cque.cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ cque.cpp 28 Mar 2008 03:30:21 -0000 1.1
@@ -0,0 +1,263 @@
+//
+// Copyright (C) 2008 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
+//
+
+#ifdef HAVE_CONFIG_H
+#include "gnashconfig.h"
+#endif
+
+#include <string>
+#include <vector>
+#include <deque>
+#include <time.h>
+
+#include "log.h"
+#include "gmemory.h"
+#include "buffer.h"
+#include "cque.h"
+
+using namespace gnash;
+using namespace std;
+using namespace boost;
+
+
+namespace cygnal
+{
+
+CQue::CQue()
+{
+// GNASH_REPORT_FUNCTION;
+ _stats.totalbytes = 0;
+ _stats.totalin = 0;
+ _stats.totalout = 0;
+ clock_gettime (CLOCK_REALTIME, &_stats.start);
+ _name = "default";
+}
+
+CQue::~CQue()
+{
+// GNASH_REPORT_FUNCTION;
+// clear();
+#if 0
+ deque<Buffer *>::iterator it;
+ boost::mutex::scoped_lock lock(_mutex);
+ for (it = _que.begin(); it != _que.end(); it++) {
+ Buffer *ptr = *(it);
+ delete ptr;
+ }
+#endif
+}
+
+// Wait for a condition variable to trigger
+void
+CQue::wait()
+{
+// GNASH_REPORT_FUNCTION;
+ boost::mutex::scoped_lock lk(_cond_mutex);
+ _cond.wait(lk);
+// log_debug("wait mutex released for \"%s\"", _name);
+}
+
+// Notify a condition variable to trigger
+void
+CQue::notify()
+{
+// GNASH_REPORT_FUNCTION;
+ _cond.notify_one();
+// log_debug("wait mutex triggered for \"%s\"", _name);
+}
+
+size_t
+CQue::size()
+{
+// GNASH_REPORT_FUNCTION;
+ boost::mutex::scoped_lock lock(_mutex);
+ return _que.size();
+}
+
+bool
+CQue::push(Buffer *data)
+{
+// GNASH_REPORT_FUNCTION;
+ boost::mutex::scoped_lock lock(_mutex);
+ _que.push_back(data);
+#ifdef USE_STATS_QUEUE
+ _stats.totalbytes += data->size();
+ _stats.totalin++;
+#endif
+ return true;
+}
+
+// Push data
+bool
+CQue::push(gnash::Network::byte_t *data, int nbytes)
+{
+// GNASH_REPORT_FUNCTION;
+ Buffer *buf = new Buffer;
+ std::copy(data, data + nbytes, buf->reference());
+ return push(buf);
+}
+
+
+// Pop the first date element off the FIFO
+Buffer *
+CQue::pop()
+{
+// GNASH_REPORT_FUNCTION;
+ Buffer *buf = 0;
+ boost::mutex::scoped_lock lock(_mutex);
+ if (_que.size()) {
+ buf = _que.front();
+ _que.pop_front();
+#ifdef USE_STATS_QUEUE
+ _stats.totalout++;
+#endif
+ }
+ return buf;
+}
+
+// Peek at the first data element without removing it
+Buffer *
+CQue::peek()
+{
+// GNASH_REPORT_FUNCTION;
+ boost::mutex::scoped_lock lock(_mutex);
+ if (_que.size()) {
+ return _que.front();
+ }
+ return 0;
+}
+
+// Return the size of the queues
+void
+CQue::clear()
+{
+// GNASH_REPORT_FUNCTION;
+ boost::mutex::scoped_lock lock(_mutex);
+ _que.clear();
+}
+
+// Remove a range of elements
+void
+CQue::remove(Buffer *begin, Buffer *end)
+{
+ GNASH_REPORT_FUNCTION;
+ deque<Buffer *>::iterator it;
+ deque<Buffer *>::iterator start;
+ deque<Buffer *>::iterator stop;
+ boost::mutex::scoped_lock lock(_mutex);
+ Buffer *ptr;
+ for (it = _que.begin(); it != _que.end(); it++) {
+ ptr = *(it);
+ if (ptr->reference() == begin->reference()) {
+ start = it;
+ }
+ if (ptr->reference() == end->reference()) {
+ stop = it;
+ break;
+ }
+ }
+ _que.erase(start, stop);
+}
+
+// Remove an element
+void
+CQue::remove(Buffer *element)
+{
+ GNASH_REPORT_FUNCTION;
+ deque<Buffer *>::iterator it;
+ boost::mutex::scoped_lock lock(_mutex);
+ for (it = _que.begin(); it != _que.end(); it++) {
+ Buffer *ptr = *(it);
+ if (ptr->reference() == element->reference()) {
+ _que.erase(it);
+ }
+ }
+}
+
+// Merge sucessive buffers into one single larger buffer. This is for some
+// protocols, than have very long headers.
+Buffer *
+CQue::merge(Buffer *begin)
+{
+ GNASH_REPORT_FUNCTION;
+ int totalsize = 0;
+ deque<Buffer *>::iterator it;
+ vector<deque<Buffer *>::iterator> elements;
+ vector<deque<Buffer *>::iterator>::iterator eit;
+ boost::mutex::scoped_lock lock(_mutex);
+ for (it = _que.begin(); it != _que.end(); it++) {
+ Buffer *ptr = *(it);
+ if (totalsize > 0) {
+ totalsize += ptr->size();
+ elements.push_back(it);
+ if (ptr->size() < gnash::NETBUFSIZE) {
+ Buffer *newbuf = new Buffer(totalsize);
+ Network::byte_t *tmp = newbuf->reference();
+ Buffer *buf;
+// _que.insert(elements.begin(), newbuf);
+ for (eit = elements.begin(); eit != elements.end(); eit++) {
+ deque<Buffer *>::iterator ita = *(eit);
+ buf = *(ita);
+ std::copy(buf->reference(), buf->reference() + buf->size(),
tmp);
+ tmp += buf->size();
+ _que.erase(ita);
+ }
+ _que.push_back(newbuf);
+ return newbuf;
+ }
+ continue;
+ }
+ if (ptr->reference() == begin->reference()) {
+ totalsize = ptr->size();
+ elements.push_back(it);
+ }
+ }
+ return 0;
+}
+
+// Dump internal data.
+void
+CQue::dump()
+{
+// GNASH_REPORT_FUNCTION;
+ deque<Buffer *>::iterator it;
+ boost::mutex::scoped_lock lock(_mutex);
+ cerr << endl << "CQue \"" << _name << "\" has "<< _que.size() << "
buffers." << endl;
+ for (it = _que.begin(); it != _que.end(); it++) {
+ Buffer *ptr = *(it);
+ ptr->dump();
+ }
+#ifdef USE_STATS_QUEUE
+ struct timespec now;
+ clock_gettime (CLOCK_REALTIME, &now);
+ cerr << "Que lifespan is " <<
+ (float)((now.tv_sec - _stats.start.tv_sec) + ((now.tv_nsec -
_stats.start.tv_nsec)/1e9)) << " seconds" << endl;
+ cerr << "Total number of bytes is " << _stats.totalbytes << " bytes" <<
endl;
+ cerr << "Total number of packets pushed to queue is: " << _stats.totalin
<< endl;
+ cerr << "Total number of packets popped from queue is: " <<
_stats.totalout << endl;
+#endif
+}
+
+} // end of cygnal namespace
+
+
+// local Variables:
+// mode: C++
+// indent-tabs-mode: t
+// End:
+
Index: cque.h
===================================================================
RCS file: cque.h
diff -N cque.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ cque.h 28 Mar 2008 03:30:21 -0000 1.1
@@ -0,0 +1,100 @@
+//
+// Copyright (C) 2008 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
+//
+
+#ifndef __CQUE_H__
+#define __CQUE_H__ 1
+
+#include <boost/cstdint.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/condition.hpp>
+#include <deque>
+#include <time.h>
+
+#include "buffer.h"
+#include "network.h"
+
+// _definst_ is the default instance name
+namespace cygnal
+{
+
+class CQue {
+public:
+#ifdef USE_STATS_QUEUE
+ typedef struct {
+ struct timespec start;
+ int totalbytes;
+ int totalin;
+ int totalout;
+ } que_stats_t;
+#endif
+ CQue();
+ CQue(const std::string &str) { _name = str; };
+ ~CQue();
+ // Push data onto the que
+ bool push(gnash::Network::byte_t *data, int nbytes);
+ bool push(Buffer *data);
+ // Pop the first date element off the que
+ Buffer *pop();
+ // Peek at the first date element witjhout removing it from the que
+ Buffer *peek();
+ // Get the number of elements in the que
+ size_t size();
+ // Wait for a condition variable to trigger
+ void wait();
+ // Notify a condition variable to trigger
+ void notify();
+ // Empty the que of all data.
+ void clear();
+ // Remove a range of elements
+ void remove(Buffer *begin, Buffer *end);
+// // Remove an element
+// void remove(Buffer *it);
+ void remove(Buffer *it);
+ // Merge sucessive buffers into one single larger buffer. This is for some
+ // protocols, than have very long headers.
+ Buffer *merge(Buffer *begin);
+
+ // Dump the data to the terminal
+ void dump();
+ que_stats_t *stats() { return &_stats; };
+ void setName(const std::string &str) { _name = str; }
+private:
+ // an optional name for the queue, only used for debugging messages to
make them unique
+ std::string _name;
+ // The queue itself
+ std::deque<Buffer *> _que;
+ // A condition variable used to signal the other thread when the que has
data
+ boost::condition _cond;
+ // This is the mutex used by the condition variable. It needs to be
separate from the
+ // one used to lock access to the que.
+ boost::mutex _cond_mutex;
+ // This is the mutex that control access to the que.
+ boost::mutex _mutex;
+#ifdef USE_STATS_QUEUE
+ que_stats_t _stats;
+#endif
+};
+
+} // end of cygnal namespace
+
+#endif // end of __CQUE_H__
+
+// local Variables:
+// mode: C++
+// indent-tabs-mode: t
+// End:
Index: cqueue.cpp
===================================================================
RCS file: cqueue.cpp
diff -N cqueue.cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ cqueue.cpp 28 Mar 2008 03:30:22 -0000 1.1
@@ -0,0 +1,116 @@
+// http.cpp: HyperText Transport Protocol handler for Cygnal, for Gnash.
+//
+// Copyright (C) 2005, 2006, 2007, 2008 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
+//
+
+#ifdef HAVE_CONFIG_H
+#include "gnashconfig.h"
+#endif
+
+#include <boost/thread/mutex.hpp>
+#include <boost/date_time/gregorian/gregorian.hpp>
+//#include <boost/date_time/local_time/local_time.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+//#include <boost/date_time/time_zone_base.hpp>
+#include <string>
+#include <vector>
+#include "log.h"
+#include "cqueue.h"
+
+using namespace gnash;
+using namespace std;
+using namespace boost;
+
+namespace cygnal
+{
+
+CQueue::CQueue()
+{
+// GNASH_REPORT_FUNCTION;
+}
+
+CQueue::~CQueue()
+{
+// GNASH_REPORT_FUNCTION;
+}
+
+#if 0
+void
+memcpy(boost::uint8_t *data, size_t size,
+ vector<uint8_t> *ptr)
+{
+// GNASH_REPORT_FUNCTION;
+ ptr->reserve(size);
+ std::copy(data, data + size, ptr->begin());
+}
+
+void
+memcpy(std::vector<uint8_t> *ptr,
+ uint8_t *data, size_t size)
+{
+// GNASH_REPORT_FUNCTION;
+ data = new uint8_t[size + 1];
+ std::copy(ptr->begin(), ptr->end(), data);
+}
+#endif
+
+// Push bytes on the FIFO
+bool
+CQueue::push(uint8_t *data, int nbytes)
+{
+// GNASH_REPORT_FUNCTION;
+ vector<boost::uint8_t> *ptr = new vector<boost::uint8_t>;
+ ptr->reserve(nbytes);
+ std::copy(data, data + nbytes, ptr->begin());
+ _queue.push_back(ptr);
+}
+
+bool
+CQueue::push(vector<uint8_t> *data)
+{
+// GNASH_REPORT_FUNCTION;
+ _queue.push_back(data);
+}
+
+// Pop the first date element off the FIFO
+vector<uint8_t> *
+CQueue::pop()
+{
+// GNASH_REPORT_FUNCTION;
+}
+
+// Peek at the first data element without removing it
+vector <uint8_t> *
+CQueue::peek()
+{
+// GNASH_REPORT_FUNCTION;
+}
+
+// Dump internal data.
+void
+CQueue::dump()
+{
+// GNASH_REPORT_FUNCTION;
+}
+
+} // end of cygnal namespace
+
+
+// local Variables:
+// mode: C++
+// indent-tabs-mode: t
+// End:
Index: cqueue.h
===================================================================
RCS file: cqueue.h
diff -N cqueue.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ cqueue.h 28 Mar 2008 03:30:22 -0000 1.1
@@ -0,0 +1,72 @@
+// http.cpp: HyperText Transport Protocol handler for Cygnal, for Gnash.
+//
+// Copyright (C) 2005, 2006, 2007, 2008 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
+//
+
+#ifdef HAVE_CONFIG_H
+#include "gnashconfig.h"
+#endif
+
+#ifndef __CQUEUE_H__
+#define __CQUEUE_H__ 1
+
+
+#include <boost/cstdint.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/date_time/gregorian/gregorian.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <string>
+#include <iostream>
+#include <vector>
+#include "log.h"
+
+namespace cygnal
+{
+
+class CQueue
+{
+public:
+ CQueue();
+ ~CQueue();
+
+ // Push bytes on the FIFO
+ bool push(boost::uint8_t *data, int nbytes);
+ bool push(std::vector<boost::uint8_t> *data);
+ // Pop the first date element off the FIFO
+ std::vector<uint8_t> *pop();
+ // Peek at the first data element without removing it
+ std::vector<uint8_t> *peek();
+// void memcpy(boost::uint8_t *data, size_t size,
+// std::vector<boost::uint8_t> *ptr);
+// void memcpy(std::vector<boost::uint8_t> *ptr,
+// boost::uint8_t *data, size_t size);
+ size_t size() { return _queue.size(); };
+ // Dump internal data.
+ void dump();
+private:
+ std::vector<std::vector<boost::uint8_t> *> _queue;
+};
+
+
+} // end of cygnal namespace
+
+#endif // end of __CQUEUE_H__
+
+// local Variables:
+// mode: C++
+// indent-tabs-mode: t
+// End:
Index: handler.cpp
===================================================================
RCS file: handler.cpp
diff -N handler.cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ handler.cpp 28 Mar 2008 03:30:22 -0000 1.1
@@ -0,0 +1,294 @@
+//
+// Copyright (C) 2008 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
+//
+
+#ifdef HAVE_CONFIG_H
+#include "gnashconfig.h"
+#endif
+
+#include <boost/thread/thread.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/bind.hpp>
+#include <algorithm>
+#include <string>
+#include <deque>
+#include <list>
+#include <map>
+
+#include "log.h"
+#include "network.h"
+#include "buffer.h"
+
+#include "rtmp.h"
+#include "http.h"
+
+using namespace gnash;
+using namespace std;
+using namespace boost;
+
+namespace cygnal
+{
+
+extern map<int, Handler *> handlers;
+
+Handler::Handler()
+ : _die(false), _netfd(0)
+{
+// GNASH_REPORT_FUNCTION;
+}
+
+Handler::~Handler()
+{
+// GNASH_REPORT_FUNCTION;
+ closeConnection();
+ _die = true;
+ notifyout();
+ notifyin();
+}
+
+bool
+Handler::push(Buffer *data, fifo_e direction)
+{
+// GNASH_REPORT_FUNCTION;
+ if (direction == Handler::OUTGOING) {
+ _outgoing.push(data);
+ return true;
+ }
+ if (direction == Handler::INCOMING) {
+ _incoming.push(data);
+ return true;
+ }
+
+ return false;
+}
+
+// Push bytes on the outgoing FIFO
+bool
+Handler::push(gnash::Network::byte_t *data, int nbytes, fifo_e direction)
+{
+// GNASH_REPORT_FUNCTION;
+ Buffer *ptr = new Buffer;
+ ptr->copy(data, nbytes);
+ return push(ptr, direction);
+}
+
+// Pop the first date element off the FIFO
+Buffer *
+Handler::pop(fifo_e direction)
+{
+// GNASH_REPORT_FUNCTION;
+ Buffer *buf;
+
+ if (direction == Handler::OUTGOING) {
+ if (_outgoing.size()) {
+ buf = _outgoing.pop();
+ return buf;
+ }
+ }
+ if (direction == Handler::INCOMING) {
+ if (_incoming.size()) {
+ buf = _incoming.pop();
+ return buf;
+ }
+ }
+
+ return buf;
+}
+
+// Peek at the first data element without removing it
+Buffer *
+Handler::peek(fifo_e direction)
+{
+// GNASH_REPORT_FUNCTION;
+ if (direction == Handler::OUTGOING) {
+ if (_outgoing.size()) {
+ return _outgoing.peek();
+ }
+ }
+ if (direction == Handler::INCOMING) {
+ if (_incoming.size()) {
+ return _incoming.peek();
+ }
+ }
+ return 0;
+}
+
+// Return the size of the queues
+size_t
+Handler::size(fifo_e direction)
+{
+// GNASH_REPORT_FUNCTION;
+ if (direction == Handler::OUTGOING) {
+ return _outgoing.size();
+ }
+ if (direction == Handler::INCOMING) {
+ return _incoming.size();
+ }
+
+ return 0; // we should never actually get to here
+}
+
+// Return the size of the queues
+void
+Handler::clear(fifo_e direction)
+{
+// GNASH_REPORT_FUNCTION;
+ if (direction == Handler::OUTGOING) {
+ _outgoing.clear();
+ }
+ if (direction == Handler::INCOMING) {
+ _incoming.clear();
+ }
+}
+
+// Dump internal data.
+void
+Handler::dump()
+{
+// GNASH_REPORT_FUNCTION;
+ _incoming.dump();
+ _outgoing.dump();
+}
+
+// start the two thread handlers for the queues
+bool
+Handler::start(thread_params_t *args)
+{
+ GNASH_REPORT_FUNCTION;
+// Handler *hand = reinterpret_cast<Handler *>(args->handle);
+
+ _incoming.setName("Incoming");
+ _outgoing.setName("Outgoing");
+
+ log_debug(_("Starting Handlers for port %d, tid %ld"),
+ args->port, pthread_self());
+
+ if (args->port == 4080) { // FIXME: hack alert!
+ boost::thread handler(boost::bind(&httphandler, args));
+ }
+ if (args->port == RTMP) {
+ boost::thread handler(boost::bind(&rtmp_handler, args));
+ }
+
+ boost::thread outport(boost::bind(&netout_handler, args));
+ boost::thread inport(boost::bind(&netin_handler, args));
+// We don't want to wait for the threads to complete, we
+// want to return to the main program so it can spawn another
+// thread for the next incoming connection.
+// inport.join();
+// outport.join();
+// handler.join();
+// if (_die) {
+// log_debug("Handler done...");
+// }
+ return true;
+}
+
+extern "C" {
+void
+netin_handler(Handler::thread_params_t *args)
+{
+// GNASH_REPORT_FUNCTION;
+
+ Handler *hand = reinterpret_cast<Handler *>(args->handle);
+
+ log_debug("Starting to wait for data in net for fd #%d", args->netfd);
+
+ do {
+ Buffer *buf = new Buffer;
+ size_t ret = hand->readNet(args->netfd, buf->reference(), buf->size(),
1);
+ // the read timed out as there was no data, but the socket is still
open.
+ if (ret == 0) {
+ continue;
+ }
+ // ret is "no position" when the socket is closed from the other end of
the connection,
+ // so we're done.
+ if (ret == string::npos) {
+ break;
+ }
+ // We got data. Resize the buffer if necessary.
+ if (ret > 0) {
+ if (ret != buf->size()) {
+ buf->resize(ret);
+ }
+ hand->push(buf);
+ hand->notify();
+ } else {
+ log_debug("no more data for fd #%d, exiting...", args->netfd);
+ hand->die();
+ break;
+ }
+ } while (!hand->timetodie());
+ // We're done. Notify the other threads the socket is closed, and tell
them to die.
+ log_debug("Net In handler done for fd #%d...", args->netfd);
+ hand->notify();
+ hand->closeNet(args->netfd);
+// hand->dump();
+}
+
+void
+netout_handler(Handler::thread_params_t *args)
+{
+// GNASH_REPORT_FUNCTION;
+ int ret;
+ Handler *hand = reinterpret_cast<Handler *>(args->handle);
+
+ log_debug("Starting to wait for data in que for fd #%d", args->netfd);
+
+ do {
+ // Don't look for any more packets in the que cause we're done
+ if (hand->timetodie()) {
+ break;
+ }
+ hand->waitout();
+ while (hand->outsize()) {
+ Buffer *buf = hand->popout();
+// log_debug("FIXME: got data in Outgoing que");
+// buf->dump();
+// ret = hand->writeNet(buf->reference(), buf->size(), 15);
+// if (buf->size() != gnash::NETBUFSIZE) {
+// log_debug("Got smaller packet, size %d", buf->size());
+// }
+ ret = hand->writeNet(args->netfd, buf);
+ delete buf;
+ }
+ } while (ret > 0);
+ hand->die();
+ log_debug("Net Out handler done for fd #%d...", args->netfd);
+ hand->notifyin();
+ hand->closeNet(args->netfd);
+#if 0
+ map<int, Handler *>::iterator hit = handlers.find(args->netfd);
+ if ((*hit).second) {
+ log_debug("Removing handle %x for fd #%d: ",
+ (void *)hand), args->netfd;
+ handlers.erase(args->netfd);
+ }
+ delete hand;
+#endif
+}
+
+} // end of extern C
+
+} // end of cygnal namespace
+
+
+// local Variables:
+// mode: C++
+// indent-tabs-mode: t
+// End:
+
Index: handler.h
===================================================================
RCS file: handler.h
diff -N handler.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ handler.h 28 Mar 2008 03:30:22 -0000 1.1
@@ -0,0 +1,163 @@
+//
+// Copyright (C) 2008 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
+//
+
+#ifndef __HANDLER_H__
+#define __HANDLER_H__ 1
+
+#include <boost/cstdint.hpp>
+//#include <boost/thread/condition.hpp>
+#include <string>
+#include <deque>
+
+#include "log.h"
+#include "network.h"
+#include "buffer.h"
+#include "cque.h"
+#include "network.h"
+
+// _definst_ is the default instance name
+namespace cygnal
+{
+
+
+class Handler : public gnash::Network
+{
+public:
+ Handler();
+ ~Handler();
+
+ typedef enum {
+ UNKNOWN,
+ STATUS,
+ POLL,
+ HELP,
+ INTERVAL,
+ QUIT,
+ } admin_cmd_e;
+ // This is used to pass parameters to a thread using boost::bind
+ typedef struct {
+ int netfd;
+ int port;
+ void *handle;
+ std::string filespec;
+ } thread_params_t ;
+
+ // Specify which queue should be used
+ typedef enum { INCOMING, OUTGOING } fifo_e;
+
+ // Push bytes on the incoming FIFO, which is the default
+ bool push(Buffer *data)
+ { return _incoming.push(data); };
+ bool push(Buffer *data, fifo_e direction);
+ bool push(gnash::Network::byte_t *data, int nbytes, fifo_e direction);
+ bool push(gnash::Network::byte_t *data, int nbytes)
+ { return _incoming.push(data, nbytes); };
+ bool pushin(gnash::Network::byte_t *data, int nbytes)
+ { return _incoming.push(data, nbytes); };
+ bool pushin(Buffer *data)
+ { return _incoming.push(data); };
+
+ // Push bytes on the incoming FIFO, which must be specified
+ bool pushout(gnash::Network::byte_t *data, int nbytes)
+ { return _outgoing.push(data, nbytes); };
+ bool pushout(Buffer *data)
+ { return _outgoing.push(data); };
+
+ // Pop the first date element off the incoming FIFO
+ Buffer *pop() { return _incoming.pop(); };
+ Buffer *pop(fifo_e direction);
+ Buffer *popin()
+ { return _incoming.pop(); };
+ // Pop the first date element off the outgoing FIFO
+ Buffer *popout()
+ { return _outgoing.pop(); };
+
+ // Peek at the first data element without removing it
+ Buffer *peek() { return _incoming.peek(); };
+ Buffer *peek(fifo_e direction);
+ Buffer *peekin()
+ { return _incoming.peek(); };
+ // Pop the first date element off the outgoing FIFO
+ Buffer *peekout()
+ { return _outgoing.peek(); };
+
+ // Removes all the buffers from the queues
+ void clear() { _incoming.clear(); };
+ void clear(fifo_e direction);
+ void clearin() { _incoming.clear(); };
+ void clearout() { _outgoing.clear(); };
+ void clearall() { _outgoing.clear(); _incoming.clear(); };
+
+ // Return the size of the queues, default to the incoming queue
+ size_t size(fifo_e direction);
+ size_t size() { return _incoming.size(); };
+ size_t insize() { return _incoming.size(); };
+ size_t outsize() { return _outgoing.size(); };
+
+ // Notify the other thread a message is in the que
+ void notify() { _incoming.notify(); };
+ void notifyin() { _incoming.notify(); };
+ void notifyout() { _outgoing.notify(); };
+
+ // Wait for a message from the other thread
+ void wait() { _incoming.wait(); };
+ void waitin() { _incoming.wait(); };
+ void waitout() { _outgoing.wait(); };
+
+ // start the two thread handlers for the queues
+ bool start(thread_params_t *args);
+
+ // Take a buffer and write it to the network
+ int writeNet(int fd, Buffer *buf)
+ { return Network::writeNet(fd, buf->reference(), buf->size()); };
+
+ int writeNet(Buffer *buf)
+ { return Network::writeNet(buf->reference(), buf->size()); };
+
+ // Dump internal data.
+ void dump();
+#ifdef USE_STATS_QUEUE
+ CQue::que_stats_t *statsin() { return _incoming.stats(); };
+ CQue::que_stats_t *statsout() { return _outgoing.stats(); };
+#endif
+ void die() { _die = true; _outgoing.notify(); };
+ bool timetodie() { return _die; };
+
+private:
+ bool _die;
+ int _netfd;
+ CQue _incoming;
+ CQue _outgoing;
+};
+
+// This is the thread for all incoming network connections, which
+// has to be in C.
+extern "C" {
+ void netin_handler(Handler::thread_params_t *args);
+ void netout_handler(Handler::thread_params_t *args);
+ void start_handler(Handler::thread_params_t *args);
+}
+
+} // end of cygnal namespace
+
+#endif // end of __HANDLER_H__
+
+// local Variables:
+// mode: C++
+// indent-tabs-mode: t
+// End:
Index: http.cpp
===================================================================
RCS file: http.cpp
diff -N http.cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ http.cpp 28 Mar 2008 03:30:22 -0000 1.1
@@ -0,0 +1,1337 @@
+// http.cpp: HyperText Transport Protocol handler for Cygnal, for Gnash.
+//
+// Copyright (C) 2005, 2006, 2007, 2008 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
+//
+
+#ifdef HAVE_CONFIG_H
+#include "gnashconfig.h"
+#endif
+
+#include <boost/thread/mutex.hpp>
+#include <boost/date_time/gregorian/gregorian.hpp>
+//#include <boost/date_time/local_time/local_time.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+//#include <boost/date_time/time_zone_base.hpp>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string>
+#include <iostream>
+#include <cstring>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <algorithm>
+
+#include "http.h"
+#include "log.h"
+#include "network.h"
+#include "handler.h"
+
+using namespace gnash;
+using namespace std;
+
+static boost::mutex stl_mutex;
+
+namespace cygnal
+{
+
+extern map<int, Handler *> handlers;
+
+// FIXME, this seems too small to me. --gnu
+static const int readsize = 1024;
+
+HTTP::HTTP()
+ : _filesize(0),
+ _port(80),
+ _keepalive(true),
+ _handler(0),
+ _clientid(0),
+ _index(0)
+{
+// GNASH_REPORT_FUNCTION;
+// struct status_codes *status = new struct status_codes;
+
+// _status_codes(CONTINUE, status);
+}
+
+HTTP::HTTP(Handler *hand)
+ : _filesize(0), _port(80), _keepalive(false)
+{
+// GNASH_REPORT_FUNCTION;
+ _handler = hand;
+}
+
+HTTP::~HTTP()
+{
+// GNASH_REPORT_FUNCTION;
+}
+
+bool
+HTTP::clearHeader()
+{
+ _header.str("");
+ _body.str("");
+ _charset.clear();
+ _connections.clear();
+ _language.clear();
+ _encoding.clear();
+ _te.clear();
+ _accept.clear();
+ _filesize = 0;
+ _clientid = 0;
+ _index = 0;
+ return true;
+}
+
+HTTP &
+HTTP::operator = (HTTP& /*obj*/)
+{
+ GNASH_REPORT_FUNCTION;
+// this = obj;
+ // TODO: FIXME !
+ return *this;
+}
+
+bool
+HTTP::waitForGetRequest(Network& /*net*/)
+{
+ GNASH_REPORT_FUNCTION;
+ return false; // FIXME: this should be finished
+}
+
+bool
+HTTP::waitForGetRequest()
+{
+ GNASH_REPORT_FUNCTION;
+
+// Network::byte_t buffer[readsize+1];
+// const char *ptr = reinterpret_cast<const char *>(buffer);
+// memset(buffer, 0, readsize+1);
+
+// _handler->wait();
+ Buffer *buf = _handler->pop();
+
+ if (buf == 0) {
+ log_debug("Que empty, net connection dropped for fd #%d",
_handler->getFileFd());
+ return false;
+ }
+
+ clearHeader();
+ extractCommand(buf);
+ extractAccept(buf);
+ extractMethod(buf);
+ extractReferer(buf);
+ extractHost(buf);
+ extractAgent(buf);
+ extractLanguage(buf);
+ extractCharset(buf);
+ extractConnection(buf);
+ extractKeepAlive(buf);
+ extractEncoding(buf);
+ extractTE(buf);
+// dump();
+
+ delete buf; // we're done with the buffer
+
+ _filespec = _url;
+ if (!_url.empty()) {
+ return true;
+ }
+ return false;
+}
+
+bool
+HTTP::formatHeader(http_status_e type)
+{
+// GNASH_REPORT_FUNCTION;
+
+ formatHeader(_filesize, type);
+ return true;
+}
+
+
+bool
+HTTP::formatHeader(int filesize, http_status_e type)
+{
+// GNASH_REPORT_FUNCTION;
+
+ _header << "HTTP/1.1 200 OK" << "\r\n";
+ formatDate();
+ formatServer();
+// if (type == NONE) {
+// formatConnection("close"); // this is the default for HTTP 1.1
+// }
+// _header << "Accept-Ranges: bytes" << "\r\n";
+ formatLastModified();
+ formatEtag("24103b9-1c54-ec8632c0"); // FIXME: borrowed from tcpdump
+ formatAcceptRanges("bytes");
+ formatContentLength(filesize);
+ formatKeepAlive("timeout=15, max=100");
+ formatConnection("Keep-Alive");
+ formatContentType();
+ // All HTTP messages are followed by a blank line.
+ terminateHeader();
+ return true;
+}
+
+bool
+HTTP::formatErrorResponse(http_status_e code)
+{
+// GNASH_REPORT_FUNCTION;
+
+ // First build the message body, so we know how to set Content-Length
+ _body << "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">" << "\r\n";
+ _body << "<html><head>" << "\r\n";
+ _body << "<title>" << code << " Not Found</title>" << "\r\n";
+ _body << "</head><body>" << "\r\n";
+ _body << "<h1>Not Found</h1>" << "\r\n";
+ _body << "<p>The requested URL " << _filespec << " was not found on this
server.</p>" << "\r\n";
+ _body << "<hr>" << "\r\n";
+ _body << "<address>Cygnal (GNU/Linux) Server at localhost Port " << _port
<< " </address>" << "\r\n";
+ _body << "</body></html>" << "\r\n";
+ _body << "\r\n";
+
+ // First build the header
+ _header << "HTTP/1.1 " << code << " Not Found" << "\r\n";
+ formatDate();
+ formatServer();
+ _filesize = _body.str().size();
+ formatContentLength(_filesize);
+ formatConnection("close");
+ formatContentType(HTTP::HTML);
+ return true;
+}
+
+bool
+HTTP::formatDate()
+{
+// GNASH_REPORT_FUNCTION;
+ boost::posix_time::ptime now =
boost::posix_time::second_clock::local_time();
+
+// cout << now.time_of_day() << "\r\n";
+
+ boost::gregorian::date d(now.date());
+// boost::gregorian::date d(boost::gregorian::day_clock::local_day());
+// cout << boost::posix_time::to_simple_string(now) << "\r\n";
+// cout << d.day_of_week() << "\r\n";
+// cout << d.day() << "\r\n";
+// cout << d.year() << "\r\n";
+// cout << d.month() << "\r\n";
+
+// boost::date_time::time_zone_ptr zone(new posix_time_zone("MST"));
+// boost::date_time::time_zone_base b(now "MST");
+// cout << zone.dst_zone_abbrev() << "\r\n";
+
+ _header << "Date: " << d.day_of_week();
+ _header << ", " << d.day();
+ _header << " " << d.month();
+ _header << " " << d.year();
+ _header << " " << now.time_of_day();
+ _header << " GMT" << "\r\n";
+ return true;
+}
+
+bool
+HTTP::formatServer()
+{
+// GNASH_REPORT_FUNCTION;
+ _header << "Server: Cygnal (GNU/Linux)" << "\r\n";
+ return true;
+}
+
+bool
+HTTP::formatServer(const string &data)
+{
+// GNASH_REPORT_FUNCTION;
+ _header << "Server: " << data << "\r\n";
+ return true;
+}
+
+bool
+HTTP::formatMethod(const string &data)
+{
+// GNASH_REPORT_FUNCTION;
+ _header << "Method: " << data << "\r\n";
+ return true;
+}
+
+bool
+HTTP::formatReferer(const string &refer)
+{
+// GNASH_REPORT_FUNCTION;
+ _header << "Referer: " << refer << "\r\n";
+ return true;
+}
+
+bool
+HTTP::formatConnection(const string &options)
+{
+// GNASH_REPORT_FUNCTION;
+ _header << "Connection: " << options << "\r\n";
+ return true;
+}
+
+bool
+HTTP::formatKeepAlive(const string &options)
+{
+// GNASH_REPORT_FUNCTION;
+ _header << "Keep-Alive: " << options << "\r\n";
+ return true;
+}
+
+bool
+HTTP::formatContentType()
+{
+ return formatContentType(_filetype);
+}
+
+bool
+HTTP::formatContentType(filetype_e filetype)
+{
+// GNASH_REPORT_FUNCTION;
+
+ switch (filetype) {
+ case HTML:
+ _header << "Content-Type: text/html" << "\r\n";
+// _header << "Content-Type: text/html; charset=UTF-8" << "\r\n";
+ break;
+ case SWF:
+ _header << "Content-Type: application/x-shockwave-flash" << "\r\n";
+// _header << "Content-Type: application/futuresplash" << "\r\n";
+ break;
+ case VIDEO:
+ _header << "Content-Type: video/flv" << "\r\n";
+ break;
+ case MP3:
+ _header << "Content-Type: audio/mpeg" << "\r\n";
+ break;
+ case FCS:
+ _header << "Content-Type: application/x-fcs" << "\r\n";
+ break;
+ default:
+ _header << "Content-Type: text/html" << "\r\n";
+// _header << "Content-Type: text/html; charset=UTF-8" << "\r\n";
+ }
+ return true;
+}
+
+bool
+HTTP::formatContentLength()
+{
+// GNASH_REPORT_FUNCTION;
+ _header << "Content-Length: " << _filesize << "\r\n";
+ return true;
+}
+
+bool
+HTTP::formatContentLength(int filesize)
+{
+// GNASH_REPORT_FUNCTION;
+ _header << "Content-Length: " << filesize << "\r\n";
+ return true;
+}
+
+bool
+HTTP::formatHost(const string &host)
+{
+// GNASH_REPORT_FUNCTION;
+ _header << "Host: " << host << "\r\n";
+ return true;
+}
+
+bool
+HTTP::formatAgent(const string &agent)
+{
+// GNASH_REPORT_FUNCTION;
+ _header << "User-Agent: " << agent << "\r\n";
+ return true;
+}
+
+bool
+HTTP::formatAcceptRanges(const string &range)
+{
+// GNASH_REPORT_FUNCTION;
+ _header << "Accept-Ranges: " << range << "\r\n";
+ return true;
+}
+
+bool
+HTTP::formatEtag(const string &tag)
+{
+// GNASH_REPORT_FUNCTION;
+ _header << "Etag: " << tag << "\r\n";
+ return true;
+}
+
+bool
+HTTP::formatLastModified(const string &date)
+{
+ _header << "Last-Modified: " << date << "\r\n";
+}
+
+bool
+HTTP::formatLastModified()
+{
+// GNASH_REPORT_FUNCTION;
+ boost::posix_time::ptime now =
boost::posix_time::second_clock::local_time();
+ stringstream date;
+
+ boost::gregorian::date d(now.date());
+
+ date << d.day_of_week();
+ date << ", " << d.day();
+ date << " " << d.month();
+ date << " " << d.year();
+ date << " " << now.time_of_day();
+ date << " GMT";
+
+ return formatLastModified(date.str());
+}
+
+bool
+HTTP::formatLanguage(const string &lang)
+{
+// GNASH_REPORT_FUNCTION;
+
+ // For some browsers this appears to also be Content-Language
+ _header << "Accept-Language: " << lang << "\r\n";
+ return true;
+}
+
+bool
+HTTP::formatCharset(const string &set)
+{
+// GNASH_REPORT_FUNCTION;
+ // For some browsers this appears to also be Content-Charset
+ _header << "Accept-Charset: " << set << "\r\n";
+ return true;
+}
+
+bool
+HTTP::formatEncoding(const string &code)
+{
+// GNASH_REPORT_FUNCTION;
+ _header << "Accept-Encoding: " << code << "\r\n";
+ return true;
+}
+
+bool
+HTTP::formatTE(const string &te)
+{
+// GNASH_REPORT_FUNCTION;
+ _header << "TE: " << te << "\r\n";
+ return true;
+}
+
+bool
+HTTP::sendGetReply(http_status_e code)
+{
+ GNASH_REPORT_FUNCTION;
+
+ formatHeader(_filesize, code);
+// int ret = Network::writeNet(_header.str());
+ Buffer *buf = new Buffer;
+// Network::byte_t *ptr = (Network::byte_t *)_body.str().c_str();
+// buf->copy(ptr, _body.str().size());
+// _handler->dump();
+ if (_header.str().size()) {
+ buf->resize(_header.str().size());
+ string str = _header.str();
+ buf->copy(str);
+ _handler->pushout(buf);
+ _handler->notifyout();
+ log_debug (_("Sent GET Reply"));
+ return true; // Default to true
+ } else {
+ clearHeader();
+ log_debug (_("Couldn't send GET Reply, no header data"));
+ }
+
+ return false;
+}
+
+bool
+HTTP::sendPostReply(rtmpt_cmd_e code)
+{
+ GNASH_REPORT_FUNCTION;
+
+ _header << "HTTP/1.1 200 OK" << "\r\n";
+ formatDate();
+ formatServer();
+ formatContentType(HTTP::FCS);
+ // All HTTP messages are followed by a blank line.
+ terminateHeader();
+ return true;
+
+#if 0
+ formatHeader(_filesize, code);
+ Buffer *buf = new Buffer;
+ if (_header.str().size()) {
+ buf->resize(_header.str().size());
+ string str = _header.str();
+ buf->copy(str);
+ _handler->pushout(buf);
+ _handler->notifyout();
+ log_debug (_("Sent GET Reply"));
+ return true; // Default to true
+ } else {
+ clearHeader();
+ log_debug (_("Couldn't send POST Reply, no header data"));
+ }
+#endif
+ return false;
+}
+
+bool
+HTTP::formatRequest(const string &url, http_method_e req)
+{
+// GNASH_REPORT_FUNCTION;
+
+ _header.str("");
+
+ _header << req << " " << url << "HTTP/1.1" << "\r\n";
+ _header << "User-Agent: Opera/9.01 (X11; Linux i686; U; en)" << "\r\n";
+ _header << "Accept: text/html, application/xml;q=0.9,
application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap,
*/*;q=0.1" << "\r\n";
+
+ _header << "Accept-Language: en" << "\r\n";
+ _header << "Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1" << "\r\n";
+
+ _header << "Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0" <<
"\r\n";
+ _header << "Referer: " << url << "\r\n";
+
+ _header << "Connection: Keep-Alive, TE" << "\r\n";
+ _header << "TE: deflate, gzip, chunked, identity, trailers" << "\r\n";
+ return true;
+}
+// bool
+// HTTP::sendGetReply(Network &net)
+// {
+// GNASH_REPORT_FUNCTION;
+// }
+
+// This is what a GET request looks like.
+// GET
/software/gnash/tests/flvplayer2.swf?file=http://localhost:4080/software/gnash/tests/lulutest.flv
HTTP/1.1
+// User-Agent: Opera/9.01 (X11; Linux i686; U; en)
+// Host: localhost:4080
+// Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png,
image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
+// Accept-Language: en
+// Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1
+// Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0
+// Referer: http://localhost/software/gnash/tests/
+// Connection: Keep-Alive, TE
+// TE: deflate, gzip, chunked, identity, trailers
+int
+HTTP::extractAccept(Network::byte_t *data) {
+// GNASH_REPORT_FUNCTION;
+
+ string body = reinterpret_cast<const char *>(data);
+ string::size_type start, end, length, pos;
+ string pattern = "Accept: ";
+
+ start = body.find(pattern, 0);
+ if (start == string::npos) {
+ return -1;
+ }
+ end = body.find("\r\n", start);
+ if (end == string::npos) {
+ end = body.find("\n", start);
+// return "error";
+ }
+
+ length = end-start-pattern.size();
+ start = start+pattern.size();
+ pos = start;
+ while (pos <= end) {
+ pos = (body.find(",", start) + 2);
+ if (pos <= start) {
+ return _encoding.size();
+ }
+ if ((pos == string::npos) || (pos > end)) {
+ length = end - start;
+ } else {
+ length = pos - start - 2;
+ }
+ string substr = body.substr(start, length);
+// printf("FIXME: \"%s\"\n", substr.c_str());
+ _accept.push_back(substr);
+ start = pos;
+ }
+
+ return _accept.size();
+}
+
+/// These methods extract data from an RTMPT message. RTMP is an
+/// extension to HTTP that adds commands to manipulate the
+/// connection's persistance.
+//
+/// The URL to be opened has the following form:
+/// http://server/<comand>/[<client>/]<index>
+/// <command>
+/// denotes the RTMPT request type, "OPEN", "SEND", "IDLE", "CLOSE")
+/// <client>
+/// specifies the id of the client that performs the requests
+/// (only sent for established sessions)
+/// <index>
+/// is a consecutive number that seems to be used to detect missing packages
+HTTP::rtmpt_cmd_e
+HTTP::extractRTMPT(gnash::Network::byte_t *data)
+{
+ GNASH_REPORT_FUNCTION;
+
+ string body = reinterpret_cast<const char *>(data);
+ string tmp, cid, indx;
+ HTTP::rtmpt_cmd_e cmd;
+
+ // force the case to make comparisons easier
+ std::transform(body.begin(), body.end(), body.begin(),
+ (int(*)(int)) toupper);
+ string::size_type start, end;
+
+ // Extract the command first
+ start = body.find("OPEN", 0);
+ if (start != string::npos) {
+ cmd = HTTP::OPEN;
+ }
+ start = body.find("SEND", 0);
+ if (start != string::npos) {
+ cmd = HTTP::SEND;
+ }
+ start = body.find("IDLE", 0);
+ if (start != string::npos) {
+ cmd = HTTP::IDLE;
+ }
+ start = body.find("CLOSE", 0);
+ if (start != string::npos) {
+ cmd = HTTP::CLOSE;
+ }
+
+ // Extract the optional client id
+ start = body.find("/", start+1);
+ if (start != string::npos) {
+ end = body.find("/", start+1);
+ if (end != string::npos) {
+ indx = body.substr(end, body.size());
+ cid = body.substr(start, (end-start));
+ } else {
+ cid = body.substr(start, body.size());
+ }
+ }
+
+ _index = strtol(indx.c_str(), NULL, 0);
+ _clientid = strtol(cid.c_str(), NULL, 0);
+ end = body.find("\r\n", start);
+// if (end != string::npos) {
+// cmd = HTTP::CLOSE;
+// }
+
+ return cmd;
+}
+
+HTTP::http_method_e
+HTTP::extractCommand(gnash::Network::byte_t *data)
+{
+// GNASH_REPORT_FUNCTION;
+
+ string body = reinterpret_cast<const char *>(data);
+ HTTP::http_method_e cmd;
+
+ // force the case to make comparisons easier
+// std::transform(body.begin(), body.end(), body.begin(),
+// (int(*)(int)) toupper);
+ string::size_type start, end;
+
+ // Extract the command
+ start = body.find("GET", 0);
+ if (start != string::npos) {
+ cmd = HTTP::GET;
+ }
+ start = body.find("POST", 0);
+ if (start != string::npos) {
+ cmd = HTTP::POST;
+ }
+ start = body.find("HEAD", 0);
+ if (start != string::npos) {
+ cmd = HTTP::HEAD;
+ }
+ start = body.find("CONNECT", 0);
+ if (start != string::npos) {
+ cmd = HTTP::CONNECT;
+ }
+ start = body.find("TRACE", 0);
+ if (start != string::npos) {
+ cmd = HTTP::TRACE;
+ }
+ start = body.find("OPTIONS", 0);
+ if (start != string::npos) {
+ cmd = HTTP::OPTIONS;
+ }
+ start = body.find("PUT", 0);
+ if (start != string::npos) {
+ cmd = HTTP::PUT;
+ }
+ start = body.find("DELETE", 0);
+ if (start != string::npos) {
+ cmd = HTTP::DELETE;
+ }
+
+ _command = cmd;
+ return cmd;
+}
+
+string &
+HTTP::extractAcceptRanges(Network::byte_t *data) {
+// GNASH_REPORT_FUNCTION;
+
+ string body = reinterpret_cast<const char *>(data);
+ string::size_type start, end, length, pos;
+ string pattern = "Accept-Ranges: ";
+ start = body.find(pattern, 0);
+ if (start == string::npos) {
+ _acceptranges = "error";
+ return _acceptranges;
+ }
+ end = body.find("\r\n", start);
+ if (end == string::npos) {
+ _acceptranges = "error";
+ return _acceptranges;
+ }
+
+ _acceptranges = body.substr(start+pattern.size(), end-start-1);
+ return _acceptranges;
+}
+
+string &
+HTTP::extractMethod(Network::byte_t *data) {
+// GNASH_REPORT_FUNCTION;
+
+ boost::mutex::scoped_lock lock(stl_mutex);
+ string body = reinterpret_cast<const char *>(data);
+ string::size_type start, end;
+ int length;
+
+ length = body.size();
+ start = body.find(" ", 0);
+ if (start == string::npos) {
+ _method = "error";
+ return _method;
+ }
+ _method = body.substr(0, start);
+ end = body.find(" ", start+1);
+ if (end == string::npos) {
+ _method = "error";
+ return _method;
+ }
+ _url = body.substr(start+1, end-start-1);
+ _version = body.substr(end+1, length);
+
+ end = _url.find("?", 0);
+// _filespec = _url.substr(start+1, end);
+ return _method;
+}
+
+string &
+HTTP::extractReferer(Network::byte_t *data) {
+// GNASH_REPORT_FUNCTION;
+
+ string body = reinterpret_cast<const char *>(data);
+ string::size_type start, end;
+ string pattern = "Referer: ";
+
+ start = body.find(pattern, 0);
+ if (start == string::npos) {
+ _referer = "error";
+ return _referer;
+ }
+ end = body.find("\r\n", start);
+ if (end == string::npos) {
+ _referer = "error";
+ return _referer;
+ }
+
+ _referer = body.substr(start+pattern.size(), end-start-1);
+ return _referer;
+}
+
+int
+HTTP::extractConnection(Network::byte_t *data) {
+// GNASH_REPORT_FUNCTION;
+
+ string body = reinterpret_cast<const char *>(data);
+ string::size_type start, end, length, pos;
+ string pattern = "Connection: ";
+
+ start = body.find(pattern, 0);
+ if (start == string::npos) {
+ return -1;
+ }
+ end = body.find("\r\n", start);
+ if (end == string::npos) {
+ end = body.find("\n", start);
+// return "error";
+ }
+
+ length = end-start-pattern.size();
+ start = start+pattern.size();
+ string _connection = body.substr(start, length);
+ pos = start;
+ while (pos <= end) {
+ pos = (body.find(",", start) + 2);
+ if (pos <= start) {
+ return _encoding.size();
+ }
+ if ((pos == string::npos) || (pos > end)) {
+ length = end - start;
+ } else {
+ length = pos - start - 2;
+ }
+ string substr = body.substr(start, length);
+// printf("FIXME: \"%s\"\n", substr.c_str());
+ _connections.push_back(substr);
+ // Opera uses upper case first letters, Firefox doesn't.
+ if ((substr == "Keep-Alive") || (substr == "keep-alive")) {
+ _keepalive = true;
+ }
+ start = pos;
+ }
+
+ return _connections.size();
+}
+
+int
+HTTP::extractKeepAlive(Network::byte_t *data) {
+// GNASH_REPORT_FUNCTION;
+
+ string body = reinterpret_cast<const char *>(data);
+ string::size_type start, end, length, pos;
+ string pattern = "Keep-Alive: ";
+
+ start = body.find(pattern, 0);
+ if (start == string::npos) {
+ return -1;
+ }
+ end = body.find("\r\n", start);
+ if (end == string::npos) {
+ end = body.find("\n", start);
+// return "error";
+ }
+
+ length = end-start-pattern.size();
+ start = start+pattern.size();
+ string _connection = body.substr(start, length);
+ pos = start;
+ while (pos <= end) {
+ pos = (body.find(",", start) + 2);
+ if (pos <= start) {
+ return _encoding.size();
+ }
+ if ((pos == string::npos) || (pos > end)) {
+ length = end - start;
+ } else {
+ length = pos - start - 2;
+ }
+ string substr = body.substr(start, length);
+// printf("FIXME: \"%s\"\n", substr.c_str());
+ _kalive.push_back(substr);
+ _keepalive = true; // if we get this header setting, we want to
keep alive
+ start = pos;
+ }
+
+ return _connections.size();
+}
+
+string &
+HTTP::extractHost(Network::byte_t *data) {
+// GNASH_REPORT_FUNCTION;
+
+ string body = reinterpret_cast<const char *>(data);
+ string::size_type start, end;
+ string pattern = "Host: ";
+
+ start = body.find(pattern, 0);
+ if (start == string::npos) {
+ _host = "error";
+ return _host;
+ }
+ end = body.find("\r\n", start);
+ if (end == string::npos) {
+ _host = "error";
+ return _host;
+ }
+
+ _host = body.substr(start+pattern.size(), end-start-1);
+ return _host;
+}
+
+string &
+HTTP::extractAgent(Network::byte_t *data) {
+// GNASH_REPORT_FUNCTION;
+
+ string body = reinterpret_cast<const char *>(data);
+ string::size_type start, end;
+ string pattern = "User-Agent: ";
+ _agent = "error";
+
+ start = body.find(pattern, 0);
+ if (start == string::npos) {
+ return _agent;
+ }
+ end = body.find("\r\n", start);
+ if (end == string::npos) {
+ return _agent;
+ }
+
+ _agent = body.substr(start+pattern.size(), end-start-1);
+ return _agent;
+}
+
+int
+HTTP::extractLanguage(Network::byte_t *data) {
+// GNASH_REPORT_FUNCTION;
+
+ string body = reinterpret_cast<const char *>(data);
+ string::size_type start, end, length, pos, terminate;
+ // match both Accept-Language and Content-Language
+ string pattern = "-Language: ";
+
+ start = body.find(pattern, 0);
+ if (start == string::npos) {
+ return -1;
+ }
+ end = body.find("\r\n", start);
+ if (end == string::npos) {
+ end = body.find("\n", start);
+// return "error";
+ }
+ length = end-start-pattern.size();
+ start = start+pattern.size();
+ pos = start;
+ terminate = (body.find(";", start));
+ if (terminate == string::npos) {
+ terminate = end;
+ }
+
+ while (pos <= end) {
+ pos = (body.find(",", start));
+ if (pos <= start) {
+ return _encoding.size();
+ }
+ if ((pos == string::npos) || (pos >= terminate)) {
+ length = terminate - start;
+ } else {
+ length = pos - start;
+ }
+ string substr = body.substr(start, length);
+// printf("FIXME: \"%s\"\n", substr.c_str());
+ _language.push_back(substr);
+ start = pos + 1;
+ }
+
+// _language = body.substr(start+pattern.size(), end-start-1);
+ return _language.size();
+}
+
+int
+HTTP::extractCharset(Network::byte_t *data) {
+// GNASH_REPORT_FUNCTION;
+
+ string body = reinterpret_cast<const char *>(data);
+ string::size_type start, end, length, pos, terminate;
+// match both Accept-Charset and Content-Charset
+ string pattern = "-Charset: ";
+
+ start = body.find(pattern, 0);
+ if (start == string::npos) {
+ return -1;
+ }
+ end = body.find("\r\n", start);
+ if (end == string::npos) {
+ end = body.find("\n", start);
+// return "error";
+ }
+
+ length = end-start-pattern.size();
+ start = start+pattern.size();
+ string _connection = body.substr(start, length);
+ pos = start;
+ terminate = (body.find(";", start));
+ if (terminate == string::npos) {
+ terminate = end;
+ }
+ while (pos <= end) {
+ pos = (body.find(",", start) + 2);
+ if (pos <= start) {
+ return _encoding.size();
+ }
+ if ((pos == string::npos) || (pos >= terminate)) {
+ length = terminate - start;
+ } else {
+ length = pos - start - 2;
+ }
+ string substr = body.substr(start, length);
+// printf("FIXME: \"%s\"\n", substr.c_str());
+ _charset.push_back(substr);
+ start = pos;
+ }
+// _charset = body.substr(start+pattern.size(), end-start-1);
+ return _charset.size();
+}
+
+int
+HTTP::extractEncoding(Network::byte_t *data) {
+// GNASH_REPORT_FUNCTION;
+
+ string body = reinterpret_cast<const char *>(data);
+ string::size_type start, end, length, pos, terminate;
+ // match both Accept-Encoding and Content-Encoding
+ string pattern = "-Encoding: ";
+
+ start = body.find(pattern, 0);
+ if (start == string::npos) {
+ return -1;
+ }
+ end = body.find("\r\n", start);
+ if (end == string::npos) {
+ end = body.find("\n", start);
+// return "error";
+ }
+
+ length = end-start-pattern.size();
+ start = start+pattern.size();
+ string _connection = body.substr(start, length);
+ pos = start;
+ // Drop anything after a ';' character
+ terminate = (body.find(";", start));
+ if (terminate == string::npos) {
+ terminate = end;
+ }
+ while (pos <= end) {
+ pos = (body.find(",", start) + 2);
+ if (pos <= start) {
+ return _encoding.size();
+ }
+ if ((pos == string::npos) || (pos >= terminate)) {
+ length = terminate - start;
+ } else {
+ length = pos - start - 2;
+ }
+ string substr = body.substr(start, length);
+// printf("FIXME: \"%s\"\n", substr.c_str());
+ _encoding.push_back(substr);
+ start = pos;
+ }
+
+// _encoding = body.substr(start+pattern.size(), end-start-1);
+ return _encoding.size();
+}
+
+int
+HTTP::extractTE(Network::byte_t *data) {
+// GNASH_REPORT_FUNCTION;
+
+ string body = reinterpret_cast<const char *>(data);
+ string::size_type start, end, length, pos;
+ string pattern = "TE: ";
+
+ start = body.find(pattern, 0);
+ if (start == string::npos) {
+ return -1;
+ }
+ end = body.find("\r\n", start);
+ if (end == string::npos) {
+ end = body.find("\n", start);
+// return "error";
+ }
+
+ length = end-start-pattern.size();
+ start = start+pattern.size();
+ pos = start;
+ while (pos <= end) {
+ pos = (body.find(",", start));
+ if (pos <= start) {
+ return _encoding.size();
+ }
+ if ((pos == string::npos) || (pos >= end)) {
+ length = end - start;
+ } else {
+ length = pos - start;
+ }
+ string substr = body.substr(start, length);
+// printf("FIXME: \"%s\"\n", substr.c_str());
+ _te.push_back(substr);
+ start = pos + 2;
+ }
+ return _te.size();
+}
+
+// Get the file type, so we know how to set the
+// Content-type in the header.
+HTTP::filetype_e
+HTTP::getFileStats(std::string &filespec)
+{
+// GNASH_REPORT_FUNCTION;
+ bool try_again = true;
+ string actual_filespec = filespec;
+ struct stat st;
+
+ while (try_again) {
+ try_again = false;
+// cerr << "Trying to open " << actual_filespec << "\r\n";
+ if (stat(actual_filespec.c_str(), &st) == 0) {
+ // If it's a directory, then we emulate what apache
+ // does, which is to load the index.html file in that
+ // directry if it exists.
+ if (S_ISDIR(st.st_mode)) {
+ log_debug("%s is a directory\n", actual_filespec.c_str());
+ if (actual_filespec[actual_filespec.size()-1] != '/') {
+ actual_filespec += '/';
+ }
+ actual_filespec += "index.html";
+ try_again = true;
+ continue;
+ } else { // not a directory
+ log_debug("%s is not a directory\n", actual_filespec.c_str());
+ _filespec = actual_filespec;
+ string::size_type pos;
+ pos = filespec.rfind(".");
+ if (pos != string::npos) {
+ string suffix = filespec.substr(pos, filespec.size());
+ if (suffix == "html") {
+ _filetype = HTML;
+ log_debug("HTML content found");
+ }
+ if (suffix == "swf") {
+ _filetype = SWF;
+ log_debug("SWF content found");
+ }
+ if (suffix == "flv") {
+ _filetype = VIDEO;
+ log_debug("FLV content found");
+ }
+ if (suffix == "mp3") {
+ _filetype = AUDIO;
+ log_debug("MP3 content found");
+ }
+ }
+ }
+ } else {
+ _filetype = HTTP::ERROR;
+ } // end of stat()
+ } // end of try_waiting
+
+ _filesize = st.st_size;
+ return _filetype;
+}
+
+void
+HTTP::dump() {
+// GNASH_REPORT_FUNCTION;
+
+ boost::mutex::scoped_lock lock(stl_mutex);
+ vector<string>::iterator it;
+
+ log_debug (_("==== The HTTP header breaks down as follows: ===="));
+ log_debug (_("Filespec: %s"), _filespec.c_str());
+ log_debug (_("URL: %s"), _url.c_str());
+ log_debug (_("Version: %s"), _version.c_str());
+ for (it = _accept.begin(); it != _accept.end(); it++) {
+ log_debug("Accept param: \"%s\"", (*(it)).c_str());
+ }
+ log_debug (_("Method: %s"), _method.c_str());
+ log_debug (_("Referer: %s"), _referer.c_str());
+ log_debug (_("Connections:"));
+ for (it = _connections.begin(); it != _connections.end(); it++) {
+ log_debug("Connection param is: \"%s\"", (*(it)).c_str());
+ }
+ log_debug (_("Host: %s"), _host.c_str());
+ log_debug (_("User Agent: %s"), _agent.c_str());
+ for (it = _language.begin(); it != _language.end(); it++) {
+ log_debug("Language param: \"%s\"", (*(it)).c_str());
+ }
+ for (it = _charset.begin(); it != _charset.end(); it++) {
+ log_debug("Charset param: \"%s\"", (*(it)).c_str());
+ }
+ for (it = _encoding.begin(); it != _encoding.end(); it++) {
+ log_debug("Encodings param: \"%s\"", (*(it)).c_str());
+ }
+ for (it = _te.begin(); it != _te.end(); it++) {
+ log_debug("TE param: \"%s\"", (*(it)).c_str());
+ }
+
+ // Dump the RTMPT fields
+ log_debug("RTMPT optional index is: ", _index);
+ log_debug("RTMPT optional client ID is: ", _clientid);
+ log_debug (_("==== ==== ===="));
+}
+
+extern "C" {
+void
+httphandler(Handler::thread_params_t *args)
+{
+ GNASH_REPORT_FUNCTION;
+ int retries = 10;
+// struct thread_params thread_data;
+ string url, filespec, parameters;
+ string::size_type pos;
+ Handler *hand = reinterpret_cast<Handler *>(args->handle);
+ HTTP www;
+ www.setHandler(hand);
+
+ log_debug(_("Starting HTTP Handler for fd #%d, tid %ld"),
+ args->netfd, pthread_self());
+
+ string docroot = args->filespec;
+
+ while (!hand->timetodie()) {
+ log_debug(_("Waiting for HTTP GET request on fd #%d..."), args->netfd);
+ hand->wait();
+ // This thread is the last to wake up when the browser
+ // closes the network connection. When browsers do this
+ // varies, elinks and lynx are very forgiving to a more
+ // flexible HTTP protocol, which Firefox/Mozilla & Opera
+ // are much pickier, and will hang or fail to load if
+ // you aren't careful.
+ if (hand->timetodie()) {
+ log_debug("Not waiting no more, no more for more HTTP data for fd
#%d...", args->netfd);
+ map<int, Handler *>::iterator hit = handlers.find(args->netfd);
+ if ((*hit).second) {
+ log_debug("Removing handle %x for HTTP on fd #%d", (void
*)hand), args->netfd;
+ handlers.erase(args->netfd);
+ delete (*hit).second;
+ }
+ return;
+ }
+#ifdef USE_STATISTICS
+ struct timespec start;
+ clock_gettime (CLOCK_REALTIME, &start);
+#endif
+
+// conndata->statistics->setFileType(NetStats::RTMPT);
+// conndata->statistics->startClock();
+// args->netfd = www.getFileFd();
+ if (!www.waitForGetRequest()) {
+ hand->clearout(); // remove all data from the outgoing que
+ hand->die(); // tell all the threads for this connection to
die
+ hand->notifyin();
+ log_debug("Net HTTP done for fd #%d...", args->netfd);
+// hand->closeNet(args->netfd);
+ return;
+ }
+ url = docroot;
+ url += www.getURL();
+ pos = url.find("?");
+ filespec = url.substr(0, pos);
+ parameters = url.substr(pos + 1, url.size());
+ // Get the file size for the HTTP header
+
+ if (www.getFileStats(filespec) == HTTP::ERROR) {
+ www.formatErrorResponse(HTTP::NOT_FOUND);
+ }
+ www.sendGetReply(HTTP::LIFE_IS_GOOD);
+// strcpy(thread_data.filespec, filespec.c_str());
+// thread_data.statistics = conndata->statistics;
+
+ // Keep track of the network statistics
+// conndata->statistics->stopClock();
+// log_debug (_("Bytes read: %d"), www.getBytesIn());
+// log_debug (_("Bytes written: %d"), www.getBytesOut());
+// st.setBytes(www.getBytesIn() + www.getBytesOut());
+// conndata->statistics->addStats();
+
+ if (filespec[filespec.size()-1] == '/') {
+ filespec += "/index.html";
+ }
+ if (url != docroot) {
+ log_debug (_("File to load is: %s"), filespec.c_str());
+ log_debug (_("Parameters are: %s"), parameters.c_str());
+ struct stat st;
+ int filefd, ret;
+#ifdef USE_STATISTICS
+ struct timespec start;
+ clock_gettime (CLOCK_REALTIME, &start);
+#endif
+ if (stat(filespec.c_str(), &st) == 0) {
+ filefd = ::open(filespec.c_str(), O_RDONLY);
+ log_debug (_("File \"%s\" is %lld bytes in size, disk fd #%d"),
filespec,
+ st.st_size, filefd);
+ do {
+ Buffer *buf = new Buffer;
+ ret = read(filefd, buf->reference(), buf->size());
+ if (ret == 0) { // the file is done
+ delete buf;
+ break;
+ }
+ if (ret != buf->size()) {
+ buf->resize(ret);
+// log_debug("Got last data block from disk file, size
%d", buf->size());
+ }
+// log_debug("Read %d bytes from %s.", ret, filespec);
+#if 1
+ hand->pushout(buf);
+ hand->notifyout();
+#else
+ // Don't bother with the outgoing que
+ if (ret > 0) {
+ ret = hand->writeNet(buf);
+ }
+ delete buf;
+#endif
+ } while(ret > 0);
+ log_debug("Done transferring %s to net fd #%d",
+ filespec, args->netfd);
+ ::close(filefd); // close the disk file
+ // See if this is a persistant connection
+// if (!www.keepAlive()) {
+// log_debug("Keep-Alive is off", www.keepAlive());
+// // hand->closeConnection();
+// }
+#ifdef USE_STATISTICS
+ struct timespec end;
+ clock_gettime (CLOCK_REALTIME, &end);
+ log_debug("Read %d bytes from \"%s\" in %f seconds",
+ st.st_size, filespec,
+ (float)((end.tv_sec - start.tv_sec) + ((end.tv_nsec -
start.tv_nsec)/1e9)));
+#endif
+ }
+
+// memset(args->filespec, 0, 256);
+// memcpy(->filespec, filespec.c_str(), filespec.size());
+// boost::thread sendthr(boost::bind(&stream_thread, args));
+// sendthr.join();
+ }
+#ifdef USE_STATISTICS
+ struct timespec end;
+ clock_gettime (CLOCK_REALTIME, &end);
+ log_debug("Processing time for GET request was %f seconds",
+ (float)((end.tv_sec - start.tv_sec) + ((end.tv_nsec -
start.tv_nsec)/1e9)));
+#endif
+// conndata->statistics->dump();
+// }
+ } // end of while retries
+
+ log_debug("httphandler all done now finally...");
+
+} // end of httphandler
+
+} // end of extern C
+
+} // end of cygnal namespace
+
+
+// local Variables:
+// mode: C++
+// indent-tabs-mode: t
+// End:
Index: http.h
===================================================================
RCS file: http.h
diff -N http.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ http.h 28 Mar 2008 03:30:22 -0000 1.1
@@ -0,0 +1,304 @@
+//
+// Copyright (C) 2007, 2008 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
+//
+
+#ifndef _HTTP_H_
+#define _HTTP_H_
+
+#ifdef HAVE_CONFIG_H
+#include "gnashconfig.h"
+#endif
+
+#include <string>
+#include <map>
+#include <vector>
+
+#include "rtmp.h"
+#include "handler.h"
+#include "network.h"
+
+namespace cygnal
+{
+
+class HTTP
+{
+public:
+// as defined by the W3: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+ typedef enum {
+ // 1xx: Informational - Request received, continuing process
+ CONTINUE = 100,
+ SWITCHPROTOCOLS = 101,
+ // 2xx: Success - The action was successfully received,
+ // understood, and accepted
+ OK = 200,
+ CREATED = 201,
+ ACCEPTED = 202,
+ NON_AUTHORITATIVE = 203,
+ NO_CONTENT = 204,
+ RESET_CONTENT = 205,
+ PARTIAL_CONTENT = 206,
+ // 3xx: Redirection - Further action must be taken in order to
+ // complete the request
+ MULTIPLE_CHOICES = 300,
+ MOVED_PERMANENTLY = 301,
+ FOUND = 302,
+ SEE_OTHER = 303,
+ NOT_MODIFIED = 304,
+ USE_PROXY = 305,
+ TEMPORARY_REDIRECT = 307,
+ // 4xx: Client Error - The request contains bad syntax or
+ // cannot be fulfilled
+ BAD_REQUEST = 400,
+ UNAUTHORIZED = 401,
+ PAYMENT_REQUIRED = 402,
+ FORBIDDEN = 403,
+ NOT_FOUND = 404,
+ METHOD_NOT_ALLOWED = 405,
+ NOT_ACCEPTABLE = 406,
+ PROXY_AUTHENTICATION_REQUIRED = 407,
+ REQUEST_TIMEOUT = 408,
+ CONFLICT = 409,
+ GONE = 410,
+ LENGTH_REQUIRED = 411,
+ PRECONDITION_FAILED = 412,
+ REQUEST_ENTITY_TOO_LARGE = 413,
+ REQUEST_URI_TOO_LARGE = 414,
+ UNSUPPORTED_MEDIA_TYPE = 415,
+ REQUESTED_RANGE_NOT_SATISFIABLE = 416,
+ EXPECTATION_FAILED = 417,
+ // 5xx: Server Error - The server failed to fulfill an apparently
valid request
+ INTERNAL_SERVER_ERROR = 500,
+ NOT_IMPLEMENTED = 501,
+ BAD_GATEWAY = 502,
+ SERVICE_UNAVAILABLE = 503,
+ GATEWAY_TIMEOUT = 504,
+ HTTP_VERSION_NOT_SUPPORTED = 505,
+ // Gnash/Cygnal extensions for internal use
+ LIFE_IS_GOOD = 1234,
+ CLOSEPIPE = 1235
+ } http_status_e;
+ typedef enum {
+ OPTIONS,
+ GET,
+ HEAD,
+ POST,
+ PUT,
+ DELETE,
+ TRACE,
+ CONNECT
+ } http_method_e;
+ typedef enum {
+ OPEN,
+ SEND,
+ IDLE,
+ CLOSE
+ } rtmpt_cmd_e;
+ struct status_codes {
+ const char *code;
+ const char *msg;
+ };
+ typedef enum {
+ ERROR = -1,
+ NONE = 0,
+ HTML,
+ SWF,
+ VIDEO,
+ AUDIO,
+ MP3,
+ FCS,
+ OSCP
+ } filetype_e;
+ HTTP();
+ HTTP(Handler *hand);
+ ~HTTP();
+ bool waitForGetRequest();
+ bool waitForGetRequest(gnash::Network &net);
+
+ // Handle the GET request response
+ bool sendGetReply(http_status_e code);
+ bool sendPostReply(rtmpt_cmd_e code);
+// bool sendGetReply(Network &net);
+
+ // Make copies of ourself
+ HTTP &operator = (HTTP &obj);
+
+ // These methods extract data from an RTMPT message. RTMP is an
+ // extension to HTTP that adds commands to manipulate the
+ // connection's persistance.
+ rtmpt_cmd_e extractRTMPT(gnash::Network::byte_t *data);
+ rtmpt_cmd_e extractRTMPT(Buffer *data)
+ { return extractRTMPT(data->reference()); };
+
+ // These methods extract the fields in the HTTP header.
+ // These all return the number of items found, or 0
+ http_method_e extractCommand(gnash::Network::byte_t *data);
+ http_method_e extractCommand(Buffer *data)
+ { return extractCommand(data->reference()); };
+ int extractAccept(gnash::Network::byte_t *data);
+ int extractAccept(Buffer *data)
+ { return extractAccept(data->reference()); };
+ std::string &extractAcceptRanges(gnash::Network::byte_t *data);
+ std::string &extractAcceptRanges(Buffer *data)
+ { return extractAcceptRanges(data->reference()); };
+ int extractLanguage(gnash::Network::byte_t *data);
+ int extractLanguage(Buffer *data)
+ { return extractLanguage(data->reference()); };
+ int extractCharset(gnash::Network::byte_t *data);
+ int extractCharset(Buffer *data)
+ { return extractCharset(data->reference()); };
+ int extractEncoding(gnash::Network::byte_t *data);
+ int extractEncoding(Buffer *data)
+ { return extractEncoding(data->reference()); };
+ int extractTE(gnash::Network::byte_t *data);
+ int extractTE(Buffer *data)
+ { return extractTE(data->reference()); };
+ int extractConnection(gnash::Network::byte_t *data);
+ int extractConnection(Buffer *data)
+ { return extractConnection(data->reference()); };
+ int extractKeepAlive(gnash::Network::byte_t *data);
+ int extractKeepAlive(Buffer *data)
+ { return extractConnection(data->reference()); };
+
+ // These return the string that was found for this field.
+ std::string &extractMethod(gnash::Network::byte_t *data);
+ std::string &extractMethod(Buffer *data)
+ { return extractMethod(data->reference()); };
+ std::string &extractReferer(gnash::Network::byte_t *data);
+ std::string &extractReferer(Buffer *data)
+ { return extractReferer(data->reference()); };
+ std::string &extractHost(gnash::Network::byte_t *data);
+ std::string &extractHost(Buffer *data)
+ { return extractHost(data->reference()); };
+ std::string &extractAgent(gnash::Network::byte_t *data);
+ std::string &extractAgent(Buffer *data)
+ { return extractAgent(data->reference()); };
+
+ // These methods add data to the fields in the HTTP header.
+ // These return true if OK, false if error.
+ bool clearHeader();
+ bool formatHeader(int filesize, http_status_e type);
+ bool formatHeader(http_status_e type);
+ bool formatRequest(const std::string &url, http_method_e req);
+ bool formatMethod(const std::string &data);
+ bool formatDate();
+ bool formatServer();
+ bool formatServer(const std::string &data);
+ bool formatReferer(const std::string &data);
+ bool formatConnection(const std::string &data);
+ bool formatKeepAlive(const std::string &data);
+ bool formatContentLength();
+ bool formatContentLength(int filesize);
+ bool formatContentType();
+ bool formatContentType(filetype_e type);
+ bool formatHost(const std::string &data);
+ bool formatAgent(const std::string &data);
+ bool formatAcceptRanges(const std::string &data);
+ bool formatLastModified();
+ bool formatLastModified(const std::string &data);
+ bool formatEtag(const std::string &data);
+ bool formatLanguage(const std::string &data);
+ bool formatCharset(const std::string &data);
+ bool formatEncoding(const std::string &data);
+ bool formatTE(const std::string &data);
+
+ bool formatErrorResponse(http_status_e err);
+
+ // All HTTP messages are terminated with a blank line
+ void terminateHeader() { _header << "\r\n"; };
+
+ // Return the header that's been built up.
+ std::string getHeader() { return _header.str(); };
+
+ // Return the body that's been built up.
+ std::string getBody() { return _body.str(); };
+
+ // Get the file type, so we know how to set the
+ // Content-type in the header.
+// filetype_e getFileType(std::string &filespec);
+ filetype_e getFileStats(std::string &filespec);
+ void dump();
+
+ // These accessors are used mostly just for debugging.
+ bool keepAlive() { return _keepalive; }
+ int getFileSize() { return _filesize; }
+ std::string &getFilespec() { return _filespec; }
+ std::string &getURL() { return _url; }
+ std::map<int, struct status_codes *> getStatusCodes()
+ { return _status_codes; }
+ std::string getVersion() { return _version; }
+ std::string getMethod() { return _method; }
+ std::string getReferer() { return _referer; }
+ std::vector<std::string> getLanguage() { return _language; }
+ std::vector<std::string> getConnection() { return _connections; }
+ std::vector<std::string> getKeepAlive() { return _kalive; }
+ std::vector<std::string> getTE() { return _te; }
+ std::vector<std::string> getCharset() { return _charset; }
+ std::vector<std::string> getEncoding() { return _encoding; }
+
+ int getHostPort(){ return _port; }
+ std::string getHost() { return _host; }
+ std::string getUserAgent() { return _agent; }
+
+ void setHandler(Handler *hand) { _handler = hand; };
+private:
+ std::stringstream _header;
+ std::stringstream _body;
+ std::string _command;
+ filetype_e _filetype;
+ std::string _filespec;
+ int _filesize;
+ std::string _url;
+ std::map<int, struct status_codes *> _status_codes;
+ std::string _version;
+ std::string _method;
+ std::string _referer;
+ std::string _host;
+ int _port;
+ std::string _agent;
+ std::string _acceptranges;
+ std::vector<std::string> _connections;
+ std::vector<std::string> _language;
+ std::vector<std::string> _charset;
+ std::vector<std::string> _encoding;
+ std::vector<std::string> _te;
+ std::vector<std::string> _accept;
+ std::vector<std::string> _kalive;
+ // Connection parameters we care about
+ bool _keepalive;
+ Handler *_handler;
+ // These two field hold the data from an RTMPT message
+ int _clientid;
+ int _index;
+// bool _te;
+};
+
+// This is the thread for all incoming HTTP connections
+extern "C" {
+ void httphandler(Handler::thread_params_t *args);
+}
+
+
+} // end of cygnal namespace
+
+// end of _HTTP_H_
+#endif
+
+
+// local Variables:
+// mode: C++
+// indent-tabs-mode: t
+// End:
Index: netstats.cpp
===================================================================
RCS file: netstats.cpp
diff -N netstats.cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ netstats.cpp 28 Mar 2008 03:30:23 -0000 1.1
@@ -0,0 +1,75 @@
+//
+// Copyright (C) 2005, 2006, 2007, 2008 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
+//
+
+
+#ifdef HAVE_CONFIG_H
+#include "gnashconfig.h"
+#endif
+
+#include <boost/date_time/date.hpp>
+#include "netstats.h"
+#include "log.h"
+
+using namespace gnash;
+using namespace boost::posix_time;
+
+namespace cygnal {
+
+NetStats::NetStats()
+{
+// GNASH_REPORT_FUNCTION;
+}
+
+NetStats::~NetStats()
+{
+// GNASH_REPORT_FUNCTION;
+}
+
+boost::posix_time::ptime
+NetStats::startClock()
+{
+// GNASH_REPORT_FUNCTION;
+
+ _starttime = boost::posix_time::microsec_clock::local_time();
+ return _stoptime;
+}
+
+boost::posix_time::ptime
+NetStats::stopClock()
+{
+// GNASH_REPORT_FUNCTION;
+
+ _stoptime = boost::posix_time::microsec_clock::local_time();
+ return _stoptime;
+}
+
+NetStats &
+NetStats::operator = (NetStats &stats) {
+ _starttime = stats.getStartTime();
+ _stoptime = stats.getStopTime();
+ _bytes = stats.getBytes();
+ _type = stats.getFileType();
+ return *this;
+}
+
+} // end of cygnal namespace
+
+// local Variables:
+// mode: C++
+// indent-tabs-mode: t
+// End:
Index: netstats.h
===================================================================
RCS file: netstats.h
diff -N netstats.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ netstats.h 28 Mar 2008 03:30:23 -0000 1.1
@@ -0,0 +1,125 @@
+//
+// Copyright (C) 2005, 2006, 2007, 2008 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
+//
+
+#ifndef __NETSTATS_H__
+#define __NETSTATS_H__
+
+#ifdef HAVE_CONFIG_H
+#include "gnashconfig.h"
+#endif
+
+//include all types plus i/o
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <string>
+
+// This is what the ActionScript 'Client' class returns:
+//
+// bytes_in Total number of bytes received.
+// bytes_out Total number of bytes sent.
+// msg_in Total number of RTMP messages received.
+// msg_out Total number of RTMP messages sent.
+// msg_dropped Total number of dropped RTMP messages.
+// ping_rtt Length of time the client takes to respond to a ping
message.
+// audio_queue_msgs Current number of audio messages in the queue waiting
to be delivered to the client.
+// video_queue_msgs Current number of video messages in the queue waiting
to be delivered to the client.
+// so_queue_msgs Current number of shared object messages in the queue
waiting to be delivered to the client.
+// data_queue_msgs Current number of data messages in the queue waiting to
be delivered to the client.
+// dropped_audio_msgs Number of audio messages that were dropped.
+// dropped_video_msgs Number of video messages that were dropped.
+// audio_queue_bytes Total size of all audio messages (in bytes) in the
queue waiting to be delivered to the client.
+// video_queue_bytes Total size of all video messages (in bytes) in the
queue waiting to be delivered to the client.
+// so_queue_bytes Total size of all shared object messages (in bytes) in
the queue waiting to be delivered to the client.
+// data_queue_bytes Total size of all data messages (in bytes) in the queue
waiting to be delivered to the client.
+// dropped_audio_bytes Total size of all audio messages (in bytes) that were
dropped.
+// dropped_video_bytes Total size of all video messages (in bytes) that were
dropped.
+// bw_out Current upstream (client to server) bandwidth for this
client.
+// bw_in Current downstream (server to client) bandwidth for
this client.
+// client_id A unique ID issued by the server for this client.
+//
+// samples are taken every 3 seconds, or the interval supplied in
Client::setInterval()
+
+namespace cygnal
+{
+
+class NetStats {
+public:
+ NetStats();
+ ~NetStats();
+ typedef enum {
+ NO_CODEC,
+ OGG,
+ THEORA,
+ DIRAC,
+ SNOW,
+ MP3,
+ MPEG4,
+ H264,
+ H263,
+ FLV,
+ VP6,
+ VP7
+ } codec_e;
+ typedef enum {
+ NO_FILETYPE,
+ HTTP,
+ RTMP,
+ RTMPT,
+ RTMPTS,
+ SWF,
+ SWF6,
+ SWF7,
+ SWF8,
+ SWF9,
+ AUDIO,
+ VIDEO
+ } filetypes_e;
+ // start the clock counting down
+ boost::posix_time::ptime startClock();
+ // stop the clock from counting down
+ boost::posix_time::ptime stopClock();
+
+ // Accessors to set to the private data
+ void setStartTime(boost::posix_time::ptime x) { _starttime = x; };
+ void setStopTime(boost::posix_time::ptime x) { _stoptime = x; };
+ void setBytes(int x) { _bytes = x; };
+ void setFileType(filetypes_e x) { _type = x; };
+ // Accumulate the byts transferred
+ int addBytes(int x) { _bytes += x; return _bytes; };
+
+ // Accessors to get to the private data
+ int getBytes() { return _bytes; };
+ filetypes_e getFileType() { return _type; };
+ boost::posix_time::ptime getStartTime() { return _starttime; };
+ boost::posix_time::ptime getStopTime() { return _stoptime; };
+ boost::posix_time::time_duration getTimeSpan() { return _stoptime -
_starttime; };
+ NetStats &operator = (NetStats &stats);
+private:
+ boost::posix_time::ptime _starttime;
+ boost::posix_time::ptime _stoptime;
+ int _bytes;
+ filetypes_e _type;
+};
+
+} // end of cygnal namespace
+
+#endif // __NETSTATS_H__
+
+// local Variables:
+// mode: C++
+// indent-tabs-mode: t
+// End:
Index: network.cpp
===================================================================
RCS file: network.cpp
diff -N network.cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ network.cpp 28 Mar 2008 03:30:23 -0000 1.1
@@ -0,0 +1,911 @@
+//
+// Copyright (C) 2005, 2006, 2007, 2008 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
+//
+
+#ifdef HAVE_CONFIG_H
+#include "gnashconfig.h"
+#endif
+
+#include "utility.h"
+#include "log.h"
+#include "network.h"
+
+#include <sys/types.h>
+#include <cstring>
+#include <cstdio>
+#include <cerrno>
+#include <fcntl.h>
+#if defined(HAVE_WINSOCK_H) && !defined(__OS2__)
+# include <winsock2.h>
+# include <windows.h>
+# include <sys/stat.h>
+# include <io.h>
+# include <ws2tcpip.h>
+#else
+# include <sys/time.h>
+# include <unistd.h>
+# include <sys/select.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <sys/socket.h>
+# include <sys/un.h>
+# include <netdb.h>
+# include <sys/param.h>
+# include <sys/select.h>
+#endif
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 256
+#endif
+
+using namespace std;
+
+namespace gnash {
+
+static const char *DEFAULTPROTO = "tcp";
+static const short DEFAULTPORT = RTMP;
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+Network::Network()
+ :
+ _ipaddr(INADDR_ANY),
+ _sockfd(0),
+ _listenfd(0),
+ _port(0),
+ _connected(false),
+ _debug(false),
+ _timeout(0)
+{
+// GNASH_REPORT_FUNCTION;
+#if defined(HAVE_WINSOCK_H) && !defined(__OS2__)
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ wVersionRequested = MAKEWORD(1, 1); // Windows Sockets 1.1
+ if (WSAStartup( wVersionRequested, &wsaData ) != 0) {
+ log_error(_("Could not find a usable WinSock DLL"));
+ exit(1);
+ }
+#endif
+
+}
+
+Network::~Network()
+{
+// GNASH_REPORT_FUNCTION;
+#if defined(HAVE_WINSOCK_H) && !defined(__OS2__)
+ WSACleanup();
+#else
+ closeNet();
+#endif
+}
+
+// Description: Create a tcp/ip network server. This creates a server
+// that listens for incoming socket connections. This
+// supports IP aliasing on the host, and will sequntially
+// look for IP address to bind this port to.
+int
+Network::createServer(void)
+{
+// GNASH_REPORT_FUNCTION;
+
+ return createServer(DEFAULTPORT);
+}
+
+// FIXME: Should also support IPv6 (AF_INET6)
+int
+Network::createServer(short port)
+{
+// GNASH_REPORT_FUNCTION;
+
+ struct protoent *ppe;
+ struct sockaddr_in sock_in;
+ int on, type;
+ int retries = 0;
+ in_addr_t nodeaddr;
+
+#if 0
+ if (port < 1024) {
+ log_error(_("Can't connect to privileged port #%d"), port);
+ return -1;
+ }
+#endif
+
+ if (_listenfd >= 2) {
+ log_debug("already connected to port %hd", port);
+ return _listenfd;
+ }
+
+ const struct hostent *host = gethostbyname("localhost");
+ struct in_addr *thisaddr = reinterpret_cast<struct in_addr
*>(host->h_addr_list[0]);
+ _ipaddr = thisaddr->s_addr;
+ memset(&sock_in, 0, sizeof(sock_in));
+
+#if 0
+ // Accept incoming connections only on our IP number
+ sock_in.sin_addr.s_addr = thisaddr->s_addr;
+#else
+ // Accept incoming connections on any IP number
+ sock_in.sin_addr.s_addr = INADDR_ANY;
+#endif
+
+ _ipaddr = sock_in.sin_addr.s_addr;
+ sock_in.sin_family = AF_INET;
+ sock_in.sin_port = htons(port);
+
+ if ((ppe = getprotobyname(DEFAULTPROTO)) == 0) {
+ log_error(_("unable to get protocol entry for %s"),
+ DEFAULTPROTO);
+ return -1;
+ }
+
+ // set protocol type
+ if ( strcmp(DEFAULTPROTO, "udp") == 0) {
+ type = SOCK_DGRAM;
+ } else {
+ type = SOCK_STREAM;
+ }
+
+ // Get a file descriptor for this socket connection
+ _listenfd = socket(PF_INET, type, ppe->p_proto);
+
+ // error, wasn't able to create a socket
+ if (_listenfd < 0) {
+ log_error(_("unable to create socket: %s"), strerror(errno));
+ return -1;
+ }
+
+ on = 1;
+ if (setsockopt(_listenfd, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&on, sizeof(on)) < 0) {
+ log_error(_("setsockopt SO_REUSEADDR failed"));
+ return -1;
+ }
+
+ retries = 0;
+
+ nodeaddr = inet_lnaof(*thisaddr);
+ while (retries < 5) {
+ if (bind(_listenfd, reinterpret_cast<struct sockaddr *>(&sock_in),
+ sizeof(sock_in)) == -1) {
+ log_error(_("unable to bind to port %hd: %s"),
+ port, strerror(errno));
+// inet_ntoa(sock_in.sin_addr), strerror(errno));
+ retries++;
+ }
+
+ if (_debug) {
+// char ascip[INET_ADDRSTRLEN];
+// inet_ntop(sock_in.sin_family, &_ipaddr, ascip, INET_ADDRSTRLEN);
+ char *ascip = ::inet_ntoa(sock_in.sin_addr);
+ log_debug(_("Server bound to service on %s, port %hd, using fd
#%d"),
+ ascip, ntohs(sock_in.sin_port),
+ _listenfd);
+ }
+
+ if (type == SOCK_STREAM && listen(_listenfd, 5) < 0) {
+ log_error(_("unable to listen on port: %hd: %s "),
+ port, strerror(errno));
+ return -1;
+ }
+
+ // We have a socket created
+ _port = port;
+ return _listenfd;
+ }
+ return -1;
+}
+
+// Description: Accept a new network connection for the port we have
+// created a server for.
+// The default is to block.
+int
+Network::newConnection(void)
+{
+// GNASH_REPORT_FUNCTION;
+
+ return newConnection(true, _listenfd);
+}
+
+int
+Network::newConnection(int fd)
+{
+// GNASH_REPORT_FUNCTION;
+
+ return newConnection(true, fd);
+}
+
+int
+Network::newConnection(bool block)
+{
+// GNASH_REPORT_FUNCTION;
+
+ return newConnection(block, _listenfd);
+}
+
+int
+Network::newConnection(bool block, int fd)
+{
+ GNASH_REPORT_FUNCTION;
+
+ struct sockaddr newfsin;
+ socklen_t alen;
+ int ret;
+ struct timeval tval;
+ fd_set fdset;
+ int retries = 3;
+
+ alen = sizeof(struct sockaddr_in);
+
+ if (fd <= 2) {
+ return -1;
+ }
+ if (_debug) {
+ log_debug(_("Trying to accept net traffic on fd #%d for port %d"), fd,
_port);
+ }
+
+ while (retries--) {
+ // We use select to wait for the read file descriptor to be
+ // active, which means there is a client waiting to connect.
+ FD_ZERO(&fdset);
+ // also return on any input from stdin
+// if (_console) {
+// FD_SET(fileno(stdin), &fdset);
+// }
+ FD_SET(fd, &fdset);
+
+ // Reset the timeout value, since select modifies it on return. To
+ // block, set the timeout to zero.
+ tval.tv_sec = 1;
+ tval.tv_usec = 0;
+
+ if (block) {
+ ret = select(fd+1, &fdset, NULL, NULL, NULL);
+ } else {
+ ret = select(fd+1, &fdset, NULL, NULL, &tval);
+ }
+
+ if (FD_ISSET(0, &fdset)) {
+ if (_debug) {
+ log_debug(_("There is data at the console for stdin"));
+ }
+ return 1;
+ }
+
+ // If interupted by a system call, try again
+ if (ret == -1 && errno == EINTR) {
+ log_debug(_("The accept() socket for fd #%d was interupted by a
system call"), fd);
+ }
+
+ if (ret == -1) {
+ log_debug(_("The accept() socket for fd #%d never was available
for writing"), fd);
+ return -1;
+ }
+
+ if (ret == 0) {
+ if (_debug) {
+ log_debug(_("The accept() socket for fd #%d timed out waiting
to write"), fd);
+ }
+ }
+ }
+
+#ifndef HAVE_WINSOCK_H
+ fcntl(_listenfd, F_SETFL, O_NONBLOCK); // Don't let accept() block
+#endif
+ _sockfd = accept(fd, &newfsin, &alen);
+
+ if (_sockfd < 0) {
+ log_error(_("unable to accept: %s"), strerror(errno));
+ return -1;
+ }
+
+ if (_debug) {
+ log_debug(_("Accepting tcp/ip connection on fd #%d for port %d"),
_sockfd, _port);
+ }
+
+ return _sockfd;
+}
+
+#ifdef _WIN32
+/* from sys/socket.h */
+typedef unsigned short sa_family_t;
+
+/* from sys/un.h */
+#define UNIX_PATH_MAX 108
+
+struct sockaddr_un {
+ sa_family_t sun_family; /* AF_UNIX */
+ char sun_path[UNIX_PATH_MAX]; /* pathname */
+};
+
+#endif /* _WIN32 */
+
+// Connect to a named pipe
+bool
+Network::connectSocket(const string &sockname)
+{
+// GNASH_REPORT_FUNCTION;
+
+ struct sockaddr_un addr;
+ fd_set fdset;
+ struct timeval tval;
+ int ret;
+ int retries;
+
+ addr.sun_family = AF_UNIX;
+ // socket names must be 108 bytes or less as specifiec in sys/un.h.
+ strncpy(addr.sun_path, sockname.c_str(), 100);
+
+ _sockfd = ::socket(AF_UNIX, SOCK_STREAM, 0);
+ if (_sockfd < 0)
+ {
+ log_error(_("unable to create socket: %s"), strerror(errno));
+ _sockfd = -1;
+ return false;
+ }
+
+ retries = 2;
+ while (retries-- > 0) {
+ // We use select to wait for the read file descriptor to be
+ // active, which means there is a client waiting to connect.
+ FD_ZERO(&fdset);
+ FD_SET(_sockfd, &fdset);
+
+ // Reset the timeout value, since select modifies it on return. To
+ // block, set the timeout to zero.
+ tval.tv_sec = 5;
+ tval.tv_usec = 0;
+
+ ret = ::select(_sockfd+1, &fdset, NULL, NULL, &tval);
+
+ // If interupted by a system call, try again
+ if (ret == -1 && errno == EINTR)
+ {
+ log_debug(_("The connect() socket for fd %d was interupted by
a system call"),
+ _sockfd);
+ continue;
+ }
+
+ if (ret == -1)
+ {
+ log_debug(_("The connect() socket for fd %d never was
available for writing"),
+ _sockfd);
+#ifdef HAVE_WINSOCK_H
+ ::shutdown(_sockfd, 0); // FIXME: was SHUT_BOTH
+#else
+ ::shutdown(_sockfd, SHUT_RDWR);
+#endif
+ _sockfd = -1;
+ return false;
+ }
+ if (ret == 0) {
+ log_error(_("The connect() socket for fd %d timed out waiting to
write"),
+ _sockfd);
+ continue;
+ }
+
+ if (ret > 0) {
+ ret = ::connect(_sockfd, reinterpret_cast<struct sockaddr
*>(&addr), sizeof(addr));
+ if (ret == 0) {
+ log_debug(_("\tsocket name %s for fd %d"), sockname, _sockfd);
+ _connected = true;
+ assert(_sockfd > 0);
+ return true;
+ }
+ if (ret == -1) {
+ log_error(_("The connect() socket for fd %d never was
available for writing"),
+ _sockfd);
+ _sockfd = -1;
+ assert(!_connected);
+ return false;
+ }
+ }
+ }
+
+
+#ifndef HAVE_WINSOCK_H
+ fcntl(_sockfd, F_SETFL, O_NONBLOCK);
+#endif
+
+ _connected = true;
+ assert(_sockfd > 0);
+ return true;
+}
+
+// Create a client connection to a tcp/ip based service
+bool
+Network::createClient(void)
+{
+// GNASH_REPORT_FUNCTION;
+
+ return createClient("localhost", RTMP);
+}
+bool
+Network::createClient(short /* port */)
+{
+// GNASH_REPORT_FUNCTION;
+
+ return false;
+}
+
+bool
+Network::createClient(const string &hostname)
+{
+// GNASH_REPORT_FUNCTION;
+
+ return createClient(hostname, RTMP);
+}
+
+bool
+Network::createClient(const string &hostname, short port)
+{
+ GNASH_REPORT_FUNCTION;
+
+ struct sockaddr_in sock_in;
+ fd_set fdset;
+ struct timeval tval;
+ int ret;
+ int retries;
+ char thishostname[MAXHOSTNAMELEN];
+ struct protoent *proto;
+
+ assert( ! connected() );
+
+ if (port < 1024) {
+ log_error(_("Can't connect to privileged port %hd"), port);
+ _connected = false;
+ return false;
+ }
+
+ log_debug(_("%s: to host %s at port %d"), __FUNCTION__, hostname, port);
+
+ memset(&sock_in, 0, sizeof(struct sockaddr_in));
+ memset(&thishostname, 0, MAXHOSTNAMELEN);
+ if (hostname.size() == 0) {
+ if (gethostname(thishostname, MAXHOSTNAMELEN) == 0) {
+ log_debug(_("The hostname for this machine is %s"), thishostname);
+ } else {
+ log_debug(_("Couldn't get the hostname for this machine"));
+ return false;
+ }
+ }
+ const struct hostent *hent = ::gethostbyname(hostname.c_str());
+ if (hent > 0) {
+ ::memcpy(&sock_in.sin_addr, hent->h_addr, hent->h_length);
+ }
+ sock_in.sin_family = AF_INET;
+ sock_in.sin_port = ntohs(static_cast<short>(port));
+
+#if 0
+ char ascip[INET_ADDRSTRLEN];
+ inet_ntop(sock_in.sin_family, &sock_in.sin_addr.s_addr, ascip,
INET_ADDRSTRLEN);
+ log_debug(_("The IP address for this client socket is %s"), ascip);
+#endif
+
+ proto = ::getprotobyname("TCP");
+
+ _sockfd = ::socket(PF_INET, SOCK_STREAM, proto->p_proto);
+ if (_sockfd < 0)
+ {
+ log_error(_("unable to create socket: %s"), strerror(errno));
+ _sockfd = -1;
+ return false;
+ }
+
+ retries = 2;
+ while (retries-- > 0) {
+ // We use select to wait for the read file descriptor to be
+ // active, which means there is a client waiting to connect.
+ FD_ZERO(&fdset);
+ FD_SET(_sockfd, &fdset);
+
+ // Reset the timeout value, since select modifies it on return. To
+ // block, set the timeout to zero.
+ tval.tv_sec = 5;
+ tval.tv_usec = 0;
+
+ ret = ::select(_sockfd+1, &fdset, NULL, NULL, &tval);
+
+ // If interupted by a system call, try again
+ if (ret == -1 && errno == EINTR)
+ {
+ log_debug(_("The connect() socket for fd %d was interupted by
a system call"),
+ _sockfd);
+ continue;
+ }
+
+ if (ret == -1)
+ {
+ log_debug(_("The connect() socket for fd %d never was
available for writing"),
+ _sockfd);
+#ifdef HAVE_WINSOCK_H
+ ::shutdown(_sockfd, 0); // FIXME: was SHUT_BOTH
+#else
+ ::shutdown(_sockfd, SHUT_RDWR);
+#endif
+ _sockfd = -1;
+ return false;
+ }
+ if (ret == 0) {
+ log_error(_("The connect() socket for fd %d timed out waiting to
write"),
+ _sockfd);
+ continue;
+ }
+
+ if (ret > 0) {
+ ret = ::connect(_sockfd, reinterpret_cast<struct sockaddr
*>(&sock_in), sizeof(sock_in));
+ if (ret == 0) {
+ char *ascip = ::inet_ntoa(sock_in.sin_addr);
+// char ascip[INET_ADDRSTRLEN];
+// inet_ntop(sock_in.sin_family, &sock_in.sin_addr.s_addr, ascip,
INET_ADDRSTRLEN);
+ log_debug(_("\tport %d at IP %s for fd %d"), port,
+ ascip, _sockfd);
+ _connected = true;
+ assert(_sockfd > 0);
+ return true;
+ }
+ if (ret == -1) {
+ log_error(_("The connect() socket for fd %d never was
available for writing"),
+ _sockfd);
+ _sockfd = -1;
+ assert(!_connected);
+ return false;
+ }
+ }
+ }
+ // ::close(_sockfd);
+ // return false;
+
+ printf("\tConnected at port %d on IP %s for fd #%d", port,
+ ::inet_ntoa(sock_in.sin_addr), _sockfd);
+
+#ifndef HAVE_WINSOCK_H
+ fcntl(_sockfd, F_SETFL, O_NONBLOCK);
+#endif
+
+ _connected = true;
+ assert(_sockfd > 0);
+ return true;
+}
+
+bool
+Network::closeNet()
+{
+ GNASH_REPORT_FUNCTION;
+
+ if ((_sockfd > 0) && (_connected)) {
+ closeNet(_sockfd);
+ _sockfd = 0;
+ _connected = false;
+ }
+
+ return false;
+}
+
+bool
+Network::closeNet(int sockfd)
+{
+ GNASH_REPORT_FUNCTION;
+
+ int retries = 0;
+
+ // If we can't close the socket, other processes must be
+ // locked on it, so we wait a second, and try again. After a
+ // few tries, we give up, cause there must be something
+ // wrong.
+
+ if (sockfd <= 0) {
+ return true;
+ }
+
+ while (retries < 3) {
+ if (sockfd) {
+ // Shutdown the socket connection
+#if 0
+ if (shutdown(sockfd, SHUT_RDWR) < 0) {
+ if (errno != ENOTCONN) {
+ cerr << "WARNING: Unable to shutdown socket for fd #"
+ << sockfd << strerror(errno) << endl;
+ } else {
+ cerr << "The socket using fd #" << sockfd
+ << " has been shut down successfully." << endl;
+ return true;
+ }
+ }
+#endif
+ if (::close(sockfd) < 0) {
+ // If we have a bad file descriptor, it's because
+ // this got closed already, usually by another
+ // thread being paranoid.
+ if (errno != EBADF) {
+ log_error(_("Unable to close the socket for fd #%d: %s"),
+ sockfd, strerror(errno));
+ }
+#ifndef HAVE_WINSOCK_H
+ sleep(1);
+#endif
+ retries++;
+ } else {
+ log_debug(_("Closed the socket on fd #%d for port %d"), sockfd,
_port);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+// Description: Close an open socket connection.
+bool
+Network::closeConnection(void)
+{
+// GNASH_REPORT_FUNCTION;
+
+ closeConnection(_sockfd);
+ _sockfd = 0;
+ closeConnection(_listenfd);
+ _listenfd = 0;
+ _connected = false;
+
+ return false;
+}
+
+bool
+Network::closeConnection(int fd)
+{
+ GNASH_REPORT_FUNCTION;
+
+ if (fd > 0) {
+ ::close(fd);
+ log_debug("%s: Closed fd #%d", __FUNCTION__, fd);
+// closeNet(fd);
+ }
+
+ return false;
+}
+
+// Read from the connection
+int
+Network::readNet(byte_t *buffer, int nbytes)
+{
+ return readNet(_sockfd, buffer, nbytes, _timeout);
+}
+
+int
+Network::readNet(byte_t *buffer, int nbytes, int timeout)
+{
+ return readNet(_sockfd, buffer, nbytes, timeout);
+}
+
+int
+Network::readNet(int fd, byte_t *buffer, int nbytes)
+{
+ return readNet(fd, buffer, nbytes, _timeout);
+}
+
+int
+Network::readNet(int fd, byte_t *buffer, int nbytes, int timeout)
+{
+ fd_set fdset;
+ int ret = -1;
+ struct timeval tval;
+
+#ifdef NET_TIMING
+ if (_timing_debug)
+ {
+ gettimeofday(&tp, NULL);
+ read_start_time = static_cast<double>(tp.tv_sec)
+ + static_cast<double>(tp.tv_usec*1e-6);
+ }
+#endif
+ if (fd > 2) {
+ FD_ZERO(&fdset);
+ FD_SET(fd, &fdset);
+
+ if (timeout == 0) {
+ ret = select(fd+1, &fdset, NULL, NULL, NULL);
+ } else {
+ tval.tv_sec = timeout;
+ tval.tv_usec = 0;
+ ret = select(fd+1, &fdset, NULL, NULL, &tval);
+ }
+
+ // If interupted by a system call, try again
+ if (ret == -1 && errno == EINTR) {
+ log_error (_("The socket for fd %d was interupted by a system
call"), fd);
+ }
+
+ if (ret == -1) {
+ log_error (_("The socket for fd %d was never available for
reading"), fd);
+ return -1;
+ }
+
+ if (ret == 0) {
+ if (_debug) {
+ log_debug (_("The socket for fd %d timed out waiting to read"),
fd);
+ }
+ return 0;
+ }
+
+ ret = read(fd, buffer, nbytes);
+ // If we read zero bytes, the network is closed, as we returned from
the select()
+ if (ret == 0) {
+ return -1;
+ }
+
+ if (_debug) {
+ log_debug (_("read %d bytes from fd %d from port %d"), ret, fd,
_port);
+ }
+#if 0
+ if (ret) {
+ log_debug (_("%s: Read packet data from fd %d (%d bytes): \n%s"),
+ __FUNCTION__, fd, ret, hexify(buffer, ret, true));
+ }
+#endif
+ }
+
+ return ret;
+
+}
+
+// Write to the connection
+int
+Network::writeNet(const std::string& buffer)
+{
+ return writeNet(reinterpret_cast<const byte_t *>(buffer.c_str()),
buffer.size());
+}
+
+int
+Network::writeNet(const byte_t *buffer, int nbytes)
+{
+ return writeNet(_sockfd, buffer, nbytes, _timeout);
+}
+
+// int
+// Network::writeNet(const byte_t *buffer, int nbytes)
+// {
+// return writeNet(_sockfd, buffer, nbytes, _timeout);
+// }
+
+// int
+// Network::writeNet(int fd, const byte_t *buffer)
+// {
+// return writeNet(fd, buffer, strlen(buffer), _timeout);
+// }
+
+int
+Network::writeNet(int fd, const byte_t *buffer, int nbytes)
+{
+ return writeNet(fd, buffer, nbytes, _timeout);
+}
+
+int
+Network::writeNet(int fd, const byte_t *buffer, int nbytes, int timeout)
+{
+ fd_set fdset;
+ int ret = -1;
+ struct timeval tval;
+
+ // We need a writable, and not const point for byte arithmetic.
+ byte_t *bufptr = const_cast<byte_t *>(buffer);
+
+#ifdef NET_TIMING
+ // If we are debugging the tcp/ip timings, get the initial time.
+ if (_timing_debug)
+ {
+ gettimeofday(&starttime, 0);
+ }
+#endif
+ if (fd > 2) {
+ FD_ZERO(&fdset);
+ FD_SET(fd, &fdset);
+
+ // Reset the timeout value, since select modifies it on return
+ if (timeout <= 0) {
+ timeout = 5;
+ }
+ tval.tv_sec = timeout;
+ tval.tv_usec = 0;
+ ret = select(fd+1, NULL, &fdset, NULL, &tval);
+
+ // If interupted by a system call, try again
+ if (ret == -1 && errno == EINTR) {
+ log_error (_("The socket for fd %d was interupted by a system
call"), fd);
+ }
+
+ if (ret == -1) {
+ log_error (_("The socket for fd %d was never available for
writing"), fd);
+ }
+
+ if (ret == 0) {
+ log_debug (_("The socket for fd %d timed out waiting to write"),
fd);
+ return 0;
+ }
+
+ ret = write(fd, bufptr, nbytes);
+
+ if (ret == 0) {
+ log_error (_("Wrote zero out of %d bytes to fd %d: %s"),
+ nbytes, fd, strerror(errno));
+ return ret;
+ }
+ if (ret < 0) {
+ log_error (_("Couldn't write %d bytes to fd %d: %s"),
+ nbytes, fd, strerror(errno));
+ return ret;
+ }
+ if (ret > 0) {
+ bufptr += ret;
+ if (ret != nbytes) {
+ if (_debug) {
+ log_debug (_("wrote %d bytes to fd %d, expected %d"),
+ ret, fd, nbytes);
+ }
+ } else {
+ if (_debug) {
+ log_debug (_("wrote %d bytes to fd %d for port %d"),
+ ret, fd, _port);
+ }
+// return ret;
+ }
+ }
+#if 0
+ if (ret) {
+ log_debug (_("%s: Wrote packet data to fd %d: \n%s"),
+ __FUNCTION__, fd, hexify(buffer, ret, true));
+ }
+#endif
+ }
+
+#ifdef NET_TIMING
+ if (_timing_debug)
+ {
+ gettimeofday(&endtime, 0);
+
+ if ((endtime.tv_sec - starttime.tv_sec) &&
+ endtime.tv_usec - starttime.tv_usec)
+ {
+ log_debug (_("took %d usec to write (%d bytes)"),
+ endtime.tv_usec - starttime.tv_usec, bytes_written);
+ }
+ }
+#endif
+
+
+ return ret;
+}
+
+void
+Network::toggleDebug(bool val)
+{
+ // Turn on our own debugging
+ _debug = val;
+
+ // Turn on debugging for the utility methods
+ // recursive on all control paths,
+ // function will cause runtime stack overflow
+
+ // toggleDebug(true);
+}
+
+
+} // end of gnash namespace
+
+// Local Variables:
+// mode: C++
+// indent-tabs-mode: t
+// End:
Index: network.h
===================================================================
RCS file: network.h
diff -N network.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ network.h 28 Mar 2008 03:30:23 -0000 1.1
@@ -0,0 +1,160 @@
+//
+// Copyright (C) 2005, 2006, 2007, 2008 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
+
+#ifndef __NETWORK_H__
+#define __NETWORK_H__
+
+#ifdef HAVE_CONFIG_H
+#include "gnashconfig.h"
+#endif
+
+#if !defined(HAVE_WINSOCK_H) || defined(__OS2__)
+# include <netinet/in.h>
+# include <arpa/inet.h>
+#else
+# include <winsock2.h>
+# include <windows.h>
+# include <fcntl.h>
+# include <sys/stat.h>
+# include <io.h>
+#endif
+
+#include "dsodefs.h" //For DSOEXPORT.
+#include <boost/cstdint.hpp>
+#include <cassert>
+#include <string>
+
+namespace gnash {
+
+// Define the ports for the RTMP protocols
+const short ADMIN = 1111;
+const short RTMP = 1935;
+const short RTMPT = 80;
+const short RTMPTS = 443;
+
+#ifdef __OS2__
+ typedef int socklen_t;
+ #define SHUT_RDWR 0x2
+#endif
+
+#if defined(HAVE_WINSOCK_H) && !defined(__OS2__)
+ typedef long in_addr_t;
+# define inet_lnaof(x) inet_addr(inet_ntoa(x))
+ typedef int socklen_t;
+#endif
+
+// Adjust for the constant size
+const size_t NETBUFSIZE = 2048; // 1500 appears to be the default size
as used by FMS
+
+class Network {
+public:
+ typedef boost::uint8_t byte_t;
+
+ DSOEXPORT Network();
+ DSOEXPORT ~Network();
+
+ // Create a new server. After creating it, then you have to wait
+ // for an incoming connection.
+ int createServer(void);
+ DSOEXPORT int createServer(short port);
+
+ // Accept a client connection for the current server.
+ int newConnection(void);
+ int newConnection(int fd);
+ int newConnection(bool block, int fd);
+ DSOEXPORT int newConnection(bool block);
+
+ // Connect to a named pipe
+ bool connectSocket(const std::string &sock);
+
+ // Create a client connection to a tcp/ip server
+ bool createClient(void);
+ bool createClient(short port);
+ bool createClient(const std::string &hostname);
+ DSOEXPORT bool createClient(const std::string &hostname, short port);
+
+ // Read from the connection
+ int readNet(byte_t *buffer, int nbytes);
+ DSOEXPORT int readNet(byte_t *buffer, int nbytes, int timeout);
+ int readNet(int fd, byte_t *buffer, int nbytes);
+ int readNet(int fd, byte_t *buffer, int nbytes, int timeout);
+
+ // Write to the connection
+ int writeNet(const std::string &buffer);
+ DSOEXPORT int writeNet(const byte_t *buffer, int nbytes);
+// int writeNet(int fd, const byte_t *buffer);
+ int writeNet(int fd, const byte_t *buffer, int nbytes);
+ int writeNet(int fd, const byte_t *buffer, int nbytes, int timeout);
+
+ // Close the connection
+ DSOEXPORT bool closeNet();
+ bool closeNet(int fd);
+ DSOEXPORT bool closeConnection();
+ bool closeConnection(int fd);
+
+ // Change the debug flag
+ void toggleDebug(bool val);
+
+ bool send(const char *str);
+
+ // Accessors for testing
+ bool connected()
+ {
+ assert ( ( _connected && _sockfd > 0 ) || ( ! _connected && _sockfd <=
0 ) );
+ return _connected;
+ };
+
+ void setPort(short x) { _port = x; };
+ short getPort() const { return _port; };
+ void setFileFd(int x) { _sockfd = x; };
+ int getFileFd() const { return _sockfd; };
+ int getListenFd() const { return _listenfd; };
+ void setListenFd(int x) { _listenfd = x; };
+ const std::string& getURL() const { return _url; }
+ const std::string& getProtocol() const { return _protocol; }
+ const std::string& getHost() const { return _host; }
+ const std::string& getPortStr() const { return _portstr; }
+ const std::string& getPath() const { return _path; }
+ int getTimeout() const { return _timeout; }
+
+ // Network is not copiable !
+ //Network &operator = (Network &net) {}
+
+ protected:
+ in_addr_t _ipaddr;
+ int _sockfd; // the file descriptor used for reading and
writing
+ int _listenfd; // the file descriptor used to listen for new
connections
+ short _port;
+ std::string _portstr;
+ std::string _url;
+ std::string _protocol;
+ std::string _host;
+ std::string _path;
+ bool _connected;
+ bool _debug;
+ int _timeout;
+};
+
+} // end of gnash namespace
+
+// __NETWORK_H__
+#endif
+
+// Local Variables:
+// mode: C++
+// indent-tabs-mode: t
+// End:
Index: rtmp.cpp
===================================================================
RCS file: rtmp.cpp
diff -N rtmp.cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ rtmp.cpp 28 Mar 2008 03:30:23 -0000 1.1
@@ -0,0 +1,520 @@
+// rtmp.cpp: Adobe/Macromedia Real Time Message Protocol handler, for Gnash.
+//
+// Copyright (C) 2005, 2006, 2007, 2008 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
+//
+
+#ifdef HAVE_CONFIG_H
+#include "gnashconfig.h"
+#endif
+
+#include <iostream>
+
+#if ! (defined(_WIN32) || defined(WIN32))
+# include <netinet/in.h>
+#endif
+
+#include "log.h"
+#include "amf.h"
+#include "rtmp.h"
+#include "network.h"
+#include "handler.h"
+
+using namespace amf;
+using namespace gnash;
+using namespace std;
+
+namespace cygnal
+{
+
+extern map<int, Handler *> handlers;
+
+// These are the textual responses
+const char *response_str[] = {
+ "/onStatus",
+ "/onResult",
+ "/onDebugEvents"
+};
+
+int
+RTMPproto::headerSize(Network::byte_t header)
+{
+// GNASH_REPORT_FUNCTION;
+
+ int headersize = -1;
+
+ switch (header & AMF_HEADSIZE_MASK) {
+ case HEADER_12:
+ headersize = 12;
+ break;
+ case HEADER_8:
+ headersize = 8;
+ break;
+ case HEADER_4:
+ headersize = 4;
+ break;
+ case HEADER_1:
+ headersize = 11;
+ break;
+ default:
+ log_error(_("AMF Header size bits (0x%X) out of range"),
+ header & AMF_HEADSIZE_MASK);
+ headersize = 1;
+ break;
+ };
+
+ return headersize;
+}
+
+RTMPproto::RTMPproto()
+ : _handshake(0), _handler(0)
+{
+// GNASH_REPORT_FUNCTION;
+// _inbytes = 0;
+// _outbytes = 0;
+
+// _body = new unsigned char(RTMP_BODY_SIZE+1);
+// memset(_body, 0, RTMP_BODY_SIZE+1);
+}
+
+RTMPproto::~RTMPproto()
+{
+// GNASH_REPORT_FUNCTION;
+ _variables.clear();
+// delete _body;
+}
+
+void
+RTMPproto::addVariable(char *name, char *value)
+{
+ _variables[name] = value;
+}
+
+std::string
+RTMPproto::getVariable(char *name)
+{
+ return _variables[name];
+}
+
+// The handshake is a byte with the value of 0x3, followed by 1536
+// bytes of gibberish which we need to store for later.
+bool
+RTMPproto::handShakeWait()
+{
+ GNASH_REPORT_FUNCTION;
+
+// char buffer[RTMP_BODY_SIZE+16];
+// memset(buffer, 0, RTMP_BODY_SIZE+16);
+ Buffer *buf = _handler->pop();
+
+ if (buf == 0) {
+ log_debug("Que empty, net connection dropped for fd #%d",
_handler->getFileFd());
+ return false;
+ }
+// if (readNet(buffer, 1) == 1) {
+ log_debug (_("Read initial Handshake Request"));
+// } else {
+// log_error (_("Couldn't read initial Handshake Request"));
+// return false;
+// }
+// _inbytes += 1;
+
+ if (*(buf->reference()) == 0x3) {
+ log_debug (_("Handshake is correct"));
+ } else {
+ log_error (_("Handshake isn't correct"));
+ return false;
+ }
+
+// if (buf->size() >= RTMP_BODY_SIZE) {
+// secret = _handler->merge(buf->reference());
+// }
+
+ if (buf->size() >= RTMP_BODY_SIZE) {
+ _handshake = new Buffer(RTMP_BODY_SIZE);
+ _handshake->copy(buf->reference() + 1, RTMP_BODY_SIZE);
+ log_debug (_("Handshake Data matched"));
+ delete buf; // we're done with the buffer
+ return true;
+ } else {
+ delete buf; // we're done with the buffer
+ log_error (_("Handshake Data didn't match"));
+ return false;
+ }
+
+ return true;
+}
+
+// A request for a handshake is initiated by sending a byte with a
+// value of 0x3, followed by a message body of unknown format.
+bool
+RTMPproto::handShakeRequest()
+{
+ GNASH_REPORT_FUNCTION;
+
+#if 0
+ char buffer[RTMP_BODY_SIZE+1];
+ char c = 0x3;
+ int i, ret;
+
+ ret = writeNet(&c, 1);
+ _outbytes += 1;
+ // something went wrong, chances are the other end of the network
+ // connection is down, or never initialized.
+ if (ret <= 0) {
+ return false;
+ }
+
+ // Since we don't know what the format is, create a pattern we can
+ // recognize if we stumble across it later on.
+ for (i=0; i<RTMP_BODY_SIZE; i++) {
+ buffer[i] = i^256;
+ }
+
+ _outbytes += RTMP_BODY_SIZE;
+ ret = writeNet(buffer, RTMP_BODY_SIZE);
+#endif
+
+ return true;
+}
+
+// The response is the gibberish sent back twice, preceeded by a byte
+// with the value of 0x3.
+bool
+RTMPproto::handShakeResponse()
+{
+ GNASH_REPORT_FUNCTION;
+
+ Buffer *buf = new Buffer((RTMP_BODY_SIZE * 2) + 1);
+ Network::byte_t *ptr = buf->reference();
+ *ptr = 0x3;
+
+ std::copy(_handshake->begin(), _handshake->end(), (ptr + 1));
+ std::copy(_handshake->begin(), _handshake->end(), ptr + _handshake->size()
+ 1);
+ _handler->pushout(buf);
+ _handler->notifyout();
+
+ log_debug("Sent RTMP Handshake response");
+
+ return true;
+}
+
+// The client finished the handshake process by sending the second
+// data block we get from the server as the response
+bool
+RTMPproto::clientFinish()
+{
+ GNASH_REPORT_FUNCTION;
+
+#if 0
+ char buffer[RTMP_BODY_SIZE+1];
+ memset(buffer, 0, RTMP_BODY_SIZE+1);
+
+ if (readNet(buffer, RTMP_BODY_SIZE) == RTMP_BODY_SIZE) {
+ log_debug (_("Read first data block in handshake"));
+ } else {
+ log_error (_("Couldn't read first data block in handshake"));
+ return false;
+ }
+ _inbytes += RTMP_BODY_SIZE;
+ if (readNet(buffer, RTMP_BODY_SIZE) == RTMP_BODY_SIZE) {
+ log_debug (_("Read second data block in handshake"));
+// _body = new char(RTMP_BODY_SIZE+1);
+// memcpy(_body, buffer, RTMP_BODY_SIZE);
+ } else {
+ log_error (_("Couldn't read second data block in handshake"));
+ return false;
+ }
+ _inbytes += RTMP_BODY_SIZE;
+
+ writeNet(buffer, RTMP_BODY_SIZE);
+ _outbytes += RTMP_BODY_SIZE;
+#endif
+
+ return true;
+}
+
+bool
+RTMPproto::serverFinish()
+{
+ GNASH_REPORT_FUNCTION;
+
+ Buffer *buf = _handler->pop();
+ Buffer *obj = buf;
+
+ if (buf == 0) {
+ log_debug("Que empty, net connection dropped for fd #%d",
_handler->getFileFd());
+ return false;
+ }
+
+ // The first data packet is often buried in with the end of the handshake.
+ // So after the handshake block, we strip that part off, and just pass on
+ // the remainder for processing.
+ if (buf->size() > RTMP_BODY_SIZE) {
+ int size = buf->size() - RTMP_BODY_SIZE;
+ obj = new Buffer[size];
+ obj->copy(buf->begin()+RTMP_BODY_SIZE, size);
+ } else {
+ _handler->wait();
+ obj = _handler->pop();
+ }
+
+ int diff = std::memcmp(buf->begin(), _handshake->begin(), RTMP_BODY_SIZE);
+ delete buf; // we're done with the buffer
+ if (diff == 0) {
+ log_debug (_("Handshake Finish Data matched"));
+ } else {
+ log_error (_("Handshake Finish Data didn't match by %d bytes"), diff);
+// return false;
+ }
+
+ packetRead(obj);
+
+ return true;
+}
+
+bool
+RTMPproto::packetRequest()
+{
+ GNASH_REPORT_FUNCTION;
+ return false;
+}
+
+bool
+RTMPproto::packetSend(Buffer *buf)
+{
+ GNASH_REPORT_FUNCTION;
+ return false;
+}
+
+bool
+RTMPproto::packetRead(Buffer *buf)
+{
+ GNASH_REPORT_FUNCTION;
+
+ int ret;
+ int packetsize = 0;
+ unsigned int amf_index, headersize;
+ Network::byte_t *ptr = buf->reference();
+ AMF amf;
+
+//
address@hidden@\000\000\000\000\000\000\003\000\003app\002\000#software/gnash/tests/1153948634.flv\000\bflashVer\002\000\fLNX
6,0,82,0\000\006swfUrl\002\000\035file:///file|address@hidden://localhost/software/gnash/tests/1153948634
+ amf_index = *buf->reference() & AMF_INDEX_MASK;
+ headersize = headerSize(*buf->reference());
+ log_debug (_("The Header size is: %d"), headersize);
+ log_debug (_("The AMF index is: 0x%x"), amf_index);
+
+// if (headersize > 1) {
+// packetsize = parseHeader(ptr);
+// if (packetsize) {
+// log_debug (_("Read first RTMP packet header of size %d"),
packetsize);
+// } else {
+// log_error (_("Couldn't read first RTMP packet header"));
+// return false;
+// }
+// }
+
+ Network::byte_t *end = buf->find(0xc3);
+ log_debug("END is 0x%x", (void *)end);
+ *end = '*';
+ packetsize = parseHeader(ptr);
+ ptr += headersize;
+
+ Element el;
+ ptr = amf.extractElement(&el, ptr);
+ el.dump();
+ ptr = amf.extractElement(&el, ptr) + 1;
+ el.dump();
+ log_debug (_("Reading AMF packets till we're done..."));
+// buf->dump();
+ while (ptr < end) {
+ amf::Element *el = new amf::Element;
+ ptr = amf.extractVariable(el, ptr);
+ el->dump();
+// if (ptr != 0) {
+// ptr += 1;
+// // addObj(el);
+// } else {
+// break;
+// }
+ }
+ ptr += 1;
+ while (ptr < buf->end()) {
+ amf::Element *el = new amf::Element;
+ ptr = amf.extractVariable(el, ptr);
+ el->dump();
+ }
+
+ return true;
+}
+
+int
+RTMPproto::parseHeader(Network::byte_t *in)
+{
+// GNASH_REPORT_FUNCTION;
+
+ Network::byte_t *tmpptr = in;
+
+ _amf_index = *tmpptr & AMF_INDEX_MASK;
+ log_debug (_("The AMF channel index is %d"), _amf_index);
+
+ _header_size = headerSize(*tmpptr++);
+ log_debug (_("The header size is %d"), _header_size);
+
+ if (_header_size >= 4) {
+ _mystery_word = *tmpptr++;
+ _mystery_word = (_mystery_word << 12) + *tmpptr++;
+ _mystery_word = (_mystery_word << 8) + *tmpptr++;
+ log_debug(_("The mystery word is: %d"), _mystery_word);
+ }
+
+ if (_header_size >= 8) {
+ _total_size = *tmpptr++;
+ _total_size = (_total_size << 12) + *tmpptr++;
+ _total_size = (_total_size << 8) + *tmpptr++;
+ _total_size = _total_size & 0xffffff;
+// _amf_data = new uint8_t(_total_size+1);
+// _seekptr = _amf_data;
+// memset(_amf_data, 0, _total_size+1);
+ log_debug(_("The body size is: %d"), _total_size);
+ }
+
+ if (_header_size >= 8) {
+ _type = *(AMF::content_types_e *)tmpptr;
+ tmpptr++;
+ log_debug(_("The type is: 0x%x"), _type);
+ }
+
+ switch(_type) {
+ case AMF::CHUNK_SIZE:
+ case AMF::BYTES_READ:
+ case AMF::PING:
+ case AMF::SERVER:
+ case AMF::CLIENT:
+ case AMF::VIDEO_DATA:
+ case AMF::NOTIFY:
+ case AMF::SHARED_OBJ:
+ case AMF::INVOKE:
+ _packet_size = AMF_VIDEO_PACKET_SIZE;
+ break;
+ case AMF::AUDIO_DATA:
+ _packet_size = AMF_AUDIO_PACKET_SIZE;
+ break;
+ default:
+ log_error (_("ERROR: Unidentified AMF header data type 0x%x"),
_type);
+ break;
+ };
+
+ if (_header_size == 12) {
+// hexify((Network::byte_t *)hexint, (Network::byte_t *)tmpptr, 3,
false);
+ _src_dest = *(reinterpret_cast<rtmp_source_e *>(tmpptr));
+ tmpptr += sizeof(unsigned int);
+// log_debug(_("The source/destination is: %d, or 0x%s"), _src_dest,
hexint);
+ }
+
+ return _packet_size;
+}
+
+// This is the thread for all incoming RTMP connections
+void
+rtmp_handler(Handler::thread_params_t *args)
+{
+ GNASH_REPORT_FUNCTION;
+ Handler *hand = reinterpret_cast<Handler *>(args->handle);
+ RTMPproto rtmp;
+
+ rtmp.setHandler(hand);
+ string docroot = args->filespec;
+
+ log_debug(_("Starting RTMP Handler for fd #%d, tid %ld"),
+ args->netfd, pthread_self());
+
+ while (!hand->timetodie()) {
+ log_debug(_("Waiting for RTMP request on fd #%d..."), args->netfd);
+ hand->wait();
+ // This thread is the last to wake up when the browser
+ // closes the network connection. When browsers do this
+ // varies, elinks and lynx are very forgiving to a more
+ // flexible HTTP protocol, which Firefox/Mozilla & Opera
+ // are much pickier, and will hang or fail to load if
+ // you aren't careful.
+ if (hand->timetodie()) {
+ log_debug("Not waiting no more, no more for RTMP data for fd
#%d...", args->netfd);
+ map<int, Handler *>::iterator hit = handlers.find(args->netfd);
+ if ((*hit).second) {
+ log_debug("Removing handle %x for RTMP on fd #%d", (void
*)hand), args->netfd;
+ handlers.erase(args->netfd);
+ }
+
+ return;
+ }
+#ifdef USE_STATISTICS
+ struct timespec start;
+ clock_gettime (CLOCK_REALTIME, &start);
+#endif
+ if (!rtmp.handShakeWait()) {
+ hand->clearout(); // remove all data from the outgoing que
+ hand->die(); // tell all the threads for this connection to
die
+ hand->notifyin();
+ log_debug("Net RTMP done for fd #%d...", args->netfd);
+// hand->closeNet(args->netfd);
+ return;
+ }
+ string url, filespec;
+ url = docroot;
+
+ rtmp.handShakeResponse();
+
+ hand->wait();
+ // This thread is the last to wake up when the browser
+ // closes the network connection. When browsers do this
+ // varies, elinks and lynx are very forgiving to a more
+ // flexible HTTP protocol, which Firefox/Mozilla & Opera
+ // are much pickier, and will hang or fail to load if
+ // you aren't careful.
+ if (hand->timetodie()) {
+ log_debug("Not waiting no more, no more for RTMP data for fd
#%d...", args->netfd);
+ map<int, Handler *>::iterator hit = handlers.find(args->netfd);
+ if ((*hit).second) {
+ log_debug("Removing handle %x for RTMP on fd #%d", (void
*)hand), args->netfd;
+ handlers.erase(args->netfd);
+ }
+
+ return;
+ }
+ rtmp.serverFinish();
+
+ // Keep track of the network statistics
+// Statistics st;
+// st.setFileType(NetStats::RTMP);
+// st.stopClock();
+// log_debug (_("Bytes read: %d"), proto.getBytesIn());
+// log_debug (_("Bytes written: %d"), proto.getBytesOut());
+// st.setBytes(proto.getBytesIn() + proto.getBytesOut());
+// st.addStats();
+// proto.resetBytesIn();
+// proto.resetBytesOut();
+
+// st.dump();
+ }
+}
+
+} // end of cygnal namespace
+
+// local Variables:
+// mode: C++
+// indent-tabs-mode: t
+// End:
Index: rtmp.h
===================================================================
RCS file: rtmp.h
diff -N rtmp.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ rtmp.h 28 Mar 2008 03:30:23 -0000 1.1
@@ -0,0 +1,165 @@
+//
+// Copyright (C) 2005, 2006, 2007, 2008 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
+
+#ifndef _RTMP_H_
+#define _RTMP_H_
+
+#ifdef HAVE_CONFIG_H
+#include "gnashconfig.h"
+#endif
+
+#include "tu_config.h"
+
+#include <boost/cstdint.hpp>
+#include <vector>
+
+#include "amf.h"
+#include "handler.h"
+#include "network.h"
+
+namespace cygnal
+{
+
+#define RTMP_HANDSHAKE 0x3
+#define RTMP_BODY_SIZE 1536
+#define MAX_AMF_INDEXES 64
+
+class DSOEXPORT RTMPproto
+{
+public:
+ typedef enum {
+ CLIENT, // Flash player
+ SERVER // Flash com server
+ } rtmp_source_e;
+ typedef enum {
+ CONNECT = 0x1,
+ DISCONNECT = 0x2,
+ SET_ATTRIBUTE = 0x3,
+ UPDATE_DATA = 0x4,
+ UPDATE_ATTRIBUTE = 0x5,
+ SEND_MESSAGE = 0x6,
+ STATUS = 0x7,
+ CLEAR_DATA = 0x8,
+ DELETE_DATA = 0x9,
+ DELETE_ATTRIBUTE = 0xa,
+ INITIAL_DATA = 0xb
+ } sharedobj_types_e;
+ typedef enum {
+ RTMP_STATE_HANDSHAKE_SEND,
+ RTMP_STATE_HANDSHAKE_RECV,
+ RTMP_STATE_HANDSHAKE_ACK,
+ RTMP_STATE_CONNECT,
+ RTMP_STATE_NETCONNECT,
+ RTMP_STATE_NETSTREAM,
+ RTMP_STATE_HEADER,
+ RTMP_STATE_DONE
+ } rtmp_state_t;
+ typedef enum {
+ RTMP_ERR_UNDEF=0,
+ RTMP_ERR_NOTFOUND,
+ RTMP_ERR_PERM,
+ RTMP_ERR_DISKFULL,
+ RTMP_ERR_ILLEGAL,
+ RTMP_ERR_UNKNOWNID,
+ RTMP_ERR_EXISTS,
+ RTMP_ERR_NOSUCHUSER,
+ RTMP_ERR_TIMEOUT,
+ RTMP_ERR_NORESPONSE
+ } rtmp_error_t;
+
+// Each header consists of the following:
+//
+// * UTF string (including length bytes) - name
+// * Boolean - specifies if understanding the header is `required'
+// * Long - Length in bytes of header
+// * Variable - Actual data (including a type code)
+ typedef struct {
+ amf::amfutf8_t name;
+ boost::uint8_t required;
+ boost::uint32_t length;
+ void *data;
+ } rtmp_head_t;
+ typedef enum {
+ HEADER_12 = 0x0,
+ HEADER_8 = 0x40,
+ HEADER_4 = 0x80,
+ HEADER_1 = 0xc0
+ } rtmp_headersize_e;
+
+// Each body consists of the following:
+//
+// * UTF String - Target
+// * UTF String - Response
+// * Long - Body length in bytes
+// * Variable - Actual data (including a type code)
+ typedef struct {
+ amf::amfutf8_t target;
+ amf::amfutf8_t response;
+ boost::uint32_t length;
+ void *data;
+ } rtmp_body_t;
+
+ RTMPproto();
+ ~RTMPproto();
+ bool handShakeWait();
+ bool handShakeRequest();
+ bool handShakeResponse();
+ bool clientFinish();
+ bool serverFinish();
+ bool packetRequest();
+ bool packetSend(Buffer *buf);
+ bool packetRead(Buffer *buf);
+
+ void addVariable(char *name, char *value);
+ std::string getVariable(char *name);
+ void setHandler(Handler *hand) { _handler = hand; };
+ int headerSize(gnash::Network::byte_t header);
+ int parseHeader(gnash::Network::byte_t *header);
+
+ int getHeaderSize() { return _header_size; };
+ int getTotalSize() { return _total_size; };
+ int getPacketSize() { return _packet_size; };
+ int getMysteryWord() { return _mystery_word; };
+ rtmp_source_e getRouting() { return _src_dest; };
+ int getAMFIndex() { return _amf_index; };
+ private:
+ std::map<char *, std::string> _variables;
+// unsigned char _body[RTMP_BODY_SIZE+1];
+// std::vector<amf::AMF *> _amfs;
+ Buffer *_handshake;
+ Handler *_handler;
+ int _amf_index;
+ int _header_size;
+ int _total_size;
+ int _packet_size;
+ rtmp_source_e _src_dest;
+ amf::AMF::content_types_e _type;
+ int _mystery_word;
+};
+
+// This is the thread for all incoming RTMP connections
+void rtmp_handler(Handler::thread_params_t *args);
+
+} // end of cygnal namespace
+// end of _RTMP_H_
+#endif
+
+// local Variables:
+// mode: C++
+// indent-tabs-mode: t
+// End:
+
Index: statistics.cpp
===================================================================
RCS file: statistics.cpp
diff -N statistics.cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ statistics.cpp 28 Mar 2008 03:30:23 -0000 1.1
@@ -0,0 +1,137 @@
+// statistics.cpp: Network performance stats for Cygnal, for Gnash.
+//
+// Copyright (C) 2005, 2006, 2007, 2008 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
+//
+
+
+#ifdef HAVE_CONFIG_H
+#include "gnashconfig.h"
+#endif
+
+#include <boost/thread/mutex.hpp>
+#include <string>
+#include <list>
+#include <iostream>
+
+#include "log.h"
+#include "netstats.h"
+#include "statistics.h"
+
+using namespace gnash;
+using namespace std;
+
+static boost::mutex io_mutex;
+
+// The string versions of the codec, used for debugging. If you add
+// another enum type to codec_e, you have to add the string
+// representation here or you'll get the wrong output.
+const char *codec_names[] = {
+ "NO_CODEC",
+ "Ogg",
+ "Theora",
+ "Dirac",
+ "Snow",
+ "MP3",
+ "MPEG4",
+ "H264",
+ "H263",
+ "FLV",
+ "VP6",
+ "VP7"
+};
+
+// The string versions of the file type, used for debugging. If you add
+// another enum type to filetypes_e, you have to add the string
+// representation here or you'll get the wrong output.
+const char *filetype_names[] = {
+ "NO_FILETYPE",
+ "HTTP",
+ "RTMP",
+ "RTMPT",
+ "RTMPTS",
+ "SWF",
+ "SWF5",
+ "SWF6",
+ "SWF7",
+ "SWF8",
+ "SWF9",
+ "AUDIO",
+ "VIDEO"
+};
+
+namespace cygnal
+{
+
+Statistics::Statistics() {
+}
+
+Statistics::~Statistics() {
+ dump();
+}
+
+float
+Statistics::getFPS() {
+ return 0.0; // TODO: FIXME !
+}
+
+int
+Statistics::getBitRate() {
+
+ return (getStartTime() - getStopTime()).seconds() / getBytes();
+}
+
+int
+Statistics::addStats() {
+ NetStats *st = new NetStats;
+
+ st->setStartTime(getStartTime());
+ st->setStopTime(getStopTime());
+ st->setBytes(getBytes());
+ st->setFileType(getFileType());
+
+ boost::mutex::scoped_lock lock(io_mutex);
+ _netstats.push_back(st);
+
+ return _netstats.size();
+}
+
+void
+Statistics::dump() {
+ boost::mutex::scoped_lock lock(io_mutex);
+ list<NetStats *>::iterator it;
+
+ for (it = _netstats.begin(); it != _netstats.end(); it++) {
+ NetStats *stats = (*it);
+ if (stats->getFileType() <= VIDEO) {
+ log_debug (_("Stream type is: %s"),
filetype_names[stats->getFileType()]);
+ }
+// if (((stats->getFileType() == VIDEO) || (stats->getFileType() ==
AUDIO)) &&
+// stats->getCodec() <= VP7) {
+// log_debug (_("Stream codec is: %s"),
codec_names[stats->getCodec()]);
+// }
+ log_debug (_("%d bytes were transfered in %s seconds"),
+ stats->getBytes(),
+ to_simple_string(stats->getTimeSpan()).c_str());
+ }
+}
+
+} // end of cygnal namespace
+
+// local Variables:
+// mode: C++
+// indent-tabs-mode: t
+// End:
Index: statistics.h
===================================================================
RCS file: statistics.h
diff -N statistics.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ statistics.h 28 Mar 2008 03:30:23 -0000 1.1
@@ -0,0 +1,95 @@
+//
+// Copyright (C) 2006, 2007, 2008 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
+//
+
+#ifndef __STATISTICS_H__
+#define __STATISTICS_H__
+
+#ifdef HAVE_CONFIG_H
+#include "gnashconfig.h"
+#endif
+
+#include <sys/time.h>
+#include <network.h>
+#include <list>
+
+#include "netstats.h"
+
+namespace cygnal
+{
+
+class Statistics : public NetStats {
+public:
+ Statistics();
+ ~Statistics();
+ typedef enum {
+ NO_BROWSER,
+ MOZILLA,
+ FIREFOX,
+ OPERA,
+ KONQUEROR,
+ GALEON,
+ EPIPHANY,
+ SAFARI,
+ IE
+ } browser_e;
+ typedef enum {
+ NO_OSTYPE,
+ LINUX,
+ BSDS,
+ DARWIN,
+ WIN32,
+ SOLARIS
+ } ostype_e;
+
+ // Add a sample
+ int addStats();
+
+ // these make calculations on the collected network data.
+ float getFPS();
+ int getBitRate();
+
+ // Accessors
+ void setIPaddr(in_addr_t x) { _ipaddr = x; };
+ void setBrowser(browser_e x) { _browser = x; } ;
+ void setOS(ostype_e x) { _os = x; } ;
+ in_addr_t getIPaddr() { return _ipaddr; };
+ browser_e getBrowser() { return _browser; };
+ ostype_e getOS() { return _os; };
+
+// void setFilespec(std::string &x) { _filespec = x; } ;
+// std::string &getFilespec() { return _filespec; };
+ // Dump the collected network statistics in a human readable form.
+ void dump();
+ void clear();
+private:
+ in_addr_t _ipaddr;
+ browser_e _browser;
+ ostype_e _os;
+ std::list<NetStats *> _netstats;
+ boost::uint32_t _msg_count;
+ std::vector<std::string> _filespec;
+};
+
+} // end of cygnal namespace
+
+#endif // __STATISTICS_H__
+
+// local Variables:
+// mode: C++
+// indent-tabs-mode: t
+// End:
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Gnash-commit] gnash/libnet Makefile.am buffer.cpp buffer.h cq...,
Rob Savoye <=