From ced440c14eaf1e9408f994ef1e7be2deb8a7db0b Mon Sep 17 00:00:00 2001 From: Adam Dickmeiss Date: Sun, 16 Oct 2005 16:05:44 +0000 Subject: [PATCH] Added Z3950 Client Filter, AKA Z39.50 backend. --- src/.cvsignore | 1 + src/Makefile.am | 10 +- src/ex_filter_frontend_net.cpp | 53 +++----- src/filter.hpp | 8 +- src/filter_log.cpp | 11 +- src/filter_z3950_client.cpp | 268 ++++++++++++++++++++++++++++++++++++++ src/filter_z3950_client.hpp | 38 ++++++ src/test_filter_frontend_net.cpp | 4 +- src/test_filter_z3950_client.cpp | 183 ++++++++++++++++++++++++++ 9 files changed, 534 insertions(+), 42 deletions(-) create mode 100644 src/filter_z3950_client.cpp create mode 100644 src/filter_z3950_client.hpp create mode 100644 src/test_filter_z3950_client.cpp diff --git a/src/.cvsignore b/src/.cvsignore index 22baa74..fe76ca1 100644 --- a/src/.cvsignore +++ b/src/.cvsignore @@ -18,3 +18,4 @@ test_package1 test_thread_pool_observer test_session1 test_session2 +test_filter_z3950_client diff --git a/src/Makefile.am b/src/Makefile.am index 0e13dd7..71e71dc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ -## $Id: Makefile.am,v 1.19 2005-10-15 11:02:08 adam Exp $ +## $Id: Makefile.am,v 1.20 2005-10-16 16:05:44 adam Exp $ MAINTAINERCLEANFILES = Makefile.in config.in config.hpp @@ -13,7 +13,8 @@ libyp2_la_SOURCES = \ session.cpp session.hpp package.hpp filter.hpp router.hpp \ thread_pool_observer.cpp thread_pool_observer.hpp \ filter_frontend_net.cpp filter_frontend_net.hpp \ - filter_log.cpp filter_log.hpp + filter_log.cpp filter_log.hpp \ + filter_z3950_client.cpp filter_z3950_client.hpp # Rules for programs.. @@ -32,7 +33,8 @@ check_PROGRAMS = \ test_session1 test_session2 \ test_thread_pool_observer \ test_boost_threads test_boost_time \ - test_filter_frontend_net + test_filter_frontend_net \ + test_filter_z3950_client TESTS=$(check_PROGRAMS) @@ -45,6 +47,7 @@ test_boost_threads_SOURCES=test_boost_threads.cpp test_boost_time_SOURCES=test_boost_time.cpp test_thread_pool_observer_SOURCES = test_thread_pool_observer.cpp test_filter_frontend_net_SOURCES = test_filter_frontend_net.cpp +test_filter_z3950_client_SOURCES = test_filter_z3950_client.cpp TESTLDADD = $(LDADD) -lboost_unit_test_framework @@ -57,6 +60,7 @@ test_boost_time_LDADD = $(TESTLDADD) test_thread_pool_observer_LDADD = $(TESTLDADD) test_package1_LDADD = $(TESTLDADD) test_filter_frontend_net_LDADD = $(TESTLDADD) +test_filter_z3950_client_LDADD = $(TESTLDADD) # doxygen target dox: diff --git a/src/ex_filter_frontend_net.cpp b/src/ex_filter_frontend_net.cpp index 76264f9..58d4a53 100644 --- a/src/ex_filter_frontend_net.cpp +++ b/src/ex_filter_frontend_net.cpp @@ -1,4 +1,4 @@ -/* $Id: ex_filter_frontend_net.cpp,v 1.7 2005-10-15 14:09:09 adam Exp $ +/* $Id: ex_filter_frontend_net.cpp,v 1.8 2005-10-16 16:05:44 adam Exp $ Copyright (c) 2005, Index Data. %LICENSE% @@ -14,51 +14,36 @@ namespace po = boost::program_options; #include "config.hpp" #include "filter_frontend_net.hpp" +#include "filter_z3950_client.hpp" #include "filter_log.hpp" #include "router.hpp" #include "session.hpp" #include "package.hpp" -class FilterInit: public yp2::filter::Base { +class HTTPFilter: public yp2::filter::Base { public: void process(yp2::Package & package) const { - if (package.session().is_closed()) { // std::cout << "Got Close.\n"; } Z_GDU *gdu = package.request().get(); - if (gdu) + if (gdu && gdu->which == Z_GDU_HTTP_Request) { ODR odr = odr_createmem(ODR_ENCODE); - switch(gdu->which) - { - case Z_GDU_Z3950: - // std::cout << "Got PDU. Sending init response\n"; - Z_APDU *apdu = zget_APDU(odr, Z_APDU_initResponse); - - apdu->u.initResponse->implementationName = "YP2/YAZ"; - - package.response() = apdu; - break; - case Z_GDU_HTTP_Request: - Z_GDU *gdu = z_get_HTTP_Response(odr, 200); - Z_HTTP_Response *http_res = gdu->u.HTTP_Response; - - z_HTTP_header_add(odr, &http_res->headers, - "Content-Type", "text/plain"); - - http_res->content_buf = - odr_strdup(odr, "Welcome to YP2"); - http_res->content_len = strlen(http_res->content_buf); - - package.response() = gdu; - break; - default: - break; - } + Z_GDU *gdu = z_get_HTTP_Response(odr, 200); + Z_HTTP_Response *http_res = gdu->u.HTTP_Response; + + z_HTTP_header_add(odr, &http_res->headers, + "Content-Type", "text/plain"); + + http_res->content_buf = + odr_strdup(odr, "Welcome to YP2"); + http_res->content_len = strlen(http_res->content_buf); + + package.response() = gdu; odr_destroy(odr); } return package.move(); @@ -114,10 +99,14 @@ int main(int argc, char **argv) yp2::filter::Log filter_log; router.rule(filter_log); - // put backend init filter in router - FilterInit filter_init; + // put HTTP backend filter in router + HTTPFilter filter_init; router.rule(filter_init); + // put Z39.50 backend filter in router + yp2::filter::Z3950Client z3950_client; + router.rule(z3950_client); + yp2::Session session; yp2::Origin origin; yp2::Package pack(session, origin); diff --git a/src/filter.hpp b/src/filter.hpp index faaefc6..45846f8 100644 --- a/src/filter.hpp +++ b/src/filter.hpp @@ -1,4 +1,4 @@ -/* $Id: filter.hpp,v 1.4 2005-10-15 14:09:09 adam Exp $ +/* $Id: filter.hpp,v 1.5 2005-10-16 16:05:44 adam Exp $ Copyright (c) 2005, Index Data. %LICENSE% @@ -19,9 +19,9 @@ namespace yp2 { virtual ~Base(){}; ///sends Package off to next Filter, returns altered Package - virtual void process(Package & package) const { - }; - virtual void configure(){}; + virtual void process(Package & package) const = 0; + + virtual void configure(){}; /// get function - right val in assignment std::string name() const { diff --git a/src/filter_log.cpp b/src/filter_log.cpp index 742fd2b..a1f0ad7 100644 --- a/src/filter_log.cpp +++ b/src/filter_log.cpp @@ -1,4 +1,4 @@ -/* $Id: filter_log.cpp,v 1.3 2005-10-15 14:09:09 adam Exp $ +/* $Id: filter_log.cpp,v 1.4 2005-10-16 16:05:44 adam Exp $ Copyright (c) 2005, Index Data. %LICENSE% @@ -24,6 +24,10 @@ void yp2::filter::Log::process(Package &package) const { Z_GDU *gdu; + std::cout << "---- req id=" << package.session().id(); + + std::cout << " close=" << (package.session().is_closed() ? "yes" : "no") + << "\n"; gdu = package.request().get(); if (gdu) { @@ -33,6 +37,11 @@ void yp2::filter::Log::process(Package &package) const { } package.move(); + + std::cout << "---- res id=" << package.session().id(); + + std::cout << " close=" << (package.session().is_closed() ? "yes" : "no") + << "\n"; gdu = package.response().get(); if (gdu) { diff --git a/src/filter_z3950_client.cpp b/src/filter_z3950_client.cpp new file mode 100644 index 0000000..18d5c58 --- /dev/null +++ b/src/filter_z3950_client.cpp @@ -0,0 +1,268 @@ +/* $Id: filter_z3950_client.cpp,v 1.1 2005-10-16 16:05:44 adam Exp $ + Copyright (c) 2005, Index Data. + +%LICENSE% + */ + +#include "config.hpp" + +#include "filter.hpp" +#include "router.hpp" +#include "package.hpp" + +#include + +#include "filter_z3950_client.hpp" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace yf = yp2::filter; + +namespace yp2 { + namespace filter { + class Z3950Client::Assoc : public yazpp_1::Z_Assoc{ + friend class Pimpl; + public: + Assoc(yp2::Session id, yazpp_1::SocketManager *socket_manager, + yazpp_1::IPDU_Observable *PDU_Observable, + std::string host); + ~Assoc(); + void connectNotify(); + void failNotify(); + void timeoutNotify(); + void recv_GDU(Z_GDU *gdu, int len); + yazpp_1::IPDU_Observer* sessionNotify( + yazpp_1::IPDU_Observable *the_PDU_Observable, + int fd); + private: + yp2::Session m_session_id; + yazpp_1::SocketManager *m_socket_manager; + yazpp_1::IPDU_Observable *m_PDU_Observable; + Package *m_package; + bool m_waiting; + bool m_connected; + std::string m_host; + }; + + class Z3950Client::Pimpl { + public: + boost::mutex m_mutex; + std::list m_clients; + Z3950Client::Assoc *get_assoc(Package &package); + void send_and_receive(Package &package, + yf::Z3950Client::Assoc *c); + }; + } +} + + +yf::Z3950Client::Assoc::Assoc(yp2::Session id, + yazpp_1::SocketManager *socket_manager, + yazpp_1::IPDU_Observable *PDU_Observable, + std::string host) + : Z_Assoc(PDU_Observable), m_session_id(id), + m_socket_manager(socket_manager), m_PDU_Observable(PDU_Observable), + m_package(0), m_waiting(false), m_connected(false), + m_host(host) +{ +} + +yf::Z3950Client::Assoc::~Assoc() +{ + delete m_socket_manager; +} + +void yf::Z3950Client::Assoc::connectNotify() +{ + m_waiting = false; + + m_connected = true; +} + +void yf::Z3950Client::Assoc::failNotify() +{ + m_waiting = false; + + ODR odr = odr_createmem(ODR_ENCODE); + + Z_APDU *apdu = zget_APDU(odr, Z_APDU_close); + + *apdu->u.close->closeReason = Z_Close_peerAbort; + + if (m_package) + { + m_package->response() = apdu; + m_package->session().close(); + } + + odr_destroy(odr); +} + +void yf::Z3950Client::Assoc::timeoutNotify() +{ + m_waiting = false; + + ODR odr = odr_createmem(ODR_ENCODE); + + Z_APDU *apdu = zget_APDU(odr, Z_APDU_close); + + *apdu->u.close->closeReason = Z_Close_lackOfActivity; + + if (m_package) + { + m_package->response() = apdu; + m_package->session().close(); + } + odr_destroy(odr); +} + +void yf::Z3950Client::Assoc::recv_GDU(Z_GDU *gdu, int len) +{ + m_waiting = false; + + if (m_package) + m_package->response() = gdu; +} + +yazpp_1::IPDU_Observer *yf::Z3950Client::Assoc::sessionNotify( + yazpp_1::IPDU_Observable *the_PDU_Observable, + int fd) +{ + return 0; +} + + +yf::Z3950Client::Z3950Client() { + m_p = new yf::Z3950Client::Pimpl; +} + +yf::Z3950Client::~Z3950Client() { + delete m_p; +} + +yf::Z3950Client::Assoc *yf::Z3950Client::Pimpl::get_assoc(Package &package) +{ + Z_GDU *gdu = package.request().get(); + + // only deal with Z39.50 + if (!gdu || gdu->which != Z_GDU_Z3950) + { + package.move(); + return 0; + } + + // only one thread messes with the clients list at a time + boost::mutex::scoped_lock lock(m_mutex); + + Z_APDU *apdu = gdu->u.z3950; + + std::list::iterator it; + + for (it = m_clients.begin(); it != m_clients.end(); it++) + { + if ((*it)->m_session_id == package.session()) + break; + } + if (it != m_clients.end()) + return *it; + + // new session .. + + // check that it is init. If not, close + if (apdu->which != Z_APDU_initRequest) + { + ODR odr = odr_createmem(ODR_ENCODE); + Z_APDU *apdu = zget_APDU(odr, Z_APDU_close); + + *apdu->u.close->closeReason = Z_Close_protocolError; + package.response() = apdu; + + package.session().close(); + odr_destroy(odr); + return 0; + } + // check virtual host + const char *vhost = + yaz_oi_get_string_oidval(&apdu->u.initRequest->otherInfo, + VAL_PROXY, 1, 0); + if (!vhost) + { + ODR odr = odr_createmem(ODR_ENCODE); + Z_APDU *apdu = zget_APDU(odr, Z_APDU_initResponse); + + apdu->u.initResponse->userInformationField = + zget_init_diagnostics(odr, + YAZ_BIB1_INIT_NEGOTIATION_OPTION_REQUIRED, + "Virtual host not given"); + package.response() = apdu; + + package.session().close(); + odr_destroy(odr); + return 0; + } + + yazpp_1::SocketManager *sm = new yazpp_1::SocketManager; + yazpp_1::PDU_Assoc *pdu_as = new yazpp_1::PDU_Assoc(sm); + yf::Z3950Client::Assoc *as = new yf::Z3950Client::Assoc(package.session(), + sm, pdu_as, + vhost); + m_clients.push_back(as); + return as; +} + +void yf::Z3950Client::Pimpl::send_and_receive(Package &package, + yf::Z3950Client::Assoc *c) +{ + // we should lock c! + + c->m_package = &package; + c->m_waiting = true; + if (!c->m_connected) + { + c->client(c->m_host.c_str()); + + while (c->m_waiting && c->m_socket_manager->processEvent() > 0) + ; + } + if (!c->m_connected) + { + return; + } + + // prepare response + c->m_waiting = true; + + // relay the package .. + int len; + c->send_GDU(package.request().get(), &len); + + while (c->m_waiting && c->m_socket_manager->processEvent() > 0) + ; +} + +void yf::Z3950Client::process(Package &package) const +{ + yf::Z3950Client::Assoc *c = m_p->get_assoc(package); + if (!c) + return; + m_p->send_and_receive(package, c); +} + + +/* + * Local variables: + * c-basic-offset: 4 + * indent-tabs-mode: nil + * c-file-style: "stroustrup" + * End: + * vim: shiftwidth=4 tabstop=8 expandtab + */ diff --git a/src/filter_z3950_client.hpp b/src/filter_z3950_client.hpp new file mode 100644 index 0000000..4a6f161 --- /dev/null +++ b/src/filter_z3950_client.hpp @@ -0,0 +1,38 @@ +/* $Id: filter_z3950_client.hpp,v 1.1 2005-10-16 16:05:44 adam Exp $ + Copyright (c) 2005, Index Data. + +%LICENSE% + */ + +#ifndef FILTER_Z3950_CLIENT_HPP +#define FILTER_Z3950_CLIENT_HPP + +#include +#include + +#include "filter.hpp" + +namespace yp2 { + namespace filter { + class Z3950Client : public Base { + class Pimpl; + class Assoc; + public: + ~Z3950Client(); + Z3950Client(); + void process(yp2::Package & package) const; + private: + Pimpl *m_p; + }; + } +} + +#endif +/* + * Local variables: + * c-basic-offset: 4 + * indent-tabs-mode: nil + * c-file-style: "stroustrup" + * End: + * vim: shiftwidth=4 tabstop=8 expandtab + */ diff --git a/src/test_filter_frontend_net.cpp b/src/test_filter_frontend_net.cpp index 7bdb01a..8af770d 100644 --- a/src/test_filter_frontend_net.cpp +++ b/src/test_filter_frontend_net.cpp @@ -1,4 +1,4 @@ -/* $Id: test_filter_frontend_net.cpp,v 1.7 2005-10-15 14:09:09 adam Exp $ +/* $Id: test_filter_frontend_net.cpp,v 1.8 2005-10-16 16:05:44 adam Exp $ Copyright (c) 2005, Index Data. %LICENSE% @@ -22,7 +22,7 @@ using namespace boost::unit_test; class FilterInit: public yp2::filter::Base { public: void process(yp2::Package & package) const { - + if (package.session().is_closed()) { // std::cout << "Got Close.\n"; diff --git a/src/test_filter_z3950_client.cpp b/src/test_filter_z3950_client.cpp new file mode 100644 index 0000000..d8a691d --- /dev/null +++ b/src/test_filter_z3950_client.cpp @@ -0,0 +1,183 @@ +/* $Id: test_filter_z3950_client.cpp,v 1.1 2005-10-16 16:05:44 adam Exp $ + Copyright (c) 2005, Index Data. + +%LICENSE% + */ + +#include "config.hpp" +#include +#include + +#include "filter_z3950_client.hpp" + +#include "router.hpp" +#include "session.hpp" +#include "package.hpp" + +#define BOOST_AUTO_TEST_MAIN +#include + +#include +#include +using namespace boost::unit_test; + + +BOOST_AUTO_TEST_CASE( test_filter_z3950_client_1 ) +{ + try + { + { + yp2::filter::Z3950Client zc; + } + } + catch ( ... ) { + BOOST_CHECK (false); + } +} + +BOOST_AUTO_TEST_CASE( test_filter_z3950_client_2 ) +{ + try + { + { + yp2::RouterChain router; + + yp2::filter::Z3950Client zc; + + router.rule(zc); + + // Create package with Z39.50 init request in it + yp2::Package pack; + + ODR odr = odr_createmem(ODR_ENCODE); + Z_APDU *apdu = zget_APDU(odr, Z_APDU_initRequest); + + BOOST_CHECK(apdu); + + pack.request() = apdu; + odr_destroy(odr); + + // Put it in router + pack.router(router).move(); + + // Inspect that we got Z39.50 init Response - a Z39.50 session + // specify a virtual host + yazpp_1::GDU *gdu = &pack.response(); + + BOOST_CHECK(pack.session().is_closed()); + + Z_GDU *z_gdu = gdu->get(); + BOOST_CHECK(z_gdu); + if (z_gdu) { + BOOST_CHECK_EQUAL(z_gdu->which, Z_GDU_Z3950); + BOOST_CHECK_EQUAL(z_gdu->u.z3950->which, Z_APDU_initResponse); + } + } + } + catch ( ... ) { + BOOST_CHECK (false); + } +} + +BOOST_AUTO_TEST_CASE( test_filter_z3950_client_3 ) +{ + try + { + { + yp2::RouterChain router; + + yp2::filter::Z3950Client zc; + + router.rule(zc); + + // Create package with Z39.50 present request in it + yp2::Package pack; + + ODR odr = odr_createmem(ODR_ENCODE); + Z_APDU *apdu = zget_APDU(odr, Z_APDU_presentRequest); + + BOOST_CHECK(apdu); + + pack.request() = apdu; + odr_destroy(odr); + + // Put it in router + pack.router(router).move(); + + // Inspect that we got Z39.50 close - a Z39.50 session must start + // with an init ! + yazpp_1::GDU *gdu = &pack.response(); + + BOOST_CHECK(pack.session().is_closed()); + + Z_GDU *z_gdu = gdu->get(); + BOOST_CHECK(z_gdu); + if (z_gdu) { + BOOST_CHECK_EQUAL(z_gdu->which, Z_GDU_Z3950); + BOOST_CHECK_EQUAL(z_gdu->u.z3950->which, Z_APDU_close); + } + } + } + catch ( ... ) { + BOOST_CHECK (false); + } +} + +BOOST_AUTO_TEST_CASE( test_filter_z3950_client_4 ) +{ + try + { + { + yp2::RouterChain router; + + yp2::filter::Z3950Client zc; + + router.rule(zc); + + // Create package with Z39.50 init request in it + yp2::Package pack; + + ODR odr = odr_createmem(ODR_ENCODE); + Z_APDU *apdu = zget_APDU(odr, Z_APDU_initRequest); + + const char *vhost = "localhost:9999"; + if (vhost) + yaz_oi_set_string_oidval(&apdu->u.initRequest->otherInfo, + odr, VAL_PROXY, 1, vhost); + + BOOST_CHECK(apdu); + + pack.request() = apdu; + odr_destroy(odr); + + // Put it in router + pack.router(router).move(); + + // Inspect that we got Z39.50 close - a Z39.50 session must start + // with an init ! + yazpp_1::GDU *gdu = &pack.response(); + + BOOST_CHECK(!pack.session().is_closed()); + + Z_GDU *z_gdu = gdu->get(); + BOOST_CHECK(z_gdu); + if (z_gdu) { + BOOST_CHECK_EQUAL(z_gdu->which, Z_GDU_Z3950); + BOOST_CHECK_EQUAL(z_gdu->u.z3950->which, Z_APDU_initResponse); + } + } + } + catch ( ... ) { + BOOST_CHECK (false); + } +} + + +/* + * Local variables: + * c-basic-offset: 4 + * indent-tabs-mode: nil + * c-file-style: "stroustrup" + * End: + * vim: shiftwidth=4 tabstop=8 expandtab + */ -- 1.7.10.4