Implement search limit (<limit><search>n</search></limit>).
authorAdam Dickmeiss <adam@indexdata.dk>
Thu, 6 Apr 2006 01:16:54 +0000 (01:16 +0000)
committerAdam Dickmeiss <adam@indexdata.dk>
Thu, 6 Apr 2006 01:16:54 +0000 (01:16 +0000)
Implement connect limit -- which results in delay. This complements
the connect max -- which blocks out a client completely.
Clean up file access using docpath configuration..
Documentation updates for these.

doc/installation.xml
doc/reference.xml
etc/config.xml
etc/voyager.xml
include/yazproxy/proxy.h
src/proxyp.h
src/yaz-proxy-config.cpp
src/yaz-proxy.cpp

index 9cc450d..96ced05 100644 (file)
@@ -1,5 +1,5 @@
 <chapter id="installation">
-  <!-- $Id: installation.xml,v 1.8 2005-02-07 09:50:23 adam Exp $ -->
+  <!-- $Id: installation.xml,v 1.9 2006-04-06 01:16:54 adam Exp $ -->
   <title>Installation</title>
   <para>
    You need a C++ compiler to compile and use YAZ proxy.
      <varlistentry>
       <term><literal>--with-yazpp </literal>directory</term>
       <listitem><para>
-        Specifies the location of <filename>yaz++-config</filename>.
-        The <filename>yaz++-config</filename> program is generated in
+        Specifies the location of <filename>yazpp-config</filename>.
+        The <filename>yazpp-config</filename> program is generated in
         the source directory of YAZ++ as well as the binaries
         directory when YAZ++ is installed (via make install).
         </para>
        <para>
         If you don't supply this option, <literal>configure</literal> will
-        look for <filename>yaz++-config</filename> in directories of the
+        look for <filename>yazpp-config</filename> in directories of the
         <envar>PATH</envar> environment - which is nearly always
         what you want.
        </para></listitem>
index 3e52ecb..28cd49f 100644 (file)
     <para>
      <screen>
      &lt;?xml version="1.0"?>
-     &lt;proxy xmlns="http://indexdata.dk/yazproxy/schema/0.8/">
+     &lt;proxy xmlns="http://indexdata.dk/yazproxy/schema/0.9/">
       &lt;target name="server1" default="1">
        &lt;!-- description of server1 .. -->
       &lt;/target>
      The proxy records bandwidth/pdu requests during the last 60 seconds
      (1 minute). The <literal>limit</literal> may include the
      elements <literal>bandwidth</literal>, <literal>pdu</literal>,
-     and <literal>retrieve</literal>. The <literal>bandwidth</literal>
+     <literal>retrieve</literal> and <literal>search</literal>.
+     The <literal>bandwidth</literal>
      measures the number of bytes transferred within the last minute.
      The <literal>pdu</literal> is the number of requests in the last
      minute. The <literal>retrieve</literal> holds the maximum records to
-     be retrieved in one Present Request.
+     which may be retrieved in one Present Request.
+     The <literal>search</literal> is the maximum number of searches
+     within the last minute.
     </para>
     <para>
-     If a bandwidth/pdu limit is reached the proxy will postpone the
+     If a bandwidth/pdu/search limit is reached the proxy will postpone the
      requests to the target and wait one or more seconds. The idea of the
      limit is to ensure that clients that downloads hundreds or thousands of
      records do not hurt other users.
      </screen>
     </para>
    </section>
