zoom: allow Torus record wrapped in single "record" elem
[metaproxy-moved-to-github.git] / src / filter_zoom.cpp
index 0f340a0..ae84b7b 100644 (file)
@@ -76,6 +76,7 @@ namespace metaproxy_1 {
             std::string urlRecipe;
             std::string contentConnector;
             std::string sortStrategy;
+            std::string rpn2cql_fname;
             bool use_turbomarc;
             bool piggyback;
             CCL_bibset ccl_bibset;
@@ -97,6 +98,8 @@ namespace metaproxy_1 {
             bool enable_cproxy;
             bool enable_explain;
             xmlDoc *explain_doc;
+            std::string m_proxy;
+            cql_transform_t cqlt;
         public:
             Backend();
             ~Backend();
@@ -126,6 +129,7 @@ namespace metaproxy_1 {
                                       int *error,
                                       char **addinfo,
                                       mp::odr &odr,
+                                      std::string torus_url,
                                       std::string &torus_db,
                                       std::string &realm);
             void handle_present(mp::Package &package);
@@ -134,8 +138,7 @@ namespace metaproxy_1 {
                                                   int *error,
                                                   char **addinfo,
                                                   mp::odr &odr,
-                                                  int *proxy_step,
-                                                  std::string &proxy);
+                                                  int *proxy_step);
 
             bool create_content_session(mp::Package &package,
                                         BackendPtr b,
@@ -172,7 +175,12 @@ namespace metaproxy_1 {
                                            ODR odr, BackendPtr b,
                                            Odr_oid *preferredRecordSyntax,
                                            const char *element_set_name);
-
+            bool retry(mp::Package &package,
+                       mp::odr &odr,
+                       BackendPtr b, 
+                       int &error, char **addinfo,
+                       int &proxy_step, int &same_retries,
+                       int &proxy_retries);
             void log_diagnostic(mp::Package &package,
                                 int error, const char *addinfo);
         public:
@@ -190,6 +198,9 @@ namespace metaproxy_1 {
         private:
             void configure_local_records(const xmlNode * ptr, bool test_only);
             bool check_proxy(const char *proxy);
+
+
+
             FrontendPtr get_frontend(mp::Package &package);
             void release_frontend(mp::Package &package);
             SearchablePtr parse_torus_record(const xmlNode *ptr);
@@ -284,6 +295,7 @@ yf::Zoom::Backend::~Backend()
         xsltFreeStylesheet(xsp);
     if (explain_doc)
         xmlFreeDoc(explain_doc);
+    cql_transform_close(cqlt);
     ZOOM_connection_destroy(m_connection);
     ZOOM_resultset_destroy(m_resultset);
 }
@@ -585,6 +597,8 @@ yf::Zoom::SearchablePtr yf::Zoom::Impl::parse_torus_record(const xmlNode *ptr)
         {
             s->sortStrategy = mp::xml::get_text(ptr);
         }
+        else if (!strcmp((const char *) ptr->name, "rpn2cql"))
+            s->rpn2cql_fname = mp::xml::get_text(ptr);
     }
     return s;
 }
