libshevek
server.hh
1 /* server.hh - a line-protocol network server
2  * Copyright 2003-2005 Bas Wijnen <wijnen@debian.org>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #ifndef SHEVEK_SERVER_HH
19 #define SHEVEK_SERVER_HH
20 
21 #include "refbase.hh"
22 #include "telnet.hh"
23 #include <list>
24 #include <signal.h>
25 
26 namespace
27 {
28  sighandler_t ignore_broken_pipes = ::signal (SIGPIPE, SIG_IGN);
29 }
30 
31 namespace shevek
32 {
34 
95  template <typename client, typename serverdata>
96  class server : virtual public shevek::refbase
97  {
98  static inline fd::read_lines_t get_connection_cb_from_friend (client *who)
99  { return sigc::mem_fun (who, &client::read); }
100  public:
102  typedef typename std::list <Glib::RefPtr <client> >::iterator iterator;
104  typedef typename std::list <Glib::RefPtr <client> >::const_iterator
105  const_iterator;
107 
110  struct connection : virtual public refbase
111  {
113  Glib::RefPtr <shevek::fd> in;
115  Glib::RefPtr <shevek::fd> out;
118  {
119  in->read_lines (server <client, serverdata>
120  ::get_connection_cb_from_friend
121  (dynamic_cast <client *> (this) ) );
122  }
125  protected:
129  Glib::RefPtr <server <client, serverdata> > get_server () { return m_server; }
131  void disconnect () { m_server->l_error (m_self); }
132  private:
133  friend class server <client, serverdata>;
134  Glib::RefPtr <server <client, serverdata> > m_server;
135  iterator m_self;
136  };
137  private:
138  std::list <Glib::RefPtr <client> > m_connections;
139  Glib::RefPtr <socket> m_listener;
140  serverdata m_data;
141  server ();
142  void l_read (std::string const &line, Glib::RefPtr <client> conn);
143  void l_connect (Glib::RefPtr <fd> in, Glib::RefPtr <fd> out,
144  bool is_stdio);
145  void l_pickup ();
146  void l_delayed_disconnect (typename std::list <Glib::RefPtr <client> >::iterator conn);
147  void l_error (typename std::list <Glib::RefPtr <client> >::iterator conn);
148  public:
150  static Glib::RefPtr <server> create ();
152 
154  void open (std::string const &port, bool use_stdio = true);
156  serverdata &data () { return m_data; }
158  serverdata const &data () const { return m_data;}
160  iterator begin () { return m_connections.begin (); }
162  iterator end () { return m_connections.end (); }
164  const_iterator begin () const { return m_connections.begin (); }
166  const_iterator end () const { return m_connections.end (); }
168  void shutdown ();
170  ~server () { shutdown (); }
171  };
172 
173  template <typename client, typename serverdata>
174  void server <client, serverdata>::shutdown ()
175  {
176  if (m_listener)
177  {
178  m_listener->disconnect ();
179  m_listener = Glib::RefPtr <socket> ();
180  }
181  while (!m_connections.empty () )
182  m_connections.front ()->disconnect ();
183  }
184 
185  template <typename client, typename serverdata>
186  void server <client, serverdata>::l_read (std::string const &line,
187  Glib::RefPtr <client> conn)
188  {
189  conn->read (line);
190  }
191 
192  template <typename client, typename serverdata>
193  void server <client, serverdata>::l_delayed_disconnect (typename std::list <Glib::RefPtr <client> >::iterator conn)
194  {
195  Glib::RefPtr <telnet> s = Glib::RefPtr <telnet>::cast_dynamic ( (*conn)->in);
196  if (s)
197  s->disconnect ();
198  m_connections.erase (conn);
199  }
200 
201  template <typename client, typename serverdata>
202  void server <client, serverdata>::l_error (typename std::list <Glib::RefPtr <client> >::iterator conn)
203  {
204  Glib::RefPtr <telnet> s = Glib::RefPtr <telnet>::cast_dynamic ( (*conn)->in);
205  if (s)
206  {
207  s->signal_disconnect ().connect (sigc::bind (sigc::mem_fun (*this, &server <client, serverdata>::l_delayed_disconnect), conn) );
208  s->disconnect ();
209  }
210  else
211  {
212  (*conn)->in->unread ();
213  m_connections.erase (conn);
214  }
215  }
216 
217  template <typename client, typename serverdata>
218  void server <client, serverdata>::l_connect (Glib::RefPtr <fd> in,
219  Glib::RefPtr <fd> out,
220  bool is_stdio)
221  {
222  m_connections.push_back (client::create () );
223  m_connections.back ()->in = in;
224  m_connections.back ()->out = out;
225  m_connections.back ()->m_server = refptr_this <server <client, serverdata> > ();
226  m_connections.back ()->m_self = --m_connections.end ();
227  in->set_error (sigc::bind (sigc::mem_fun (*this, &server <client, serverdata>::l_error),
228  --m_connections.end () ) );
229  in->set_eof (sigc::bind (sigc::mem_fun (*this, &server <client, serverdata>::l_error),
230  --m_connections.end () ) );
231  m_connections.back ()->continue_reading ();
232  out->set_error (sigc::bind (sigc::mem_fun (*this, &server <client, serverdata>::l_error),
233  --m_connections.end () ) );
234  m_connections.back ()->pickup (is_stdio);
235  }
236 
237  template <typename client, typename serverdata>
238  void server <client, serverdata>::l_pickup ()
239  {
240  Glib::RefPtr <telnet> newfd = telnet::create ();
241  m_listener->accept (newfd);
242  l_connect (newfd, newfd, false);
243  }
244 
245  template <typename client, typename serverdata>
246  server <client, serverdata>::server ()
247  {
248  }
249 
250  template <typename client, typename serverdata>
251  Glib::RefPtr <server <client, serverdata> > server <client, serverdata>::create ()
252  {
253  return Glib::RefPtr <server <client, serverdata> > (new server <client, serverdata> () );
254  }
255 
256  template <typename client, typename serverdata>
257  void server <client, serverdata>::open (std::string const &port,
258  bool use_stdio)
259  {
260  if (m_listener)
261  m_listener->disconnect ();
262  if (!port.empty () )
263  {
264  m_listener = socket::create ();
265  m_listener->listen (port, sigc::mem_fun (*this, &server <client, serverdata>::l_pickup) );
266  }
267  if (use_stdio)
268  {
269  l_connect
270  (fd::create (STDIN_FILENO), fd::create (STDOUT_FILENO), true);
271  }
272  }
273 }
274 
275 #endif