+
+   <section id="proxy-max-connect">
+    <title>max-connect</title>
+    <para>
+     The element <literal>max-connect</literal> is a child of element
+     <literal>proxy</literal> and specifies the maximum number
+     of connections to be initiated within the last minute.
+    </para>
+    <para>
+     If the maximum number is reached the proxy will terminate the
+     just initiated session (connection terminated).
+    </para>
+   </section>
+
+   <section id="proxy-limit-connect">
+    <title>limit-connect</title>
+    <para>
+     The element <literal>max-connect</literal> is a child of element
+     <literal>proxy</literal> and specifies the limit of number
+     of connections to be initiated within the last minute.
+    </para>
+    <para>
+     If the maximum number is reached the proxy delay the first operatation
+     in the session (Thus delaying the connection).
+    </para>
+   </section>
+
+   <section id="proxy-docpath">
+    <title>docpath</title>
+    <para>
+     The element <literal>docpath</literal> is a child of element
+     <literal>proxy</literal> and specifies an allowed HTTP path
+     for local file access. Using <literal>docpath</literal> the
+     proxy may return static file content.
+    </para>
+    <para>
+     The value of docpath both serves as a HTTP path prefix 
+     <emphasis>and</emphasis> as a local file prefix. 
+     If a value of <literal>etc</literal> is used only URLs with the
+     prefix <literal>/etc/</literal> results in a local file access to the
+     directory <literal>etc</literal> within the working directory
+     of yazproxy.
+    </para>
+    <note>
+    <para>
+      Care has been taken to ensure that hostile URLs are rejected - including
+      strings such as <literal>..</literal> and <literal>/</literal> (absolute
+      file system access).
+     </para>
+    </note>
+   </section>
+
   </section>
   <section id="proxy-usage">
    <title>Proxy Manual Pages</title>
    <screen><![CDATA[
 <?xml version="1.0"?>
 <!-- XML Schema for YAZ proxy config file.
-    $Id: reference.xml,v 1.14 2006-03-29 18:09:01 adam Exp $
+    $Id: reference.xml,v 1.15 2006-04-06 01:16:54 adam Exp $
 -->
 <xs:schema
   xmlns:xs="http://www.w3.org/2001/XMLSchema"
index 0379c13..a709758 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!-- $Id: config.xml,v 1.16 2006-03-30 10:35:14 adam Exp $ -->
+<!-- $Id: config.xml,v 1.17 2006-04-06 01:16:54 adam Exp $ -->
 <proxy xmlns="http://indexdata.dk/yazproxy/schema/0.9/"
  xmlns:xi="http://www.w3.org/2001/XInclude"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -19,7 +19,7 @@
       <bandwidth>2000000</bandwidth>
       <pdu>60</pdu>
       <retrieve>100</retrieve>
-      <connect>3</connect>
+      <search>3</search>
     </limit>
     <attribute type="1" value="1-11,13-1016"/>
     <attribute type="1" value="*" error="114"/>
@@ -47,7 +47,9 @@
     <client-timeout>30</client-timeout>
     <!-- everything else -->
   </target>
-  <max-clients>2</max-clients>
-  <max-connect>3</max-connect>
+  <max-clients>30</max-clients>
+  <max-connect>10</max-connect>
+  <limit-connect>5</limit-connect>
   <log>client-requests server-requests</log>
+  <docpath>doc</docpath>
 </proxy>
index c28da9e..ce55a62 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<!-- $Id: voyager.xml,v 1.6 2005-06-10 17:54:10 adam Exp $ -->
+<!-- $Id: voyager.xml,v 1.7 2006-04-06 01:16:54 adam Exp $ -->
 <proxy xmlns="http://indexdata.dk/yazproxy/schema/0.9/"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://indexdata.dk/yazproxy/schema/0.9/ yazproxy.xsd"
@@ -28,6 +28,7 @@
       <bandwidth>200000</bandwidth>
       <pdu>31</pdu>
       <retrieve>50</retrieve>
+      <search>15</search>
     </limit>
 
     <!-- use attributes -->
   <!-- what we log. Allowed tokens: client-apdu, server-apdu,
   client-requests, server-requests -->
   <log>client-requests server-requests</log>
-  <xlog></xlog>
+  <!-- <xlog></xlog> -->
+  <max-connect>10</max-connect>
+  <limit-connect>5</limit-connect>
 </proxy>
index fe0f2df..d50b28a 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: proxy.h,v 1.29 2006-03-30 10:35:15 adam Exp $
+/* $Id: proxy.h,v 1.30 2006-04-06 01:16:55 adam Exp $
    Copyright (c) 1998-2006, Index Data.
 
 This file is part of the yazproxy.
@@ -106,12 +106,16 @@ class YAZ_EXPORT Yaz_Proxy : public yazpp_1::Z_Assoc {
         timeout_xsl
     } m_timeout_mode;
 
+    int m_initial_reduce;
     int m_connect_max;
+    int m_limit_connect;
     int m_search_max;
     Yaz_bw m_bw_stat;
     int m_pdu_max;
     Yaz_bw m_pdu_stat;
     int m_max_record_retrieve;
+    Yaz_bw m_search_stat;
+
     void handle_max_record_retrieve(Z_APDU *apdu);
     void display_diagrecs(Z_DiagRec **pp, int num);
     Z_Records *create_nonSurrogateDiagnostics(ODR o, int error,
index 1e62cca..c412566 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: proxyp.h,v 1.14 2006-03-30 10:35:15 adam Exp $
+/* $Id: proxyp.h,v 1.15 2006-04-06 01:16:55 adam Exp $
    Copyright (c) 1998-2006, Index Data.
 
 This file is part of the yazproxy.
@@ -105,7 +105,9 @@ public:
                       const char **default_client_query_charset);
 
     void get_generic_info(int *log_mask, int *max_clients,
-                          int *max_connect);
+                          int *max_connect, int *limit_connect);
+
+    int get_file_access_info(const char *path);
 
     void get_target_info(const char *name, const char **url,
                          int *limit_bw, int *limit_pdu, int *limit_req,
index 219bf9a..c4c29f8 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: yaz-proxy-config.cpp,v 1.26 2006-03-30 10:35:15 adam Exp $
+/* $Id: yaz-proxy-config.cpp,v 1.27 2006-04-06 01:16:55 adam Exp $
    Copyright (c) 1998-2006, Index Data.
 
 This file is part of the yazproxy.
@@ -981,11 +981,35 @@ int Yaz_ProxyConfigP::mycmp(const char *hay, const char *item, size_t len)
     return 0;
 }
 
+int Yaz_ProxyConfig::get_file_access_info(const char *path)
+{
+#if HAVE_XSLT
+    xmlNodePtr ptr;
+    if (!m_cp->m_proxyPtr)
+        return 0;
+    for (ptr = m_cp->m_proxyPtr->children; ptr; ptr = ptr->next)
+    {
+        if (ptr->type == XML_ELEMENT_NODE
+            && !strcmp((const char *) ptr->name, "docpath"))
+        {
+            const char *docpath = m_cp->get_text(ptr);
+            size_t docpath_len = strlen(docpath);
+            if (docpath_len < strlen(path) && path[docpath_len] == '/'
+                && !memcmp(docpath, path, docpath_len))
+                return 1;
+        }
+    }
+#endif
+    return 0;
+}
+
 void Yaz_ProxyConfig::get_generic_info(int *log_mask,
                                        int *max_clients,
-                                       int *max_connect)
+                                       int *max_connect,
+                                       int *limit_connect)
 {
     *max_connect = 0;
+    *limit_connect = 0;
 #if HAVE_XSLT
     xmlNodePtr ptr;
     if (!m_cp->m_proxyPtr)
@@ -1020,7 +1044,7 @@ void Yaz_ProxyConfig::get_generic_info(int *log_mask,
                 v = cp;
             }
         }
-        if (ptr->type == XML_ELEMENT_NODE &&
+        else if (ptr->type == XML_ELEMENT_NODE &&
             !strcmp((const char *) ptr->name, "max-clients"))
         {
             const char *t = m_cp->get_text(ptr);
@@ -1031,13 +1055,31 @@ void Yaz_ProxyConfig::get_generic_info(int *log_mask,
                     *max_clients = 1;
             }
         }
-        if (ptr->type == XML_ELEMENT_NODE &&
+        else if (ptr->type == XML_ELEMENT_NODE &&
             !strcmp((const char *) ptr->name, "max-connect"))
         {
             const char *t = m_cp->get_text(ptr);
             if (t)
                 *max_connect = atoi(t);
         }
+        else if (ptr->type == XML_ELEMENT_NODE &&
+            !strcmp((const char *) ptr->name, "limit-connect"))
+        {
+            const char *t = m_cp->get_text(ptr);
+            if (t)
+                *limit_connect = atoi(t);
+        }
+        else if (ptr->type == XML_ELEMENT_NODE &&
+            !strcmp((const char *) ptr->name, "target"))
+            ;
+        else if (ptr->type == XML_ELEMENT_NODE &&
+            !strcmp((const char *) ptr->name, "docpath"))
+            ;
+        else if (ptr->type == XML_ELEMENT_NODE)
+        {
+            yaz_log(YLOG_WARN, "0 Unknown element %s in yazproxy config",
+                    ptr->name);
+        }
     }
 #endif
 }
index 11112ed..e2195ba 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: yaz-proxy.cpp,v 1.48 2006-03-30 14:22:06 adam Exp $
+/* $Id: yaz-proxy.cpp,v 1.49 2006-04-06 01:16:55 adam Exp $
    Copyright (c) 1998-2006, Index Data.
 
 This file is part of the yazproxy.
@@ -202,7 +202,8 @@ Yaz_Proxy::Yaz_Proxy(IPDU_Observable *the_PDU_Observable,
                      ISocketObservable *the_socket_observable,
                      Yaz_Proxy *parent)
     :
-    Z_Assoc(the_PDU_Observable), m_bw_stat(60), m_pdu_stat(60)
+    Z_Assoc(the_PDU_Observable),
+    m_bw_stat(60), m_pdu_stat(60), m_search_stat(60)
 {
     m_PDU_Observable = the_PDU_Observable;
     m_socket_observable = the_socket_observable;
@@ -232,6 +233,7 @@ Yaz_Proxy::Yaz_Proxy(IPDU_Observable *the_PDU_Observable,
     m_pdu_max = 0;
     m_search_max = 0;
     m_connect_max = 0;
+    m_limit_connect = 0;
     m_timeout_mode = timeout_normal;
     m_timeout_gdu = 0;
     m_max_record_retrieve = 0;
@@ -291,6 +293,7 @@ Yaz_Proxy::Yaz_Proxy(IPDU_Observable *the_PDU_Observable,
     m_ref_count = 1;
     m_main_ptr_dec = false;
     m_peername = 0;
+    m_initial_reduce = 0;
 }
 
 void Yaz_Proxy::inc_ref()
@@ -352,7 +355,7 @@ int Yaz_Proxy::set_config(const char *config)
     int r = m_config->read_xml(config);
     if (!r)
         m_config->get_generic_info(&m_log_mask, &m_max_clients,
-                                   &m_connect_max);
+                                   &m_connect_max, &m_limit_connect);
     return r;
 }
 
@@ -404,7 +407,7 @@ Yaz_ProxyConfig *Yaz_Proxy::check_reconfigure()
             {
                 m_log_mask = 0;
                 cfg->get_generic_info(&m_log_mask, &m_max_clients,
-                                      &m_connect_max);
+                                      &m_connect_max, &m_limit_connect);
             }
         }
         else
@@ -442,6 +445,10 @@ IPDU_Observer *Yaz_Proxy::sessionNotify(IPDU_Observable
     
     Yaz_Proxy *new_proxy = new Yaz_Proxy(the_PDU_Observable,
                                          m_socket_observable, this);
+
+    if (m_limit_connect)
+        new_proxy->m_initial_reduce = connect_total / m_limit_connect;
+
     new_proxy->m_config = 0;
     new_proxy->m_config_fname = 0;
     new_proxy->timeout(m_client_idletime);
@@ -987,7 +994,6 @@ void Yaz_Proxy::convert_to_frontend_type(Z_NamePlusRecordList *p)
 void Yaz_Proxy::convert_records_charset(Z_NamePlusRecordList *p,
                                         const char *backend_charset)
 {
-    yaz_log(YLOG_LOG, "%sconvert_to_marc", m_session_str);
     int sel =   m_charset_converter->get_client_charset_selected();
     const char *client_record_charset =
         m_charset_converter->get_client_query_charset();
@@ -1046,10 +1052,6 @@ void Yaz_Proxy::convert_records_charset(Z_NamePlusRecordList *p,
             yaz_iconv_close(cd);
         yaz_marc_destroy(mt);
     }
-    else
-    {
-        yaz_log(YLOG_LOG, "%sSkipping marc convert", m_session_str);
-    }
 }
 
 void Yaz_Proxy::convert_to_marcxml(Z_NamePlusRecordList *p,
@@ -1857,21 +1859,20 @@ void Yaz_Proxy::recv_GDU(Z_GDU *apdu, int len)
 
 void Yaz_Proxy::recv_GDU_reduce(GDU *gdu)
 {
-    int reduce = 0;
+    int reduce = m_initial_reduce; // initial reduce from connect phase..
+    m_initial_reduce = 0;  // reset it..
 
     int bw_total = m_bw_stat.get_total();
     int pdu_total = m_pdu_stat.get_total();
+    int search_total = m_search_stat.get_total();
 
     assert(m_timeout_mode == timeout_busy);
     assert(m_timeout_gdu == 0);
 
+    if (m_search_max)
+        reduce += search_total / m_search_max;
     if (m_bw_max)
-    {
-        if (bw_total > m_bw_max)
-        {
-            reduce = (bw_total/m_bw_max);
-        }
-    }
+        reduce += (bw_total/m_bw_max);
     if (m_pdu_max)
     {
         if (pdu_total > m_pdu_max)
@@ -1891,9 +1892,9 @@ void Yaz_Proxy::recv_GDU_reduce(GDU *gdu)
 #endif
     if (reduce)
     {
-        yaz_log(YLOG_LOG, "%sdelay=%d bw=%d pdu=%d limit-bw=%d limit-pdu=%d",
-                m_session_str, reduce, bw_total, pdu_total,
-                m_bw_max, m_pdu_max);
+        yaz_log(YLOG_LOG, "%sdelay=%d bw=%d pdu=%d search=%d limit-bw=%d limit-pdu=%d limit-search=%d",
+                m_session_str, reduce, bw_total, pdu_total, search_total,
+                m_bw_max, m_pdu_max, m_search_max);
 
         m_timeout_mode = timeout_reduce;
         m_timeout_gdu = gdu;
@@ -2473,39 +2474,45 @@ int Yaz_Proxy::file_access(Z_HTTP_Request *hreq)
     if (strcmp(hreq->method, "GET"))
         return 0;
     if (hreq->path[0] != '/')
-    {
-        yaz_log(YLOG_WARN, "Bad path: %s", hreq->path);
         return 0;
-    }
     const char *cp = hreq->path;
     while (*cp)
     {
         if (*cp == '/' && strchr("/.", cp[1]))
         {
-            yaz_log(YLOG_WARN, "Bad path: %s", hreq->path);
+            yaz_log(YLOG_LOG, "%sRejecting path %s", m_session_str,
+                    hreq->path);
             return 0;
         }
         cp++;
     }
+
+    Yaz_ProxyConfig *cfg = check_reconfigure();
+
+    if (!cfg->get_file_access_info(hreq->path+1))
+        return 0;
+
     const char *fname = hreq->path+1;
     if (stat(fname, &sbuf))
     {
-        yaz_log(YLOG_WARN|YLOG_ERRNO, "%s: stat failed", fname);
+        yaz_log(YLOG_LOG|YLOG_ERRNO, "%sstat failed for %s", m_session_str,
+                fname);
         return 0;
     }
     if ((sbuf.st_mode & S_IFMT) != S_IFREG)
     {
-        yaz_log(YLOG_WARN, "%s: not a regular file", fname);
+        yaz_log(YLOG_LOG, "%sNot a regular file %s", m_session_str, fname);
         return 0;
     }
     if (sbuf.st_size > (off_t) 1000000)
     {
-        yaz_log(YLOG_WARN, "%s: too large for transfer", fname);
+        yaz_log(YLOG_WARN, "%sFile %s too large for transfer", m_session_str,
+                fname);
         return 0;
     }
 
     ODR o = odr_encode();
-    Yaz_ProxyConfig *cfg = check_reconfigure();
+
     const char *ctype = cfg->check_mime_type(fname);
     Z_GDU *gdu = z_get_HTTP_Response(o, 200);
     Z_HTTP_Response *hres = gdu->u.HTTP_Response;
@@ -2623,6 +2630,7 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
 
         if (srw_pdu->which == Z_SRW_searchRetrieve_request)
         {
+
             Z_SRW_searchRetrieveRequest *srw_req = srw_pdu->u.request;
 
             const char *backend_db = srw_req->database;
@@ -3033,6 +3041,9 @@ void Yaz_Proxy::handle_incoming_Z_PDU(Z_APDU *apdu)
         apdu = m_initRequest_apdu;     // but throw an init to the target
     }
 
+    if (apdu->which == Z_APDU_searchRequest)
+        m_search_stat.add_bytes(1);
+
     // Determine our client.
     Z_OtherInformation **oi;
     get_otherInfoAPDU(apdu, &oi);