@@ -977,15 +991,17 @@ bool yf::Zoom::Frontend::create_content_session(mp::Package &package,
 yf::Zoom::BackendPtr yf::Zoom::Frontend::get_backend_from_databases(
     mp::Package &package,
     std::string &database, int *error, char **addinfo, mp::odr &odr,
-    int *proxy_step, std::string &proxy)
+    int *proxy_step)
 {
-    proxy.clear();
+    bool connection_reuse = false;
+    std::string proxy;
+
     std::list<BackendPtr>::const_iterator map_it;
     if (m_backend && !m_backend->enable_explain && 
         m_backend->m_frontend_database == database)
     {
-        m_backend->connect("", error, addinfo, odr);
-        return m_backend;
+        connection_reuse = true;
+        proxy = m_backend->m_proxy;
     }
 
     std::string input_args;
@@ -1049,15 +1065,31 @@ yf::Zoom::BackendPtr yf::Zoom::Frontend::get_backend_from_databases(
             char **dstr;
             int dnum = 0;
             nmem_strsplit(((ODR) odr)->mem, ",", value, &dstr, &dnum);
-            if (*proxy_step >= dnum)
-                *proxy_step = 0;
+            if (connection_reuse)
+            {
+                // find the step after our current proxy
+                int i;
+                for (i = 0; i < dnum; i++)
+                    if (!strcmp(proxy.c_str(), dstr[i]))
+                        break;
+                if (i >= dnum - 1)
+                    *proxy_step = 0;
+                else
+                    *proxy_step = i + 1;
+            }
             else
             {
-                proxy = dstr[*proxy_step];
-                
-                (*proxy_step)++;
-                if (*proxy_step == dnum)
+                // step is known.. Guess our proxy from it
+                if (*proxy_step >= dnum)
                     *proxy_step = 0;
+                else
+                {
+                    proxy = dstr[*proxy_step];
+                    
+                    (*proxy_step)++;
+                    if (*proxy_step == dnum)
+                        *proxy_step = 0;
+                }
             }
         }
         else if (!strcmp(name, "cproxysession"))
@@ -1068,6 +1100,8 @@ yf::Zoom::BackendPtr yf::Zoom::Frontend::get_backend_from_databases(
         }
         else if (!strcmp(name, "realm"))
             realm = value;
+        else if (!strcmp(name, "torus_url"))
+            torus_url = value;
         else if (name[0] == 'x' && name[1] == '-')
         {
             out_names[no_out_args] = name;
@@ -1082,7 +1116,16 @@ yf::Zoom::BackendPtr yf::Zoom::Frontend::get_backend_from_databases(
             *addinfo = msg;
             return notfound;
         }
+    }    
+    if (proxy.length())
+        package.log("zoom", YLOG_LOG, "proxy: %s", proxy.c_str());
+
+    if (connection_reuse)
+    {
+        m_backend->connect("", error, addinfo, odr);
+        return m_backend;
     }
+
     if (param_user)
     {
         authentication = std::string(param_user);
@@ -1097,8 +1140,8 @@ yf::Zoom::BackendPtr yf::Zoom::Frontend::get_backend_from_databases(
     }
 
     if (torus_db.compare("IR-Explain---1") == 0)
-        return explain_search(package, database, error, addinfo, odr, torus_db,
-            realm);
+        return explain_search(package, database, error, addinfo, odr, torus_url,
+                              torus_db, realm);
     
     SearchablePtr sptr;
 
@@ -1114,33 +1157,51 @@ yf::Zoom::BackendPtr yf::Zoom::Frontend::get_backend_from_databases(
                                          realm, m_p->proxy);
         if (!doc)
         {
-            *error = YAZ_BIB1_DATABASE_DOES_NOT_EXIST;
-            *addinfo = odr_strdup(odr, torus_db.c_str());
+            *error = YAZ_BIB1_UNSPECIFIED_ERROR;
+            *addinfo = odr_strdup(odr, "Torus server unavailable or "
+                                  "incorrectly configured");
             BackendPtr b;
             return b;
         }
         const xmlNode *ptr = xmlDocGetRootElement(doc);
-        if (ptr)
-        {   // presumably ptr is a records element node
-            // parse first record in document
-            for (ptr = ptr->children; ptr; ptr = ptr->next)
+        if (ptr && ptr->type == XML_ELEMENT_NODE)
+        {
+            if (!strcmp((const char *) ptr->name, "record"))
             {
-                if (ptr->type == XML_ELEMENT_NODE
-                    && !strcmp((const char *) ptr->name, "record"))
+                sptr = m_p->parse_torus_record(ptr);
+            }
+            else if (!strcmp((const char *) ptr->name, "records"))
+            {
+                for (ptr = ptr->children; ptr; ptr = ptr->next)
                 {
-                    if (sptr)
+                    if (ptr->type == XML_ELEMENT_NODE
+                        && !strcmp((const char *) ptr->name, "record"))
                     {
-                        *error = YAZ_BIB1_UNSPECIFIED_ERROR;
-                        *addinfo = (char*) odr_malloc(odr, 40 + database.length()),
-                        sprintf(*addinfo, "multiple records for udb=%s",
-                                 database.c_str());
-                        xmlFreeDoc(doc);
-                        BackendPtr b;
-                        return b;
+                        if (sptr)
+                        {
+                            *error = YAZ_BIB1_UNSPECIFIED_ERROR;
+                            *addinfo = (char*)
+                                odr_malloc(odr, 40 + torus_db.length());
+                            sprintf(*addinfo, "multiple records for udb=%s",
+                                    database.c_str());
+                            xmlFreeDoc(doc);
+                            BackendPtr b;
+                            return b;
+                        }
+                        sptr = m_p->parse_torus_record(ptr);
                     }
-                    sptr = m_p->parse_torus_record(ptr);
                 }
             }
+            else
+            {
+                *error = YAZ_BIB1_UNSPECIFIED_ERROR;
+                *addinfo = (char*) odr_malloc(
+                    odr, 40 + strlen((const char *) ptr->name));
+                sprintf(*addinfo, "bad root element for torus: %s", ptr->name);
+                xmlFreeDoc(doc);
+                BackendPtr b;
+                return b;
+            }
         }
         xmlFreeDoc(doc);
     }
@@ -1221,10 +1282,32 @@ yf::Zoom::BackendPtr yf::Zoom::Frontend::get_backend_from_databases(
         }
     }
 
+    cql_transform_t cqlt = 0;
+    if (sptr->rpn2cql_fname.length())
+    {
+        char fullpath[1024];
+        char *cp = yaz_filepath_resolve(sptr->rpn2cql_fname.c_str(),
+                                        m_p->file_path.c_str(), 0, fullpath);
+        if (cp)
+            cqlt = cql_transform_open_fname(fullpath);
+    }
+    else
+        cqlt = cql_transform_create();
+
+    if (!cqlt)
+    {
+        *error = YAZ_BIB1_TEMPORARY_SYSTEM_ERROR;
+        *addinfo = odr_strdup(odr, "Missing/invalid cql2rpn file");
+        BackendPtr b;
+        xsltFreeStylesheet(xsp);
+        return b;
+    }
+
     m_backend.reset();
 
     BackendPtr b(new Backend);
 
+    b->cqlt = cqlt;
     b->sptr = sptr;
     b->xsp = xsp;
     b->m_frontend_database = database;
@@ -1248,6 +1331,7 @@ yf::Zoom::BackendPtr yf::Zoom::Frontend::get_backend_from_databases(
 
     if (proxy.length() == 0)
         proxy = sptr->cfProxy;
+    b->m_proxy = proxy;
     
     if (sptr->cfAuth.length())
     {
@@ -1304,9 +1388,6 @@ yf::Zoom::BackendPtr yf::Zoom::Frontend::get_backend_from_databases(
         if (proxy.length())
             b->set_option("proxy", proxy);
     }
-    if (proxy.length())
-        package.log("zoom", YLOG_LOG, "proxy: %s", proxy.c_str());
-                
     std::string url;
     if (sptr->sru.length())
     {
@@ -1410,7 +1491,7 @@ void yf::Zoom::Frontend::prepare_elements(BackendPtr b,
 }
 
 Z_Records *yf::Zoom::Frontend::get_explain_records(
-    Package &package,
+    mp::Package &package,
     Odr_int start,
     Odr_int number_to_present,
     int *error,
@@ -1469,7 +1550,7 @@ Z_Records *yf::Zoom::Frontend::get_explain_records(
 }
 
 
-Z_Records *yf::Zoom::Frontend::get_records(Package &package,
+Z_Records *yf::Zoom::Frontend::get_records(mp::Package &package,
                                            Odr_int start,
                                            Odr_int number_to_present,
                                            int *error,
@@ -1769,6 +1850,7 @@ yf::Zoom::BackendPtr yf::Zoom::Frontend::explain_search(mp::Package &package,
                                                         int *error,
                                                         char **addinfo,
                                                         mp::odr &odr,
+                                                        std::string torus_url,
                                                         std::string &torus_db,
                                                         std::string &realm)
 {
@@ -1795,7 +1877,6 @@ yf::Zoom::BackendPtr yf::Zoom::Frontend::explain_search(mp::Package &package,
     else if (query->which == Z_Query_type_104 &&
         query->u.type_104->which == Z_External_CQL)
     {
-        std::string torus_url = m_p->torus_searchable_url;
         std::string torus_query(query->u.type_104->u.cql);
         xmlDoc *doc = mp::get_searchable(package, torus_url, "",
                                          torus_query,
@@ -1810,8 +1891,8 @@ yf::Zoom::BackendPtr yf::Zoom::Frontend::explain_search(mp::Package &package,
         if (!doc)
         {
             *error = YAZ_BIB1_UNSPECIFIED_ERROR;
-            *addinfo = odr_strdup(odr, "IR-Explain---1 problem. " 
-                                  "Could not obtain Torus records for Explain");
+            *addinfo = odr_strdup(odr, "Torus server unavailable or "
+                                  "incorrectly configured");
         }
         else
         {
@@ -1845,7 +1926,7 @@ static bool wait_conn(COMSTACK cs, int secs)
     yaz_poll_add(pfd.input_mask, yaz_poll_except);
     if (cs->io_pending && CS_WANT_WRITE)
         yaz_poll_add(pfd.input_mask, yaz_poll_write);
-    else if (cs->io_pending & CS_WANT_READ)
+    if (cs->io_pending & CS_WANT_READ)
         yaz_poll_add(pfd.input_mask, yaz_poll_read);
 
     pfd.fd = cs_fileno(cs);
@@ -1886,12 +1967,47 @@ bool yf::Zoom::Impl::check_proxy(const char *proxy)
                 if (!wait_conn(conn, proxy_timeout))
                     break;
             }
+            if (ret == 0)
+                outcome = true;
         }
     }
     cs_close(conn);
     return outcome;
 }
 
+bool yf::Zoom::Frontend::retry(mp::Package &package,
+                               mp::odr &odr,
+                               BackendPtr b, 
+                               int &error, char **addinfo,
+                               int &proxy_step, int &same_retries,
+                               int &proxy_retries)
+{
+    if (b && b->m_proxy.length() && !m_p->check_proxy(b->m_proxy.c_str()))
+    {
+        log_diagnostic(package, error, *addinfo);
+        package.log("zoom", YLOG_LOG, "proxy %s fails", b->m_proxy.c_str());
+        m_backend.reset();
+        if (proxy_step) // there is a failover
+        {
+            proxy_retries++;
+            package.log("zoom", YLOG_WARN, "search failed: trying next proxy");
+            return true;
+        }
+        error = YAZ_BIB1_PROXY_FAILURE;
+        *addinfo = odr_strdup(odr, b->m_proxy.c_str());
+    }
+    else if (same_retries == 0 && proxy_retries == 0)
+    {
+        log_diagnostic(package, error, *addinfo);
+        same_retries++;
+        package.log("zoom", YLOG_WARN, "search failed: retry");
+        m_backend.reset();
+        proxy_step = 0;
+        return true;
+    }
+    return false;
+}
+
 void yf::Zoom::Frontend::handle_search(mp::Package &package)
 {
     Z_GDU *gdu = package.request().get();
@@ -1916,34 +2032,14 @@ next_proxy:
     int error = 0;
     char *addinfo = 0;
     std::string db(sr->databaseNames[0]);
-    std::string proxy;
 
     BackendPtr b = get_backend_from_databases(package, db, &error,
-                                              &addinfo, odr, &proxy_step,
-                                              proxy);
-
+                                              &addinfo, odr, &proxy_step);
     if (error)
     {
-        if (proxy.length() && !m_p->check_proxy(proxy.c_str()))
-        {
-            if (proxy_step) // there is a failover
-            {
-                proxy_retries++;
-                package.log("zoom", YLOG_WARN, "search failed: trying next proxy");
-                m_backend.reset();
-                goto next_proxy;
-            }
-            error = YAZ_BIB1_INIT_AC_AUTHENTICATION_SYSTEM_ERROR;
-            addinfo = odr_strdup(odr, "proxy failure");
-        }
-        else if (same_retries == 0 && proxy_retries == 0)
-        {
-            same_retries++;
-            package.log("zoom", YLOG_WARN, "search failed: trying first proxy");
-            m_backend.reset();
-            proxy_step = 0;
+        if (retry(package, odr, b, error, &addinfo, proxy_step,
+                  same_retries, proxy_retries))
             goto next_proxy;
-        }
     }
     if (error)
     {
@@ -2019,8 +2115,7 @@ next_proxy:
             cql_parser_destroy(cp);
             return;
         }
-        char ccl_buf[1024];
-        r = cql_to_ccl_buf(cn, ccl_buf, sizeof(ccl_buf));
+        r = cql_to_ccl(cn, wrbuf_vp_puts,  ccl_wrbuf);
         if (r)
         {
             error = YAZ_BIB1_MALFORMED_QUERY;
@@ -2048,8 +2143,6 @@ next_proxy:
         mp::wrbuf sort_spec_wrbuf;
         yaz_srw_sortkeys_to_sort_spec(wrbuf_cstr(sru_sortkeys_wrbuf),
                                       sort_spec_wrbuf);
-        wrbuf_puts(ccl_wrbuf, ccl_buf);
-        
         yaz_tok_cfg_t tc = yaz_tok_cfg_create();
         yaz_tok_parse_t tp =
             yaz_tok_parse_buf(tc, wrbuf_cstr(sort_spec_wrbuf));
@@ -2155,11 +2248,7 @@ next_proxy:
         }
         else
         {
-            cql_transform_t cqlt = cql_transform_create();
-            
-            status = cql_transform_rpn2cql_wrbuf(cqlt, wrb, zquery);
-            
-            cql_transform_close(cqlt);
+            status = cql_transform_rpn2cql_wrbuf(b->cqlt, wrb, zquery);
         }
         if (status == 0)
         {
@@ -2189,26 +2278,9 @@ next_proxy:
 
     if (error)
     {
-        if (proxy.length() && !m_p->check_proxy(proxy.c_str()))
-        {
-            if (proxy_step) // there is a failover
-            {
-                proxy_retries++;
-                package.log("zoom", YLOG_WARN, "search failed: trying next proxy");
-                m_backend.reset();
-                goto next_proxy;
-            }
-            error = YAZ_BIB1_INIT_AC_AUTHENTICATION_SYSTEM_ERROR;
-            addinfo = odr_strdup(odr, "proxy failure");
-        }
-        else if (same_retries == 0 && proxy_retries == 0)
-        {
-            same_retries++;
-            package.log("zoom", YLOG_WARN, "search failed: trying first proxy");
-            m_backend.reset();
-            proxy_step = 0;
+        if (retry(package, odr, b, error, &addinfo, proxy_step,
+                  same_retries, proxy_retries))
             goto next_proxy;
-        }
     }
 
     const char *element_set_name = 0;