libshevek
src/server.hh
00001 /* server.hh - a line-protocol network server
00002  * Copyright 2003-2005 Bas Wijnen <wijnen@debian.org>
00003  *
00004  * This program is free software: you can redistribute it and/or modify
00005  * it under the terms of the GNU General Public License as published by
00006  * the Free Software Foundation, either version 3 of the License, or
00007  * (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016  */
00017 
00018 #ifndef SHEVEK_SERVER_HH
00019 #define SHEVEK_SERVER_HH
00020 
00021 #include "refbase.hh"
00022 #include "telnet.hh"
00023 #include <list>
00024 #include <signal.h>
00025 
00026 namespace
00027 {
00028   sighandler_t ignore_broken_pipes = ::signal (SIGPIPE, SIG_IGN);
00029 }
00030 
00031 namespace shevek
00032 {
00034 
00095   template <typename client, typename serverdata>
00096   class server : virtual public shevek::refbase
00097   {
00098     static inline fd::read_lines_t get_connection_cb_from_friend (client *who)
00099     { return sigc::mem_fun (who, &client::read); }
00100   public:
00102     typedef typename std::list <Glib::RefPtr <client> >::iterator iterator;
00104     typedef typename std::list <Glib::RefPtr <client> >::const_iterator
00105     const_iterator;
00107 
00110     struct connection : virtual public refbase
00111     {
00113       Glib::RefPtr <shevek::fd> in;
00115       Glib::RefPtr <shevek::fd> out;
00117       void continue_reading ()
00118       {
00119         in->read_lines (server <client, serverdata>
00120                         ::get_connection_cb_from_friend
00121                         (dynamic_cast <client *> (this) ) );
00122       }
00124       ~connection () {}
00125     protected:
00127       connection () {}
00129       Glib::RefPtr <server <client, serverdata> > get_server () { return m_server; }
00131       void disconnect () { m_server->l_error (m_self); }
00132     private:
00133       friend class server <client, serverdata>;
00134       Glib::RefPtr <server <client, serverdata> > m_server;
00135       iterator m_self;
00136     };
00137   private:
00138     std::list <Glib::RefPtr <client> > m_connections;
00139     Glib::RefPtr <socket> m_listener;
00140     serverdata m_data;
00141     server ();
00142     void l_read (std::string const &line, Glib::RefPtr <client> conn);
00143     void l_connect (Glib::RefPtr <fd> in, Glib::RefPtr <fd> out,
00144                     bool is_stdio);
00145     void l_pickup ();
00146     void l_delayed_disconnect (typename std::list <Glib::RefPtr <client> >::iterator conn);
00147     void l_error (typename std::list <Glib::RefPtr <client> >::iterator conn);
00148   public:
00150     static Glib::RefPtr <server> create ();
00152 
00154     void open (std::string const &port, bool use_stdio = true);
00156     serverdata &data () { return m_data; }
00158     serverdata const &data () const { return m_data;}
00160     iterator begin () { return m_connections.begin (); }
00162     iterator end () { return m_connections.end (); }
00164     const_iterator begin () const { return m_connections.begin (); }
00166     const_iterator end () const { return m_connections.end (); }
00168     void shutdown ();
00170     ~server () { shutdown (); }
00171   };
00172 
00173   template <typename client, typename serverdata>
00174   void server <client, serverdata>::shutdown ()
00175   {
00176     if (m_listener)
00177       {
00178         m_listener->disconnect ();
00179         m_listener = Glib::RefPtr <socket> ();
00180       }
00181     while (!m_connections.empty () )
00182       m_connections.front ()->disconnect ();
00183   }
00184 
00185   template <typename client, typename serverdata>
00186   void server <client, serverdata>::l_read (std::string const &line,
00187                               Glib::RefPtr <client> conn)
00188   {
00189     conn->read (line);
00190   }
00191 
00192   template <typename client, typename serverdata>
00193   void server <client, serverdata>::l_delayed_disconnect (typename std::list <Glib::RefPtr <client> >::iterator conn)
00194   {
00195     Glib::RefPtr <telnet> s = Glib::RefPtr <telnet>::cast_dynamic ( (*conn)->in);
00196     if (s)
00197       s->disconnect ();
00198     m_connections.erase (conn);
00199   }
00200 
00201   template <typename client, typename serverdata>
00202   void server <client, serverdata>::l_error (typename std::list <Glib::RefPtr <client> >::iterator conn)
00203   {
00204     Glib::RefPtr <telnet> s = Glib::RefPtr <telnet>::cast_dynamic ( (*conn)->in);
00205     if (s)
00206       {
00207         s->signal_disconnect ().connect (sigc::bind (sigc::mem_fun (*this, &server <client, serverdata>::l_delayed_disconnect), conn) );
00208         s->disconnect ();
00209       }
00210     else
00211       {
00212         (*conn)->in->unread ();
00213         m_connections.erase (conn);
00214       }
00215   }
00216 
00217   template <typename client, typename serverdata>
00218   void server <client, serverdata>::l_connect (Glib::RefPtr <fd> in,
00219                                              Glib::RefPtr <fd> out,
00220                                              bool is_stdio)
00221   {
00222     m_connections.push_back (client::create () );
00223     m_connections.back ()->in = in;
00224     m_connections.back ()->out = out;
00225     m_connections.back ()->m_server = refptr_this <server <client, serverdata> > ();
00226     m_connections.back ()->m_self = --m_connections.end ();
00227     in->set_error (sigc::bind (sigc::mem_fun (*this, &server <client, serverdata>::l_error),
00228                                --m_connections.end () ) );
00229     in->set_eof (sigc::bind (sigc::mem_fun (*this, &server <client, serverdata>::l_error),
00230                                --m_connections.end () ) );
00231     m_connections.back ()->continue_reading ();
00232     out->set_error (sigc::bind (sigc::mem_fun (*this, &server <client, serverdata>::l_error),
00233                                 --m_connections.end () ) );
00234     m_connections.back ()->pickup (is_stdio);
00235   }
00236 
00237   template <typename client, typename serverdata>
00238   void server <client, serverdata>::l_pickup ()
00239   {
00240     Glib::RefPtr <telnet> newfd = telnet::create ();
00241     m_listener->accept (newfd);
00242     l_connect (newfd, newfd, false);
00243   }
00244 
00245   template <typename client, typename serverdata>
00246   server <client, serverdata>::server ()
00247   {
00248   }
00249 
00250   template <typename client, typename serverdata>
00251   Glib::RefPtr <server <client, serverdata> > server <client, serverdata>::create ()
00252   {
00253     return Glib::RefPtr <server <client, serverdata> > (new server <client, serverdata> () );
00254   }
00255 
00256   template <typename client, typename serverdata>
00257   void server <client, serverdata>::open (std::string const &port,
00258                   bool use_stdio)
00259   {
00260     if (m_listener)
00261       m_listener->disconnect ();
00262     if (!port.empty () )
00263       {
00264         m_listener = socket::create ();
00265         m_listener->listen (port, sigc::mem_fun (*this, &server <client, serverdata>::l_pickup) );
00266       }
00267     if (use_stdio)
00268       {
00269         l_connect
00270                 (fd::create (STDIN_FILENO), fd::create (STDOUT_FILENO), true);
00271       }
00272   }
00273 }
00274 
00275 #endif