Allow stack-size to be set for ThreadPoolSocketObserver
[metaproxy-moved-to-github.git] / src / filter_frontend_net.cpp
1 /* This file is part of Metaproxy.
2    Copyright (C) Index Data
3
4 Metaproxy is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Metaproxy is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18
19 #include "config.hpp"
20
21 #include <sstream>
22 #include <iomanip>
23 #include <metaproxy/util.hpp>
24 #include "pipe.hpp"
25 #include <metaproxy/filter.hpp>
26 #include <metaproxy/package.hpp>
27 #include "thread_pool_observer.hpp"
28 #include "filter_frontend_net.hpp"
29 #include <yazpp/z-assoc.h>
30 #include <yazpp/pdu-assoc.h>
31 #include <yazpp/socket-manager.h>
32 #include <yazpp/limit-connect.h>
33 #include <yaz/timing.h>
34 #include <yaz/log.h>
35 #include <yaz/daemon.h>
36 #include "gduutil.hpp"
37 #include <signal.h>
38
39 #include <iostream>
40
41 namespace mp = metaproxy_1;
42 namespace yf = metaproxy_1::filter;
43
44 namespace metaproxy_1 {
45     namespace filter {
46         class FrontendNet::Port {
47             friend class Rep;
48             friend class FrontendNet;
49             std::string port;
50             std::string route;
51             std::string cert_fname;
52             int max_recv_bytes;
53         };
54         class FrontendNet::Rep {
55             friend class FrontendNet;
56
57             int m_no_threads;
58             int m_max_threads;
59             int m_stack_size;
60             std::vector<Port> m_ports;
61             int m_listen_duration;
62             int m_session_timeout;
63             int m_connect_max;
64             int m_http_req_max;
65             std::string m_msg_config;
66             std::string m_stat_req;
67             yazpp_1::SocketManager mySocketManager;
68             ZAssocServer **az;
69             yazpp_1::PDU_Assoc **pdu;
70             int m_duration_freq[22];
71             double m_duration_lim[22];
72             double m_duration_max;
73             double m_duration_min;
74             double m_duration_total;
75             int m_stop_signo;
76         public:
77             Rep();
78             ~Rep();
79         };
80         class FrontendNet::My_Timer_Thread : public yazpp_1::ISocketObserver {
81         private:
82             yazpp_1::ISocketObservable *m_obs;
83             Pipe m_pipe;
84             bool m_timeout;
85         public:
86             My_Timer_Thread(yazpp_1::ISocketObservable *obs, int duration);
87             void socketNotify(int event);
88             bool timeout();
89         };
90         class FrontendNet::ZAssocChild : public yazpp_1::Z_Assoc {
91         public:
92             ~ZAssocChild();
93             ZAssocChild(yazpp_1::IPDU_Observable *the_PDU_Observable,
94                         mp::ThreadPoolSocketObserver *m_thread_pool_observer,
95                         const mp::Package *package,
96                         Port *port,
97                         Rep *rep,
98                         yazpp_1::LimitConnect &limit_connect);
99             int m_no_requests;
100             Port *m_port;
101         private:
102             yazpp_1::IPDU_Observer* sessionNotify(
103                 yazpp_1::IPDU_Observable *the_PDU_Observable,
104                 int fd);
105             void recv_GDU(Z_GDU *apdu, int len);
106             void report(Z_HTTP_Request *hreq);
107             void failNotify();
108             void timeoutNotify();
109             void connectNotify();
110         private:
111             mp::ThreadPoolSocketObserver *m_thread_pool_observer;
112             mp::Session m_session;
113             mp::Origin m_origin;
114             bool m_delete_flag;
115             const mp::Package *m_package;
116             Rep *m_p;
117             yazpp_1::LimitConnect &m_limit_connect;
118         };
119         class FrontendNet::ThreadPoolPackage : public mp::IThreadPoolMsg {
120         public:
121             ThreadPoolPackage(mp::Package *package,
122                               yf::FrontendNet::ZAssocChild *ses,
123                               Rep *rep);
124             ~ThreadPoolPackage();
125             IThreadPoolMsg *handle();
126             void result(const char *t_info);
127             bool cleanup(void *info);
128         private:
129             yaz_timing_t timer;
130             ZAssocChild *m_assoc_child;
131             mp::Package *m_package;
132             Rep *m_p;
133         };
134         class FrontendNet::ZAssocServer : public yazpp_1::Z_Assoc {
135         public:
136             ~ZAssocServer();
137             ZAssocServer(yazpp_1::IPDU_Observable *PDU_Observable,
138                          FrontendNet::Port *port,
139                          Rep *rep);
140             void set_package(const mp::Package *package);
141             void set_thread_pool(ThreadPoolSocketObserver *observer);
142         private:
143             yazpp_1::IPDU_Observer* sessionNotify(
144                 yazpp_1::IPDU_Observable *the_PDU_Observable,
145                 int fd);
146             void recv_GDU(Z_GDU *apdu, int len);
147
148             void failNotify();
149             void timeoutNotify();
150             void connectNotify();
151         private:
152             mp::ThreadPoolSocketObserver *m_thread_pool_observer;
153             const mp::Package *m_package;
154             yazpp_1::LimitConnect limit_connect;
155             Port *m_port;
156             Rep *m_p;
157         };
158     }
159 }
160
161 yf::FrontendNet::ThreadPoolPackage::ThreadPoolPackage(mp::Package *package,
162                                                       ZAssocChild *ses,
163                                                       Rep *rep) :
164     m_assoc_child(ses), m_package(package), m_p(rep)
165 {
166     timer = yaz_timing_create();
167 }
168
169 yf::FrontendNet::ThreadPoolPackage::~ThreadPoolPackage()
170 {
171     yaz_timing_destroy(&timer); // timer may be NULL
172     delete m_package;
173 }
174
175 bool yf::FrontendNet::ThreadPoolPackage::cleanup(void *info)
176 {
177     mp::Session *ses = (mp::Session *) info;
178
179     return *ses == m_package->session();
180 }
181
182 void yf::FrontendNet::ThreadPoolPackage::result(const char *t_info)
183 {
184     m_assoc_child->m_no_requests--;
185
186     yazpp_1::GDU *gdu = &m_package->response();
187
188     if (gdu->get())
189     {
190         int len;
191         m_assoc_child->send_GDU(gdu->get(), &len);
192
193         yaz_timing_stop(timer);
194         double duration = yaz_timing_get_real(timer);
195
196         size_t ent = 0;
197         while (m_p->m_duration_lim[ent] != 0.0 && duration > m_p->m_duration_lim[ent])
198             ent++;
199         m_p->m_duration_freq[ent]++;
200
201         m_p->m_duration_total += duration;
202
203         if (m_p->m_duration_max < duration)
204             m_p->m_duration_max = duration;
205
206         if (m_p->m_duration_min == 0.0 || m_p->m_duration_min > duration)
207             m_p->m_duration_min = duration;
208
209         if (m_p->m_msg_config.length())
210         {
211             Z_GDU *z_gdu = gdu->get();
212
213             std::ostringstream os;
214             os  << m_p->m_msg_config << " "
215                 << *m_package << " "
216                 << std::fixed << std::setprecision (6) << duration << " ";
217
218             if (z_gdu)
219                 os << *z_gdu;
220             else
221                 os << "-";
222
223             yaz_log(YLOG_LOG, "%s %s", os.str().c_str(), t_info);
224         }
225     }
226     else if (!m_package->session().is_closed())
227     {
228         // no response package and yet the session is still open..
229         // means that request is unhandled..
230         yazpp_1::GDU *gdu_req = &m_package->request();
231         Z_GDU *z_gdu = gdu_req->get();
232         if (z_gdu && z_gdu->which == Z_GDU_Z3950)
233         {
234             // For Z39.50, response with a Close and shutdown
235             mp::odr odr;
236             int len;
237             Z_APDU *apdu_response = odr.create_close(
238                 z_gdu->u.z3950, Z_Close_systemProblem,
239                 "unhandled Z39.50 request");
240
241             m_assoc_child->send_Z_PDU(apdu_response, &len);
242         }
243         else if (z_gdu && z_gdu->which == Z_GDU_HTTP_Request)
244         {
245             // For HTTP, respond with Server Error
246             int len;
247             mp::odr odr;
248             Z_GDU *zgdu_res
249                 = odr.create_HTTP_Response(m_package->session(),
250                                            z_gdu->u.HTTP_Request, 500);
251             m_assoc_child->send_GDU(zgdu_res, &len);
252         }
253         m_package->session().close();
254     }
255
256     if (m_assoc_child->m_no_requests == 0 && m_package->session().is_closed())
257     {
258         m_assoc_child->close();
259     }
260
261
262     delete this;
263 }
264
265 mp::IThreadPoolMsg *yf::FrontendNet::ThreadPoolPackage::handle()
266 {
267     m_package->move(m_assoc_child->m_port->route);
268     return this;
269 }
270
271 yf::FrontendNet::ZAssocChild::ZAssocChild(
272     yazpp_1::IPDU_Observable *PDU_Observable,
273     mp::ThreadPoolSocketObserver *my_thread_pool,
274     const mp::Package *package,
275     Port *port, Rep *rep,
276     yazpp_1::LimitConnect &limit_connect)
277     :  Z_Assoc(PDU_Observable), m_p(rep), m_limit_connect(limit_connect)
278 {
279     m_thread_pool_observer = my_thread_pool;
280     m_no_requests = 0;
281     m_delete_flag = false;
282     m_package = package;
283     m_port = port;
284     const char *peername = PDU_Observable->getpeername();
285     if (!peername)
286         peername = "unknown";
287     else
288     {
289         const char *cp = strchr(peername, ':');
290         if (cp)
291             peername = cp + 1;
292     }
293     std::string addr;
294     addr.append(peername);
295     addr.append(" ");
296     addr.append(port->port);
297     m_origin.set_tcpip_address(addr, m_session.id());
298     timeout(m_p->m_session_timeout);
299 }
300
301 yazpp_1::IPDU_Observer *yf::FrontendNet::ZAssocChild::sessionNotify(
302     yazpp_1::IPDU_Observable *the_PDU_Observable, int fd)
303 {
304     return 0;
305 }
306
307 yf::FrontendNet::ZAssocChild::~ZAssocChild()
308 {
309 }
310
311 void yf::FrontendNet::ZAssocChild::report(Z_HTTP_Request *hreq)
312 {
313     mp::odr o;
314
315     Z_GDU *gdu_res = o.create_HTTP_Response(m_session, hreq, 200);
316
317     Z_HTTP_Response *hres = gdu_res->u.HTTP_Response;
318
319     mp::wrbuf w;
320     size_t i;
321     int number_total = 0;
322
323     for (i = 0; m_p->m_duration_lim[i] != 0.0; i++)
324         number_total += m_p->m_duration_freq[i];
325     number_total += m_p->m_duration_freq[i];
326
327     wrbuf_puts(w, "<?xml version=\"1.0\"?>\n");
328     wrbuf_puts(w, "<frontend_net>\n");
329     wrbuf_printf(w, "  <responses frequency=\"%d\">\n", number_total);
330     for (i = 0; m_p->m_duration_lim[i] != 0.0; i++)
331     {
332         if (m_p->m_duration_freq[i] > 0)
333             wrbuf_printf(
334                 w, "    <response duration_start=\"%f\" "
335                 "duration_end=\"%f\" frequency=\"%d\"/>\n",
336                 i > 0 ? m_p->m_duration_lim[i - 1] : 0.0,
337                 m_p->m_duration_lim[i], m_p->m_duration_freq[i]);
338     }
339
340     if (m_p->m_duration_freq[i] > 0)
341         wrbuf_printf(
342             w, "    <response duration_start=\"%f\" frequency=\"%d\"/>\n",
343             m_p->m_duration_lim[i - 1], m_p->m_duration_freq[i]);
344
345     if (m_p->m_duration_max != 0.0)
346         wrbuf_printf(
347             w, "    <response duration_max=\"%f\"/>\n",
348             m_p->m_duration_max);
349     if (m_p->m_duration_min != 0.0)
350         wrbuf_printf(
351             w, "    <response duration_min=\"%f\"/>\n",
352             m_p->m_duration_min);
353     if (m_p->m_duration_total != 0.0)
354         wrbuf_printf(
355             w, "    <response duration_average=\"%f\"/>\n",
356             m_p->m_duration_total / number_total);
357
358     wrbuf_puts(w, " </responses>\n");
359
360     int thread_busy;
361     int thread_total;
362     m_thread_pool_observer->get_thread_info(thread_busy, thread_total);
363
364     wrbuf_printf(w, " <thread_info busy=\"%d\" total=\"%d\"/>\n",
365                  thread_busy, thread_total);
366
367     wrbuf_puts(w, "</frontend_net>\n");
368
369     hres->content_len = w.len();
370     hres->content_buf = (char *) w.buf();
371
372     int len;
373     send_GDU(gdu_res, &len);
374 }
375
376 void yf::FrontendNet::ZAssocChild::recv_GDU(Z_GDU *z_pdu, int len)
377 {
378     m_no_requests++;
379
380     mp::Package *p = new mp::Package(m_session, m_origin);
381
382     if (z_pdu && z_pdu->which == Z_GDU_HTTP_Request)
383     {
384         Z_HTTP_Request *hreq = z_pdu->u.HTTP_Request;
385
386         const char *f = z_HTTP_header_lookup(hreq->headers, "X-Forwarded-For");
387         if (f)
388             p->origin().set_tcpip_address(std::string(f), m_session.id());
389
390         if (m_p->m_stat_req.length()
391             && !strcmp(hreq->path, m_p->m_stat_req.c_str()))
392         {
393             report(hreq);
394             return;
395         }
396         std::string peername = p->origin().get_address();
397
398         m_limit_connect.add_connect(peername.c_str());
399         m_limit_connect.cleanup(false);
400         int con_sz = m_limit_connect.get_total(peername.c_str());
401
402         if (m_p->m_http_req_max && con_sz >= m_p->m_http_req_max)
403         {
404             mp::odr o;
405             Z_GDU *gdu_res = o.create_HTTP_Response(m_session, hreq, 500);
406             int len;
407             send_GDU(gdu_res, &len);
408             return;
409         }
410     }
411
412     ThreadPoolPackage *tp = new ThreadPoolPackage(p, this, m_p);
413     p->copy_route(*m_package);
414     p->request() = yazpp_1::GDU(z_pdu);
415
416     if (m_p->m_msg_config.length())
417     {
418         if (z_pdu)
419         {
420             std::ostringstream os;
421             os  << m_p->m_msg_config << " "
422                 << *p << " "
423                 << "0.000000" << " "
424                 << *z_pdu;
425             yaz_log(YLOG_LOG, "%s", os.str().c_str());
426         }
427     }
428     m_thread_pool_observer->put(tp);
429 }
430
431 void yf::FrontendNet::ZAssocChild::failNotify()
432 {
433     // TODO: send Package to signal "close"
434     if (m_session.is_closed())
435     {
436         if (m_no_requests == 0)
437             delete this;
438         return;
439     }
440     m_no_requests++;
441
442     m_session.close();
443
444     mp::Package *p = new mp::Package(m_session, m_origin);
445
446     ThreadPoolPackage *tp = new ThreadPoolPackage(p, this, m_p);
447     p->copy_route(*m_package);
448     m_thread_pool_observer->cleanup(tp, &m_session);
449     m_thread_pool_observer->put(tp);
450 }
451
452 void yf::FrontendNet::ZAssocChild::timeoutNotify()
453 {
454     failNotify();
455 }
456
457 void yf::FrontendNet::ZAssocChild::connectNotify()
458 {
459
460 }
461
462 yf::FrontendNet::ZAssocServer::ZAssocServer(
463     yazpp_1::IPDU_Observable *PDU_Observable,
464     Port *port,
465     Rep *rep)
466     :
467     Z_Assoc(PDU_Observable), m_port(port), m_p(rep)
468 {
469     m_package = 0;
470 }
471
472
473 void yf::FrontendNet::ZAssocServer::set_package(const mp::Package *package)
474 {
475     m_package = package;
476 }
477
478 void yf::FrontendNet::ZAssocServer::set_thread_pool(
479     mp::ThreadPoolSocketObserver *observer)
480 {
481     m_thread_pool_observer = observer;
482 }
483
484 yazpp_1::IPDU_Observer *yf::FrontendNet::ZAssocServer::sessionNotify(
485     yazpp_1::IPDU_Observable *the_PDU_Observable, int fd)
486 {
487
488     const char *peername = the_PDU_Observable->getpeername();
489     if (peername)
490     {
491         limit_connect.add_connect(peername);
492         limit_connect.cleanup(false);
493         int con_sz = limit_connect.get_total(peername);
494         if (m_p->m_connect_max && con_sz > m_p->m_connect_max)
495             return 0;
496     }
497     ZAssocChild *my = new ZAssocChild(the_PDU_Observable,
498                                       m_thread_pool_observer,
499                                       m_package, m_port, m_p, limit_connect);
500     return my;
501 }
502
503 yf::FrontendNet::ZAssocServer::~ZAssocServer()
504 {
505 }
506
507 void yf::FrontendNet::ZAssocServer::recv_GDU(Z_GDU *apdu, int len)
508 {
509 }
510
511 void yf::FrontendNet::ZAssocServer::failNotify()
512 {
513 }
514
515 void yf::FrontendNet::ZAssocServer::timeoutNotify()
516 {
517 }
518
519 void yf::FrontendNet::ZAssocServer::connectNotify()
520 {
521 }
522
523 yf::FrontendNet::FrontendNet() : m_p(new Rep)
524 {
525 }
526
527 yf::FrontendNet::Rep::Rep()
528 {
529     m_max_threads = m_no_threads = 5;
530     m_stack_size = 0;
531     m_listen_duration = 0;
532     m_session_timeout = 300; // 5 minutes
533     m_connect_max = 0;
534     az = 0;
535     size_t i;
536     for (i = 0; i < 22; i++)
537         m_duration_freq[i] = 0;
538     m_duration_lim[0] = 0.000001;
539     m_duration_lim[1] = 0.00001;
540     m_duration_lim[2] = 0.0001;
541     m_duration_lim[3] = 0.001;
542     m_duration_lim[4] = 0.01;
543     m_duration_lim[5] = 0.1;
544     m_duration_lim[6] = 0.2;
545     m_duration_lim[7] = 0.3;
546     m_duration_lim[8] = 0.5;
547     m_duration_lim[9] = 1.0;
548     m_duration_lim[10] = 1.5;
549     m_duration_lim[11] = 2.0;
550     m_duration_lim[12] = 3.0;
551     m_duration_lim[13] = 4.0;
552     m_duration_lim[14] = 5.0;
553     m_duration_lim[15] = 6.0;
554     m_duration_lim[16] = 8.0;
555     m_duration_lim[17] = 10.0;
556     m_duration_lim[18] = 15.0;
557     m_duration_lim[19] = 20.0;
558     m_duration_lim[20] = 30.0;
559     m_duration_lim[21] = 0.0;
560     m_duration_max = 0.0;
561     m_duration_min = 0.0;
562     m_duration_total = 0.0;
563     m_stop_signo = 0;
564 }
565
566 yf::FrontendNet::Rep::~Rep()
567 {
568     if (az)
569     {
570         size_t i;
571         for (i = 0; i < m_ports.size(); i++)
572             delete az[i];
573         delete [] az;
574         delete [] pdu;
575     }
576     az = 0;
577 }
578
579 yf::FrontendNet::~FrontendNet()
580 {
581 }
582
583 void yf::FrontendNet::stop(int signo) const
584 {
585     m_p->m_stop_signo = signo;
586 }
587
588 bool yf::FrontendNet::My_Timer_Thread::timeout()
589 {
590     return m_timeout;
591 }
592
593 yf::FrontendNet::My_Timer_Thread::My_Timer_Thread(
594     yazpp_1::ISocketObservable *obs,
595     int duration) :
596     m_obs(obs), m_pipe(9123), m_timeout(false)
597 {
598     obs->addObserver(m_pipe.read_fd(), this);
599     obs->maskObserver(this, yazpp_1::SOCKET_OBSERVE_READ);
600     obs->timeoutObserver(this, duration);
601 }
602
603 void yf::FrontendNet::My_Timer_Thread::socketNotify(int event)
604 {
605     m_timeout = true;
606     m_obs->deleteObserver(this);
607 }
608
609 void yf::FrontendNet::process(mp::Package &package) const
610 {
611     if (m_p->az == 0)
612         return;
613     size_t i;
614     My_Timer_Thread *tt = 0;
615
616     if (m_p->m_listen_duration)
617         tt = new My_Timer_Thread(&m_p->mySocketManager,
618                                  m_p->m_listen_duration);
619
620     ThreadPoolSocketObserver tp(&m_p->mySocketManager, m_p->m_no_threads,
621                                 m_p->m_max_threads,
622                                 m_p->m_stack_size);
623
624     for (i = 0; i<m_p->m_ports.size(); i++)
625     {
626         m_p->az[i]->set_package(&package);
627         m_p->az[i]->set_thread_pool(&tp);
628     }
629     while (m_p->mySocketManager.processEvent() > 0)
630     {
631         if (m_p->m_stop_signo == SIGTERM)
632         {
633             yaz_log(YLOG_LOG, "metaproxy received SIGTERM");
634             if (m_p->az)
635             {
636                 size_t i;
637                 for (i = 0; i < m_p->m_ports.size(); i++)
638                 {
639                     m_p->pdu[i]->shutdown();
640                     m_p->az[i]->server("");
641                 }
642                 yaz_daemon_stop();
643             }
644             break; /* stop right away */
645         }
646 #ifndef WIN32
647         if (m_p->m_stop_signo == SIGUSR1)
648         {    /* just stop listeners and cont till all sessions are done*/
649             yaz_log(YLOG_LOG, "metaproxy received SIGUSR1");
650             m_p->m_stop_signo = 0;
651             if (m_p->az)
652             {
653                 size_t i;
654                 for (i = 0; i < m_p->m_ports.size(); i++)
655                     m_p->az[i]->server("");
656                 yaz_daemon_stop();
657             }
658         }
659 #endif
660         int no = m_p->mySocketManager.getNumberOfObservers();
661         if (no <= 1)
662             break;
663         if (tt && tt->timeout())
664             break;
665     }
666     delete tt;
667 }
668
669 void yf::FrontendNet::configure(const xmlNode * ptr, bool test_only,
670                                 const char *path)
671 {
672     if (!ptr || !ptr->children)
673     {
674         throw yf::FilterException("No ports for Frontend");
675     }
676     std::vector<Port> ports;
677     for (ptr = ptr->children; ptr; ptr = ptr->next)
678     {
679         if (ptr->type != XML_ELEMENT_NODE)
680             continue;
681         if (!strcmp((const char *) ptr->name, "port"))
682         {
683             Port port;
684
685             const char *names[5] = {"route", "max_recv_bytes", "port",
686                                     "cert_fname", 0};
687             std::string values[4];
688
689             mp::xml::parse_attr(ptr, names, values);
690             port.route = values[0];
691             if (values[1].length() > 0)
692                 port.max_recv_bytes = atoi(values[1].c_str());
693             else
694                 port.max_recv_bytes = 0;
695             if (values[2].length() > 0)
696                 port.port = values[2];
697             else
698                 port.port = mp::xml::get_text(ptr);
699             port.cert_fname = values[3];
700             ports.push_back(port);
701         }
702         else if (!strcmp((const char *) ptr->name, "threads"))
703         {
704             std::string threads_str = mp::xml::get_text(ptr);
705             int threads = atoi(threads_str.c_str());
706             if (threads < 1)
707                 throw yf::FilterException("Bad value for threads: "
708                                                    + threads_str);
709             m_p->m_no_threads = threads;
710         }
711         else if (!strcmp((const char *) ptr->name, "max-threads"))
712         {
713             std::string threads_str = mp::xml::get_text(ptr);
714             int threads = atoi(threads_str.c_str());
715             if (threads < 1)
716                 throw yf::FilterException("Bad value for max-threads: "
717                                                    + threads_str);
718             m_p->m_max_threads = threads;
719         }
720         else if (!strcmp((const char *) ptr->name, "stack-size"))
721         {
722             std::string sz_str = mp::xml::get_text(ptr);
723             int sz = atoi(sz_str.c_str());
724             if (sz < 0)
725                 throw yf::FilterException("Bad value for stack-size: "
726                                                    + sz_str);
727             m_p->m_stack_size = sz * 1024;
728         }
729         else if (!strcmp((const char *) ptr->name, "timeout"))
730         {
731             std::string timeout_str = mp::xml::get_text(ptr);
732             int timeout = atoi(timeout_str.c_str());
733             if (timeout < 1)
734                 throw yf::FilterException("Bad value for timeout: "
735                                                    + timeout_str);
736             m_p->m_session_timeout = timeout;
737         }
738         else if (!strcmp((const char *) ptr->name, "connect-max"))
739         {
740             m_p->m_connect_max = mp::xml::get_int(ptr, 0);
741         }
742         else if (!strcmp((const char *) ptr->name, "http-req-max"))
743         {
744             m_p->m_http_req_max = mp::xml::get_int(ptr, 0);
745         }
746         else if (!strcmp((const char *) ptr->name, "message"))
747         {
748             m_p->m_msg_config = mp::xml::get_text(ptr);
749         }
750         else if (!strcmp((const char *) ptr->name, "stat-req"))
751         {
752             m_p->m_stat_req = mp::xml::get_text(ptr);
753         }
754         else
755         {
756             throw yf::FilterException("Bad element "
757                                       + std::string((const char *)
758                                                     ptr->name));
759         }
760     }
761     if (test_only)
762         return;
763     set_ports(ports);
764 }
765
766 void yf::FrontendNet::set_ports(std::vector<std::string> &ports)
767 {
768     std::vector<Port> nports;
769     size_t i;
770
771     for (i = 0; i < ports.size(); i++)
772     {
773         Port nport;
774
775         nport.port = ports[i];
776
777         nports.push_back(nport);
778     }
779     set_ports(nports);
780 }
781
782
783 void yf::FrontendNet::set_ports(std::vector<Port> &ports)
784 {
785     m_p->m_ports = ports;
786
787     m_p->az = new yf::FrontendNet::ZAssocServer *[m_p->m_ports.size()];
788     m_p->pdu = new yazpp_1::PDU_Assoc *[m_p->m_ports.size()];
789
790     // Create yf::FrontendNet::ZAssocServer for each port
791     size_t i;
792     for (i = 0; i < m_p->m_ports.size(); i++)
793         m_p->az[i] = 0;
794     for (i = 0; i < m_p->m_ports.size(); i++)
795     {
796         // create a PDU assoc object (one per yf::FrontendNet::ZAssocServer)
797         yazpp_1::PDU_Assoc *as = new yazpp_1::PDU_Assoc(&m_p->mySocketManager);
798
799         if (m_p->m_ports[i].cert_fname.length())
800             as->set_cert_fname(m_p->m_ports[i].cert_fname.c_str());
801         // create ZAssoc with PDU Assoc
802         m_p->pdu[i] = as;
803         m_p->az[i] = new yf::FrontendNet::ZAssocServer(
804             as, &m_p->m_ports[i], m_p.get());
805         if (m_p->az[i]->server(m_p->m_ports[i].port.c_str()))
806         {
807             throw yf::FilterException("Unable to bind to address "
808                                       + std::string(m_p->m_ports[i].port));
809         }
810         COMSTACK cs = as->get_comstack();
811
812         if (cs && m_p->m_ports[i].max_recv_bytes)
813             cs_set_max_recv_bytes(cs, m_p->m_ports[i].max_recv_bytes);
814
815     }
816 }
817
818 void yf::FrontendNet::set_listen_duration(int d)
819 {
820     m_p->m_listen_duration = d;
821 }
822
823 static yf::Base* filter_creator()
824 {
825     return new yf::FrontendNet;
826 }
827
828 extern "C" {
829     struct metaproxy_1_filter_struct metaproxy_1_filter_frontend_net = {
830         0,
831         "frontend_net",
832         filter_creator
833     };
834 }
835
836 /*
837  * Local variables:
838  * c-basic-offset: 4
839  * c-file-style: "Stroustrup"
840  * indent-tabs-mode: nil
841  * End:
842  * vim: shiftwidth=4 tabstop=8 expandtab
843  */
844