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