Added Z3950 Client Filter, AKA Z39.50 backend.
authorAdam Dickmeiss <adam@indexdata.dk>
Sun, 16 Oct 2005 16:05:44 +0000 (16:05 +0000)
committerAdam Dickmeiss <adam@indexdata.dk>
Sun, 16 Oct 2005 16:05:44 +0000 (16:05 +0000)
src/.cvsignore
src/Makefile.am
src/ex_filter_frontend_net.cpp
src/filter.hpp
src/filter_log.cpp
src/filter_z3950_client.cpp [new file with mode: 0644]
src/filter_z3950_client.hpp [new file with mode: 0644]
src/test_filter_frontend_net.cpp
src/test_filter_z3950_client.cpp [new file with mode: 0644]

index 22baa74..fe76ca1 100644 (file)
@@ -18,3 +18,4 @@ test_package1
 test_thread_pool_observer
 test_session1
 test_session2
+test_filter_z3950_client
index 0e13dd7..71e71dc 100644 (file)
@@ -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:
index 76264f9..58d4a53 100644 (file)
@@ -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);
index faaefc6..45846f8 100644 (file)
@@ -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 {
index 742fd2b..a1f0ad7 100644 (file)
@@ -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 (file)
index 0000000..18d5c58
--- /dev/null
@@ -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 <boost/thread/mutex.hpp>
+
+#include "filter_z3950_client.hpp"
+
+#include <yaz/zgdu.h>
+#include <yaz/log.h>
+#include <yaz/otherinfo.h>
+#include <yaz/diagbib1.h>
+
+#include <yaz++/socket-manager.h>
+#include <yaz++/pdu-assoc.h>
+#include <yaz++/z-assoc.h>
+
+#include <iostream>
+
+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<Z3950Client::Assoc *> 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<yf::Z3950Client::Assoc *>::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 (file)
index 0000000..4a6f161
--- /dev/null
@@ -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 <stdexcept>
+#include <list>
+
+#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
+ */
index 7bdb01a..8af770d 100644 (file)
@@ -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 (file)
index 0000000..d8a691d
--- /dev/null
@@ -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 <iostream>
+#include <stdexcept>
+
+#include "filter_z3950_client.hpp"
+
+#include "router.hpp"
+#include "session.hpp"
+#include "package.hpp"
+
+#define BOOST_AUTO_TEST_MAIN
+#include <boost/test/auto_unit_test.hpp>
+
+#include <yaz/zgdu.h>
+#include <yaz/otherinfo.h>
+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
+ */