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