Move yaz-proxy to separate sub directory.
authorAdam Dickmeiss <adam@indexdata.dk>
Mon, 29 Mar 2004 22:46:50 +0000 (22:46 +0000)
committerAdam Dickmeiss <adam@indexdata.dk>
Mon, 29 Mar 2004 22:46:50 +0000 (22:46 +0000)
Change license for proxy to GPL.
Add packages libyazproxy, libyazproxy-dev, yazproxy.

30 files changed:
LICENSE
LICENSE.proxy [new file with mode: 0644]
Makefile.am
NEWS
README
configure.in
debian/changelog
debian/control
debian/libyaz++-dev.install
debian/libyaz++.install
debian/rules
debian/yaz++.install
include/yaz++/Makefile.am
include/yaz++/cql2rpn.h [new file with mode: 0644]
include/yaz++/proxy.h [deleted file]
include/yaz++/proxy/Makefile.am [new file with mode: 0644]
include/yaz++/proxy/bw.h [new file with mode: 0644]
include/yaz++/proxy/proxy.h [new file with mode: 0644]
include/yaz++/record-cache.h [new file with mode: 0644]
proxy/Makefile.am [new file with mode: 0644]
proxy/yaz-bw.cpp [new file with mode: 0644]
proxy/yaz-proxy-config.cpp [new file with mode: 0644]
proxy/yaz-proxy.cpp [new file with mode: 0644]
src/Makefile.am
src/yaz-cql2rpn.cpp
src/yaz-proxy-config.cpp [deleted file]
src/yaz-proxy-main.cpp [deleted file]
src/yaz-proxy.cpp [deleted file]
src/yaz-z-cache.cpp
yaz++-config.in

diff --git a/LICENSE b/LICENSE
index edb61b4..4a33351 100644 (file)
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 1999-2004, Index Data and Mike Taylor.
+Copyright (c) 1998-2004, Index Data and Mike Taylor.
 
 Permission to use, copy, modify, distribute, and sell this software and
 its documentation, in whole or in part, for any purpose, is hereby granted,
diff --git a/LICENSE.proxy b/LICENSE.proxy
new file mode 100644 (file)
index 0000000..5a965fb
--- /dev/null
@@ -0,0 +1,280 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
index 130a1b7..4603278 100644 (file)
@@ -1,11 +1,11 @@
 AUTOMAKE_OPTIONS = foreign
 
-SUBDIRS = src include zlint zoom etc doc 
+SUBDIRS = src include proxy zlint zoom etc doc 
 
 aclocaldir=$(datadir)/aclocal
 aclocal_DATA = yazpp.m4
 
-EXTRA_DIST= LICENSE TODO yaz++-config.in yazpp.m4
+EXTRA_DIST= LICENSE LICENSE.proxy TODO yaz++-config.in yazpp.m4
 
 dist-hook:
        cp -R $(srcdir)/win $(distdir)
diff --git a/NEWS b/NEWS
index 7564a76..7b3e89c 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,7 @@
+--- 0.7.13 2004/03/30 Internal release.
+
+Move proxy to separate sub directory and change license to GPL.
+
 --- 0.7.12 2004/03/17 Internal release.
 
 Force libXSLT to reports errors to log file (similar to libXML2).
diff --git a/README b/README
index c4124fd..94fb285 100644 (file)
--- a/README
+++ b/README
@@ -1,6 +1,6 @@
 YAZ++ - A C++ library for YAZ
 
-$Id: README,v 1.1 2004-03-29 20:00:07 adam Exp $
+$Id: README,v 1.2 2004-03-29 22:46:50 adam Exp $
  
 
 Introduction
@@ -30,9 +30,11 @@ example applications:
 
 Directory structure of the YAZ++ package:
 
-  -- src (C++ library and proxy source)
+  -- src (C++ library)
   -- zoom (C++ source for ZOOM)
+  -- proxy (C++ source for proxy)
   -- include/yaz++ (C++ headers) 
+  -- include/yaz++/proxy (C++ headers for proxy) 
   -- lib (compiled libraries)
   -- win (Windows build files)
   -- doc (DocBook-format documentation)
index 9bb17d8..f02c615 100644 (file)
@@ -1,5 +1,5 @@
 AC_INIT(configure.in)
-AM_INIT_AUTOMAKE("yaz++",0.7.12)
+AM_INIT_AUTOMAKE("yaz++",0.7.13)
 
 AC_PROG_CC
 AC_PROG_CPP
@@ -28,7 +28,6 @@ if test "$xsltdir" != "no"; then
        AC_MSG_CHECKING(for libXSLT)
        if test -x $xsltdir/bin/xslt-config; then
                XSLT_LIBS=`$xsltdir/bin/xslt-config --libs`
-               LIBS="$XSLT_LIBS $LIBS"
                XSLT_CFLAGS=`$xsltdir/bin/xslt-config --cflags`
                XSLT_VER=`$xsltdir/bin/xslt-config --version`
                AC_MSG_RESULT($XSLT_VER)
@@ -101,6 +100,8 @@ AC_OUTPUT([
        src/Makefile
        include/Makefile
        include/yaz++/Makefile
+       include/yaz++/proxy/Makefile
+       proxy/Makefile
        zlint/Makefile
        yaz++-config
        zoom/Makefile
index 2f45828..a6e296a 100644 (file)
@@ -1,3 +1,9 @@
+yaz++ (0.7.13-1) unstable; urgency=low
+
+  * Upstream.
+
+ -- Adam Dickmeiss <adam@indexdata.dk>  Mon, 29 Mar 2004 23:39:01 +0200
+
 yaz++ (0.7.9-1) unstable; urgency=low
 
   * Upstream.
index b38fa9d..f6190d9 100644 (file)
@@ -15,7 +15,7 @@ Description: YAZ++ development library for Z39.50 applications in C++.
 Package: libyaz++-dev
 Section: devel
 Architecture: any
-Depends: libyaz++ (= ${Source-Version}), libxml2-dev, libxslt-dev, libyaz-dev
+Depends: libyaz++ (= ${Source-Version}), libyaz-dev
 Description: YAZ++ library for Z39.50 applications in C++.
  YAZ++ is a C++ library with an object oriented interface to YAZ and ZOOM.
 
@@ -31,3 +31,24 @@ Section: doc
 Architecture: all
 Description: Documentation for YAZ++.
  YAZ++ is a C++ library with an object oriented interface to YAZ and ZOOM.
+
+Package: libyazproxy
+Section: libs
+Architecture: any
+Depends: ${shlibs:Depends}, libyaz++ (= ${Source-Version})
+Description: YAZ Proxy.
+ Libraries for YAZ proxy.
+
+Package: libyazproxy-dev
+Section: devel
+Architecture: any
+Depends: libyazproxy (= ${Source-Version}), libyaz++-dev (= ${Source-Version}), libxml2-dev, libxslt-dev
+Description: YAZ Proxy.
+ Development libraries for YAZ proxy.
+
+Package: yazproxy
+Section: utils
+Architecture: any
+Depends: libyazproxy (= ${Source-Version})
+Description: YAZ Proxy.
+ Powerful Proxy that converts Z39.50/SRW/SRU to Z39.50.
index 60c4ea0..2b94fd7 100644 (file)
@@ -1,6 +1,9 @@
-debian/tmp/usr/include/yaz++/*
-debian/tmp/usr/lib/lib*.a
-debian/tmp/usr/lib/lib*.so
-debian/tmp/usr/lib/lib*.la
+debian/tmp/usr/include/yaz++/*.h
+debian/tmp/usr/lib/libyazcpp*.a
+debian/tmp/usr/lib/libzoomcpp*.a
+debian/tmp/usr/lib/libyazcpp.so
+debian/tmp/usr/lib/libzoomcpp.so
+debian/tmp/usr/lib/libyazcpp.la
+debian/tmp/usr/lib/libzoomcpp.la
 debian/tmp/usr/bin/yaz++-config
 debian/tmp/usr/share/aclocal/yazpp.m4
index c45ebcf..d15a942 100644 (file)
@@ -1 +1,2 @@
-debian/tmp/usr/lib/lib*.so.*
+debian/tmp/usr/lib/libyazcpp*.so.*
+debian/tmp/usr/lib/libzoomcpp*.so.*
index be136fe..3c1ecf2 100755 (executable)
@@ -84,8 +84,8 @@ binary-arch: build install
        dh_testdir
        dh_testroot
        cp LICENSE debian/copyright
-       dh_installdocs -A README.txt
-       dh_installchangelogs ChangeLog
+       dh_installdocs -A README LICENSE.proxy
+       dh_installchangelogs NEWS
        dh_installdocs
        dh_installexamples
        mv debian/tmp/usr/share/doc/yaz++ debian/tmp/usr/share/doc/yaz++-doc
@@ -106,9 +106,9 @@ binary-arch: build install
        dh_fixperms
 #      dh_perl
 #      dh_python
-       dh_makeshlibs -V 'libyaz++ (>= 0.7.8)'
+       dh_makeshlibs -V 'libyaz++ (>= 0.7.13)'
        dh_installdeb
-       dh_shlibdeps -l debian/libyaz++/usr/lib
+       dh_shlibdeps -L libyaz++ -l debian/libyaz++/usr/lib -L libyazproxy -l debian/libyazproxy/usr/lib
        dh_gencontrol
        dh_md5sums
        dh_builddeb
index 725035c..e69de29 100644 (file)
@@ -1,3 +0,0 @@
-debian/tmp/usr/bin/yaz-proxy
-debian/tmp/usr/bin/zlint
-debian/tmp/usr/share/man/man8/yaz-proxy.8
index 515b5d7..f3e9884 100644 (file)
@@ -1,13 +1,16 @@
 
+SUBDIRS = proxy
+
 pkginclude_HEADERS = \
        ir-assoc.h \
        pdu-assoc.h \
        pdu-observer.h \
-       proxy.h \
        query.h \
        socket-manager.h \
        socket-observer.h \
        z-assoc.h \
        z-query.h \
        z-server.h \
-       z-databases.h
+       z-databases.h \
+       record-cache.h \
+       cql2rpn.h
diff --git a/include/yaz++/cql2rpn.h b/include/yaz++/cql2rpn.h
new file mode 100644 (file)
index 0000000..be277a8
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 1998-2004, Index Data.
+ * See the file LICENSE for details.
+ * 
+ * $Id: cql2rpn.h,v 1.1 2004-03-29 22:46:50 adam Exp $
+ */
+
+#include <yaz/cql.h>
+#include <yaz/z-core.h>
+
+class YAZ_EXPORT Yaz_cql2rpn {
+ public:
+    Yaz_cql2rpn();
+    ~Yaz_cql2rpn();
+    void set_pqf_file(const char *fname);
+    int query_transform(const char *cql, Z_RPNQuery **rpnquery, ODR o,
+                       char **addinfop);
+ private:
+    cql_transform_t m_transform;
+};
+
diff --git a/include/yaz++/proxy.h b/include/yaz++/proxy.h
deleted file mode 100644 (file)
index aafef3c..0000000
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * Copyright (c) 1998-2004, Index Data.
- * See the file LICENSE for details.
- * 
- * $Id: proxy.h,v 1.41 2004-02-27 00:42:57 adam Exp $
- */
-
-#include <sys/time.h>
-#include <yaz++/z-assoc.h>
-#include <yaz++/z-query.h>
-#include <yaz++/z-databases.h>
-#include <yaz/cql.h>
-#if HAVE_XSLT
-#include <libxml/parser.h>
-#include <libxml/tree.h>
-#include <libxslt/xsltutils.h>
-#include <libxslt/transform.h>
-#endif
-
-class Yaz_Proxy;
-
-#define MAX_ZURL_PLEX 10
-
-#define PROXY_LOG_APDU_CLIENT 1
-#define PROXY_LOG_APDU_SERVER 2
-#define PROXY_LOG_REQ_CLIENT 4
-#define PROXY_LOG_REQ_SERVER 8
-
-struct Yaz_RecordCache_Entry;
-
-class YAZ_EXPORT Yaz_ProxyConfig {
-public:
-    Yaz_ProxyConfig();
-    ~Yaz_ProxyConfig();
-    int read_xml(const char *fname);
-
-    int get_target_no(int no,
-                     const char **name,
-                     const char **url,
-                     int *limit_bw,
-                     int *limit_pdu,
-                     int *limit_req,
-                     int *target_idletime,
-                     int *client_idletime,
-                     int *max_clients,
-                     int *keepalive_limit_bw,
-                     int *keepalive_limit_pdu,
-                     int *pre_init,
-                     const char **cql2rpn);
-    
-    void get_generic_info(int *log_mask, int *max_clients);
-
-    void get_target_info(const char *name, const char **url,
-                        int *limit_bw, int *limit_pdu, int *limit_req,
-                        int *target_idletime, int *client_idletime,
-                        int *max_clients,
-                        int *keepalive_limit_bw, int *keepalive_limit_pdu,
-                        int *pre_init,
-                        const char **cql2rpn);
-
-    int check_query(ODR odr, const char *name, Z_Query *query, char **addinfo);
-    int check_syntax(ODR odr, const char *name,
-                    Odr_oid *syntax, Z_RecordComposition *comp,
-                    char **addinfo, char **stylesheet, char **schema);
-    char *get_explain(ODR odr, const char *name, const char *db,
-                     int *len);
-private:
-    void operator=(const Yaz_ProxyConfig &conf);
-    int mycmp(const char *hay, const char *item, size_t len);
-#if HAVE_XSLT
-    int check_schema(xmlNodePtr ptr, Z_RecordComposition *comp,
-                    const char *schema_identifier);
-    xmlDocPtr m_docPtr;
-    xmlNodePtr m_proxyPtr;
-    void return_target_info(xmlNodePtr ptr, const char **url,
-                           int *limit_bw, int *limit_pdu, int *limit_req,
-                           int *target_idletime, int *client_idletime,
-                           int *keepalive_limit_bw, int *keepalive_limit_pdu,
-                           int *pre_init, const char **cql2rpn);
-    void return_limit(xmlNodePtr ptr,
-                     int *limit_bw, int *limit_pdu, int *limit_req);
-    int check_type_1(ODR odr, xmlNodePtr ptr, Z_RPNQuery *query,
-                    char **addinfo);
-    xmlNodePtr find_target_node(const char *name, const char *db);
-    xmlNodePtr find_target_db(xmlNodePtr ptr, const char *db);
-    const char *get_text(xmlNodePtr ptr);
-    int check_type_1_attributes(ODR odr, xmlNodePtr ptr,
-                               Z_AttributeList *attrs,
-                               char **addinfo);
-    int check_type_1_structure(ODR odr, xmlNodePtr ptr, Z_RPNStructure *q,
-                              char **addinfo);
-#endif
-    int m_copy;
-    int match_list(int v, const char *m);
-    int atoi_l(const char **cp);
-};
-
-class YAZ_EXPORT Yaz_RecordCache {
- public:
-    Yaz_RecordCache ();
-    ~Yaz_RecordCache ();
-    void add (ODR o, Z_NamePlusRecordList *npr, int start, int hits);
-    
-    int lookup (ODR o, Z_NamePlusRecordList **npr, int start, int num,
-               Odr_oid *syntax, Z_RecordComposition *comp);
-    void clear();
-
-    void copy_searchRequest(Z_SearchRequest *sr);
-    void copy_presentRequest(Z_PresentRequest *pr);
-    void set_max_size(int sz);
- private:
-    NMEM m_mem;
-    Yaz_RecordCache_Entry *m_entries;
-    Z_SearchRequest *m_searchRequest;
-    Z_PresentRequest *m_presentRequest;
-    int match (Yaz_RecordCache_Entry *entry,
-              Odr_oid *syntax, int offset,
-              Z_RecordComposition *comp);
-    int m_max_size;
-};
-
-class YAZ_EXPORT Yaz_bw {
- public:
-    Yaz_bw(int sz);
-    ~Yaz_bw();
-    void add_bytes(int m);
-    int get_total();
- private:
-    long m_sec;   // time of most recent bucket
-    int *m_bucket;
-    int m_ptr;
-    int m_size;
-};
-
-/// Private class
-class YAZ_EXPORT Yaz_ProxyClient : public Yaz_Z_Assoc {
-    friend class Yaz_Proxy;
-    Yaz_ProxyClient(IYaz_PDU_Observable *the_PDU_Observable,
-                   Yaz_Proxy *parent);
-    ~Yaz_ProxyClient();
-    void recv_GDU(Z_GDU *apdu, int len);
-    void recv_Z_PDU(Z_APDU *apdu, int len);
-    void recv_HTTP_response(Z_HTTP_Response *apdu, int len);
-    IYaz_PDU_Observer* sessionNotify
-       (IYaz_PDU_Observable *the_PDU_Observable, int fd);
-    void shutdown();
-    Yaz_Proxy *m_server;
-    void failNotify();
-    void timeoutNotify();
-    void connectNotify();
-    int send_to_target(Z_APDU *apdu);
-    const char *get_session_str();
-    char *m_cookie;
-    Yaz_ProxyClient *m_next;
-    Yaz_ProxyClient **m_prev;
-    int m_init_flag;
-    Yaz_Z_Query *m_last_query;
-    Yaz_Z_Databases m_last_databases;
-    char *m_last_resultSetId;
-    int m_last_ok;
-    int m_last_resultCount;
-    int m_sr_transform;
-    int m_seqno;
-    int m_waiting;
-    int m_resultSetStartPoint;
-    int m_bytes_sent;
-    int m_bytes_recv;
-    int m_pdu_recv;
-    ODR m_init_odr;
-    Z_APDU *m_initResponse;
-    Z_Options *m_initResponse_options;
-    Z_ProtocolVersion *m_initResponse_version;
-    int m_initResponse_preferredMessageSize;
-    int m_initResponse_maximumRecordSize;
-    Yaz_RecordCache m_cache;
-    void pre_init_client();
-    int m_target_idletime;
-    Yaz_Proxy *m_root;
-};
-
-class YAZ_EXPORT Yaz_cql2rpn {
- public:
-    Yaz_cql2rpn();
-    ~Yaz_cql2rpn();
-    void set_pqf_file(const char *fname);
-    int query_transform(const char *cql, Z_RPNQuery **rpnquery, ODR o,
-                       char **addinfop);
- private:
-    cql_transform_t m_transform;
-};
-
-
-/// Information Retrieval Proxy Server.
-class YAZ_EXPORT Yaz_Proxy : public Yaz_Z_Assoc {
- private:
-    char *get_cookie(Z_OtherInformation **otherInfo);
-    char *get_proxy(Z_OtherInformation **otherInfo);
-    Yaz_ProxyClient *get_client(Z_APDU *apdu, const char *cookie,
-                               const char *proxy_host);
-    Z_APDU *result_set_optimize(Z_APDU *apdu);
-    void shutdown();
-    
-    Yaz_ProxyClient *m_client;
-    IYaz_PDU_Observable *m_PDU_Observable;
-    Yaz_ProxyClient *m_clientPool;
-    Yaz_Proxy *m_parent;
-    int m_seqno;
-    int m_max_clients;
-    int m_log_mask;
-    int m_keepalive_limit_bw;
-    int m_keepalive_limit_pdu;
-    int m_client_idletime;
-    int m_target_idletime;
-    char *m_proxyTarget;
-    char *m_default_target;
-    char *m_proxy_authentication;
-    long m_seed;
-    char *m_optimize;
-    int m_session_no;         // sequence for each client session
-    char m_session_str[30];  // session string (time:session_no)
-    Yaz_ProxyConfig *m_config;
-    char *m_config_fname;
-    int m_bytes_sent;
-    int m_bytes_recv;
-    int m_bw_max;
-    Yaz_bw m_bw_stat;
-    int m_pdu_max;
-    Yaz_bw m_pdu_stat;
-    Z_GDU *m_bw_hold_PDU;
-    int m_max_record_retrieve;
-    void handle_max_record_retrieve(Z_APDU *apdu);
-    void display_diagrecs(Z_DiagRec **pp, int num);
-    Z_Records *create_nonSurrogateDiagnostics(ODR o, int error,
-                                             const char *addinfo);
-
-    Z_APDU *handle_query_validation(Z_APDU *apdu);
-    Z_APDU *handle_query_transformation(Z_APDU *apdu);
-
-    Z_APDU *handle_syntax_validation(Z_APDU *apdu);
-    const char *load_balance(const char **url);
-    int m_reconfig_flag;
-    Yaz_ProxyConfig *check_reconfigure();
-    int m_request_no;
-    int m_invalid_session;
-    int m_marcxml_flag;
-    xsltStylesheetPtr m_stylesheet_xsp;
-    int m_stylesheet_offset;
-    Z_APDU *m_stylesheet_apdu;
-    Z_NamePlusRecordList *m_stylesheet_nprl;
-    char *m_schema;
-    void convert_to_marcxml(Z_NamePlusRecordList *p);
-    int convert_xsl(Z_NamePlusRecordList *p, Z_APDU *apdu);
-    void convert_xsl_delay();
-    Z_APDU *m_initRequest_apdu;
-    int m_initRequest_preferredMessageSize;
-    int m_initRequest_maximumRecordSize;
-    Z_Options *m_initRequest_options;
-    Z_ProtocolVersion *m_initRequest_version;
-    NMEM m_initRequest_mem;
-    Z_APDU *m_apdu_invalid_session;
-    NMEM m_mem_invalid_session;
-    int send_PDU_convert(Z_APDU *apdu);
-    ODR m_s2z_odr_init;
-    ODR m_s2z_odr_search;
-    int m_s2z_hit_count;
-    int m_s2z_packing;
-    char *m_s2z_database;
-    Z_APDU *m_s2z_init_apdu;
-    Z_APDU *m_s2z_search_apdu;
-    Z_APDU *m_s2z_present_apdu;
-    char *m_s2z_stylesheet;
-    char *m_soap_ns;
-    int send_to_srw_client_error(int error, const char *add);
-    int send_to_srw_client_ok(int hits, Z_Records *records, int start);
-    int send_http_response(int code);
-    int send_srw_response(Z_SRW_PDU *srw_pdu);
-    int send_srw_explain_response(Z_SRW_diagnostic *diagnostics,
-                                 int num_diagnostics);
-    int z_to_srw_diag(ODR o, Z_SRW_searchRetrieveResponse *srw_res,
-                     Z_DefaultDiagFormat *ddf);
-    int m_http_keepalive;
-    const char *m_http_version;
-    Yaz_cql2rpn m_cql2rpn;
-    struct timeval m_time_tv;
-    void logtime();
-    Z_ElementSetNames *mk_esn_from_schema(ODR o, const char *schema);
-    Z_ReferenceId *m_referenceId;
-    NMEM m_referenceId_mem;
-#define NO_SPARE_SOLARIS_FD 10
-    int m_lo_fd[NO_SPARE_SOLARIS_FD];
-    void low_socket_open();
-    void low_socket_close();
- public:
-    Yaz_Proxy(IYaz_PDU_Observable *the_PDU_Observable,
-             Yaz_Proxy *parent = 0);
-    ~Yaz_Proxy();
-    void inc_request_no();
-    void recv_GDU(Z_GDU *apdu, int len);
-    void handle_incoming_HTTP(Z_HTTP_Request *req);
-    void handle_incoming_Z_PDU(Z_APDU *apdu);
-    IYaz_PDU_Observer* sessionNotify
-       (IYaz_PDU_Observable *the_PDU_Observable, int fd);
-    void failNotify();
-    void timeoutNotify();
-    void connectNotify();
-    void markInvalid();
-    const char *option(const char *name, const char *value);
-    void set_default_target(const char *target);
-    void set_proxy_authentication (const char *auth);
-    char *get_proxy_target() { return m_proxyTarget; };
-    char *get_session_str() { return m_session_str; };
-    void set_max_clients(int m) { m_max_clients = m; };
-    void set_client_idletime (int t) { m_client_idletime = (t > 1) ? t : 600; };
-    void set_target_idletime (int t) { m_target_idletime = (t > 1) ? t : 600; };
-    int get_target_idletime () { return m_target_idletime; }
-    int set_config(const char *name);
-    void reconfig() { m_reconfig_flag = 1; }
-    int send_to_client(Z_APDU *apdu);
-    int server(const char *addr);
-    void pre_init();
-    int get_log_mask() { return m_log_mask; };
-    int handle_init_response_for_invalid_session(Z_APDU *apdu);
-};
-
diff --git a/include/yaz++/proxy/Makefile.am b/include/yaz++/proxy/Makefile.am
new file mode 100644 (file)
index 0000000..ea7d926
--- /dev/null
@@ -0,0 +1,4 @@
+
+proxyincludedir=$(pkgincludedir)/proxy
+
+proxyinclude_HEADERS = proxy.h bw.h
diff --git a/include/yaz++/proxy/bw.h b/include/yaz++/proxy/bw.h
new file mode 100644 (file)
index 0000000..13851dc
--- /dev/null
@@ -0,0 +1,34 @@
+/* $Id: bw.h,v 1.1 2004-03-29 22:46:50 adam Exp $
+   Copyright (c) 1998-2004, Index Data.
+
+This file is part of the yaz-proxy.
+
+Zebra is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with Zebra; see the file LICENSE.proxy.  If not, write to the
+Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.
+ */
+
+class YAZ_EXPORT Yaz_bw {
+ public:
+    Yaz_bw(int sz);
+    ~Yaz_bw();
+    void add_bytes(int m);
+    int get_total();
+ private:
+    long m_sec;   // time of most recent bucket
+    int *m_bucket;
+    int m_ptr;
+    int m_size;
+};
+
diff --git a/include/yaz++/proxy/proxy.h b/include/yaz++/proxy/proxy.h
new file mode 100644 (file)
index 0000000..13e33a5
--- /dev/null
@@ -0,0 +1,316 @@
+/* $Id: proxy.h,v 1.1 2004-03-29 22:46:50 adam Exp $
+   Copyright (c) 1998-2004, Index Data.
+
+This file is part of the yaz-proxy.
+
+Zebra is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with Zebra; see the file LICENSE.proxy.  If not, write to the
+Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.
+ */
+
+#include <sys/time.h>
+#include <yaz++/z-assoc.h>
+#include <yaz++/z-query.h>
+#include <yaz++/z-databases.h>
+#include <yaz++/cql2rpn.h>
+#include <yaz/cql.h>
+#include <yaz++/proxy/bw.h>
+#if HAVE_XSLT
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxslt/xsltutils.h>
+#include <libxslt/transform.h>
+#endif
+
+class Yaz_Proxy;
+
+#define MAX_ZURL_PLEX 10
+
+#define PROXY_LOG_APDU_CLIENT 1
+#define PROXY_LOG_APDU_SERVER 2
+#define PROXY_LOG_REQ_CLIENT 4
+#define PROXY_LOG_REQ_SERVER 8
+
+struct Yaz_RecordCache_Entry;
+
+class YAZ_EXPORT Yaz_ProxyConfig {
+public:
+    Yaz_ProxyConfig();
+    ~Yaz_ProxyConfig();
+    int read_xml(const char *fname);
+
+    int get_target_no(int no,
+                     const char **name,
+                     const char **url,
+                     int *limit_bw,
+                     int *limit_pdu,
+                     int *limit_req,
+                     int *target_idletime,
+                     int *client_idletime,
+                     int *max_clients,
+                     int *keepalive_limit_bw,
+                     int *keepalive_limit_pdu,
+                     int *pre_init,
+                     const char **cql2rpn);
+    
+    void get_generic_info(int *log_mask, int *max_clients);
+
+    void get_target_info(const char *name, const char **url,
+                        int *limit_bw, int *limit_pdu, int *limit_req,
+                        int *target_idletime, int *client_idletime,
+                        int *max_clients,
+                        int *keepalive_limit_bw, int *keepalive_limit_pdu,
+                        int *pre_init,
+                        const char **cql2rpn);
+
+    int check_query(ODR odr, const char *name, Z_Query *query, char **addinfo);
+    int check_syntax(ODR odr, const char *name,
+                    Odr_oid *syntax, Z_RecordComposition *comp,
+                    char **addinfo, char **stylesheet, char **schema);
+    char *get_explain(ODR odr, const char *name, const char *db,
+                     int *len);
+private:
+    void operator=(const Yaz_ProxyConfig &conf);
+    int mycmp(const char *hay, const char *item, size_t len);
+#if HAVE_XSLT
+    int check_schema(xmlNodePtr ptr, Z_RecordComposition *comp,
+                    const char *schema_identifier);
+    xmlDocPtr m_docPtr;
+    xmlNodePtr m_proxyPtr;
+    void return_target_info(xmlNodePtr ptr, const char **url,
+                           int *limit_bw, int *limit_pdu, int *limit_req,
+                           int *target_idletime, int *client_idletime,
+                           int *keepalive_limit_bw, int *keepalive_limit_pdu,
+                           int *pre_init, const char **cql2rpn);
+    void return_limit(xmlNodePtr ptr,
+                     int *limit_bw, int *limit_pdu, int *limit_req);
+    int check_type_1(ODR odr, xmlNodePtr ptr, Z_RPNQuery *query,
+                    char **addinfo);
+    xmlNodePtr find_target_node(const char *name, const char *db);
+    xmlNodePtr find_target_db(xmlNodePtr ptr, const char *db);
+    const char *get_text(xmlNodePtr ptr);
+    int check_type_1_attributes(ODR odr, xmlNodePtr ptr,
+                               Z_AttributeList *attrs,
+                               char **addinfo);
+    int check_type_1_structure(ODR odr, xmlNodePtr ptr, Z_RPNStructure *q,
+                              char **addinfo);
+#endif
+    int m_copy;
+    int match_list(int v, const char *m);
+    int atoi_l(const char **cp);
+};
+
+class YAZ_EXPORT Yaz_RecordCache {
+ public:
+    Yaz_RecordCache ();
+    ~Yaz_RecordCache ();
+    void add (ODR o, Z_NamePlusRecordList *npr, int start, int hits);
+    
+    int lookup (ODR o, Z_NamePlusRecordList **npr, int start, int num,
+               Odr_oid *syntax, Z_RecordComposition *comp);
+    void clear();
+
+    void copy_searchRequest(Z_SearchRequest *sr);
+    void copy_presentRequest(Z_PresentRequest *pr);
+    void set_max_size(int sz);
+ private:
+    NMEM m_mem;
+    Yaz_RecordCache_Entry *m_entries;
+    Z_SearchRequest *m_searchRequest;
+    Z_PresentRequest *m_presentRequest;
+    int match (Yaz_RecordCache_Entry *entry,
+              Odr_oid *syntax, int offset,
+              Z_RecordComposition *comp);
+    int m_max_size;
+};
+
+/// Private class
+class YAZ_EXPORT Yaz_ProxyClient : public Yaz_Z_Assoc {
+    friend class Yaz_Proxy;
+    Yaz_ProxyClient(IYaz_PDU_Observable *the_PDU_Observable,
+                   Yaz_Proxy *parent);
+    ~Yaz_ProxyClient();
+    void recv_GDU(Z_GDU *apdu, int len);
+    void recv_Z_PDU(Z_APDU *apdu, int len);
+    void recv_HTTP_response(Z_HTTP_Response *apdu, int len);
+    IYaz_PDU_Observer* sessionNotify
+       (IYaz_PDU_Observable *the_PDU_Observable, int fd);
+    void shutdown();
+    Yaz_Proxy *m_server;
+    void failNotify();
+    void timeoutNotify();
+    void connectNotify();
+    int send_to_target(Z_APDU *apdu);
+    const char *get_session_str();
+    char *m_cookie;
+    Yaz_ProxyClient *m_next;
+    Yaz_ProxyClient **m_prev;
+    int m_init_flag;
+    Yaz_Z_Query *m_last_query;
+    Yaz_Z_Databases m_last_databases;
+    char *m_last_resultSetId;
+    int m_last_ok;
+    int m_last_resultCount;
+    int m_sr_transform;
+    int m_seqno;
+    int m_waiting;
+    int m_resultSetStartPoint;
+    int m_bytes_sent;
+    int m_bytes_recv;
+    int m_pdu_recv;
+    ODR m_init_odr;
+    Z_APDU *m_initResponse;
+    Z_Options *m_initResponse_options;
+    Z_ProtocolVersion *m_initResponse_version;
+    int m_initResponse_preferredMessageSize;
+    int m_initResponse_maximumRecordSize;
+    Yaz_RecordCache m_cache;
+    void pre_init_client();
+    int m_target_idletime;
+    Yaz_Proxy *m_root;
+};
+
+
+/// Information Retrieval Proxy Server.
+class YAZ_EXPORT Yaz_Proxy : public Yaz_Z_Assoc {
+ private:
+    char *get_cookie(Z_OtherInformation **otherInfo);
+    char *get_proxy(Z_OtherInformation **otherInfo);
+    Yaz_ProxyClient *get_client(Z_APDU *apdu, const char *cookie,
+                               const char *proxy_host);
+    Z_APDU *result_set_optimize(Z_APDU *apdu);
+    void shutdown();
+    
+    Yaz_ProxyClient *m_client;
+    IYaz_PDU_Observable *m_PDU_Observable;
+    Yaz_ProxyClient *m_clientPool;
+    Yaz_Proxy *m_parent;
+    int m_seqno;
+    int m_max_clients;
+    int m_log_mask;
+    int m_keepalive_limit_bw;
+    int m_keepalive_limit_pdu;
+    int m_client_idletime;
+    int m_target_idletime;
+    char *m_proxyTarget;
+    char *m_default_target;
+    char *m_proxy_authentication;
+    long m_seed;
+    char *m_optimize;
+    int m_session_no;         // sequence for each client session
+    char m_session_str[30];  // session string (time:session_no)
+    Yaz_ProxyConfig *m_config;
+    char *m_config_fname;
+    int m_bytes_sent;
+    int m_bytes_recv;
+    int m_bw_max;
+    Yaz_bw m_bw_stat;
+    int m_pdu_max;
+    Yaz_bw m_pdu_stat;
+    Z_GDU *m_bw_hold_PDU;
+    int m_max_record_retrieve;
+    void handle_max_record_retrieve(Z_APDU *apdu);
+    void display_diagrecs(Z_DiagRec **pp, int num);
+    Z_Records *create_nonSurrogateDiagnostics(ODR o, int error,
+                                             const char *addinfo);
+
+    Z_APDU *handle_query_validation(Z_APDU *apdu);
+    Z_APDU *handle_query_transformation(Z_APDU *apdu);
+
+    Z_APDU *handle_syntax_validation(Z_APDU *apdu);
+    const char *load_balance(const char **url);
+    int m_reconfig_flag;
+    Yaz_ProxyConfig *check_reconfigure();
+    int m_request_no;
+    int m_invalid_session;
+    int m_marcxml_flag;
+    xsltStylesheetPtr m_stylesheet_xsp;
+    int m_stylesheet_offset;
+    Z_APDU *m_stylesheet_apdu;
+    Z_NamePlusRecordList *m_stylesheet_nprl;
+    char *m_schema;
+    void convert_to_marcxml(Z_NamePlusRecordList *p);
+    int convert_xsl(Z_NamePlusRecordList *p, Z_APDU *apdu);
+    void convert_xsl_delay();
+    Z_APDU *m_initRequest_apdu;
+    int m_initRequest_preferredMessageSize;
+    int m_initRequest_maximumRecordSize;
+    Z_Options *m_initRequest_options;
+    Z_ProtocolVersion *m_initRequest_version;
+    NMEM m_initRequest_mem;
+    Z_APDU *m_apdu_invalid_session;
+    NMEM m_mem_invalid_session;
+    int send_PDU_convert(Z_APDU *apdu);
+    ODR m_s2z_odr_init;
+    ODR m_s2z_odr_search;
+    int m_s2z_hit_count;
+    int m_s2z_packing;
+    char *m_s2z_database;
+    Z_APDU *m_s2z_init_apdu;
+    Z_APDU *m_s2z_search_apdu;
+    Z_APDU *m_s2z_present_apdu;
+    char *m_s2z_stylesheet;
+    char *m_soap_ns;
+    int send_to_srw_client_error(int error, const char *add);
+    int send_to_srw_client_ok(int hits, Z_Records *records, int start);
+    int send_http_response(int code);
+    int send_srw_response(Z_SRW_PDU *srw_pdu);
+    int send_srw_explain_response(Z_SRW_diagnostic *diagnostics,
+                                 int num_diagnostics);
+    int z_to_srw_diag(ODR o, Z_SRW_searchRetrieveResponse *srw_res,
+                     Z_DefaultDiagFormat *ddf);
+    int m_http_keepalive;
+    const char *m_http_version;
+    Yaz_cql2rpn m_cql2rpn;
+    struct timeval m_time_tv;
+    void logtime();
+    Z_ElementSetNames *mk_esn_from_schema(ODR o, const char *schema);
+    Z_ReferenceId *m_referenceId;
+    NMEM m_referenceId_mem;
+#define NO_SPARE_SOLARIS_FD 10
+    int m_lo_fd[NO_SPARE_SOLARIS_FD];
+    void low_socket_open();
+    void low_socket_close();
+ public:
+    Yaz_Proxy(IYaz_PDU_Observable *the_PDU_Observable,
+             Yaz_Proxy *parent = 0);
+    ~Yaz_Proxy();
+    void inc_request_no();
+    void recv_GDU(Z_GDU *apdu, int len);
+    void handle_incoming_HTTP(Z_HTTP_Request *req);
+    void handle_incoming_Z_PDU(Z_APDU *apdu);
+    IYaz_PDU_Observer* sessionNotify
+       (IYaz_PDU_Observable *the_PDU_Observable, int fd);
+    void failNotify();
+    void timeoutNotify();
+    void connectNotify();
+    void markInvalid();
+    const char *option(const char *name, const char *value);
+    void set_default_target(const char *target);
+    void set_proxy_authentication (const char *auth);
+    char *get_proxy_target() { return m_proxyTarget; };
+    char *get_session_str() { return m_session_str; };
+    void set_max_clients(int m) { m_max_clients = m; };
+    void set_client_idletime (int t) { m_client_idletime = (t > 1) ? t : 600; };
+    void set_target_idletime (int t) { m_target_idletime = (t > 1) ? t : 600; };
+    int get_target_idletime () { return m_target_idletime; }
+    int set_config(const char *name);
+    void reconfig() { m_reconfig_flag = 1; }
+    int send_to_client(Z_APDU *apdu);
+    int server(const char *addr);
+    void pre_init();
+    int get_log_mask() { return m_log_mask; };
+    int handle_init_response_for_invalid_session(Z_APDU *apdu);
+};
+
diff --git a/include/yaz++/record-cache.h b/include/yaz++/record-cache.h
new file mode 100644 (file)
index 0000000..e05a4fd
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2002-2004, Index Data.
+ * See the file LICENSE for details.
+ * 
+ * $Id: record-cache.h,v 1.1 2004-03-29 22:46:50 adam Exp $
+ */
+
+
+#include <yaz/nmem.h>
+#include <yaz/z-core.h>
+
+class Yaz_RecordCache_Entry;
+
+class YAZ_EXPORT Yaz_RecordCache {
+ public:
+    Yaz_RecordCache ();
+    ~Yaz_RecordCache ();
+    void add (ODR o, Z_NamePlusRecordList *npr, int start, int hits);
+    
+    int lookup (ODR o, Z_NamePlusRecordList **npr, int start, int num,
+               Odr_oid *syntax, Z_RecordComposition *comp);
+    void clear();
+
+    void copy_searchRequest(Z_SearchRequest *sr);
+    void copy_presentRequest(Z_PresentRequest *pr);
+    void set_max_size(int sz);
+ private:
+    NMEM m_mem;
+    Yaz_RecordCache_Entry *m_entries;
+    Z_SearchRequest *m_searchRequest;
+    Z_PresentRequest *m_presentRequest;
+    int match (Yaz_RecordCache_Entry *entry,
+              Odr_oid *syntax, int offset,
+              Z_RecordComposition *comp);
+    int m_max_size;
+};
diff --git a/proxy/Makefile.am b/proxy/Makefile.am
new file mode 100644 (file)
index 0000000..30332af
--- /dev/null
@@ -0,0 +1,15 @@
+## $Id: Makefile.am,v 1.1 2004-03-29 22:46:51 adam Exp $
+
+AM_CXXFLAGS = $(YAZINC) -I$(srcdir)/../include $(XSLT_CFLAGS)
+
+lib_LTLIBRARIES = libyazproxy.la
+libyazproxy_la_LDFLAGS=-version-info 1:0:0
+
+libyazproxy_la_SOURCES= yaz-proxy.cpp yaz-proxy-config.cpp yaz-bw.cpp
+
+bin_PROGRAMS = yaz-proxy
+
+yaz_proxy_SOURCES=yaz-proxy-main.cpp
+
+LDADD=libyazproxy.la ../src/libyazcpp.la $(YAZLALIB) $(XSLT_LIBS)
+libyazproxy_la_LIBADD = $(XSLT_LIBS)
diff --git a/proxy/yaz-bw.cpp b/proxy/yaz-bw.cpp
new file mode 100644 (file)
index 0000000..fabf086
--- /dev/null
@@ -0,0 +1,65 @@
+/* $Id: yaz-bw.cpp,v 1.1 2004-03-29 22:46:51 adam Exp $
+   Copyright (c) 1998-2004, Index Data.
+
+This file is part of the yaz-proxy.
+
+Zebra is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with Zebra; see the file LICENSE.proxy.  If not, write to the
+Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.
+ */
+
+#include <time.h>
+#include <yaz/log.h>
+#include <yaz++/proxy/bw.h>
+
+Yaz_bw::Yaz_bw(int sz)
+{
+    m_sec = 0;
+    m_size = sz;
+    m_bucket = new int[m_size];
+    m_ptr = 0;
+}
+
+Yaz_bw::~Yaz_bw()
+{
+    delete [] m_bucket;
+}
+
+int Yaz_bw::get_total()
+{
+    add_bytes(0);
+    int bw = 0;
+    int i;
+    for (i = 0; i<m_size; i++)
+       bw += m_bucket[i];
+    return bw;
+}
+
+void Yaz_bw::add_bytes(int b)
+{
+    long now = time(0);
+
+    int d = now - m_sec;
+    if (d > m_size)
+       d = m_size;
+    while (--d >= 0)
+    {
+       if (++m_ptr == m_size)
+           m_ptr = 0;
+       m_bucket[m_ptr] = 0;
+    }
+    m_bucket[m_ptr] += b;
+    m_sec = now;
+}
+
diff --git a/proxy/yaz-proxy-config.cpp b/proxy/yaz-proxy-config.cpp
new file mode 100644 (file)
index 0000000..fa55d53
--- /dev/null
@@ -0,0 +1,810 @@
+/* $Id: yaz-proxy-config.cpp,v 1.1 2004-03-29 22:46:51 adam Exp $
+   Copyright (c) 1998-2004, Index Data.
+
+This file is part of the yaz-proxy.
+
+Zebra is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with Zebra; see the file LICENSE.proxy.  If not, write to the
+Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.
+ */
+
+#include <ctype.h>
+#include <yaz/log.h>
+#include <yaz++/proxy/proxy.h>
+
+Yaz_ProxyConfig::Yaz_ProxyConfig()
+{
+    m_copy = 0;
+#if HAVE_XSLT
+    m_docPtr = 0;
+    m_proxyPtr = 0;
+#endif
+}
+
+Yaz_ProxyConfig::~Yaz_ProxyConfig()
+{
+#if HAVE_XSLT
+    if (!m_copy && m_docPtr)
+       xmlFreeDoc(m_docPtr);
+#endif
+}
+
+int Yaz_ProxyConfig::read_xml(const char *fname)
+{
+#if HAVE_XSLT
+    xmlDocPtr ndoc = xmlParseFile(fname);
+
+    if (!ndoc)
+    {
+       yaz_log(LOG_WARN, "Config file %s not found or parse error", fname);
+       return -1;  // no good
+    }
+    xmlNodePtr proxyPtr = xmlDocGetRootElement(ndoc);
+    if (!proxyPtr || proxyPtr->type != XML_ELEMENT_NODE ||
+       strcmp((const char *) proxyPtr->name, "proxy"))
+    {
+       yaz_log(LOG_WARN, "No proxy element in %s", fname);
+       xmlFreeDoc(ndoc);
+       return -1;
+    }
+    m_proxyPtr = proxyPtr;
+
+    // OK: release previous and make it the current one.
+    if (m_docPtr)
+       xmlFreeDoc(m_docPtr);
+    m_docPtr = ndoc;
+    return 0;
+#else
+    return -2;
+#endif
+}
+
+#if HAVE_XSLT
+const char *Yaz_ProxyConfig::get_text(xmlNodePtr ptr)
+{
+    for(ptr = ptr->children; ptr; ptr = ptr->next)
+       if (ptr->type == XML_TEXT_NODE)
+       {
+           xmlChar *t = ptr->content;
+           if (t)
+           {
+               while (*t == ' ')
+                   t++;
+               return (const char *) t;
+           }
+       }
+    return 0;
+}
+#endif
+
+#if HAVE_XSLT
+void Yaz_ProxyConfig::return_limit(xmlNodePtr ptr,
+                                  int *limit_bw,
+                                  int *limit_pdu,
+                                  int *limit_req)
+{
+    for (ptr = ptr->children; ptr; ptr = ptr->next)
+    {
+       if (ptr->type == XML_ELEMENT_NODE 
+           && !strcmp((const char *) ptr->name, "bandwidth"))
+       {
+           const char *t = get_text(ptr);
+           if (t)
+               *limit_bw = atoi(t);
+       }
+       if (ptr->type == XML_ELEMENT_NODE 
+           && !strcmp((const char *) ptr->name, "retrieve"))
+       {
+           const char *t = get_text(ptr);
+           if (t)
+               *limit_req = atoi(t);
+       }
+       if (ptr->type == XML_ELEMENT_NODE 
+           && !strcmp((const char *) ptr->name, "pdu"))
+       {
+           const char *t = get_text(ptr);
+           if (t)
+               *limit_pdu = atoi(t);
+       }
+    }
+}
+#endif
+
+#if HAVE_XSLT
+void Yaz_ProxyConfig::return_target_info(xmlNodePtr ptr,
+                                        const char **url,
+                                        int *limit_bw,
+                                        int *limit_pdu,
+                                        int *limit_req,
+                                        int *target_idletime,
+                                        int *client_idletime,
+                                        int *keepalive_limit_bw,
+                                        int *keepalive_limit_pdu,
+                                        int *pre_init,
+                                        const char **cql2rpn)
+{
+    *pre_init = 0;
+    int no_url = 0;
+    ptr = ptr->children;
+    for (; ptr; ptr = ptr->next)
+    {
+       if (ptr->type == XML_ELEMENT_NODE 
+           && !strcmp((const char *) ptr->name, "preinit"))
+       {
+           const char *v = get_text(ptr);
+           *pre_init = v ? atoi(v) : 1;
+       }
+       if (ptr->type == XML_ELEMENT_NODE 
+           && !strcmp((const char *) ptr->name, "url"))
+       {
+           const char *t = get_text(ptr);
+           if (t && no_url < MAX_ZURL_PLEX)
+           {
+               url[no_url++] = t;
+               url[no_url] = 0;
+           }
+       }
+       if (ptr->type == XML_ELEMENT_NODE 
+           && !strcmp((const char *) ptr->name, "keepalive"))
+       {
+           int dummy;
+           *keepalive_limit_bw = 500000;
+           *keepalive_limit_pdu = 1000;
+           return_limit(ptr, keepalive_limit_bw, keepalive_limit_pdu,
+                        &dummy);
+       }
+       if (ptr->type == XML_ELEMENT_NODE 
+           && !strcmp((const char *) ptr->name, "limit"))
+           return_limit(ptr, limit_bw, limit_pdu, limit_req);
+       if (ptr->type == XML_ELEMENT_NODE 
+           && !strcmp((const char *) ptr->name, "target-timeout"))
+       {
+           const char *t = get_text(ptr);
+           if (t)
+           {
+               *target_idletime = atoi(t);
+               if (*target_idletime < 0)
+                   *target_idletime = 0;
+           }
+       }
+       if (ptr->type == XML_ELEMENT_NODE 
+           && !strcmp((const char *) ptr->name, "client-timeout"))
+       {
+           const char *t = get_text(ptr);
+           if (t)
+           {
+               *client_idletime = atoi(t);
+               if (*client_idletime < 0)
+                   *client_idletime = 0;
+           }
+       }
+       if (ptr->type == XML_ELEMENT_NODE 
+           && !strcmp((const char *) ptr->name, "cql2rpn"))
+       {
+           const char *t = get_text(ptr);
+           if (t)
+               *cql2rpn = t;
+       }
+    }
+}
+#endif
+
+int Yaz_ProxyConfig::atoi_l(const char **cp)
+{
+    int v = 0;
+    while (**cp && isdigit(**cp))
+    {
+       v = v*10 + (**cp - '0');
+       (*cp)++;
+    }
+    return v;
+}
+
+int Yaz_ProxyConfig::match_list(int v, const char *m)
+{
+  while(m && *m)
+  {
+      while(*m && isspace(*m))
+         m++;
+      if (*m == '*')
+         return 1;
+      int l = atoi_l(&m);
+      int h = l;
+      if (*m == '-')
+      {
+         ++m;
+         h = atoi_l(&m);
+      }
+      if (v >= l && v <= h)
+         return 1;
+      if (*m == ',')
+         m++;
+  }
+  return 0;
+}
+
+#if HAVE_XSLT
+int Yaz_ProxyConfig::check_type_1_attributes(ODR odr, xmlNodePtr ptrl,
+                                            Z_AttributeList *attrs,
+                                            char **addinfo)
+{
+    int i;
+    for (i = 0; i<attrs->num_attributes; i++)
+    {
+       Z_AttributeElement *el = attrs->attributes[i];
+       
+       if (!el->attributeType)
+           continue;
+       int type = *el->attributeType;
+       int *value = 0;
+       
+       if (el->which == Z_AttributeValue_numeric && el->value.numeric)
+           value = el->value.numeric;
+       
+       xmlNodePtr ptr;
+       for(ptr = ptrl->children; ptr; ptr = ptr->next)
+       {
+           if (ptr->type == XML_ELEMENT_NODE &&
+               !strcmp((const char *) ptr->name, "attribute"))
+           {
+               const char *match_type = 0;
+               const char *match_value = 0;
+               const char *match_error = 0;
+               struct _xmlAttr *attr;
+               for (attr = ptr->properties; attr; attr = attr->next)
+               {
+                   if (!strcmp((const char *) attr->name, "type") &&
+                       attr->children && attr->children->type == XML_TEXT_NODE)
+                       match_type = (const char *) attr->children->content;
+                   if (!strcmp((const char *) attr->name, "value") &&
+                       attr->children && attr->children->type == XML_TEXT_NODE)
+                       match_value = (const char *) attr->children->content;
+                   if (!strcmp((const char *) attr->name, "error") &&
+                       attr->children && attr->children->type == XML_TEXT_NODE)
+                       match_error = (const char *) attr->children->content;
+               }
+               if (match_type && match_value)
+               {
+                   char addinfo_str[20];
+                   if (!match_list(type, match_type))
+                       continue;
+                   
+                   *addinfo_str = '\0';
+                   if (!strcmp(match_type, "*"))
+                       sprintf (addinfo_str, "%d", type);
+                   else if (value)
+                   {
+                       if (!match_list(*value, match_value))
+                           continue;
+                       sprintf (addinfo_str, "%d", *value);
+                   }
+                   else
+                       continue;
+                   
+                   if (match_error)
+                   {
+                       if (*addinfo_str)
+                           *addinfo = odr_strdup(odr, addinfo_str);
+                       return atoi(match_error);
+                   }
+                   break;
+               }
+           }
+       }
+    }
+    return 0;
+}
+#endif
+
+#if HAVE_XSLT
+int Yaz_ProxyConfig::check_type_1_structure(ODR odr, xmlNodePtr ptr,
+                                           Z_RPNStructure *q,
+                                           char **addinfo)
+{
+    if (q->which == Z_RPNStructure_complex)
+    {
+       int e = check_type_1_structure(odr, ptr, q->u.complex->s1, addinfo);
+       if (e)
+           return e;
+       e = check_type_1_structure(odr, ptr, q->u.complex->s2, addinfo);
+       return e;
+    }
+    else if (q->which == Z_RPNStructure_simple)
+    {
+       if (q->u.simple->which == Z_Operand_APT)
+       {
+           return check_type_1_attributes(
+               odr, ptr, q->u.simple->u.attributesPlusTerm->attributes,
+               addinfo);
+       }
+    }
+    return 0;
+}
+#endif
+
+#if HAVE_XSLT
+int Yaz_ProxyConfig::check_type_1(ODR odr, xmlNodePtr ptr, Z_RPNQuery *query,
+                                 char **addinfo)
+{
+    // possibly check for Bib-1
+    return check_type_1_structure(odr, ptr, query->RPNStructure, addinfo);
+}
+#endif
+
+int Yaz_ProxyConfig::check_query(ODR odr, const char *name, Z_Query *query,
+                                char **addinfo)
+{
+#if HAVE_XSLT
+    xmlNodePtr ptr;
+    
+    ptr = find_target_node(name, 0);
+    if (ptr)
+    {
+       if (query->which == Z_Query_type_1 || query->which == Z_Query_type_101)
+           return check_type_1(odr, ptr, query->u.type_1, addinfo);
+    }
+#endif
+    return 0;
+}
+
+#if HAVE_XSLT
+int Yaz_ProxyConfig::check_schema(xmlNodePtr ptr, Z_RecordComposition *comp,
+                                 const char *schema_identifier)
+{
+    char *esn = 0;
+    int default_match = 1;
+    if (comp && comp->which == Z_RecordComp_simple &&
+       comp->u.simple && comp->u.simple->which == Z_ElementSetNames_generic)
+    {
+       esn = comp->u.simple->u.generic;
+    }
+    // if no ESN/schema was given accept..
+    if (!esn)
+       return 1;
+    // check if schema identifier match
+    if (schema_identifier && !strcmp(esn, schema_identifier))
+       return 1;
+    // Check each name element
+    for (; ptr; ptr = ptr->next)
+    {
+       if (ptr->type == XML_ELEMENT_NODE 
+           && !strcmp((const char *) ptr->name, "name"))
+       {
+           xmlNodePtr tptr = ptr->children;
+           default_match = 0;
+           for (; tptr; tptr = tptr->next)
+               if (tptr->type == XML_TEXT_NODE && tptr->content)
+               {
+                   xmlChar *t = tptr->content;
+                   while (*t && isspace(*t))
+                       t++;
+                   int i = 0;
+                   while (esn[i] && esn[i] == t[i])
+                       i++;
+                   if (!esn[i] && (!t[i] || isspace(t[i])))
+                       return 1;
+               }
+       }
+    }
+    return default_match;
+}
+#endif
+
+int Yaz_ProxyConfig::check_syntax(ODR odr, const char *name,
+                                 Odr_oid *syntax, Z_RecordComposition *comp,
+                                 char **addinfo,
+                                 char **stylesheet, char **schema)
+{
+    if (stylesheet)
+    {
+       xfree (*stylesheet);
+       *stylesheet = 0;
+    }
+    if (schema)
+    {
+       xfree (*schema);
+       *schema = 0;
+    }
+#if HAVE_XSLT
+    int syntax_has_matched = 0;
+    xmlNodePtr ptr;
+    
+    ptr = find_target_node(name, 0);
+    if (!ptr)
+       return 0;
+    for(ptr = ptr->children; ptr; ptr = ptr->next)
+    {
+       if (ptr->type == XML_ELEMENT_NODE &&
+           !strcmp((const char *) ptr->name, "syntax"))
+       {
+           int match = 0;  // if we match record syntax
+           const char *match_type = 0;
+           const char *match_error = 0;
+           const char *match_marcxml = 0;
+           const char *match_stylesheet = 0;
+           const char *match_identifier = 0;
+           struct _xmlAttr *attr;
+           for (attr = ptr->properties; attr; attr = attr->next)
+           {
+               if (!strcmp((const char *) attr->name, "type") &&
+                   attr->children && attr->children->type == XML_TEXT_NODE)
+                   match_type = (const char *) attr->children->content;
+               if (!strcmp((const char *) attr->name, "error") &&
+                   attr->children && attr->children->type == XML_TEXT_NODE)
+                   match_error = (const char *) attr->children->content;
+               if (!strcmp((const char *) attr->name, "marcxml") &&
+                   attr->children && attr->children->type == XML_TEXT_NODE)
+                   match_marcxml = (const char *) attr->children->content;
+               if (!strcmp((const char *) attr->name, "stylesheet") &&
+                   attr->children && attr->children->type == XML_TEXT_NODE)
+                   match_stylesheet = (const char *) attr->children->content;
+               if (!strcmp((const char *) attr->name, "identifier") &&
+                   attr->children && attr->children->type == XML_TEXT_NODE)
+                   match_identifier = (const char *) attr->children->content;
+           }
+           if (match_type)
+           {
+               if (!strcmp(match_type, "*"))
+                   match = 1;
+               else if (!strcmp(match_type, "none"))
+               {
+                   if (syntax == 0)
+                       match = 1;
+               }
+               else if (syntax)
+               {
+                   int match_oid[OID_SIZE];
+                   oid_name_to_oid(CLASS_RECSYN, match_type, match_oid);
+                   if (oid_oidcmp(match_oid, syntax) == 0)
+                       match = 1;
+               }
+           }
+           if (match)
+           {
+               if (!match_error)
+                   syntax_has_matched = 1;
+               match = check_schema(ptr->children, comp, match_identifier);
+           }
+           if (match)
+           {
+               if (stylesheet && match_stylesheet)
+               {
+                   xfree(*stylesheet);
+                   *stylesheet = xstrdup(match_stylesheet);
+               }
+               if (schema && match_identifier)
+               {
+                   xfree(*schema);
+                   *schema = xstrdup(match_identifier);
+               }
+               if (match_marcxml)
+               {
+                   return -1;
+               }
+               if (match_error)
+               {
+                   if (syntax_has_matched)  // if syntax OK, bad schema/ESN
+                       return 25;
+                   if (syntax)
+                   {
+                       char dotoid_str[100];
+                       oid_to_dotstring(syntax, dotoid_str);
+                       *addinfo = odr_strdup(odr, dotoid_str);
+                   }
+                   return atoi(match_error);
+               }
+               return 0;
+           }
+       }
+    }
+#endif
+    return 0;
+}
+
+#if HAVE_XSLT
+xmlNodePtr Yaz_ProxyConfig::find_target_db(xmlNodePtr ptr, const char *db)
+{
+    xmlNodePtr dptr;
+    if (!db)
+       return ptr;
+    if (!ptr)
+       return 0;
+    for (dptr = ptr->children; dptr; dptr = dptr->next)
+       if (dptr->type == XML_ELEMENT_NODE &&
+           !strcmp((const char *) dptr->name, "database"))
+       {
+           struct _xmlAttr *attr;
+           for (attr = dptr->properties; attr; attr = attr->next)
+               if (!strcmp((const char *) attr->name, "name"))
+               {
+                   if (attr->children
+                       && attr->children->type==XML_TEXT_NODE
+                       && attr->children->content 
+                       && (!strcmp((const char *) attr->children->content, db)
+                           || !strcmp((const char *) attr->children->content,
+                                      "*")))
+                       return dptr;
+               }
+       }
+    return ptr;
+}
+    
+xmlNodePtr Yaz_ProxyConfig::find_target_node(const char *name, const char *db)
+{
+    xmlNodePtr ptr;
+    if (!m_proxyPtr)
+       return 0;
+    for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
+    {
+       if (ptr->type == XML_ELEMENT_NODE &&
+           !strcmp((const char *) ptr->name, "target"))
+       {
+           // default one ? 
+           if (!name)
+           {
+               // <target default="1"> ?
+               struct _xmlAttr *attr;
+               for (attr = ptr->properties; attr; attr = attr->next)
+                   if (!strcmp((const char *) attr->name, "default") &&
+                       attr->children && attr->children->type == XML_TEXT_NODE)
+                   {
+                       xmlChar *t = attr->children->content;
+                       if (!t || *t == '1')
+                       {
+                           return find_target_db(ptr, db);
+                       }
+                   }
+           }
+           else
+           {
+               // <target name="name"> ?
+               struct _xmlAttr *attr;
+               for (attr = ptr->properties; attr; attr = attr->next)
+                   if (!strcmp((const char *) attr->name, "name"))
+                   {
+                       if (attr->children
+                           && attr->children->type==XML_TEXT_NODE
+                           && attr->children->content 
+                           && (!strcmp((const char *) attr->children->content,
+                                       name)
+                               || !strcmp((const char *) attr->children->content,
+                                          "*")))
+                       {
+                           return find_target_db(ptr, db);
+                       }
+                   }
+           }
+       }
+    }
+    return 0;
+}
+#endif
+
+int Yaz_ProxyConfig::get_target_no(int no,
+                                  const char **name,
+                                  const char **url,
+                                  int *limit_bw,
+                                  int *limit_pdu,
+                                  int *limit_req,
+                                  int *target_idletime,
+                                  int *client_idletime,
+                                  int *max_clients,
+                                  int *keepalive_limit_bw,
+                                  int *keepalive_limit_pdu,
+                                  int *pre_init,
+                                  const char **cql2rpn)
+{
+#if HAVE_XSLT
+    xmlNodePtr ptr;
+    if (!m_proxyPtr)
+       return 0;
+    int i = 0;
+    for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
+       if (ptr->type == XML_ELEMENT_NODE &&
+           !strcmp((const char *) ptr->name, "target"))
+       {
+           if (i == no)
+           {
+               struct _xmlAttr *attr;
+               for (attr = ptr->properties; attr; attr = attr->next)
+                   if (!strcmp((const char *) attr->name, "name"))
+                   {
+                       if (attr->children
+                           && attr->children->type==XML_TEXT_NODE
+                           && attr->children->content)
+                           *name = (const char *) attr->children->content;
+                   }
+               return_target_info(ptr, url, limit_bw, limit_pdu, limit_req,
+                                  target_idletime, client_idletime,
+                                  keepalive_limit_bw, keepalive_limit_pdu,
+                                  pre_init, cql2rpn);
+               return 1;
+           }
+           i++;
+       }
+#endif
+    return 0;
+}
+
+int Yaz_ProxyConfig::mycmp(const char *hay, const char *item, size_t len)
+{
+    if (len == strlen(item) && memcmp(hay, item, len) == 0)
+       return 1;
+    return 0;
+}
+
+void Yaz_ProxyConfig::get_generic_info(int *log_mask,
+                                      int *max_clients)
+{
+#if HAVE_XSLT
+    xmlNodePtr ptr;
+    if (!m_proxyPtr)
+       return;
+    for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
+    {
+       if (ptr->type == XML_ELEMENT_NODE 
+           && !strcmp((const char *) ptr->name, "log"))
+       {
+           const char *v = get_text(ptr);
+           *log_mask = 0;
+           while (v && *v)
+           {
+               const char *cp = v;
+               while (*cp && *cp != ',' && !isspace(*cp))
+                   cp++;
+               size_t len = cp - v;
+               if (mycmp(v, "client-apdu", len))
+                   *log_mask |= PROXY_LOG_APDU_CLIENT;
+               if (mycmp(v, "server-apdu", len))
+                   *log_mask |= PROXY_LOG_APDU_SERVER;
+               if (mycmp(v, "client-requests", len))
+                   *log_mask |= PROXY_LOG_REQ_CLIENT;
+               if (mycmp(v, "server-requests", len))
+                   *log_mask |= PROXY_LOG_REQ_SERVER;
+               if (isdigit(*v))
+                   *log_mask |= atoi(v);
+               if (*cp == ',')
+                   cp++;
+               while (*cp && isspace(*cp))
+                   cp++;
+               v = cp;
+           }
+       }
+       if (ptr->type == XML_ELEMENT_NODE &&
+           !strcmp((const char *) ptr->name, "max-clients"))
+       {
+           const char *t = get_text(ptr);
+           if (t)
+           {
+               *max_clients = atoi(t);
+               if (*max_clients  < 1)
+                   *max_clients = 1;
+           }
+       }
+    }
+#endif
+}
+
+char *Yaz_ProxyConfig::get_explain(ODR odr, const char *name, const char *db,
+                                  int *len)
+{
+#if HAVE_XSLT
+    xmlNodePtr ptr = find_target_node(name, db);
+    if (ptr)
+    {
+       ptr = ptr->children;
+       for (; ptr; ptr = ptr->next)
+           if (ptr->type == XML_ELEMENT_NODE &&
+               !strcmp((const char *) ptr->name, "explain"))
+           {
+               xmlNodePtr ptr1 = ptr->children;
+               if (db)
+               {
+                   for (; ptr1; ptr1 = ptr1->next)
+                       if (ptr1->type == XML_ELEMENT_NODE &&
+                           !strcmp((const char *) ptr1->name, "serverInfo"))
+                           break;
+                   if (!ptr1)
+                       continue;
+                   for (ptr1 = ptr1->children; ptr1; ptr1 = ptr1->next)
+                       if (ptr1->type == XML_ELEMENT_NODE &&
+                           !strcmp((const char *) ptr1->name, "database"))
+                           break;
+                   
+                   if (!ptr1)
+                       continue;
+                   for (ptr1 = ptr1->children; ptr1; ptr1 = ptr1->next)
+                       if (ptr1->type == XML_TEXT_NODE &&
+                           ptr1->content &&
+                           !strcmp((const char *) ptr1->content, db))
+                           break;
+                   if (!ptr1)
+                       continue;
+               }
+               xmlNodePtr ptr2 = xmlCopyNode(ptr, 1);
+
+               xmlDocPtr doc = xmlNewDoc((const xmlChar *) "1.0");
+               
+               xmlDocSetRootElement(doc, ptr2);
+               
+               xmlChar *buf_out;
+               xmlDocDumpMemory(doc, &buf_out, len);
+               char *content = (char*) odr_malloc(odr, *len);
+               memcpy(content, buf_out, *len);
+               
+               xmlFree(buf_out);
+               xmlFreeDoc(doc);
+               return content;
+           }
+    }
+#endif
+    return 0;
+}
+
+void Yaz_ProxyConfig::get_target_info(const char *name,
+                                     const char **url,
+                                     int *limit_bw,
+                                     int *limit_pdu,
+                                     int *limit_req,
+                                     int *target_idletime,
+                                     int *client_idletime,
+                                     int *max_clients,
+                                     int *keepalive_limit_bw,
+                                     int *keepalive_limit_pdu,
+                                     int *pre_init,
+                                     const char **cql2rpn)
+{
+#if HAVE_XSLT
+    xmlNodePtr ptr;
+    if (!m_proxyPtr)
+    {
+       url[0] = name;
+       url[1] = 0;
+       return;
+    }
+    url[0] = 0;
+    for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
+    {
+       if (ptr->type == XML_ELEMENT_NODE &&
+           !strcmp((const char *) ptr->name, "max-clients"))
+       {
+           const char *t = get_text(ptr);
+           if (t)
+           {
+               *max_clients = atoi(t);
+               if (*max_clients  < 1)
+                   *max_clients = 1;
+           }
+       }
+    }
+    ptr = find_target_node(name, 0);
+    if (ptr)
+    {
+       if (name)
+       {
+           url[0] = name;
+           url[1] = 0;
+       }
+       return_target_info(ptr, url, limit_bw, limit_pdu, limit_req,
+                          target_idletime, client_idletime,
+                          keepalive_limit_bw, keepalive_limit_pdu,
+                          pre_init, cql2rpn);
+    }
+#else
+    *url = name;
+    return;
+#endif
+}
+
+
diff --git a/proxy/yaz-proxy.cpp b/proxy/yaz-proxy.cpp
new file mode 100644 (file)
index 0000000..82cd41e
--- /dev/null
@@ -0,0 +1,2596 @@
+/* $Id: yaz-proxy.cpp,v 1.1 2004-03-29 22:46:51 adam Exp $
+   Copyright (c) 1998-2004, Index Data.
+
+This file is part of the yaz-proxy.
+
+Zebra is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with Zebra; see the file LICENSE.proxy.  If not, write to the
+Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.
+ */
+
+#include <unistd.h>
+#include <assert.h>
+#include <time.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include <yaz/srw.h>
+#include <yaz/marcdisp.h>
+#include <yaz/yaz-iconv.h>
+#include <yaz/log.h>
+#include <yaz/diagbib1.h>
+#include <yaz++/proxy/proxy.h>
+#include <yaz/pquery.h>
+
+static const char *apdu_name(Z_APDU *apdu)
+{
+    switch (apdu->which)
+    {
+    case Z_APDU_initRequest:
+        return "initRequest";
+    case Z_APDU_initResponse:
+        return "initResponse";
+    case Z_APDU_searchRequest:
+       return "searchRequest";
+    case Z_APDU_searchResponse:
+       return "searchResponse";
+    case Z_APDU_presentRequest:
+       return "presentRequest";
+    case Z_APDU_presentResponse:
+       return "presentResponse";
+    case Z_APDU_deleteResultSetRequest:
+       return "deleteResultSetRequest";
+    case Z_APDU_deleteResultSetResponse:
+       return "deleteResultSetResponse";
+    case Z_APDU_scanRequest:
+       return "scanRequest";
+    case Z_APDU_scanResponse:
+       return "scanResponse";
+    case Z_APDU_sortRequest:
+       return "sortRequest";
+    case Z_APDU_sortResponse:
+       return "sortResponse";
+    case Z_APDU_extendedServicesRequest:
+       return "extendedServicesRequest";
+    case Z_APDU_extendedServicesResponse:
+       return "extendedServicesResponse";
+    case Z_APDU_close:
+       return "close";
+    }
+    return "other";
+}
+
+static const char *gdu_name(Z_GDU *gdu)
+{
+    switch(gdu->which)
+    {
+    case Z_GDU_Z3950:
+       return apdu_name(gdu->u.z3950);
+    case Z_GDU_HTTP_Request:
+       return "HTTP Request";
+    case Z_GDU_HTTP_Response:
+       return "HTTP Response";
+    }
+    return "Unknown request/response";
+}
+
+Yaz_Proxy::Yaz_Proxy(IYaz_PDU_Observable *the_PDU_Observable,
+                    Yaz_Proxy *parent) :
+    Yaz_Z_Assoc(the_PDU_Observable), m_bw_stat(60), m_pdu_stat(60)
+{
+    m_PDU_Observable = the_PDU_Observable;
+    m_client = 0;
+    m_parent = parent;
+    m_clientPool = 0;
+    m_seqno = 1;
+    m_keepalive_limit_bw = 500000;
+    m_keepalive_limit_pdu = 1000;
+    m_proxyTarget = 0;
+    m_default_target = 0;
+    m_proxy_authentication = 0;
+    m_max_clients = 150;
+    m_log_mask = 0;
+    m_seed = time(0);
+    m_client_idletime = 600;
+    m_target_idletime = 600;
+    m_optimize = xstrdup ("1");
+    strcpy(m_session_str, "0 ");
+    m_session_no=0;
+    m_bytes_sent = 0;
+    m_bytes_recv = 0;
+    m_bw_hold_PDU = 0;
+    m_bw_max = 0;
+    m_pdu_max = 0;
+    m_max_record_retrieve = 0;
+    m_reconfig_flag = 0;
+    m_config_fname = 0;
+    m_request_no = 0;
+    m_invalid_session = 0;
+    m_referenceId = 0;
+    m_referenceId_mem = nmem_create();
+    m_config = 0;
+    m_marcxml_flag = 0;
+    m_stylesheet_xsp = 0;
+    m_stylesheet_nprl = 0;
+    m_s2z_stylesheet = 0;
+    m_s2z_database = 0;
+    m_schema = 0;
+    m_initRequest_apdu = 0;
+    m_initRequest_mem = 0;
+    m_initRequest_preferredMessageSize = 0;
+    m_initRequest_maximumRecordSize = 0;
+    m_initRequest_options = 0;
+    m_initRequest_version = 0;
+    m_apdu_invalid_session = 0;
+    m_mem_invalid_session = 0;
+    m_s2z_odr_init = 0;
+    m_s2z_odr_search = 0;
+    m_s2z_init_apdu = 0;
+    m_s2z_search_apdu = 0;
+    m_s2z_present_apdu = 0;
+    m_http_keepalive = 0;
+    m_http_version = 0;
+    m_soap_ns = 0;
+    m_s2z_packing = Z_SRW_recordPacking_string;
+    m_time_tv.tv_sec = 0;
+    m_time_tv.tv_usec = 0;
+    if (!m_parent)
+       low_socket_open();
+}
+
+Yaz_Proxy::~Yaz_Proxy()
+{
+    yaz_log(LOG_LOG, "%sClosed %d/%d sent/recv bytes total", m_session_str,
+           m_bytes_sent, m_bytes_recv);
+    nmem_destroy(m_initRequest_mem);
+    nmem_destroy(m_mem_invalid_session);
+    nmem_destroy(m_referenceId_mem);
+
+    xfree (m_proxyTarget);
+    xfree (m_default_target);
+    xfree (m_proxy_authentication);
+    xfree (m_optimize);
+
+    if (m_stylesheet_xsp)
+       xsltFreeStylesheet(m_stylesheet_xsp);
+
+    xfree (m_schema);
+    if (m_s2z_odr_init)
+       odr_destroy(m_s2z_odr_init);
+    if (m_s2z_odr_search)
+       odr_destroy(m_s2z_odr_search);
+    if (!m_parent)
+       low_socket_close();
+    delete m_config;
+}
+
+int Yaz_Proxy::set_config(const char *config)
+{
+    delete m_config;
+    m_config = new Yaz_ProxyConfig();
+    xfree(m_config_fname);
+    m_config_fname = xstrdup(config);
+    int r = m_config->read_xml(config);
+    if (!r)
+       m_config->get_generic_info(&m_log_mask, &m_max_clients);
+    return r;
+}
+
+void Yaz_Proxy::set_default_target(const char *target)
+{
+    xfree (m_default_target);
+    m_default_target = 0;
+    if (target)
+       m_default_target = (char *) xstrdup (target);
+}
+
+void Yaz_Proxy::set_proxy_authentication (const char *auth)
+{
+    xfree (m_proxy_authentication);
+    m_proxy_authentication = 0;
+    if (auth)
+       m_proxy_authentication = (char *) xstrdup (auth);
+}
+
+Yaz_ProxyConfig *Yaz_Proxy::check_reconfigure()
+{
+    if (m_parent)
+       return m_parent->check_reconfigure();
+
+    Yaz_ProxyConfig *cfg = m_config;
+    if (m_reconfig_flag)
+    {
+       yaz_log(LOG_LOG, "reconfigure");
+       yaz_log_reopen();
+       if (m_config_fname && cfg)
+       {
+           yaz_log(LOG_LOG, "reconfigure config %s", m_config_fname);
+           int r = cfg->read_xml(m_config_fname);
+           if (r)
+               yaz_log(LOG_WARN, "reconfigure failed");
+           else
+           {
+               m_log_mask = 0;
+               cfg->get_generic_info(&m_log_mask, &m_max_clients);
+           }
+       }
+       else
+           yaz_log(LOG_LOG, "reconfigure");
+       m_reconfig_flag = 0;
+    }
+    return cfg;
+}
+
+IYaz_PDU_Observer *Yaz_Proxy::sessionNotify(IYaz_PDU_Observable
+                                           *the_PDU_Observable, int fd)
+{
+    check_reconfigure();
+    Yaz_Proxy *new_proxy = new Yaz_Proxy(the_PDU_Observable, this);
+    new_proxy->m_config = 0;
+    new_proxy->m_config_fname = 0;
+    new_proxy->timeout(m_client_idletime);
+    new_proxy->m_target_idletime = m_target_idletime;
+    new_proxy->set_default_target(m_default_target);
+    new_proxy->m_max_clients = m_max_clients;
+    new_proxy->m_log_mask = m_log_mask;
+    new_proxy->set_APDU_log(get_APDU_log());
+    if (m_log_mask & PROXY_LOG_APDU_CLIENT)
+       new_proxy->set_APDU_yazlog(1);
+    else
+       new_proxy->set_APDU_yazlog(0);
+    new_proxy->set_proxy_authentication(m_proxy_authentication);
+    sprintf(new_proxy->m_session_str, "%ld:%d ", (long) time(0), m_session_no);
+    m_session_no++;
+    yaz_log (LOG_LOG, "%sNew session %s", new_proxy->m_session_str,
+            the_PDU_Observable->getpeername());
+    return new_proxy;
+}
+
+char *Yaz_Proxy::get_cookie(Z_OtherInformation **otherInfo)
+{
+    int oid[OID_SIZE];
+    Z_OtherInformationUnit *oi;
+    struct oident ent;
+    ent.proto = PROTO_Z3950;
+    ent.oclass = CLASS_USERINFO;
+    ent.value = (oid_value) VAL_COOKIE;
+    assert (oid_ent_to_oid (&ent, oid));
+
+    if (oid_ent_to_oid (&ent, oid) && 
+       (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
+       oi->which == Z_OtherInfo_characterInfo)
+       return oi->information.characterInfo;
+    return 0;
+}
+
+char *Yaz_Proxy::get_proxy(Z_OtherInformation **otherInfo)
+{
+    int oid[OID_SIZE];
+    Z_OtherInformationUnit *oi;
+    struct oident ent;
+    ent.proto = PROTO_Z3950;
+    ent.oclass = CLASS_USERINFO;
+    ent.value = (oid_value) VAL_PROXY;
+    if (oid_ent_to_oid (&ent, oid) &&
+       (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
+       oi->which == Z_OtherInfo_characterInfo)
+       return oi->information.characterInfo;
+    return 0;
+}
+
+const char *Yaz_Proxy::load_balance(const char **url)
+{
+    int zurl_in_use[MAX_ZURL_PLEX];
+    int zurl_in_spare[MAX_ZURL_PLEX];
+    Yaz_ProxyClient *c;
+    int i;
+
+    for (i = 0; i<MAX_ZURL_PLEX; i++)
+    {
+       zurl_in_use[i] = 0;
+       zurl_in_spare[i] = 0;
+    }
+    for (c = m_parent->m_clientPool; c; c = c->m_next)
+    {
+       for (i = 0; url[i]; i++)
+           if (!strcmp(url[i], c->get_hostname()))
+           {
+               zurl_in_use[i]++;
+               if (c->m_cookie == 0 && c->m_server == 0 && c->m_waiting == 0)
+                   zurl_in_spare[i]++;
+           }
+    }
+    int min_use = 100000;
+    int spare_for_min = 0;
+    int max_spare = 0;
+    const char *ret_min = 0;
+    const char *ret_spare = 0;
+    for (i = 0; url[i]; i++)
+    {
+       yaz_log(LOG_DEBUG, "%szurl=%s use=%d spare=%d",
+               m_session_str, url[i], zurl_in_use[i], zurl_in_spare[i]);
+       if (min_use > zurl_in_use[i])
+       {
+           ret_min = url[i];
+           min_use = zurl_in_use[i];
+           spare_for_min = zurl_in_spare[i];
+       }
+       if (max_spare < zurl_in_spare[i]) 
+       {
+           ret_spare = url[i];
+           max_spare = zurl_in_spare[i];
+       }
+    }
+    // use the one with minimum connections if spare is > 3
+    if (spare_for_min > 3)
+       return ret_min;
+    // use one with most spares (if any)
+    if (max_spare > 0)
+       return ret_spare;
+    return ret_min;
+}
+
+Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
+                                      const char *proxy_host)
+{
+    assert (m_parent);
+    Yaz_Proxy *parent = m_parent;
+    Yaz_ProxyClient *c = m_client;
+    
+    if (!m_proxyTarget)
+    {
+       const char *url[MAX_ZURL_PLEX];
+       Yaz_ProxyConfig *cfg = check_reconfigure();
+       if (proxy_host)
+       {
+#if 1
+/* only to be enabled for debugging... */
+           if (!strcmp(proxy_host, "stop"))
+               exit(0);
+#endif
+           xfree(m_default_target);
+           m_default_target = xstrdup(proxy_host);
+           proxy_host = m_default_target;
+       }
+       int client_idletime = -1;
+       const char *cql2rpn_fname = 0;
+       url[0] = m_default_target;
+       url[1] = 0;
+       if (cfg)
+       {
+           int pre_init = 0;
+           cfg->get_target_info(proxy_host, url, &m_bw_max,
+                                &m_pdu_max, &m_max_record_retrieve,
+                                &m_target_idletime, &client_idletime,
+                                &parent->m_max_clients,
+                                &m_keepalive_limit_bw,
+                                &m_keepalive_limit_pdu,
+                                &pre_init,
+                                &cql2rpn_fname);
+       }
+       if (client_idletime != -1)
+       {
+           m_client_idletime = client_idletime;
+           timeout(m_client_idletime);
+       }
+       if (cql2rpn_fname)
+           m_cql2rpn.set_pqf_file(cql2rpn_fname);
+       if (!url[0])
+       {
+           yaz_log(LOG_LOG, "%sNo default target", m_session_str);
+           return 0;
+       }
+       // we don't handle multiplexing for cookie session, so we just
+       // pick the first one in this case (anonymous users will be able
+       // to use any backend)
+       if (cookie && *cookie)
+           m_proxyTarget = (char*) xstrdup(url[0]);
+       else
+           m_proxyTarget = (char*) xstrdup(load_balance(url));
+    }
+    if (cookie && *cookie)
+    {   // search in sessions with a cookie
+       for (c = parent->m_clientPool; c; c = c->m_next)
+       {
+           assert (c->m_prev);
+           assert (*c->m_prev == c);
+           if (c->m_cookie && !strcmp(cookie,c->m_cookie) &&
+               !strcmp(m_proxyTarget, c->get_hostname()))
+           {
+               // Found it in cache
+               // The following handles "cancel"
+               // If connection is busy (waiting for PDU) and
+               // we have an initRequest we can safely do re-open
+               if (c->m_waiting && apdu->which == Z_APDU_initRequest)
+               {
+                   yaz_log (LOG_LOG, "%s REOPEN target=%s", m_session_str,
+                            c->get_hostname());
+                   c->close();
+                   c->m_init_flag = 0;
+                   
+                   c->m_last_ok = 0;
+                   c->m_cache.clear();
+                   c->m_last_resultCount = 0;
+                   c->m_sr_transform = 0;
+                   c->m_waiting = 0;
+                   c->m_resultSetStartPoint = 0;
+                   c->m_target_idletime = m_target_idletime;
+                   if (c->client(m_proxyTarget))
+                   {
+                       delete c;
+                       return 0;
+                   }
+                   c->timeout(30); 
+               }
+               c->m_seqno = parent->m_seqno;
+               if (c->m_server && c->m_server != this)
+                   c->m_server->m_client = 0;
+               c->m_server = this;
+               (parent->m_seqno)++;
+               yaz_log (LOG_DEBUG, "get_client 1 %p %p", this, c);
+               return c;
+           }
+       }
+    }
+    else if (!c)
+    {
+       // don't have a client session yet. Search in session w/o cookie
+       for (c = parent->m_clientPool; c; c = c->m_next)
+       {
+           assert (c->m_prev);
+           assert (*c->m_prev == c);
+           if (c->m_server == 0 && c->m_cookie == 0 && 
+               c->m_waiting == 0 &&
+               !strcmp(m_proxyTarget, c->get_hostname()))
+           {
+               // found it in cache
+               yaz_log (LOG_LOG, "%sREUSE %d %s",
+                        m_session_str, parent->m_seqno, c->get_hostname());
+               
+               c->m_seqno = parent->m_seqno;
+               assert(c->m_server == 0);
+               c->m_server = this;
+
+               if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
+                   c->set_APDU_yazlog(1);
+               else
+                   c->set_APDU_yazlog(0);
+
+               (parent->m_seqno)++;
+               
+               parent->pre_init();
+               
+               return c;
+           }
+       }
+    }
+    if (!m_client)
+    {
+       if (apdu->which != Z_APDU_initRequest)
+       {
+           yaz_log (LOG_LOG, "%sno init request as first PDU", m_session_str);
+           return 0;
+       }
+        Z_InitRequest *initRequest = apdu->u.initRequest;
+
+        if (!initRequest->idAuthentication)
+        {
+            if (m_proxy_authentication)
+            {
+                initRequest->idAuthentication =
+                    (Z_IdAuthentication *)
+                    odr_malloc (odr_encode(),
+                                sizeof(*initRequest->idAuthentication));
+                initRequest->idAuthentication->which =
+                    Z_IdAuthentication_open;
+                initRequest->idAuthentication->u.open =
+                    odr_strdup (odr_encode(), m_proxy_authentication);
+            }
+        }
+       // go through list of clients - and find the lowest/oldest one.
+       Yaz_ProxyClient *c_min = 0;
+       int min_seq = -1;
+       int no_of_clients = 0;
+       if (parent->m_clientPool)
+           yaz_log (LOG_DEBUG, "Existing sessions");
+       for (c = parent->m_clientPool; c; c = c->m_next)
+       {
+           yaz_log (LOG_DEBUG, " Session %-3d wait=%d %s cookie=%s", c->m_seqno,
+                              c->m_waiting, c->get_hostname(),
+                              c->m_cookie ? c->m_cookie : "");
+           no_of_clients++;
+           if (min_seq < 0 || c->m_seqno < min_seq)
+           {
+               min_seq = c->m_seqno;
+               c_min = c;
+           }
+       }
+       if (no_of_clients >= parent->m_max_clients)
+       {
+           c = c_min;
+           if (c->m_waiting || strcmp(m_proxyTarget, c->get_hostname()))
+           {
+               yaz_log (LOG_LOG, "%sMAXCLIENTS %d Destroy %d",
+                        m_session_str, parent->m_max_clients, c->m_seqno);
+               if (c->m_server && c->m_server != this)
+                   delete c->m_server;
+               c->m_server = 0;
+           }
+           else
+           {
+               yaz_log (LOG_LOG, "%sMAXCLIENTS %d Reuse %d %d %s",
+                        m_session_str, parent->m_max_clients,
+                        c->m_seqno, parent->m_seqno, c->get_hostname());
+               xfree (c->m_cookie);
+               c->m_cookie = 0;
+               if (cookie)
+                   c->m_cookie = xstrdup(cookie);
+               c->m_seqno = parent->m_seqno;
+               if (c->m_server && c->m_server != this)
+               {
+                   c->m_server->m_client = 0;
+                   delete c->m_server;
+               }
+               (parent->m_seqno)++;
+               c->m_target_idletime = m_target_idletime;
+               c->timeout(m_target_idletime);
+               
+               if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
+                   c->set_APDU_yazlog(1);
+               else
+                   c->set_APDU_yazlog(0);
+
+               return c;
+           }
+       }
+       else
+       {
+           yaz_log (LOG_LOG, "%sNEW %d %s",
+                    m_session_str, parent->m_seqno, m_proxyTarget);
+           c = new Yaz_ProxyClient(m_PDU_Observable->clone(), parent);
+           c->m_next = parent->m_clientPool;
+           if (c->m_next)
+               c->m_next->m_prev = &c->m_next;
+           parent->m_clientPool = c;
+           c->m_prev = &parent->m_clientPool;
+       }
+
+       xfree (c->m_cookie);
+       c->m_cookie = 0;
+       if (cookie)
+           c->m_cookie = xstrdup(cookie);
+
+       c->m_seqno = parent->m_seqno;
+       c->m_init_flag = 0;
+       c->m_last_resultCount = 0;
+        c->m_last_ok = 0;
+       c->m_cache.clear();
+       c->m_sr_transform = 0;
+       c->m_waiting = 0;
+       c->m_resultSetStartPoint = 0;
+       (parent->m_seqno)++;
+       if (c->client(m_proxyTarget))
+       {
+           delete c;
+           return 0;
+        }
+       c->m_target_idletime = m_target_idletime;
+       c->timeout(30);
+
+       if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
+           c->set_APDU_yazlog(1);
+       else
+           c->set_APDU_yazlog(0);
+    }
+    yaz_log (LOG_DEBUG, "get_client 3 %p %p", this, c);
+    return c;
+}
+
+void Yaz_Proxy::display_diagrecs(Z_DiagRec **pp, int num)
+{
+    int i;
+    for (i = 0; i<num; i++)
+    {
+       oident *ent;
+       Z_DefaultDiagFormat *r;
+        Z_DiagRec *p = pp[i];
+        if (p->which != Z_DiagRec_defaultFormat)
+        {
+           yaz_log(LOG_LOG, "%sError no diagnostics", m_session_str);
+            return;
+        }
+        else
+            r = p->u.defaultFormat;
+        if (!(ent = oid_getentbyoid(r->diagnosticSetId)) ||
+            ent->oclass != CLASS_DIAGSET || ent->value != VAL_BIB1)
+           yaz_log(LOG_LOG, "%sError unknown diagnostic set", m_session_str);
+        switch (r->which)
+        {
+        case Z_DefaultDiagFormat_v2Addinfo:
+           yaz_log(LOG_LOG, "%sError %d %s:%s",
+                   m_session_str,
+                   *r->condition, diagbib1_str(*r->condition),
+                   r->u.v2Addinfo);
+            break;
+        case Z_DefaultDiagFormat_v3Addinfo:
+           yaz_log(LOG_LOG, "%sError %d %s:%s",
+                   m_session_str,
+                   *r->condition, diagbib1_str(*r->condition),
+                   r->u.v3Addinfo);
+            break;
+        }
+    }
+}
+
+int Yaz_Proxy::convert_xsl(Z_NamePlusRecordList *p, Z_APDU *apdu)
+{
+    if (!m_stylesheet_xsp || p->num_records <= 0)
+       return 0;  /* no XSLT to be done ... */
+
+    m_stylesheet_offset = 0;
+    m_stylesheet_nprl = p;
+    m_stylesheet_apdu = apdu;
+    timeout(0);
+    return 1;
+}
+
+void Yaz_Proxy::convert_xsl_delay()
+{
+    Z_NamePlusRecord *npr = m_stylesheet_nprl->records[m_stylesheet_offset];
+    if (npr->which == Z_NamePlusRecord_databaseRecord)
+    {
+       Z_External *r = npr->u.databaseRecord;
+       if (r->which == Z_External_octet)
+       {
+#if 0
+           fwrite((char*) r->u.octet_aligned->buf, 1, r->u.octet_aligned->len, stdout);
+#endif
+           xmlDocPtr res, doc = xmlParseMemory(
+               (char*) r->u.octet_aligned->buf,
+               r->u.octet_aligned->len);
+
+           
+           yaz_log(LOG_LOG, "%sXSLT convert %d",
+                   m_session_str, m_stylesheet_offset);
+           res = xsltApplyStylesheet(m_stylesheet_xsp, doc, 0);
+
+           if (res)
+           {
+               xmlChar *out_buf;
+               int out_len;
+               xmlDocDumpFormatMemory (res, &out_buf, &out_len, 1);
+               
+               m_stylesheet_nprl->records[m_stylesheet_offset]->
+                   u.databaseRecord = 
+                   z_ext_record(odr_encode(), VAL_TEXT_XML,
+                                (char*) out_buf, out_len);
+               xmlFree(out_buf);
+               xmlFreeDoc(res);
+           }
+
+           xmlFreeDoc(doc);
+       }
+    }
+    m_stylesheet_offset++;
+    if (m_stylesheet_offset == m_stylesheet_nprl->num_records)
+    {
+       m_stylesheet_nprl = 0;
+       if (m_stylesheet_xsp)
+           xsltFreeStylesheet(m_stylesheet_xsp);
+       m_stylesheet_xsp = 0;
+       timeout(m_client_idletime);
+       send_PDU_convert(m_stylesheet_apdu);
+    }
+    else
+       timeout(0);
+}
+
+void Yaz_Proxy::convert_to_marcxml(Z_NamePlusRecordList *p)
+{
+    int i;
+
+    yaz_iconv_t cd = yaz_iconv_open("UTF-8", "MARC-8");
+    yaz_marc_t mt = yaz_marc_create();
+    yaz_marc_xml(mt, YAZ_MARC_MARCXML);
+    yaz_marc_iconv(mt, cd);
+    for (i = 0; i < p->num_records; i++)
+    {
+       Z_NamePlusRecord *npr = p->records[i];
+       if (npr->which == Z_NamePlusRecord_databaseRecord)
+       {
+           Z_External *r = npr->u.databaseRecord;
+           if (r->which == Z_External_octet)
+           {
+               int rlen;
+               char *result;
+               if (yaz_marc_decode_buf(mt, (char*) r->u.octet_aligned->buf,
+                                       r->u.octet_aligned->len,
+                                       &result, &rlen))
+               {
+                   npr->u.databaseRecord = z_ext_record(odr_encode(),
+                                                        VAL_TEXT_XML,
+                                                        result, rlen);
+               }
+           }
+       }
+    }
+    if (cd)
+       yaz_iconv_close(cd);
+    yaz_marc_destroy(mt);
+}
+
+void Yaz_Proxy::logtime()
+{
+    if (m_time_tv.tv_sec)
+    {
+       struct timeval tv;
+       gettimeofday(&tv, 0);
+       long diff = (tv.tv_sec - m_time_tv.tv_sec)*1000000 +
+           (tv.tv_usec - m_time_tv.tv_usec);
+       if (diff >= 0)
+           yaz_log(LOG_LOG, "%sElapsed %ld.%03ld", m_session_str,
+                   diff/1000000, (diff/1000)%1000);
+    }
+    m_time_tv.tv_sec = 0;
+    m_time_tv.tv_usec = 0;
+}
+
+int Yaz_Proxy::send_http_response(int code)
+{
+    ODR o = odr_encode();
+    Z_GDU *gdu = z_get_HTTP_Response(o, code);
+    Z_HTTP_Response *hres = gdu->u.HTTP_Response;
+    if (m_http_version)
+       hres->version = odr_strdup(o, m_http_version);
+    if (m_http_keepalive)
+        z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
+    else
+       timeout(0);
+    
+    if (m_log_mask & PROXY_LOG_REQ_CLIENT)
+    {
+       yaz_log (LOG_LOG, "%sSending %s to client", m_session_str,
+                gdu_name(gdu));
+    }
+    int len;
+    int r = send_GDU(gdu, &len);
+    m_bytes_sent += len;
+    m_bw_stat.add_bytes(len);
+    logtime();
+    return r;
+}
+
+int Yaz_Proxy::send_srw_response(Z_SRW_PDU *srw_pdu)
+{
+    ODR o = odr_encode();
+    const char *ctype = "text/xml";
+    Z_GDU *gdu = z_get_HTTP_Response(o, 200);
+    Z_HTTP_Response *hres = gdu->u.HTTP_Response;
+    if (m_http_version)
+       hres->version = odr_strdup(o, m_http_version);
+    z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
+    if (m_http_keepalive)
+        z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
+    else
+       timeout(0);
+
+    static Z_SOAP_Handler soap_handlers[2] = {
+#if HAVE_XSLT
+       {"http://www.loc.gov/zing/srw/", 0,
+        (Z_SOAP_fun) yaz_srw_codec},
+#endif
+       {0, 0, 0}
+    };
+    
+    Z_SOAP *soap_package = (Z_SOAP*) odr_malloc(o, sizeof(Z_SOAP));
+    soap_package->which = Z_SOAP_generic;
+    soap_package->u.generic = 
+       (Z_SOAP_Generic *) odr_malloc(o,  sizeof(*soap_package->u.generic));
+    soap_package->u.generic->no = 0;
+    soap_package->u.generic->ns = soap_handlers[0].ns;
+    soap_package->u.generic->p = (void *) srw_pdu;
+    soap_package->ns = m_soap_ns;
+    z_soap_codec_enc_xsl(o, &soap_package,
+                        &hres->content_buf, &hres->content_len,
+                        soap_handlers, 0, m_s2z_stylesheet);
+    if (m_log_mask & PROXY_LOG_REQ_CLIENT)
+    {
+       yaz_log (LOG_LOG, "%sSending %s to client", m_session_str,
+                gdu_name(gdu));
+    }
+    int len;
+    int r = send_GDU(gdu, &len);
+    m_bytes_sent += len;
+    m_bw_stat.add_bytes(len);
+    logtime();
+    return r;
+}
+
+int Yaz_Proxy::send_to_srw_client_error(int srw_error, const char *add)
+{
+    ODR o = odr_encode();
+    Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
+    Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
+
+    srw_res->num_diagnostics = 1;
+    srw_res->diagnostics = (Z_SRW_diagnostic *)
+       odr_malloc(o, sizeof(*srw_res->diagnostics));
+    yaz_mk_std_diagnostic(o, srw_res->diagnostics, srw_error, add);
+    return send_srw_response(srw_pdu);
+}
+
+int Yaz_Proxy::z_to_srw_diag(ODR o, Z_SRW_searchRetrieveResponse *srw_res,
+                            Z_DefaultDiagFormat *ddf)
+{
+    int bib1_code = *ddf->condition;
+    if (bib1_code == 109)
+       return 404;
+    srw_res->num_diagnostics = 1;
+    srw_res->diagnostics = (Z_SRW_diagnostic *)
+       odr_malloc(o, sizeof(*srw_res->diagnostics));
+    yaz_mk_std_diagnostic(o, srw_res->diagnostics,
+                         yaz_diag_bib1_to_srw(*ddf->condition), 
+                         ddf->u.v2Addinfo);
+    return 0;
+}
+
+int Yaz_Proxy::send_to_srw_client_ok(int hits, Z_Records *records, int start)
+{
+    ODR o = odr_encode();
+    Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
+    Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
+
+    srw_res->numberOfRecords = odr_intdup (o, hits);
+    if (records && records->which == Z_Records_DBOSD)
+    {
+       srw_res->num_records =
+           records->u.databaseOrSurDiagnostics->num_records;
+       int i;
+       srw_res->records = (Z_SRW_record *)
+           odr_malloc(o, srw_res->num_records * sizeof(Z_SRW_record));
+       for (i = 0; i < srw_res->num_records; i++)
+       {
+           Z_NamePlusRecord *npr = records->u.databaseOrSurDiagnostics->records[i];
+           if (npr->which != Z_NamePlusRecord_databaseRecord)
+           {
+               srw_res->records[i].recordSchema = "diagnostic";
+               srw_res->records[i].recordPacking = m_s2z_packing;
+               srw_res->records[i].recordData_buf = "67";
+               srw_res->records[i].recordData_len = 2;
+               srw_res->records[i].recordPosition = odr_intdup(o, i+start);
+               continue;
+           }
+           Z_External *r = npr->u.databaseRecord;
+           oident *ent = oid_getentbyoid(r->direct_reference);
+           if (r->which == Z_External_octet && ent->value == VAL_TEXT_XML)
+           {
+               srw_res->records[i].recordSchema = m_schema;
+               srw_res->records[i].recordPacking = m_s2z_packing;
+               srw_res->records[i].recordData_buf = (char*) 
+                   r->u.octet_aligned->buf;
+               srw_res->records[i].recordData_len = r->u.octet_aligned->len;
+               srw_res->records[i].recordPosition = odr_intdup(o, i+start);
+           }
+           else
+           {
+               srw_res->records[i].recordSchema = "diagnostic";
+               srw_res->records[i].recordPacking = m_s2z_packing;
+               srw_res->records[i].recordData_buf = "67";
+               srw_res->records[i].recordData_len = 2;
+               srw_res->records[i].recordPosition = odr_intdup(o, i+start);
+           }
+       }
+    }
+    if (records && records->which == Z_Records_NSD)
+    {
+       int http_code;
+       http_code = z_to_srw_diag(odr_encode(), srw_res,
+                                  records->u.nonSurrogateDiagnostic);
+       if (http_code)
+           return send_http_response(http_code);
+    }
+    return send_srw_response(srw_pdu);
+    
+}
+
+int Yaz_Proxy::send_srw_explain_response(Z_SRW_diagnostic *diagnostics,
+                                       int num_diagnostics)
+{
+    Yaz_ProxyConfig *cfg = check_reconfigure();
+    if (cfg)
+    {
+       int len;
+       char *b = cfg->get_explain(odr_encode(), 0 /* target */,
+                                  m_s2z_database, &len);
+       if (b)
+       {
+           Z_SRW_PDU *res = yaz_srw_get(odr_encode(), Z_SRW_explain_response);
+           Z_SRW_explainResponse *er = res->u.explain_response;
+
+           er->record.recordData_buf = b;
+           er->record.recordData_len = len;
+           er->record.recordPacking = m_s2z_packing;
+           er->record.recordSchema = "http://explain.z3950.org/dtd/2.0/";
+
+           er->diagnostics = diagnostics;
+           er->num_diagnostics = num_diagnostics;
+           return send_srw_response(res);
+       }
+    }
+    return send_http_response(404);
+}
+
+int Yaz_Proxy::send_PDU_convert(Z_APDU *apdu)
+{
+    if (m_http_version)
+    {
+       if (apdu->which == Z_APDU_initResponse)
+       {
+           Z_InitResponse *res = apdu->u.initResponse;
+           if (*res->result == 0)
+           {
+               send_to_srw_client_error(3, 0);
+           }
+           else if (!m_s2z_search_apdu)
+           {
+               send_srw_explain_response(0, 0);
+           }
+           else
+           {
+               handle_incoming_Z_PDU(m_s2z_search_apdu);
+           }
+       }
+       else if (m_s2z_search_apdu && apdu->which == Z_APDU_searchResponse)
+       {
+           m_s2z_search_apdu = 0;
+           Z_SearchResponse *res = apdu->u.searchResponse;
+           m_s2z_hit_count = *res->resultCount;
+           if (res->records && res->records->which == Z_Records_NSD)
+           {
+               send_to_srw_client_ok(0, res->records, 1);
+           }
+           else if (m_s2z_present_apdu && m_s2z_hit_count > 0)
+           {
+               // adjust 
+               Z_PresentRequest *pr = m_s2z_present_apdu->u.presentRequest;
+               
+               if (*pr->resultSetStartPoint <= m_s2z_hit_count)
+               {
+                   if (*pr->numberOfRecordsRequested+ *pr->resultSetStartPoint
+                       > m_s2z_hit_count)
+                       *pr->numberOfRecordsRequested =
+                           1 + m_s2z_hit_count - *pr->resultSetStartPoint;
+               }
+               handle_incoming_Z_PDU(m_s2z_present_apdu);
+           }
+           else
+           {
+               m_s2z_present_apdu = 0;
+               send_to_srw_client_ok(m_s2z_hit_count, res->records, 1);
+           }
+       }
+       else if (m_s2z_present_apdu && apdu->which == Z_APDU_presentResponse)
+       {
+           int start = 
+               *m_s2z_present_apdu->u.presentRequest->resultSetStartPoint;
+
+           m_s2z_present_apdu = 0;
+           Z_PresentResponse *res = apdu->u.presentResponse;
+           send_to_srw_client_ok(m_s2z_hit_count, res->records, start);
+       }
+    }
+    else
+    {
+       int len = 0;
+       if (m_log_mask & PROXY_LOG_REQ_CLIENT)
+           yaz_log (LOG_LOG, "%sSending %s to client", m_session_str,
+                    apdu_name(apdu));
+       int r = send_Z_PDU(apdu, &len);
+       m_bytes_sent += len;
+       m_bw_stat.add_bytes(len);
+       logtime();
+       return r;
+    }
+    return 0;
+}
+
+int Yaz_Proxy::send_to_client(Z_APDU *apdu)
+{
+    int kill_session = 0;
+    Z_ReferenceId **new_id = get_referenceIdP(apdu);
+
+    if (new_id)
+       *new_id = m_referenceId;
+    
+    if (apdu->which == Z_APDU_searchResponse)
+    {
+       Z_SearchResponse *sr = apdu->u.searchResponse;
+       Z_Records *p = sr->records;
+       if (p && p->which == Z_Records_NSD)
+       {
+           Z_DiagRec dr, *dr_p = &dr;
+           dr.which = Z_DiagRec_defaultFormat;
+           dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
+
+           *sr->searchStatus = 0;
+           display_diagrecs(&dr_p, 1);
+       }
+       else
+       {
+           if (p && p->which == Z_Records_DBOSD)
+           {
+               if (m_marcxml_flag)
+                   convert_to_marcxml(p->u.databaseOrSurDiagnostics);
+               if (convert_xsl(p->u.databaseOrSurDiagnostics, apdu))
+                   return 0;
+                   
+           }
+           if (sr->resultCount)
+           {
+               yaz_log(LOG_LOG, "%s%d hits", m_session_str,
+                       *sr->resultCount);
+               if (*sr->resultCount < 0)
+               {
+                   m_invalid_session = 1;
+                   kill_session = 1;
+
+                   *sr->searchStatus = 0;
+                   sr->records =
+                       create_nonSurrogateDiagnostics(odr_encode(), 2, 0);
+                   *sr->resultCount = 0;
+               }
+           }
+       }
+    }
+    else if (apdu->which == Z_APDU_presentResponse)
+    {
+       Z_PresentResponse *sr = apdu->u.presentResponse;
+       Z_Records *p = sr->records;
+       if (p && p->which == Z_Records_NSD)
+       {
+           Z_DiagRec dr, *dr_p = &dr;
+           dr.which = Z_DiagRec_defaultFormat;
+           dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
+           if (*sr->presentStatus == Z_PresentStatus_success)
+               *sr->presentStatus = Z_PresentStatus_failure;
+           display_diagrecs(&dr_p, 1);
+       }
+       if (p && p->which == Z_Records_DBOSD)
+       {
+           if (m_marcxml_flag)
+               convert_to_marcxml(p->u.databaseOrSurDiagnostics);
+           if (convert_xsl(p->u.databaseOrSurDiagnostics, apdu))
+               return 0;
+       }
+    }
+    else if (apdu->which == Z_APDU_initResponse)
+    {
+       if (m_initRequest_options)
+       {
+           Z_Options *nopt = 
+               (Odr_bitmask *)odr_malloc(odr_encode(),
+                                         sizeof(Odr_bitmask));
+           ODR_MASK_ZERO(nopt);
+
+           int i;
+           for (i = 0; i<24; i++)
+               if (ODR_MASK_GET(m_initRequest_options, i) &&
+                   ODR_MASK_GET(apdu->u.initResponse->options, i))
+                   ODR_MASK_SET(nopt, i);
+           apdu->u.initResponse->options = nopt;           
+       }
+       if (m_initRequest_version)
+       {
+           Z_ProtocolVersion *nopt = 
+               (Odr_bitmask *)odr_malloc(odr_encode(),
+                                         sizeof(Odr_bitmask));
+           ODR_MASK_ZERO(nopt);
+
+           int i;
+           for (i = 0; i<8; i++)
+               if (ODR_MASK_GET(m_initRequest_version, i) &&
+                   ODR_MASK_GET(apdu->u.initResponse->protocolVersion, i))
+                   ODR_MASK_SET(nopt, i);
+           apdu->u.initResponse->protocolVersion = nopt;           
+       }
+       apdu->u.initResponse->preferredMessageSize =
+           odr_intdup(odr_encode(),
+                      m_client->m_initResponse_preferredMessageSize >
+                      m_initRequest_preferredMessageSize ?
+                      m_initRequest_preferredMessageSize :
+                      m_client->m_initResponse_preferredMessageSize);
+       apdu->u.initResponse->maximumRecordSize =
+           odr_intdup(odr_encode(),
+                      m_client->m_initResponse_maximumRecordSize >
+                      m_initRequest_maximumRecordSize ?
+                      m_initRequest_maximumRecordSize :
+                      m_client->m_initResponse_maximumRecordSize);
+    }
+    int r = send_PDU_convert(apdu);
+    if (r)
+       return r;
+    if (kill_session)
+    {
+       delete m_client;
+       m_client = 0;
+       m_parent->pre_init();
+    }
+    return r;
+}
+
+int Yaz_ProxyClient::send_to_target(Z_APDU *apdu)
+{
+    int len = 0;
+    const char *apdu_name_tmp = apdu_name(apdu);
+    int r = send_Z_PDU(apdu, &len);
+    if (m_root->get_log_mask() & PROXY_LOG_REQ_SERVER)
+       yaz_log (LOG_LOG, "%sSending %s to %s %d bytes",
+                get_session_str(),
+                apdu_name_tmp, get_hostname(), len);
+    m_bytes_sent += len;
+    return r;
+}
+
+Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
+{
+    if (apdu->which == Z_APDU_presentRequest)
+    {
+       Z_PresentRequest *pr = apdu->u.presentRequest;
+       int toget = *pr->numberOfRecordsRequested;
+       int start = *pr->resultSetStartPoint;
+
+       yaz_log(LOG_LOG, "%sPresent %s %d+%d", m_session_str,
+               pr->resultSetId, start, toget);
+
+       if (*m_parent->m_optimize == '0')
+           return apdu;
+
+       if (!m_client->m_last_resultSetId)
+       {
+           Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
+           new_apdu->u.presentResponse->records =
+               create_nonSurrogateDiagnostics(odr_encode(), 30,
+                                              pr->resultSetId);
+           send_to_client(new_apdu);
+           return 0;
+       }
+       if (!strcmp(m_client->m_last_resultSetId, pr->resultSetId))
+       {
+           if (start+toget-1 > m_client->m_last_resultCount)
+           {
+               Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
+               new_apdu->u.presentResponse->records =
+                   create_nonSurrogateDiagnostics(odr_encode(), 13, 0);
+               send_to_client(new_apdu);
+               return 0;
+           }
+           Z_NamePlusRecordList *npr;
+           if (m_client->m_cache.lookup (odr_encode(), &npr, start, toget,
+                                         pr->preferredRecordSyntax,
+                                         pr->recordComposition))
+           {
+               yaz_log (LOG_LOG, "%sReturned cached records for present request", 
+                        m_session_str);
+               Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
+               new_apdu->u.presentResponse->referenceId = pr->referenceId;
+               
+               new_apdu->u.presentResponse->numberOfRecordsReturned
+                   = odr_intdup(odr_encode(), toget);
+                                                                
+               new_apdu->u.presentResponse->records = (Z_Records*)
+                   odr_malloc(odr_encode(), sizeof(Z_Records));
+               new_apdu->u.presentResponse->records->which = Z_Records_DBOSD;
+               new_apdu->u.presentResponse->records->u.databaseOrSurDiagnostics = npr;
+               new_apdu->u.presentResponse->nextResultSetPosition =
+                   odr_intdup(odr_encode(), start+toget);
+
+               send_to_client(new_apdu);
+               return 0;
+           }
+       }
+    }
+
+    if (apdu->which != Z_APDU_searchRequest)
+       return apdu;
+    Z_SearchRequest *sr = apdu->u.searchRequest;
+    Yaz_Z_Query *this_query = new Yaz_Z_Query;
+    Yaz_Z_Databases this_databases;
+
+    this_databases.set(sr->num_databaseNames, (const char **)
+                       sr->databaseNames);
+    
+    this_query->set_Z_Query(sr->query);
+
+    char query_str[120];
+    this_query->print(query_str, sizeof(query_str)-1);
+    yaz_log(LOG_LOG, "%sSearch %s", m_session_str, query_str);
+
+    if (*m_parent->m_optimize != '0' &&
+       m_client->m_last_ok && m_client->m_last_query &&
+       m_client->m_last_query->match(this_query) &&
+        !strcmp(m_client->m_last_resultSetId, sr->resultSetName) &&
+        m_client->m_last_databases.match(this_databases))
+    {
+       delete this_query;
+       if (m_client->m_last_resultCount > *sr->smallSetUpperBound &&
+           m_client->m_last_resultCount < *sr->largeSetLowerBound)
+       {
+           Z_NamePlusRecordList *npr;
+           int toget = *sr->mediumSetPresentNumber;
+           Z_RecordComposition *comp = 0;
+
+           if (toget > m_client->m_last_resultCount)
+               toget = m_client->m_last_resultCount;
+           
+           if (sr->mediumSetElementSetNames)
+           {
+               comp = (Z_RecordComposition *)
+                   odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
+               comp->which = Z_RecordComp_simple;
+               comp->u.simple = sr->mediumSetElementSetNames;
+           }
+           if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget,
+                                         sr->preferredRecordSyntax, comp))
+           {
+               yaz_log (LOG_LOG, "%sReturned cached records for medium set",
+                        m_session_str);
+               Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
+               new_apdu->u.searchResponse->referenceId = sr->referenceId;
+               new_apdu->u.searchResponse->resultCount =
+                   &m_client->m_last_resultCount;
+               
+               new_apdu->u.searchResponse->numberOfRecordsReturned
+                   = odr_intdup(odr_encode(), toget);
+                                                       
+               new_apdu->u.searchResponse->presentStatus =
+                   odr_intdup(odr_encode(), Z_PresentStatus_success);
+               new_apdu->u.searchResponse->records = (Z_Records*)
+                   odr_malloc(odr_encode(), sizeof(Z_Records));
+               new_apdu->u.searchResponse->records->which = Z_Records_DBOSD;
+               new_apdu->u.searchResponse->records->u.databaseOrSurDiagnostics = npr;
+               new_apdu->u.searchResponse->nextResultSetPosition =
+                   odr_intdup(odr_encode(), toget+1);
+               send_to_client(new_apdu);
+               return 0;
+           }
+           else
+           {
+               // medium Set
+               // send present request (medium size)
+               yaz_log (LOG_LOG, "%sOptimizing search for medium set",
+                        m_session_str);
+
+               Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
+               Z_PresentRequest *pr = new_apdu->u.presentRequest;
+               pr->referenceId = sr->referenceId;
+               pr->resultSetId = sr->resultSetName;
+               pr->preferredRecordSyntax = sr->preferredRecordSyntax;
+               *pr->numberOfRecordsRequested = toget;
+               pr->recordComposition = comp;
+               m_client->m_sr_transform = 1;
+               return new_apdu;
+           }
+       }
+       else if (m_client->m_last_resultCount >= *sr->largeSetLowerBound ||
+           m_client->m_last_resultCount <= 0)
+       {
+            // large set. Return pseudo-search response immediately
+           yaz_log (LOG_LOG, "%sOptimizing search for large set",
+                    m_session_str);
+           Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
+           new_apdu->u.searchResponse->referenceId = sr->referenceId;
+           new_apdu->u.searchResponse->resultCount =
+               &m_client->m_last_resultCount;
+           send_to_client(new_apdu);
+           return 0;
+       }
+       else
+       {
+           Z_NamePlusRecordList *npr;
+           int toget = m_client->m_last_resultCount;
+           Z_RecordComposition *comp = 0;
+           // small set
+            // send a present request (small set)
+           
+           if (sr->smallSetElementSetNames)
+           {
+               comp = (Z_RecordComposition *)
+                   odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
+               comp->which = Z_RecordComp_simple;
+               comp->u.simple = sr->smallSetElementSetNames;
+           }
+
+           if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget,
+                                         sr->preferredRecordSyntax, comp))
+           {
+               yaz_log (LOG_LOG, "%sReturned cached records for small set",
+                        m_session_str);
+               Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
+               new_apdu->u.searchResponse->referenceId = sr->referenceId;
+               new_apdu->u.searchResponse->resultCount =
+                   &m_client->m_last_resultCount;
+               
+               new_apdu->u.searchResponse->numberOfRecordsReturned
+                   = odr_intdup(odr_encode(), toget);
+                                                                
+               new_apdu->u.searchResponse->presentStatus =
+                   odr_intdup(odr_encode(), Z_PresentStatus_success);
+               new_apdu->u.searchResponse->records = (Z_Records*)
+                   odr_malloc(odr_encode(), sizeof(Z_Records));
+               new_apdu->u.searchResponse->records->which = Z_Records_DBOSD;
+               new_apdu->u.searchResponse->records->u.databaseOrSurDiagnostics = npr;
+               new_apdu->u.searchResponse->nextResultSetPosition =
+                   odr_intdup(odr_encode(), toget+1);
+               send_to_client(new_apdu);
+               return 0;
+           }
+           else
+           {
+               yaz_log (LOG_LOG, "%sOptimizing search for small set",
+                        m_session_str);
+               Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
+               Z_PresentRequest *pr = new_apdu->u.presentRequest;
+               pr->referenceId = sr->referenceId;
+               pr->resultSetId = sr->resultSetName;
+               pr->preferredRecordSyntax = sr->preferredRecordSyntax;
+               *pr->numberOfRecordsRequested = toget;
+               pr->recordComposition = comp;
+               m_client->m_sr_transform = 1;
+               return new_apdu;
+           }
+       }
+    }
+    else  // query doesn't match
+    {
+       delete m_client->m_last_query;
+       m_client->m_last_query = this_query;
+        m_client->m_last_ok = 0;
+       m_client->m_cache.clear();
+       m_client->m_resultSetStartPoint = 0;
+
+        xfree (m_client->m_last_resultSetId);
+        m_client->m_last_resultSetId = xstrdup (sr->resultSetName);
+
+        m_client->m_last_databases.set(sr->num_databaseNames,
+                                       (const char **) sr->databaseNames);
+    }
+    return apdu;
+}
+
+
+void Yaz_Proxy::inc_request_no()
+{
+    char *cp = strchr(m_session_str, ' ');
+    m_request_no++;
+    if (cp)
+       sprintf(cp+1, "%d ", m_request_no);
+}
+
+void Yaz_Proxy::recv_GDU(Z_GDU *apdu, int len)
+{
+    inc_request_no();
+
+    m_bytes_recv += len;
+    
+    if (m_log_mask & PROXY_LOG_REQ_CLIENT)
+       yaz_log (LOG_LOG, "%sReceiving %s from client %d bytes",
+                m_session_str, gdu_name(apdu), len);
+
+    if (m_bw_hold_PDU)     // double incoming PDU. shutdown now.
+       shutdown();
+
+    m_bw_stat.add_bytes(len);
+    m_pdu_stat.add_bytes(1);
+
+    gettimeofday(&m_time_tv, 0);
+
+    int bw_total = m_bw_stat.get_total();
+    int pdu_total = m_pdu_stat.get_total();
+
+    int reduce = 0;
+    if (m_bw_max)
+    {
+       if (bw_total > m_bw_max)
+       {
+           reduce = (bw_total/m_bw_max);
+       }
+    }
+    if (m_pdu_max)
+    {
+       if (pdu_total > m_pdu_max)
+       {
+           int nreduce = (m_pdu_max >= 60) ? 1 : 60/m_pdu_max;
+           reduce = (reduce > nreduce) ? reduce : nreduce;
+       }
+    }
+    if (reduce)  
+    {
+       yaz_log(LOG_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);
+       
+       m_bw_hold_PDU = apdu;  // save PDU and signal "on hold"
+       timeout(reduce);       // call us reduce seconds later
+    }
+    else if (apdu->which == Z_GDU_Z3950)
+       handle_incoming_Z_PDU(apdu->u.z3950);
+    else if (apdu->which == Z_GDU_HTTP_Request)
+       handle_incoming_HTTP(apdu->u.HTTP_Request);
+}
+
+void Yaz_Proxy::handle_max_record_retrieve(Z_APDU *apdu)
+{
+    if (m_max_record_retrieve)
+    {
+       if (apdu->which == Z_APDU_presentRequest)
+       {
+           Z_PresentRequest *pr = apdu->u.presentRequest;
+           if (pr->numberOfRecordsRequested && 
+               *pr->numberOfRecordsRequested > m_max_record_retrieve)
+               *pr->numberOfRecordsRequested = m_max_record_retrieve;
+       }
+    }
+}
+
+Z_Records *Yaz_Proxy::create_nonSurrogateDiagnostics(ODR odr,
+                                                    int error,
+                                                    const char *addinfo)
+{
+    Z_Records *rec = (Z_Records *)
+        odr_malloc (odr, sizeof(*rec));
+    int *err = (int *)
+        odr_malloc (odr, sizeof(*err));
+    Z_DiagRec *drec = (Z_DiagRec *)
+        odr_malloc (odr, sizeof(*drec));
+    Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)
+        odr_malloc (odr, sizeof(*dr));
+    *err = error;
+    rec->which = Z_Records_NSD;
+    rec->u.nonSurrogateDiagnostic = dr;
+    dr->diagnosticSetId =
+        yaz_oidval_to_z3950oid (odr, CLASS_DIAGSET, VAL_BIB1);
+    dr->condition = err;
+    dr->which = Z_DefaultDiagFormat_v2Addinfo;
+    dr->u.v2Addinfo = odr_strdup (odr, addinfo ? addinfo : "");
+    return rec;
+}
+
+Z_APDU *Yaz_Proxy::handle_query_transformation(Z_APDU *apdu)
+{
+    if (apdu->which == Z_APDU_searchRequest &&
+       apdu->u.searchRequest->query &&
+       apdu->u.searchRequest->query->which == Z_Query_type_104 &&
+       apdu->u.searchRequest->query->u.type_104->which == Z_External_CQL)
+    {
+       Z_RPNQuery *rpnquery = 0;
+       Z_SearchRequest *sr = apdu->u.searchRequest;
+       char *addinfo = 0;
+       
+       yaz_log(LOG_LOG, "%sCQL: %s", m_session_str,
+               sr->query->u.type_104->u.cql);
+
+       int r = m_cql2rpn.query_transform(sr->query->u.type_104->u.cql,
+                                         &rpnquery, odr_encode(),
+                                         &addinfo);
+       if (r == -3)
+           yaz_log(LOG_LOG, "%sNo CQL to RPN table", m_session_str);
+       else if (r)
+       {
+           yaz_log(LOG_LOG, "%sCQL Conversion error %d", m_session_str, r);
+           Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
+
+           new_apdu->u.searchResponse->referenceId = sr->referenceId;
+           new_apdu->u.searchResponse->records =
+               create_nonSurrogateDiagnostics(odr_encode(),
+                                              yaz_diag_srw_to_bib1(r),
+                                              addinfo);
+           *new_apdu->u.searchResponse->searchStatus = 0;
+
+           send_to_client(new_apdu);
+
+           return 0;
+       }
+       else
+       {
+           sr->query->which = Z_Query_type_1;
+           sr->query->u.type_1 = rpnquery;
+       }
+       return apdu;
+    }
+    return apdu;
+}
+
+Z_APDU *Yaz_Proxy::handle_query_validation(Z_APDU *apdu)
+{
+    if (apdu->which == Z_APDU_searchRequest)
+    {
+       Z_SearchRequest *sr = apdu->u.searchRequest;
+       int err = 0;
+       char *addinfo = 0;
+
+       Yaz_ProxyConfig *cfg = check_reconfigure();
+       if (cfg)
+           err = cfg->check_query(odr_encode(), m_default_target,
+                                  sr->query, &addinfo);
+       if (err)
+       {
+           Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
+
+           new_apdu->u.searchResponse->referenceId = sr->referenceId;
+           new_apdu->u.searchResponse->records =
+               create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
+           *new_apdu->u.searchResponse->searchStatus = 0;
+
+           send_to_client(new_apdu);
+
+           return 0;
+       }
+    }
+    return apdu;
+}
+
+Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu)
+{
+    m_marcxml_flag = 0;
+    if (apdu->which == Z_APDU_searchRequest)
+    {
+       Z_SearchRequest *sr = apdu->u.searchRequest;
+       int err = 0;
+       char *addinfo = 0;
+       Yaz_ProxyConfig *cfg = check_reconfigure();
+
+       Z_RecordComposition rc_temp, *rc = 0;
+       if (sr->smallSetElementSetNames)
+       {
+           rc_temp.which = Z_RecordComp_simple;
+           rc_temp.u.simple = sr->smallSetElementSetNames;
+           rc = &rc_temp;
+       }
+
+       char *stylesheet_name = 0;
+       if (cfg)
+           err = cfg->check_syntax(odr_encode(),
+                                   m_default_target,
+                                   sr->preferredRecordSyntax, rc,
+                                   &addinfo, &stylesheet_name, &m_schema);
+       if (stylesheet_name)
+       {
+           m_parent->low_socket_close();
+
+           if (m_stylesheet_xsp)
+               xsltFreeStylesheet(m_stylesheet_xsp);
+
+           m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*)
+                                                      stylesheet_name);
+           m_stylesheet_offset = 0;
+           xfree(stylesheet_name);
+
+           m_parent->low_socket_open();
+       }
+       if (err == -1)
+       {
+           sr->preferredRecordSyntax =
+               yaz_oidval_to_z3950oid(odr_encode(), CLASS_RECSYN, VAL_USMARC);
+           m_marcxml_flag = 1;
+       }
+       else if (err)
+       {
+           Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
+           
+           new_apdu->u.searchResponse->referenceId = sr->referenceId;
+           new_apdu->u.searchResponse->records =
+               create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
+           *new_apdu->u.searchResponse->searchStatus = 0;
+           
+           send_to_client(new_apdu);
+           
+           return 0;
+       }
+    }
+    else if (apdu->which == Z_APDU_presentRequest)
+    {
+       Z_PresentRequest *pr = apdu->u.presentRequest;
+       int err = 0;
+       char *addinfo = 0;
+       Yaz_ProxyConfig *cfg = check_reconfigure();
+
+       char *stylesheet_name = 0;
+       if (cfg)
+           err = cfg->check_syntax(odr_encode(), m_default_target,
+                                   pr->preferredRecordSyntax,
+                                   pr->recordComposition,
+                                   &addinfo, &stylesheet_name, &m_schema);
+       if (stylesheet_name)
+       {
+           m_parent->low_socket_close();
+
+           if (m_stylesheet_xsp)
+               xsltFreeStylesheet(m_stylesheet_xsp);
+
+           m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*)
+                                                      stylesheet_name);
+           m_stylesheet_offset = 0;
+           xfree(stylesheet_name);
+
+           m_parent->low_socket_open();
+       }
+       if (err == -1)
+       {
+           pr->preferredRecordSyntax =
+               yaz_oidval_to_z3950oid(odr_decode(), CLASS_RECSYN, VAL_USMARC);
+           m_marcxml_flag = 1;
+       }
+       else if (err)
+       {
+           Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
+           
+           new_apdu->u.presentResponse->referenceId = pr->referenceId;
+           new_apdu->u.presentResponse->records =
+               create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
+           *new_apdu->u.presentResponse->presentStatus =
+               Z_PresentStatus_failure;
+           
+           send_to_client(new_apdu);
+           
+           return 0;
+       }
+    }
+    return apdu;
+}
+
+Z_ElementSetNames *Yaz_Proxy::mk_esn_from_schema(ODR o, const char *schema)
+{
+    if (!schema)
+       return 0;
+    Z_ElementSetNames *esn = (Z_ElementSetNames *)
+       odr_malloc(o, sizeof(Z_ElementSetNames));
+    esn->which = Z_ElementSetNames_generic;
+    esn->u.generic = odr_strdup(o, schema);
+    return esn;
+}
+
+void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
+{
+    if (m_s2z_odr_init)
+    {
+       odr_destroy(m_s2z_odr_init);
+       m_s2z_odr_init = 0;
+    }
+    if (m_s2z_odr_search)
+    {
+       odr_destroy(m_s2z_odr_search);
+       m_s2z_odr_search = 0;
+    }
+
+    m_http_keepalive = 0;
+    m_http_version = 0;
+    if (!strcmp(hreq->version, "1.0")) 
+    {
+        const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
+        if (v && !strcmp(v, "Keep-Alive"))
+            m_http_keepalive = 1;
+        else
+            m_http_keepalive = 0;
+        m_http_version = "1.0";
+    }
+    else
+    {
+        const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
+        if (v && !strcmp(v, "close"))
+            m_http_keepalive = 0;
+        else
+            m_http_keepalive = 1;
+        m_http_version = "1.1";
+    }
+
+    Z_SRW_PDU *srw_pdu = 0;
+    Z_SOAP *soap_package = 0;
+    char *charset = 0;
+    Z_SRW_diagnostic *diagnostic = 0;
+    int num_diagnostic = 0;
+    if (yaz_srw_decode(hreq, &srw_pdu, &soap_package, odr_decode(),
+                      &charset) == 0
+       || yaz_sru_decode(hreq, &srw_pdu, &soap_package, odr_decode(),
+                         &charset, &diagnostic, &num_diagnostic) == 0)
+    {
+       m_s2z_odr_init = odr_createmem(ODR_ENCODE);
+       m_s2z_odr_search = odr_createmem(ODR_ENCODE);
+       m_soap_ns = odr_strdup(m_s2z_odr_search, soap_package->ns);
+       m_s2z_init_apdu = 0;
+       m_s2z_search_apdu = 0;
+       m_s2z_present_apdu = 0;
+
+       m_s2z_stylesheet = 0;
+       
+       if (srw_pdu->which == Z_SRW_searchRetrieve_request)
+       {
+           Z_SRW_searchRetrieveRequest *srw_req = srw_pdu->u.request;
+
+           m_s2z_database = odr_strdup(m_s2z_odr_init, srw_req->database);
+           // recordXPath unsupported.
+           if (srw_req->recordXPath)
+            {
+               yaz_add_srw_diagnostic(odr_decode(),
+                                      &diagnostic, &num_diagnostic,
+                                      72, 0);
+            }
+           // must have a query
+           if (!srw_req->query.cql)
+           {
+               yaz_add_srw_diagnostic(odr_decode(),
+                                      &diagnostic, &num_diagnostic,
+                                      7, "query");
+           }
+           // sort unsupported
+           if (srw_req->sort_type != Z_SRW_sort_type_none)
+           {
+               yaz_add_srw_diagnostic(odr_decode(),
+                                      &diagnostic, &num_diagnostic,
+                                      80, 0);
+           }
+           // save stylesheet
+           if (srw_req->stylesheet)
+               m_s2z_stylesheet =
+                   odr_strdup(m_s2z_odr_init, srw_req->stylesheet);
+                                             
+           // set packing for response records ..
+           if (srw_req->recordPacking &&
+               !strcmp(srw_req->recordPacking, "xml"))
+               m_s2z_packing = Z_SRW_recordPacking_XML;
+           else
+               m_s2z_packing = Z_SRW_recordPacking_string;
+
+           if (num_diagnostic)
+           {
+               Z_SRW_PDU *srw_pdu =
+                   yaz_srw_get(odr_encode(),
+                               Z_SRW_searchRetrieve_response);
+               Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
+               
+               srw_res->diagnostics = diagnostic;
+               srw_res->num_diagnostics = num_diagnostic;
+               send_srw_response(srw_pdu);
+               return;
+           }
+
+           // prepare search PDU
+           m_s2z_search_apdu = zget_APDU(m_s2z_odr_search,
+                                         Z_APDU_searchRequest);
+           Z_SearchRequest *z_searchRequest =
+               m_s2z_search_apdu->u.searchRequest;
+
+           z_searchRequest->num_databaseNames = 1;
+           z_searchRequest->databaseNames = (char**)
+               odr_malloc(m_s2z_odr_search, sizeof(char *));
+           z_searchRequest->databaseNames[0] = odr_strdup(m_s2z_odr_search,
+                                                          srw_req->database);
+           
+           // query transformation
+           Z_Query *query = (Z_Query *)
+               odr_malloc(m_s2z_odr_search, sizeof(Z_Query));
+           z_searchRequest->query = query;
+           
+           if (srw_req->query_type == Z_SRW_query_type_cql)
+           {
+               Z_External *ext = (Z_External *) 
+                   odr_malloc(m_s2z_odr_search, sizeof(*ext));
+               ext->direct_reference = 
+                   odr_getoidbystr(m_s2z_odr_search, "1.2.840.10003.16.2");
+               ext->indirect_reference = 0;
+               ext->descriptor = 0;
+               ext->which = Z_External_CQL;
+               ext->u.cql = srw_req->query.cql;
+               
+               query->which = Z_Query_type_104;
+               query->u.type_104 =  ext;
+           }
+           else if (srw_req->query_type == Z_SRW_query_type_pqf)
+           {
+               Z_RPNQuery *RPNquery;
+               YAZ_PQF_Parser pqf_parser;
+               
+               pqf_parser = yaz_pqf_create ();
+               
+               RPNquery = yaz_pqf_parse (pqf_parser, m_s2z_odr_search,
+                                         srw_req->query.pqf);
+               if (!RPNquery)
+               {
+                   const char *pqf_msg;
+                   size_t off;
+                   int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
+                   yaz_log(LOG_LOG, "%*s^\n", off+4, "");
+                   yaz_log(LOG_LOG, "Bad PQF: %s (code %d)\n", pqf_msg, code);
+                   
+                   send_to_srw_client_error(10, 0);
+                   return;
+               }
+               query->which = Z_Query_type_1;
+               query->u.type_1 =  RPNquery;
+               
+               yaz_pqf_destroy (pqf_parser);
+           }
+           else
+           {
+               send_to_srw_client_error(7, "query");
+               return;
+           }
+
+           // present
+           m_s2z_present_apdu = 0;
+           int max = 0;
+           if (srw_req->maximumRecords)
+               max = *srw_req->maximumRecords;
+           int start = 1;
+           if (srw_req->startRecord)
+               start = *srw_req->startRecord;
+           if (max > 0)
+           {
+                // Some backend, such as Voyager doesn't honor piggyback
+               // So we use present always (0 &&).
+               if (0 && start <= 1)  // Z39.50 piggyback
+               {
+                   *z_searchRequest->smallSetUpperBound = max;
+                   *z_searchRequest->mediumSetPresentNumber = max;
+                   *z_searchRequest->largeSetLowerBound = 2000000000; // 2e9
+
+                   z_searchRequest->preferredRecordSyntax =
+                       yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN,
+                                              VAL_TEXT_XML);
+                   if (srw_req->recordSchema)
+                   {
+                       z_searchRequest->smallSetElementSetNames =
+                           z_searchRequest->mediumSetElementSetNames =
+                           mk_esn_from_schema(m_s2z_odr_search,
+                                              srw_req->recordSchema);
+                   }
+               }
+               else   // Z39.50 present
+               {
+                   m_s2z_present_apdu = zget_APDU(m_s2z_odr_search, 
+                                                  Z_APDU_presentRequest);
+                   Z_PresentRequest *z_presentRequest = 
+                       m_s2z_present_apdu->u.presentRequest;
+                   *z_presentRequest->resultSetStartPoint = start;
+                   *z_presentRequest->numberOfRecordsRequested = max;
+                   z_presentRequest->preferredRecordSyntax =
+                       yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN,
+                                              VAL_TEXT_XML);
+                   if (srw_req->recordSchema)
+                   {
+                       z_presentRequest->recordComposition =
+                           (Z_RecordComposition *)
+                           odr_malloc(m_s2z_odr_search,
+                                      sizeof(Z_RecordComposition));
+                       z_presentRequest->recordComposition->which = 
+                           Z_RecordComp_simple;                    
+                       z_presentRequest->recordComposition->u.simple =
+                           mk_esn_from_schema(m_s2z_odr_search,
+                                              srw_req->recordSchema);
+                   }
+               }
+           }
+           if (!m_client)
+           {
+               m_s2z_init_apdu = zget_APDU(m_s2z_odr_init,
+                                           Z_APDU_initRequest);
+               
+               // prevent m_initRequest_apdu memory from being grabbed
+               // in Yaz_Proxy::handle_incoming_Z_PDU
+               m_initRequest_apdu = m_s2z_init_apdu;
+               handle_incoming_Z_PDU(m_s2z_init_apdu);
+               return;
+           }
+           else
+           {
+               handle_incoming_Z_PDU(m_s2z_search_apdu);
+               return;
+           }
+       }
+       else if (srw_pdu->which == Z_SRW_explain_request)
+       {
+           Z_SRW_explainRequest *srw_req = srw_pdu->u.explain_request;
+
+           m_s2z_database = odr_strdup(m_s2z_odr_init, srw_req->database);
+
+           // save stylesheet
+           if (srw_req->stylesheet)
+               m_s2z_stylesheet =
+                   odr_strdup(m_s2z_odr_init, srw_req->stylesheet);
+
+           if (srw_req->recordPacking &&
+               !strcmp(srw_req->recordPacking, "xml"))
+               m_s2z_packing = Z_SRW_recordPacking_XML;
+           else
+               m_s2z_packing = Z_SRW_recordPacking_string;
+
+           if (num_diagnostic)
+           {
+               send_srw_explain_response(diagnostic, num_diagnostic);
+               return;
+           }
+
+           if (!m_client)
+           {
+               m_s2z_init_apdu = zget_APDU(m_s2z_odr_init,
+                                           Z_APDU_initRequest);
+               
+               // prevent m_initRequest_apdu memory from being grabbed
+               // in Yaz_Proxy::handle_incoming_Z_PDU
+               m_initRequest_apdu = m_s2z_init_apdu;
+               handle_incoming_Z_PDU(m_s2z_init_apdu);
+           }
+           else
+               send_srw_explain_response(0, 0);
+           return;
+       }
+       else if (srw_pdu->which == Z_SRW_scan_request)
+        {
+           m_s2z_database = odr_strdup(m_s2z_odr_init,
+                                       srw_pdu->u.scan_request->database);
+
+           yaz_add_srw_diagnostic(odr_decode(),
+                                  &diagnostic, &num_diagnostic,
+                                  4, "scan");
+           Z_SRW_PDU *srw_pdu =
+               yaz_srw_get(odr_encode(),
+                           Z_SRW_scan_response);
+           Z_SRW_scanResponse *srw_res = srw_pdu->u.scan_response;
+           
+           srw_res->diagnostics = diagnostic;
+           srw_res->num_diagnostics = num_diagnostic;
+           send_srw_response(srw_pdu);
+           return;
+        }
+       else
+        {
+           m_s2z_database = 0;
+
+           send_to_srw_client_error(4, 0);
+        }
+    }
+    int len = 0;
+    Z_GDU *p = z_get_HTTP_Response(odr_encode(), 400);
+    timeout(0);
+    send_GDU(p, &len);
+}
+
+void Yaz_Proxy::handle_incoming_Z_PDU(Z_APDU *apdu)
+{
+    Z_ReferenceId **refid = get_referenceIdP(apdu);
+    nmem_reset(m_referenceId_mem);
+    if (refid && *refid)
+    {
+       m_referenceId = (Z_ReferenceId *)
+           nmem_malloc(m_referenceId_mem, sizeof(*m_referenceId));
+       m_referenceId->len = m_referenceId->size = (*refid)->len;
+       m_referenceId->buf = (unsigned char *)
+           nmem_malloc(m_referenceId_mem, (*refid)->len);
+       memcpy(m_referenceId->buf, (*refid)->buf, (*refid)->len);
+    }
+    else
+       m_referenceId = 0;
+
+    if (!m_client && m_invalid_session)
+    {
+       m_apdu_invalid_session = apdu;
+       m_mem_invalid_session = odr_extract_mem(odr_decode());
+       apdu = m_initRequest_apdu;
+    }
+    
+    // Determine our client.
+    Z_OtherInformation **oi;
+    get_otherInfoAPDU(apdu, &oi);
+    m_client = get_client(apdu, get_cookie(oi), get_proxy(oi));
+    if (!m_client)
+    {
+       delete this;
+       return;
+    }
+    m_client->m_server = this;
+
+    if (apdu->which == Z_APDU_initRequest)
+    {
+       if (apdu->u.initRequest->implementationId)
+           yaz_log(LOG_LOG, "%simplementationId: %s",
+                   m_session_str, apdu->u.initRequest->implementationId);
+       if (apdu->u.initRequest->implementationName)
+           yaz_log(LOG_LOG, "%simplementationName: %s",
+                   m_session_str, apdu->u.initRequest->implementationName);
+       if (apdu->u.initRequest->implementationVersion)
+           yaz_log(LOG_LOG, "%simplementationVersion: %s",
+                   m_session_str, apdu->u.initRequest->implementationVersion);
+       if (m_initRequest_apdu == 0)
+       {
+           if (m_initRequest_mem)
+               nmem_destroy(m_initRequest_mem);
+           m_initRequest_apdu = apdu;
+           m_initRequest_mem = odr_extract_mem(odr_decode());
+
+           m_initRequest_preferredMessageSize = *apdu->u.initRequest->
+               preferredMessageSize;
+           *apdu->u.initRequest->preferredMessageSize = 1024*1024;
+           m_initRequest_maximumRecordSize = *apdu->u.initRequest->
+               maximumRecordSize;
+           *apdu->u.initRequest->maximumRecordSize = 1024*1024;
+
+           // save init options for the response..
+           m_initRequest_options = apdu->u.initRequest->options;
+           
+           apdu->u.initRequest->options = 
+               (Odr_bitmask *)nmem_malloc(m_initRequest_mem,
+                                          sizeof(Odr_bitmask));
+           ODR_MASK_ZERO(apdu->u.initRequest->options);
+           int i;
+           for (i = 0; i<= 24; i++)
+               ODR_MASK_SET(apdu->u.initRequest->options, i);
+           ODR_MASK_CLEAR(apdu->u.initRequest->options,
+                          Z_Options_negotiationModel);
+           ODR_MASK_CLEAR(apdu->u.initRequest->options,
+                          Z_Options_concurrentOperations);
+
+           // make new version
+           m_initRequest_version = apdu->u.initRequest->protocolVersion;
+           apdu->u.initRequest->protocolVersion = 
+               (Odr_bitmask *)nmem_malloc(m_initRequest_mem,
+                                          sizeof(Odr_bitmask));
+           ODR_MASK_ZERO(apdu->u.initRequest->protocolVersion);
+
+           for (i = 0; i<= 8; i++)
+               ODR_MASK_SET(apdu->u.initRequest->protocolVersion, i);
+       }
+       if (m_client->m_init_flag)
+       {
+           if (handle_init_response_for_invalid_session(apdu))
+               return;
+           if (m_client->m_initResponse)
+           {
+               Z_APDU *apdu2 = m_client->m_initResponse;
+               apdu2->u.initResponse->otherInfo = 0;
+               if (m_client->m_cookie && *m_client->m_cookie)
+                   set_otherInformationString(apdu2, VAL_COOKIE, 1,
+                                              m_client->m_cookie);
+               apdu2->u.initResponse->referenceId =
+                   apdu->u.initRequest->referenceId;
+               apdu2->u.initResponse->options = m_client->m_initResponse_options;
+               apdu2->u.initResponse->protocolVersion = 
+                   m_client->m_initResponse_version;
+               
+               send_to_client(apdu2);
+               return;
+           }
+       }
+       m_client->m_init_flag = 1;
+    }
+    handle_max_record_retrieve(apdu);
+
+    if (apdu)
+       apdu = handle_syntax_validation(apdu);
+
+    if (apdu)
+       apdu = handle_query_transformation(apdu);
+
+    if (apdu)
+       apdu = handle_query_validation(apdu);
+
+    if (apdu)
+       apdu = result_set_optimize(apdu);
+    if (!apdu)
+    {
+       m_client->timeout(m_target_idletime);  // mark it active even 
+       // though we didn't use it
+       return;
+    }
+
+    // delete other info part from PDU before sending to target
+    get_otherInfoAPDU(apdu, &oi);
+    if (oi)
+        *oi = 0;
+
+    if (apdu->which == Z_APDU_presentRequest &&
+       m_client->m_resultSetStartPoint == 0)
+    {
+       Z_PresentRequest *pr = apdu->u.presentRequest;
+       m_client->m_resultSetStartPoint = *pr->resultSetStartPoint;
+       m_client->m_cache.copy_presentRequest(apdu->u.presentRequest);
+    } else {
+       m_client->m_resultSetStartPoint = 0;
+    }
+    if (m_client->send_to_target(apdu) < 0)
+    {
+       delete m_client;
+       m_client = 0;
+       delete this;
+    }
+    else
+       m_client->m_waiting = 1;
+}
+
+void Yaz_Proxy::connectNotify()
+{
+}
+
+void Yaz_Proxy::shutdown()
+{
+    m_invalid_session = 0;
+    // only keep if keep_alive flag is set...
+    if (m_client && 
+       m_client->m_pdu_recv < m_keepalive_limit_pdu &&
+       m_client->m_bytes_recv+m_client->m_bytes_sent < m_keepalive_limit_bw &&
+       m_client->m_waiting == 0)
+    {
+        yaz_log(LOG_LOG, "%sShutdown (client to proxy) keepalive %s",
+                m_session_str,
+                 m_client->get_hostname());
+       yaz_log(LOG_LOG, "%sbw=%d pdu=%d limit-bw=%d limit-pdu=%d",
+               m_session_str, m_client->m_pdu_recv,
+               m_client->m_bytes_sent + m_client->m_bytes_recv,
+               m_keepalive_limit_bw, m_keepalive_limit_pdu);
+        assert (m_client->m_waiting != 2);
+       // Tell client (if any) that no server connection is there..
+       m_client->m_server = 0;
+       m_invalid_session = 0;
+    }
+    else if (m_client)
+    {
+        yaz_log (LOG_LOG, "%sShutdown (client to proxy) close %s",
+                m_session_str,
+                 m_client->get_hostname());
+        assert (m_client->m_waiting != 2);
+       delete m_client;
+    }
+    else if (!m_parent)
+    {
+        yaz_log (LOG_LOG, "%sshutdown (client to proxy) bad state",
+                m_session_str);
+        assert (m_parent);
+    }
+    else 
+    {
+        yaz_log (LOG_LOG, "%sShutdown (client to proxy)",
+                m_session_str);
+    }
+    if (m_parent)
+       m_parent->pre_init();
+    delete this;
+}
+
+const char *Yaz_ProxyClient::get_session_str() 
+{
+    if (!m_server)
+       return "0 ";
+    return m_server->get_session_str();
+}
+
+void Yaz_ProxyClient::shutdown()
+{
+    yaz_log (LOG_LOG, "%sShutdown (proxy to target) %s", get_session_str(),
+            get_hostname());
+    delete m_server;
+    delete this;
+}
+
+void Yaz_Proxy::failNotify()
+{
+    inc_request_no();
+    yaz_log (LOG_LOG, "%sConnection closed by client",
+            get_session_str());
+    shutdown();
+}
+
+void Yaz_ProxyClient::failNotify()
+{
+    if (m_server)
+       m_server->inc_request_no();
+    yaz_log (LOG_LOG, "%sConnection closed by target %s", 
+            get_session_str(), get_hostname());
+    shutdown();
+}
+
+void Yaz_ProxyClient::connectNotify()
+{
+    const char *s = get_session_str();
+    const char *h = get_hostname();
+    yaz_log (LOG_LOG, "%sConnection accepted by %s timeout=%d", s, h,
+            m_target_idletime);
+    timeout(m_target_idletime);
+    if (!m_server)
+       pre_init_client();
+}
+
+IYaz_PDU_Observer *Yaz_ProxyClient::sessionNotify(IYaz_PDU_Observable
+                                                 *the_PDU_Observable, int fd)
+{
+    return new Yaz_ProxyClient(the_PDU_Observable, 0);
+}
+
+Yaz_ProxyClient::~Yaz_ProxyClient()
+{
+    if (m_prev)
+       *m_prev = m_next;
+    if (m_next)
+       m_next->m_prev = m_prev;
+    m_waiting = 2;     // for debugging purposes only.
+    odr_destroy(m_init_odr);
+    delete m_last_query;
+    xfree (m_last_resultSetId);
+    xfree (m_cookie);
+}
+
+void Yaz_ProxyClient::pre_init_client()
+{
+    Z_APDU *apdu = create_Z_PDU(Z_APDU_initRequest);
+    Z_InitRequest *req = apdu->u.initRequest;
+    
+    int i;
+    for (i = 0; i<= 24; i++)
+       ODR_MASK_SET(req->options, i);
+    ODR_MASK_CLEAR(apdu->u.initRequest->options,
+                  Z_Options_negotiationModel);
+    ODR_MASK_CLEAR(apdu->u.initRequest->options,
+                  Z_Options_concurrentOperations);
+    for (i = 0; i<= 10; i++)
+       ODR_MASK_SET(req->protocolVersion, i);
+
+    if (send_to_target(apdu) < 0)
+    {
+       delete this;
+    }
+    else
+    {
+       m_waiting = 1;
+       m_init_flag = 1;
+    }
+}
+
+void Yaz_Proxy::pre_init()
+{
+    int i;
+    const char *name = 0;
+    const char *zurl_in_use[MAX_ZURL_PLEX];
+    int limit_bw, limit_pdu, limit_req;
+    int target_idletime, client_idletime;
+    int max_clients;
+    int keepalive_limit_bw, keepalive_limit_pdu;
+    int pre_init;
+    const char *cql2rpn = 0;
+
+    Yaz_ProxyConfig *cfg = check_reconfigure();
+
+    zurl_in_use[0] = 0;
+
+    if (m_log_mask & PROXY_LOG_APDU_CLIENT)
+       set_APDU_yazlog(1);
+    else
+       set_APDU_yazlog(0);
+
+    for (i = 0; cfg && cfg->get_target_no(i, &name, zurl_in_use,
+                                         &limit_bw, &limit_pdu, &limit_req,
+                                         &target_idletime, &client_idletime,
+                                         &max_clients, 
+                                         &keepalive_limit_bw,
+                                         &keepalive_limit_pdu,
+                                         &pre_init,
+                                         &cql2rpn) ; i++)
+    {
+       if (pre_init)
+       {
+           int j;
+           for (j = 0; zurl_in_use[j]; j++)
+           {
+               Yaz_ProxyClient *c;
+               int spare = 0;
+               int spare_waiting = 0;
+               int in_use = 0;
+               int other = 0;
+               for (c = m_clientPool; c; c = c->m_next)
+               {
+                   if (!strcmp(zurl_in_use[j], c->get_hostname()))
+                   {
+                       if (c->m_cookie == 0)
+                       {
+                           if (c->m_server == 0)
+                               if (c->m_waiting)
+                                   spare_waiting++;
+                               else
+                                   spare++;
+                           else
+                               in_use++;
+                       }
+                       else
+                           other++;
+                   }
+               }
+               yaz_log(LOG_LOG, "%spre-init %s %s use=%d other=%d spare=%d "
+                       "sparew=%d preinit=%d",m_session_str,
+                       name, zurl_in_use[j], in_use, other,
+                       spare, spare_waiting, pre_init);
+               if (spare + spare_waiting < pre_init)
+               {
+                   c = new Yaz_ProxyClient(m_PDU_Observable->clone(), this);
+                   c->m_next = m_clientPool;
+                   if (c->m_next)
+                       c->m_next->m_prev = &c->m_next;
+                   m_clientPool = c;
+                   c->m_prev = &m_clientPool;
+                   
+                   if (m_log_mask & PROXY_LOG_APDU_SERVER)
+                       c->set_APDU_yazlog(1);
+                   else
+                       c->set_APDU_yazlog(0);
+
+                   if (c->client(zurl_in_use[j]))
+                   {
+                       timeout(60);
+                       delete c;
+                       return;
+                   }
+                   c->timeout(30);
+                   c->m_waiting = 1;
+                   c->m_target_idletime = target_idletime;
+                   c->m_seqno = m_seqno++;
+               }
+           }
+       }
+    }
+}
+
+void Yaz_Proxy::timeoutNotify()
+{
+    if (m_parent)
+    {
+       if (m_bw_hold_PDU)
+       {
+           timeout(m_client_idletime);
+           Z_GDU *apdu = m_bw_hold_PDU;
+           m_bw_hold_PDU = 0;
+           
+           if (apdu->which == Z_GDU_Z3950)
+               handle_incoming_Z_PDU(apdu->u.z3950);
+           else if (apdu->which == Z_GDU_HTTP_Request)
+               handle_incoming_HTTP(apdu->u.HTTP_Request);
+       }
+       else if (m_stylesheet_nprl)
+           convert_xsl_delay();
+       else
+       {
+           inc_request_no();
+
+           yaz_log (LOG_LOG, "%sTimeout (client to proxy)", m_session_str);
+           shutdown();
+       }
+    }
+    else
+    {
+       timeout(600);
+       pre_init();
+    }
+}
+
+void Yaz_Proxy::markInvalid()
+{
+    m_client = 0;
+    m_invalid_session = 1;
+}
+
+void Yaz_ProxyClient::timeoutNotify()
+{
+    if (m_server)
+       m_server->inc_request_no();
+
+    yaz_log (LOG_LOG, "%sTimeout (proxy to target) %s", get_session_str(),
+            get_hostname());
+    m_waiting = 1;
+    m_root->pre_init();
+    if (m_server && m_init_flag)
+    {
+       // target timed out in a session that was properly initialized
+       // server object stay alive but we mark it as invalid so it
+       // gets initialized again
+       m_server->markInvalid();
+       m_server = 0;
+    }
+    shutdown();
+}
+
+Yaz_ProxyClient::Yaz_ProxyClient(IYaz_PDU_Observable *the_PDU_Observable,
+                                Yaz_Proxy *parent) :
+    Yaz_Z_Assoc (the_PDU_Observable)
+{
+    m_cookie = 0;
+    m_next = 0;
+    m_prev = 0;
+    m_init_flag = 0;
+    m_last_query = 0;
+    m_last_resultSetId = 0;
+    m_last_resultCount = 0;
+    m_last_ok = 0;
+    m_sr_transform = 0;
+    m_waiting = 0;
+    m_init_odr = odr_createmem (ODR_DECODE);
+    m_initResponse = 0;
+    m_initResponse_options = 0;
+    m_initResponse_version = 0;
+    m_initResponse_preferredMessageSize = 0;
+    m_initResponse_maximumRecordSize = 0;
+    m_resultSetStartPoint = 0;
+    m_bytes_sent = m_bytes_recv = 0;
+    m_pdu_recv = 0;
+    m_server = 0;
+    m_seqno = 0;
+    m_target_idletime = 600;
+    m_root = parent;
+}
+
+const char *Yaz_Proxy::option(const char *name, const char *value)
+{
+    if (!strcmp (name, "optimize")) {
+       if (value) {
+            xfree (m_optimize);        
+           m_optimize = xstrdup (value);
+        }
+       return m_optimize;
+    }
+    return 0;
+}
+
+void Yaz_ProxyClient::recv_HTTP_response(Z_HTTP_Response *apdu, int len)
+{
+
+}
+
+void Yaz_ProxyClient::recv_GDU(Z_GDU *apdu, int len)
+{
+    if (apdu->which == Z_GDU_Z3950)
+       recv_Z_PDU(apdu->u.z3950, len);
+    else if (apdu->which == Z_GDU_HTTP_Response)
+       recv_HTTP_response(apdu->u.HTTP_Response, len);
+    else
+       shutdown();
+}
+
+int Yaz_Proxy::handle_init_response_for_invalid_session(Z_APDU *apdu)
+{
+    if (!m_invalid_session)
+       return 0;
+    m_invalid_session = 0;
+    handle_incoming_Z_PDU(m_apdu_invalid_session);
+    assert (m_mem_invalid_session);
+    nmem_destroy(m_mem_invalid_session);
+    m_mem_invalid_session = 0;
+    return 1;
+}
+
+void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len)
+{
+    m_bytes_recv += len;
+
+    m_pdu_recv++;
+    m_waiting = 0;
+    if (m_root->get_log_mask() & PROXY_LOG_REQ_SERVER)
+       yaz_log (LOG_LOG, "%sReceiving %s from %s %d bytes", get_session_str(),
+                apdu_name(apdu), get_hostname(), len);
+    if (apdu->which == Z_APDU_initResponse)
+    {
+       if (!m_server)  // if this is a pre init session , check for more
+           m_root->pre_init();
+        NMEM nmem = odr_extract_mem (odr_decode());
+       odr_reset (m_init_odr);
+        nmem_transfer (m_init_odr->mem, nmem);
+        m_initResponse = apdu;
+       m_initResponse_options = apdu->u.initResponse->options;
+       m_initResponse_version = apdu->u.initResponse->protocolVersion;
+       m_initResponse_preferredMessageSize = 
+           *apdu->u.initResponse->preferredMessageSize;
+       m_initResponse_maximumRecordSize = 
+           *apdu->u.initResponse->maximumRecordSize;
+
+       Z_InitResponse *ir = apdu->u.initResponse;
+       char *im0 = ir->implementationName;
+       
+       char *im1 = (char*) 
+           odr_malloc(m_init_odr, 20 + (im0 ? strlen(im0) : 0));
+       *im1 = '\0';
+       if (im0)
+       {
+           strcat(im1, im0);
+           strcat(im1, " ");
+       }
+       strcat(im1, "(YAZ Proxy)");
+       ir->implementationName = im1;
+
+        nmem_destroy (nmem);
+
+       if (m_server && m_server->handle_init_response_for_invalid_session(apdu))
+           return;
+    }
+    if (apdu->which == Z_APDU_searchResponse)
+    {
+       Z_SearchResponse *sr = apdu->u.searchResponse;
+       m_last_resultCount = *sr->resultCount;
+       int status = *sr->searchStatus;
+       if (status && (!sr->records || sr->records->which == Z_Records_DBOSD))
+       {
+            m_last_ok = 1;
+           
+           if (sr->records && sr->records->which == Z_Records_DBOSD)
+           {
+               m_cache.add(odr_decode(),
+                           sr->records->u.databaseOrSurDiagnostics, 1,
+                           *sr->resultCount);
+           }
+       }
+    }
+    if (apdu->which == Z_APDU_presentResponse)
+    {
+       Z_PresentResponse *pr = apdu->u.presentResponse;
+       if (m_sr_transform)
+       {
+           m_sr_transform = 0;
+           Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
+           Z_SearchResponse *sr = new_apdu->u.searchResponse;
+           sr->referenceId = pr->referenceId;
+           *sr->resultCount = m_last_resultCount;
+           sr->records = pr->records;
+           sr->nextResultSetPosition = pr->nextResultSetPosition;
+           sr->numberOfRecordsReturned = pr->numberOfRecordsReturned;
+           apdu = new_apdu;
+       }
+       if (pr->records && 
+           pr->records->which == Z_Records_DBOSD && m_resultSetStartPoint)
+       {
+           m_cache.add(odr_decode(),
+                       pr->records->u.databaseOrSurDiagnostics,
+                       m_resultSetStartPoint, -1);
+           m_resultSetStartPoint = 0;
+       }
+    }
+    if (m_cookie)
+       set_otherInformationString (apdu, VAL_COOKIE, 1, m_cookie);
+    if (m_server)
+    {
+       m_server->send_to_client(apdu);
+    }
+    if (apdu->which == Z_APDU_close)
+    {
+       shutdown();
+    }
+}
+
+void Yaz_Proxy::low_socket_close()
+{
+    int i;
+    for (i = 0; i<NO_SPARE_SOLARIS_FD; i++)
+       if  (m_lo_fd[i] >= 0)
+           ::close(m_lo_fd[i]);
+}
+
+void Yaz_Proxy::low_socket_open()
+{
+    int i;
+    for (i = 0; i<NO_SPARE_SOLARIS_FD; i++)
+       m_lo_fd[i] = open("/dev/null", O_RDONLY);
+}
+
+int Yaz_Proxy::server(const char *addr)
+{
+    int r = Yaz_Z_Assoc::server(addr);
+    if (!r)
+    {
+       yaz_log(LOG_LOG, "%sStarted proxy " VERSION " on %s", m_session_str, addr);
+       timeout(1);
+    }
+    return r;
+}
+
index cbc52c7..235289e 100644 (file)
@@ -1,17 +1,16 @@
-## $Id: Makefile.am,v 1.22 2004-02-11 10:01:54 adam Exp $
+## $Id: Makefile.am,v 1.23 2004-03-29 22:46:51 adam Exp $
 
-AM_CXXFLAGS = $(YAZINC) -I$(srcdir)/../include $(XSLT_CFLAGS)
+AM_CXXFLAGS = $(YAZINC) -I$(srcdir)/../include
 
 lib_LTLIBRARIES = libyazcpp.la
 libyazcpp_la_LDFLAGS=-version-info 1:0:0
 
 libyazcpp_la_SOURCES=yaz-socket-manager.cpp yaz-pdu-assoc.cpp \
-       yaz-z-assoc.cpp yaz-proxy.cpp yaz-z-query.cpp yaz-ir-assoc.cpp \
+       yaz-z-assoc.cpp yaz-z-query.cpp yaz-ir-assoc.cpp \
        yaz-z-server.cpp yaz-pdu-assoc-thread.cpp yaz-z-server-sr.cpp \
        yaz-z-server-ill.cpp yaz-z-server-update.cpp yaz-z-databases.cpp \
-       yaz-z-cache.cpp yaz-proxy-config.cpp yaz-bw.cpp yaz-cql2rpn.cpp
+       yaz-z-cache.cpp yaz-cql2rpn.cpp
 
-bin_PROGRAMS = yaz-proxy
 noinst_PROGRAMS = yaz-my-server yaz-my-client
 bin_SCRIPTS = yaz++-config
 
@@ -19,6 +18,4 @@ yaz_my_client_SOURCES=yaz-my-client.cpp
 
 yaz_my_server_SOURCES=yaz-my-server.cpp yaz-marc-sample.cpp
 
-yaz_proxy_SOURCES=yaz-proxy-main.cpp
-
-LDADD=libyazcpp.la $(YAZLALIB) $(XSLT_LIBS)
+LDADD=libyazcpp.la $(YAZLALIB)
index c142428..7999840 100644 (file)
@@ -1,13 +1,13 @@
 /*
- * Copyright (c) 1998-2003, Index Data.
+ * Copyright (c) 1998-2004, Index Data.
  * See the file LICENSE for details.
  * 
- * $Id: yaz-cql2rpn.cpp,v 1.3 2004-01-06 21:17:42 adam Exp $
+ * $Id: yaz-cql2rpn.cpp,v 1.4 2004-03-29 22:46:51 adam Exp $
  */
 
 #include <yaz/log.h>
 #include <yaz/pquery.h>
-#include <yaz++/proxy.h>
+#include <yaz++/cql2rpn.h>
 
 Yaz_cql2rpn::Yaz_cql2rpn()
 {
diff --git a/src/yaz-proxy-config.cpp b/src/yaz-proxy-config.cpp
deleted file mode 100644 (file)
index 88449f3..0000000
+++ /dev/null
@@ -1,796 +0,0 @@
-/*
- * Copyright (c) 1998-2004, Index Data.
- * See the file LICENSE for details.
- * 
- * $Id: yaz-proxy-config.cpp,v 1.27 2004-01-30 00:38:28 adam Exp $
- */
-
-#include <ctype.h>
-#include <yaz/log.h>
-#include <yaz++/proxy.h>
-
-Yaz_ProxyConfig::Yaz_ProxyConfig()
-{
-    m_copy = 0;
-#if HAVE_XSLT
-    m_docPtr = 0;
-    m_proxyPtr = 0;
-#endif
-}
-
-Yaz_ProxyConfig::~Yaz_ProxyConfig()
-{
-#if HAVE_XSLT
-    if (!m_copy && m_docPtr)
-       xmlFreeDoc(m_docPtr);
-#endif
-}
-
-int Yaz_ProxyConfig::read_xml(const char *fname)
-{
-#if HAVE_XSLT
-    xmlDocPtr ndoc = xmlParseFile(fname);
-
-    if (!ndoc)
-    {
-       yaz_log(LOG_WARN, "Config file %s not found or parse error", fname);
-       return -1;  // no good
-    }
-    xmlNodePtr proxyPtr = xmlDocGetRootElement(ndoc);
-    if (!proxyPtr || proxyPtr->type != XML_ELEMENT_NODE ||
-       strcmp((const char *) proxyPtr->name, "proxy"))
-    {
-       yaz_log(LOG_WARN, "No proxy element in %s", fname);
-       xmlFreeDoc(ndoc);
-       return -1;
-    }
-    m_proxyPtr = proxyPtr;
-
-    // OK: release previous and make it the current one.
-    if (m_docPtr)
-       xmlFreeDoc(m_docPtr);
-    m_docPtr = ndoc;
-    return 0;
-#else
-    return -2;
-#endif
-}
-
-#if HAVE_XSLT
-const char *Yaz_ProxyConfig::get_text(xmlNodePtr ptr)
-{
-    for(ptr = ptr->children; ptr; ptr = ptr->next)
-       if (ptr->type == XML_TEXT_NODE)
-       {
-           xmlChar *t = ptr->content;
-           if (t)
-           {
-               while (*t == ' ')
-                   t++;
-               return (const char *) t;
-           }
-       }
-    return 0;
-}
-#endif
-
-#if HAVE_XSLT
-void Yaz_ProxyConfig::return_limit(xmlNodePtr ptr,
-                                  int *limit_bw,
-                                  int *limit_pdu,
-                                  int *limit_req)
-{
-    for (ptr = ptr->children; ptr; ptr = ptr->next)
-    {
-       if (ptr->type == XML_ELEMENT_NODE 
-           && !strcmp((const char *) ptr->name, "bandwidth"))
-       {
-           const char *t = get_text(ptr);
-           if (t)
-               *limit_bw = atoi(t);
-       }
-       if (ptr->type == XML_ELEMENT_NODE 
-           && !strcmp((const char *) ptr->name, "retrieve"))
-       {
-           const char *t = get_text(ptr);
-           if (t)
-               *limit_req = atoi(t);
-       }
-       if (ptr->type == XML_ELEMENT_NODE 
-           && !strcmp((const char *) ptr->name, "pdu"))
-       {
-           const char *t = get_text(ptr);
-           if (t)
-               *limit_pdu = atoi(t);
-       }
-    }
-}
-#endif
-
-#if HAVE_XSLT
-void Yaz_ProxyConfig::return_target_info(xmlNodePtr ptr,
-                                        const char **url,
-                                        int *limit_bw,
-                                        int *limit_pdu,
-                                        int *limit_req,
-                                        int *target_idletime,
-                                        int *client_idletime,
-                                        int *keepalive_limit_bw,
-                                        int *keepalive_limit_pdu,
-                                        int *pre_init,
-                                        const char **cql2rpn)
-{
-    *pre_init = 0;
-    int no_url = 0;
-    ptr = ptr->children;
-    for (; ptr; ptr = ptr->next)
-    {
-       if (ptr->type == XML_ELEMENT_NODE 
-           && !strcmp((const char *) ptr->name, "preinit"))
-       {
-           const char *v = get_text(ptr);
-           *pre_init = v ? atoi(v) : 1;
-       }
-       if (ptr->type == XML_ELEMENT_NODE 
-           && !strcmp((const char *) ptr->name, "url"))
-       {
-           const char *t = get_text(ptr);
-           if (t && no_url < MAX_ZURL_PLEX)
-           {
-               url[no_url++] = t;
-               url[no_url] = 0;
-           }
-       }
-       if (ptr->type == XML_ELEMENT_NODE 
-           && !strcmp((const char *) ptr->name, "keepalive"))
-       {
-           int dummy;
-           *keepalive_limit_bw = 500000;
-           *keepalive_limit_pdu = 1000;
-           return_limit(ptr, keepalive_limit_bw, keepalive_limit_pdu,
-                        &dummy);
-       }
-       if (ptr->type == XML_ELEMENT_NODE 
-           && !strcmp((const char *) ptr->name, "limit"))
-           return_limit(ptr, limit_bw, limit_pdu, limit_req);
-       if (ptr->type == XML_ELEMENT_NODE 
-           && !strcmp((const char *) ptr->name, "target-timeout"))
-       {
-           const char *t = get_text(ptr);
-           if (t)
-           {
-               *target_idletime = atoi(t);
-               if (*target_idletime < 0)
-                   *target_idletime = 0;
-           }
-       }
-       if (ptr->type == XML_ELEMENT_NODE 
-           && !strcmp((const char *) ptr->name, "client-timeout"))
-       {
-           const char *t = get_text(ptr);
-           if (t)
-           {
-               *client_idletime = atoi(t);
-               if (*client_idletime < 0)
-                   *client_idletime = 0;
-           }
-       }
-       if (ptr->type == XML_ELEMENT_NODE 
-           && !strcmp((const char *) ptr->name, "cql2rpn"))
-       {
-           const char *t = get_text(ptr);
-           if (t)
-               *cql2rpn = t;
-       }
-    }
-}
-#endif
-
-int Yaz_ProxyConfig::atoi_l(const char **cp)
-{
-    int v = 0;
-    while (**cp && isdigit(**cp))
-    {
-       v = v*10 + (**cp - '0');
-       (*cp)++;
-    }
-    return v;
-}
-
-int Yaz_ProxyConfig::match_list(int v, const char *m)
-{
-  while(m && *m)
-  {
-      while(*m && isspace(*m))
-         m++;
-      if (*m == '*')
-         return 1;
-      int l = atoi_l(&m);
-      int h = l;
-      if (*m == '-')
-      {
-         ++m;
-         h = atoi_l(&m);
-      }
-      if (v >= l && v <= h)
-         return 1;
-      if (*m == ',')
-         m++;
-  }
-  return 0;
-}
-
-#if HAVE_XSLT
-int Yaz_ProxyConfig::check_type_1_attributes(ODR odr, xmlNodePtr ptrl,
-                                            Z_AttributeList *attrs,
-                                            char **addinfo)
-{
-    int i;
-    for (i = 0; i<attrs->num_attributes; i++)
-    {
-       Z_AttributeElement *el = attrs->attributes[i];
-       
-       if (!el->attributeType)
-           continue;
-       int type = *el->attributeType;
-       int *value = 0;
-       
-       if (el->which == Z_AttributeValue_numeric && el->value.numeric)
-           value = el->value.numeric;
-       
-       xmlNodePtr ptr;
-       for(ptr = ptrl->children; ptr; ptr = ptr->next)
-       {
-           if (ptr->type == XML_ELEMENT_NODE &&
-               !strcmp((const char *) ptr->name, "attribute"))
-           {
-               const char *match_type = 0;
-               const char *match_value = 0;
-               const char *match_error = 0;
-               struct _xmlAttr *attr;
-               for (attr = ptr->properties; attr; attr = attr->next)
-               {
-                   if (!strcmp((const char *) attr->name, "type") &&
-                       attr->children && attr->children->type == XML_TEXT_NODE)
-                       match_type = (const char *) attr->children->content;
-                   if (!strcmp((const char *) attr->name, "value") &&
-                       attr->children && attr->children->type == XML_TEXT_NODE)
-                       match_value = (const char *) attr->children->content;
-                   if (!strcmp((const char *) attr->name, "error") &&
-                       attr->children && attr->children->type == XML_TEXT_NODE)
-                       match_error = (const char *) attr->children->content;
-               }
-               if (match_type && match_value)
-               {
-                   char addinfo_str[20];
-                   if (!match_list(type, match_type))
-                       continue;
-                   
-                   *addinfo_str = '\0';
-                   if (!strcmp(match_type, "*"))
-                       sprintf (addinfo_str, "%d", type);
-                   else if (value)
-                   {
-                       if (!match_list(*value, match_value))
-                           continue;
-                       sprintf (addinfo_str, "%d", *value);
-                   }
-                   else
-                       continue;
-                   
-                   if (match_error)
-                   {
-                       if (*addinfo_str)
-                           *addinfo = odr_strdup(odr, addinfo_str);
-                       return atoi(match_error);
-                   }
-                   break;
-               }
-           }
-       }
-    }
-    return 0;
-}
-#endif
-
-#if HAVE_XSLT
-int Yaz_ProxyConfig::check_type_1_structure(ODR odr, xmlNodePtr ptr,
-                                           Z_RPNStructure *q,
-                                           char **addinfo)
-{
-    if (q->which == Z_RPNStructure_complex)
-    {
-       int e = check_type_1_structure(odr, ptr, q->u.complex->s1, addinfo);
-       if (e)
-           return e;
-       e = check_type_1_structure(odr, ptr, q->u.complex->s2, addinfo);
-       return e;
-    }
-    else if (q->which == Z_RPNStructure_simple)
-    {
-       if (q->u.simple->which == Z_Operand_APT)
-       {
-           return check_type_1_attributes(
-               odr, ptr, q->u.simple->u.attributesPlusTerm->attributes,
-               addinfo);
-       }
-    }
-    return 0;
-}
-#endif
-
-#if HAVE_XSLT
-int Yaz_ProxyConfig::check_type_1(ODR odr, xmlNodePtr ptr, Z_RPNQuery *query,
-                                 char **addinfo)
-{
-    // possibly check for Bib-1
-    return check_type_1_structure(odr, ptr, query->RPNStructure, addinfo);
-}
-#endif
-
-int Yaz_ProxyConfig::check_query(ODR odr, const char *name, Z_Query *query,
-                                char **addinfo)
-{
-#if HAVE_XSLT
-    xmlNodePtr ptr;
-    
-    ptr = find_target_node(name, 0);
-    if (ptr)
-    {
-       if (query->which == Z_Query_type_1 || query->which == Z_Query_type_101)
-           return check_type_1(odr, ptr, query->u.type_1, addinfo);
-    }
-#endif
-    return 0;
-}
-
-#if HAVE_XSLT
-int Yaz_ProxyConfig::check_schema(xmlNodePtr ptr, Z_RecordComposition *comp,
-                                 const char *schema_identifier)
-{
-    char *esn = 0;
-    int default_match = 1;
-    if (comp && comp->which == Z_RecordComp_simple &&
-       comp->u.simple && comp->u.simple->which == Z_ElementSetNames_generic)
-    {
-       esn = comp->u.simple->u.generic;
-    }
-    // if no ESN/schema was given accept..
-    if (!esn)
-       return 1;
-    // check if schema identifier match
-    if (schema_identifier && !strcmp(esn, schema_identifier))
-       return 1;
-    // Check each name element
-    for (; ptr; ptr = ptr->next)
-    {
-       if (ptr->type == XML_ELEMENT_NODE 
-           && !strcmp((const char *) ptr->name, "name"))
-       {
-           xmlNodePtr tptr = ptr->children;
-           default_match = 0;
-           for (; tptr; tptr = tptr->next)
-               if (tptr->type == XML_TEXT_NODE && tptr->content)
-               {
-                   xmlChar *t = tptr->content;
-                   while (*t && isspace(*t))
-                       t++;
-                   int i = 0;
-                   while (esn[i] && esn[i] == t[i])
-                       i++;
-                   if (!esn[i] && (!t[i] || isspace(t[i])))
-                       return 1;
-               }
-       }
-    }
-    return default_match;
-}
-#endif
-
-int Yaz_ProxyConfig::check_syntax(ODR odr, const char *name,
-                                 Odr_oid *syntax, Z_RecordComposition *comp,
-                                 char **addinfo,
-                                 char **stylesheet, char **schema)
-{
-    if (stylesheet)
-    {
-       xfree (*stylesheet);
-       *stylesheet = 0;
-    }
-    if (schema)
-    {
-       xfree (*schema);
-       *schema = 0;
-    }
-#if HAVE_XSLT
-    int syntax_has_matched = 0;
-    xmlNodePtr ptr;
-    
-    ptr = find_target_node(name, 0);
-    if (!ptr)
-       return 0;
-    for(ptr = ptr->children; ptr; ptr = ptr->next)
-    {
-       if (ptr->type == XML_ELEMENT_NODE &&
-           !strcmp((const char *) ptr->name, "syntax"))
-       {
-           int match = 0;  // if we match record syntax
-           const char *match_type = 0;
-           const char *match_error = 0;
-           const char *match_marcxml = 0;
-           const char *match_stylesheet = 0;
-           const char *match_identifier = 0;
-           struct _xmlAttr *attr;
-           for (attr = ptr->properties; attr; attr = attr->next)
-           {
-               if (!strcmp((const char *) attr->name, "type") &&
-                   attr->children && attr->children->type == XML_TEXT_NODE)
-                   match_type = (const char *) attr->children->content;
-               if (!strcmp((const char *) attr->name, "error") &&
-                   attr->children && attr->children->type == XML_TEXT_NODE)
-                   match_error = (const char *) attr->children->content;
-               if (!strcmp((const char *) attr->name, "marcxml") &&
-                   attr->children && attr->children->type == XML_TEXT_NODE)
-                   match_marcxml = (const char *) attr->children->content;
-               if (!strcmp((const char *) attr->name, "stylesheet") &&
-                   attr->children && attr->children->type == XML_TEXT_NODE)
-                   match_stylesheet = (const char *) attr->children->content;
-               if (!strcmp((const char *) attr->name, "identifier") &&
-                   attr->children && attr->children->type == XML_TEXT_NODE)
-                   match_identifier = (const char *) attr->children->content;
-           }
-           if (match_type)
-           {
-               if (!strcmp(match_type, "*"))
-                   match = 1;
-               else if (!strcmp(match_type, "none"))
-               {
-                   if (syntax == 0)
-                       match = 1;
-               }
-               else if (syntax)
-               {
-                   int match_oid[OID_SIZE];
-                   oid_name_to_oid(CLASS_RECSYN, match_type, match_oid);
-                   if (oid_oidcmp(match_oid, syntax) == 0)
-                       match = 1;
-               }
-           }
-           if (match)
-           {
-               if (!match_error)
-                   syntax_has_matched = 1;
-               match = check_schema(ptr->children, comp, match_identifier);
-           }
-           if (match)
-           {
-               if (stylesheet && match_stylesheet)
-               {
-                   xfree(*stylesheet);
-                   *stylesheet = xstrdup(match_stylesheet);
-               }
-               if (schema && match_identifier)
-               {
-                   xfree(*schema);
-                   *schema = xstrdup(match_identifier);
-               }
-               if (match_marcxml)
-               {
-                   return -1;
-               }
-               if (match_error)
-               {
-                   if (syntax_has_matched)  // if syntax OK, bad schema/ESN
-                       return 25;
-                   if (syntax)
-                   {
-                       char dotoid_str[100];
-                       oid_to_dotstring(syntax, dotoid_str);
-                       *addinfo = odr_strdup(odr, dotoid_str);
-                   }
-                   return atoi(match_error);
-               }
-               return 0;
-           }
-       }
-    }
-#endif
-    return 0;
-}
-
-#if HAVE_XSLT
-xmlNodePtr Yaz_ProxyConfig::find_target_db(xmlNodePtr ptr, const char *db)
-{
-    xmlNodePtr dptr;
-    if (!db)
-       return ptr;
-    if (!ptr)
-       return 0;
-    for (dptr = ptr->children; dptr; dptr = dptr->next)
-       if (dptr->type == XML_ELEMENT_NODE &&
-           !strcmp((const char *) dptr->name, "database"))
-       {
-           struct _xmlAttr *attr;
-           for (attr = dptr->properties; attr; attr = attr->next)
-               if (!strcmp((const char *) attr->name, "name"))
-               {
-                   if (attr->children
-                       && attr->children->type==XML_TEXT_NODE
-                       && attr->children->content 
-                       && (!strcmp((const char *) attr->children->content, db)
-                           || !strcmp((const char *) attr->children->content,
-                                      "*")))
-                       return dptr;
-               }
-       }
-    return ptr;
-}
-    
-xmlNodePtr Yaz_ProxyConfig::find_target_node(const char *name, const char *db)
-{
-    xmlNodePtr ptr;
-    if (!m_proxyPtr)
-       return 0;
-    for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
-    {
-       if (ptr->type == XML_ELEMENT_NODE &&
-           !strcmp((const char *) ptr->name, "target"))
-       {
-           // default one ? 
-           if (!name)
-           {
-               // <target default="1"> ?
-               struct _xmlAttr *attr;
-               for (attr = ptr->properties; attr; attr = attr->next)
-                   if (!strcmp((const char *) attr->name, "default") &&
-                       attr->children && attr->children->type == XML_TEXT_NODE)
-                   {
-                       xmlChar *t = attr->children->content;
-                       if (!t || *t == '1')
-                       {
-                           return find_target_db(ptr, db);
-                       }
-                   }
-           }
-           else
-           {
-               // <target name="name"> ?
-               struct _xmlAttr *attr;
-               for (attr = ptr->properties; attr; attr = attr->next)
-                   if (!strcmp((const char *) attr->name, "name"))
-                   {
-                       if (attr->children
-                           && attr->children->type==XML_TEXT_NODE
-                           && attr->children->content 
-                           && (!strcmp((const char *) attr->children->content,
-                                       name)
-                               || !strcmp((const char *) attr->children->content,
-                                          "*")))
-                       {
-                           return find_target_db(ptr, db);
-                       }
-                   }
-           }
-       }
-    }
-    return 0;
-}
-#endif
-
-int Yaz_ProxyConfig::get_target_no(int no,
-                                  const char **name,
-                                  const char **url,
-                                  int *limit_bw,
-                                  int *limit_pdu,
-                                  int *limit_req,
-                                  int *target_idletime,
-                                  int *client_idletime,
-                                  int *max_clients,
-                                  int *keepalive_limit_bw,
-                                  int *keepalive_limit_pdu,
-                                  int *pre_init,
-                                  const char **cql2rpn)
-{
-#if HAVE_XSLT
-    xmlNodePtr ptr;
-    if (!m_proxyPtr)
-       return 0;
-    int i = 0;
-    for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
-       if (ptr->type == XML_ELEMENT_NODE &&
-           !strcmp((const char *) ptr->name, "target"))
-       {
-           if (i == no)
-           {
-               struct _xmlAttr *attr;
-               for (attr = ptr->properties; attr; attr = attr->next)
-                   if (!strcmp((const char *) attr->name, "name"))
-                   {
-                       if (attr->children
-                           && attr->children->type==XML_TEXT_NODE
-                           && attr->children->content)
-                           *name = (const char *) attr->children->content;
-                   }
-               return_target_info(ptr, url, limit_bw, limit_pdu, limit_req,
-                                  target_idletime, client_idletime,
-                                  keepalive_limit_bw, keepalive_limit_pdu,
-                                  pre_init, cql2rpn);
-               return 1;
-           }
-           i++;
-       }
-#endif
-    return 0;
-}
-
-int Yaz_ProxyConfig::mycmp(const char *hay, const char *item, size_t len)
-{
-    if (len == strlen(item) && memcmp(hay, item, len) == 0)
-       return 1;
-    return 0;
-}
-
-void Yaz_ProxyConfig::get_generic_info(int *log_mask,
-                                      int *max_clients)
-{
-#if HAVE_XSLT
-    xmlNodePtr ptr;
-    if (!m_proxyPtr)
-       return;
-    for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
-    {
-       if (ptr->type == XML_ELEMENT_NODE 
-           && !strcmp((const char *) ptr->name, "log"))
-       {
-           const char *v = get_text(ptr);
-           *log_mask = 0;
-           while (v && *v)
-           {
-               const char *cp = v;
-               while (*cp && *cp != ',' && !isspace(*cp))
-                   cp++;
-               size_t len = cp - v;
-               if (mycmp(v, "client-apdu", len))
-                   *log_mask |= PROXY_LOG_APDU_CLIENT;
-               if (mycmp(v, "server-apdu", len))
-                   *log_mask |= PROXY_LOG_APDU_SERVER;
-               if (mycmp(v, "client-requests", len))
-                   *log_mask |= PROXY_LOG_REQ_CLIENT;
-               if (mycmp(v, "server-requests", len))
-                   *log_mask |= PROXY_LOG_REQ_SERVER;
-               if (isdigit(*v))
-                   *log_mask |= atoi(v);
-               if (*cp == ',')
-                   cp++;
-               while (*cp && isspace(*cp))
-                   cp++;
-               v = cp;
-           }
-       }
-       if (ptr->type == XML_ELEMENT_NODE &&
-           !strcmp((const char *) ptr->name, "max-clients"))
-       {
-           const char *t = get_text(ptr);
-           if (t)
-           {
-               *max_clients = atoi(t);
-               if (*max_clients  < 1)
-                   *max_clients = 1;
-           }
-       }
-    }
-#endif
-}
-
-char *Yaz_ProxyConfig::get_explain(ODR odr, const char *name, const char *db,
-                                  int *len)
-{
-#if HAVE_XSLT
-    xmlNodePtr ptr = find_target_node(name, db);
-    if (ptr)
-    {
-       ptr = ptr->children;
-       for (; ptr; ptr = ptr->next)
-           if (ptr->type == XML_ELEMENT_NODE &&
-               !strcmp((const char *) ptr->name, "explain"))
-           {
-               xmlNodePtr ptr1 = ptr->children;
-               if (db)
-               {
-                   for (; ptr1; ptr1 = ptr1->next)
-                       if (ptr1->type == XML_ELEMENT_NODE &&
-                           !strcmp((const char *) ptr1->name, "serverInfo"))
-                           break;
-                   if (!ptr1)
-                       continue;
-                   for (ptr1 = ptr1->children; ptr1; ptr1 = ptr1->next)
-                       if (ptr1->type == XML_ELEMENT_NODE &&
-                           !strcmp((const char *) ptr1->name, "database"))
-                           break;
-                   
-                   if (!ptr1)
-                       continue;
-                   for (ptr1 = ptr1->children; ptr1; ptr1 = ptr1->next)
-                       if (ptr1->type == XML_TEXT_NODE &&
-                           ptr1->content &&
-                           !strcmp((const char *) ptr1->content, db))
-                           break;
-                   if (!ptr1)
-                       continue;
-               }
-               xmlNodePtr ptr2 = xmlCopyNode(ptr, 1);
-
-               xmlDocPtr doc = xmlNewDoc((const xmlChar *) "1.0");
-               
-               xmlDocSetRootElement(doc, ptr2);
-               
-               xmlChar *buf_out;
-               xmlDocDumpMemory(doc, &buf_out, len);
-               char *content = (char*) odr_malloc(odr, *len);
-               memcpy(content, buf_out, *len);
-               
-               xmlFree(buf_out);
-               xmlFreeDoc(doc);
-               return content;
-           }
-    }
-#endif
-    return 0;
-}
-
-void Yaz_ProxyConfig::get_target_info(const char *name,
-                                     const char **url,
-                                     int *limit_bw,
-                                     int *limit_pdu,
-                                     int *limit_req,
-                                     int *target_idletime,
-                                     int *client_idletime,
-                                     int *max_clients,
-                                     int *keepalive_limit_bw,
-                                     int *keepalive_limit_pdu,
-                                     int *pre_init,
-                                     const char **cql2rpn)
-{
-#if HAVE_XSLT
-    xmlNodePtr ptr;
-    if (!m_proxyPtr)
-    {
-       url[0] = name;
-       url[1] = 0;
-       return;
-    }
-    url[0] = 0;
-    for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
-    {
-       if (ptr->type == XML_ELEMENT_NODE &&
-           !strcmp((const char *) ptr->name, "max-clients"))
-       {
-           const char *t = get_text(ptr);
-           if (t)
-           {
-               *max_clients = atoi(t);
-               if (*max_clients  < 1)
-                   *max_clients = 1;
-           }
-       }
-    }
-    ptr = find_target_node(name, 0);
-    if (ptr)
-    {
-       if (name)
-       {
-           url[0] = name;
-           url[1] = 0;
-       }
-       return_target_info(ptr, url, limit_bw, limit_pdu, limit_req,
-                          target_idletime, client_idletime,
-                          keepalive_limit_bw, keepalive_limit_pdu,
-                          pre_init, cql2rpn);
-    }
-#else
-    *url = name;
-    return;
-#endif
-}
-
-
diff --git a/src/yaz-proxy-main.cpp b/src/yaz-proxy-main.cpp
deleted file mode 100644 (file)
index ae041d1..0000000
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * Copyright (c) 1998-2004, Index Data.
- * See the file LICENSE for details.
- * 
- * $Id: yaz-proxy-main.cpp,v 1.35 2004-03-17 10:49:58 adam Exp $
- */
-
-#include <signal.h>
-#include <unistd.h>
-#include <pwd.h>
-#include <sys/types.h>
-#include <stdarg.h>
-
-#if HAVE_GETRLIMIT
-#include <sys/time.h>
-#include <sys/resource.h>
-#endif
-
-#include <yaz/log.h>
-#include <yaz/options.h>
-
-#include <yaz++/socket-manager.h>
-#include <yaz++/pdu-assoc.h>
-#include <yaz++/proxy.h>
-
-void usage(char *prog)
-{
-    fprintf (stderr, "%s: [-c config] [-l log] [-a log] [-v level] [-t target] "
-             "[-u uid] [-p pidfile] @:port\n", prog);
-    exit (1);
-}
-
-static char *pid_fname = 0;
-static char *uid = 0;
-static char *log_file = 0;
-static int debug = 0;
-static int no_limit_files = 0;
-
-int args(Yaz_Proxy *proxy, int argc, char **argv)
-{
-    char *addr = 0;
-    char *arg;
-    char *prog = argv[0];
-    int ret;
-
-    while ((ret = options("o:a:t:v:c:u:i:m:l:T:p:U:n:X",
-                         argv, argc, &arg)) != -2)
-    {
-       int err;
-        switch (ret)
-        {
-        case 0:
-            if (addr)
-           {
-               usage(prog);
-               return 1;
-           }
-           addr = arg;
-            break;
-       case 'c':
-           err = proxy->set_config(arg);
-           if (err == -2)
-           {
-               fprintf(stderr, "Config file support not enabled (proxy not compiled with libxml2 support)\n");
-               exit(1);
-           }
-           else if (err == -1)
-           {
-               fprintf(stderr, "Bad or missing file %s\n", arg);
-               exit(1);
-           }
-           break;
-       case 'a':
-           proxy->set_APDU_log(arg);
-           break;
-        case 't':
-           proxy->set_default_target(arg);
-           break;
-        case 'U':
-            proxy->set_proxy_authentication(arg);
-            break;
-        case 'o':
-           proxy->option("optimize", arg);
-           break;
-       case 'v':
-           yaz_log_init_level (yaz_log_mask_str(arg));
-           break;
-       case 'l':
-           yaz_log_init_file (arg);
-           log_file = xstrdup(arg);
-           break;
-       case 'm':
-           proxy->set_max_clients(atoi(arg));
-           break;
-        case 'i':
-           proxy->set_client_idletime(atoi(arg));
-           break;
-        case 'T':
-           proxy->set_target_idletime(atoi(arg));
-           break;
-       case 'n':
-           no_limit_files = atoi(arg);
-           break;
-       case 'X':
-           debug = 1;
-           break;
-       case 'p':
-           if (!pid_fname)
-               pid_fname = xstrdup(arg);
-           break;
-       case 'u':
-           if (!uid)
-               uid = xstrdup(arg);
-           break;
-        default:
-           usage(prog);
-           return 1;
-        }
-    }
-    if (addr)
-    {
-       if (proxy->server(addr))
-       {
-           yaz_log(LOG_FATAL|LOG_ERRNO, "listen %s", addr);
-           exit(1);
-       }
-    }
-    else
-    {
-       usage(prog);
-       return 1;
-    }
-    return 0;
-}
-
-static Yaz_Proxy *static_yaz_proxy = 0;
-static void sighup_handler(int num)
-{
-    signal(SIGHUP, sighup_handler);
-    if (static_yaz_proxy)
-       static_yaz_proxy->reconfig();
-}
-
-#if HAVE_XSLT
-static void proxy_xml_error_handler(void *ctx, const char *fmt, ...)
-{
-    char buf[1024];
-
-    va_list ap;
-    va_start(ap, fmt);
-
-    vsnprintf(buf, sizeof(buf), fmt, ap);
-
-    yaz_log(LOG_WARN, "%s: %s", (char*) ctx, buf);
-
-    va_end (ap);
-}
-#endif
-
-static void child_run(Yaz_SocketManager *m, int run)
-{
-    signal(SIGHUP, sighup_handler);
-
-#if HAVE_XSLT
-    xmlSetGenericErrorFunc((void *) "XML", proxy_xml_error_handler);
-    xsltSetGenericErrorFunc((void *) "XSLT", proxy_xml_error_handler);
-#endif
-    yaz_log(LOG_LOG, "0 proxy run=%d pid=%ld", run, (long) getpid());
-
-    if (no_limit_files)
-    {
-#if HAVE_SETRLIMIT
-       struct rlimit limit_data;
-       limit_data.rlim_cur = no_limit_files;
-       limit_data.rlim_max = no_limit_files;
-       
-       yaz_log(LOG_LOG, "0 setrlimit NOFILE cur=%d max=%d",
-               limit_data.rlim_cur, limit_data.rlim_max);
-       if (setrlimit(RLIMIT_NOFILE, &limit_data))
-           yaz_log(LOG_ERRNO|LOG_WARN, "setrlimit");
-#else
-       yaz_log(LOG_WARN, "setrlimit unavablable. Option -n ignored");
-#endif
-    }
-    if (pid_fname)
-    {
-       FILE *f = fopen(pid_fname, "w");
-       if (!f)
-       {
-           yaz_log(LOG_ERRNO|LOG_FATAL, "Couldn't create %s", pid_fname);
-           exit(0);
-       }
-       fprintf(f, "%ld", (long) getpid());
-       fclose(f);
-       xfree(pid_fname);
-    }
-    if (uid)
-    {
-       struct passwd *pw;
-
-       if (!(pw = getpwnam(uid)))
-       {
-           yaz_log(LOG_FATAL, "%s: Unknown user", uid);
-           exit(3);
-       }
-       if (log_file)
-       {
-           chown(log_file, pw->pw_uid,  pw->pw_gid);
-           xfree(log_file);
-       }
-       if (setuid(pw->pw_uid) < 0)
-       {
-           yaz_log(LOG_FATAL|LOG_ERRNO, "setuid");
-           exit(4);
-       }
-       xfree(uid);
-    }
-#if HAVE_GETRLIMIT
-    struct rlimit limit_data;
-    getrlimit(RLIMIT_NOFILE, &limit_data);
-    yaz_log(LOG_LOG, "0 getrlimit NOFILE cur=%d max=%d",
-           limit_data.rlim_cur, limit_data.rlim_max);
-#endif
-    
-    while (m->processEvent() > 0)
-       ;
-
-    exit (0);
-}
-
-int main(int argc, char **argv)
-{
-#if HAVE_XSLT
-    xmlInitMemory();
-    
-    LIBXML_TEST_VERSION
-#endif
-    int cont = 1;
-    int run = 1;
-    Yaz_SocketManager mySocketManager;
-    Yaz_Proxy proxy(new Yaz_PDU_Assoc(&mySocketManager));
-
-    static_yaz_proxy = &proxy;
-
-    args(&proxy, argc, argv);
-
-    if (debug)
-    {
-       child_run(&mySocketManager, run);
-       exit(0);
-    }
-    while (cont)
-    {
-       pid_t p = fork();
-       if (p == (pid_t) -1)
-       {
-           yaz_log(LOG_FATAL|LOG_ERRNO, "fork");
-           exit(1);
-       }
-       else if (p == 0)
-       {
-           child_run(&mySocketManager, run);
-       }
-       pid_t p1;
-       int status;
-       p1 = wait(&status);
-
-       yaz_log_reopen();
-
-       if (p1 != p)
-       {
-           yaz_log(LOG_FATAL, "p1=%d != p=%d", p1, p);
-           exit(1);
-       }
-       if (WIFSIGNALED(status))
-       {
-           switch(WTERMSIG(status)) {
-           case SIGILL:
-               yaz_log(LOG_WARN, "Received SIGILL from child %ld", (long) p);
-               cont = 1;
-               break;
-           case SIGABRT:
-               yaz_log(LOG_WARN, "Received SIGABRT from child %ld", (long) p);
-               cont = 1;
-               break ;
-           case SIGSEGV:
-               yaz_log(LOG_WARN, "Received SIGSEGV from child %ld", (long) p);
-               cont = 1;
-               break;
-           case SIGBUS:        
-               yaz_log(LOG_WARN, "Received SIGBUS from child %ld", (long) p);
-               cont = 1;
-               break;
-           case SIGTERM:
-               yaz_log(LOG_LOG, "Received SIGTERM from child %ld",
-                       (long) p);
-               cont = 0;
-               break;
-           default:
-               yaz_log(LOG_WARN, "Received SIG %d from child %ld",
-                       WTERMSIG(status), (long) p);
-               cont = 0;
-           }
-       }
-       else if (status == 0)
-           cont = 0;
-       else
-       {
-           yaz_log(LOG_LOG, "Exit %d from child %ld", status, (long) p);
-           cont = 1;
-       }
-       if (cont)
-           sleep(1 + run/5);
-       run++;
-    }
-    exit (0);
-    return 0;
-}
diff --git a/src/yaz-proxy.cpp b/src/yaz-proxy.cpp
deleted file mode 100644 (file)
index e7783f9..0000000
+++ /dev/null
@@ -1,2582 +0,0 @@
-/*
- * Copyright (c) 1998-2004, Index Data.
- * See the file LICENSE for details.
- * 
- * $Id: yaz-proxy.cpp,v 1.110 2004-03-17 10:49:22 adam Exp $
- */
-
-#include <unistd.h>
-#include <assert.h>
-#include <time.h>
-#include <sys/types.h>
-#include <fcntl.h>
-
-#include <yaz/srw.h>
-#include <yaz/marcdisp.h>
-#include <yaz/yaz-iconv.h>
-#include <yaz/log.h>
-#include <yaz/diagbib1.h>
-#include <yaz++/proxy.h>
-#include <yaz/pquery.h>
-
-static const char *apdu_name(Z_APDU *apdu)
-{
-    switch (apdu->which)
-    {
-    case Z_APDU_initRequest:
-        return "initRequest";
-    case Z_APDU_initResponse:
-        return "initResponse";
-    case Z_APDU_searchRequest:
-       return "searchRequest";
-    case Z_APDU_searchResponse:
-       return "searchResponse";
-    case Z_APDU_presentRequest:
-       return "presentRequest";
-    case Z_APDU_presentResponse:
-       return "presentResponse";
-    case Z_APDU_deleteResultSetRequest:
-       return "deleteResultSetRequest";
-    case Z_APDU_deleteResultSetResponse:
-       return "deleteResultSetResponse";
-    case Z_APDU_scanRequest:
-       return "scanRequest";
-    case Z_APDU_scanResponse:
-       return "scanResponse";
-    case Z_APDU_sortRequest:
-       return "sortRequest";
-    case Z_APDU_sortResponse:
-       return "sortResponse";
-    case Z_APDU_extendedServicesRequest:
-       return "extendedServicesRequest";
-    case Z_APDU_extendedServicesResponse:
-       return "extendedServicesResponse";
-    case Z_APDU_close:
-       return "close";
-    }
-    return "other";
-}
-
-static const char *gdu_name(Z_GDU *gdu)
-{
-    switch(gdu->which)
-    {
-    case Z_GDU_Z3950:
-       return apdu_name(gdu->u.z3950);
-    case Z_GDU_HTTP_Request:
-       return "HTTP Request";
-    case Z_GDU_HTTP_Response:
-       return "HTTP Response";
-    }
-    return "Unknown request/response";
-}
-
-Yaz_Proxy::Yaz_Proxy(IYaz_PDU_Observable *the_PDU_Observable,
-                    Yaz_Proxy *parent) :
-    Yaz_Z_Assoc(the_PDU_Observable), m_bw_stat(60), m_pdu_stat(60)
-{
-    m_PDU_Observable = the_PDU_Observable;
-    m_client = 0;
-    m_parent = parent;
-    m_clientPool = 0;
-    m_seqno = 1;
-    m_keepalive_limit_bw = 500000;
-    m_keepalive_limit_pdu = 1000;
-    m_proxyTarget = 0;
-    m_default_target = 0;
-    m_proxy_authentication = 0;
-    m_max_clients = 150;
-    m_log_mask = 0;
-    m_seed = time(0);
-    m_client_idletime = 600;
-    m_target_idletime = 600;
-    m_optimize = xstrdup ("1");
-    strcpy(m_session_str, "0 ");
-    m_session_no=0;
-    m_bytes_sent = 0;
-    m_bytes_recv = 0;
-    m_bw_hold_PDU = 0;
-    m_bw_max = 0;
-    m_pdu_max = 0;
-    m_max_record_retrieve = 0;
-    m_reconfig_flag = 0;
-    m_config_fname = 0;
-    m_request_no = 0;
-    m_invalid_session = 0;
-    m_referenceId = 0;
-    m_referenceId_mem = nmem_create();
-    m_config = 0;
-    m_marcxml_flag = 0;
-    m_stylesheet_xsp = 0;
-    m_stylesheet_nprl = 0;
-    m_s2z_stylesheet = 0;
-    m_s2z_database = 0;
-    m_schema = 0;
-    m_initRequest_apdu = 0;
-    m_initRequest_mem = 0;
-    m_initRequest_preferredMessageSize = 0;
-    m_initRequest_maximumRecordSize = 0;
-    m_initRequest_options = 0;
-    m_initRequest_version = 0;
-    m_apdu_invalid_session = 0;
-    m_mem_invalid_session = 0;
-    m_s2z_odr_init = 0;
-    m_s2z_odr_search = 0;
-    m_s2z_init_apdu = 0;
-    m_s2z_search_apdu = 0;
-    m_s2z_present_apdu = 0;
-    m_http_keepalive = 0;
-    m_http_version = 0;
-    m_soap_ns = 0;
-    m_s2z_packing = Z_SRW_recordPacking_string;
-    m_time_tv.tv_sec = 0;
-    m_time_tv.tv_usec = 0;
-    if (!m_parent)
-       low_socket_open();
-}
-
-Yaz_Proxy::~Yaz_Proxy()
-{
-    yaz_log(LOG_LOG, "%sClosed %d/%d sent/recv bytes total", m_session_str,
-           m_bytes_sent, m_bytes_recv);
-    nmem_destroy(m_initRequest_mem);
-    nmem_destroy(m_mem_invalid_session);
-    nmem_destroy(m_referenceId_mem);
-
-    xfree (m_proxyTarget);
-    xfree (m_default_target);
-    xfree (m_proxy_authentication);
-    xfree (m_optimize);
-
-    if (m_stylesheet_xsp)
-       xsltFreeStylesheet(m_stylesheet_xsp);
-
-    xfree (m_schema);
-    if (m_s2z_odr_init)
-       odr_destroy(m_s2z_odr_init);
-    if (m_s2z_odr_search)
-       odr_destroy(m_s2z_odr_search);
-    if (!m_parent)
-       low_socket_close();
-    delete m_config;
-}
-
-int Yaz_Proxy::set_config(const char *config)
-{
-    delete m_config;
-    m_config = new Yaz_ProxyConfig();
-    xfree(m_config_fname);
-    m_config_fname = xstrdup(config);
-    int r = m_config->read_xml(config);
-    if (!r)
-       m_config->get_generic_info(&m_log_mask, &m_max_clients);
-    return r;
-}
-
-void Yaz_Proxy::set_default_target(const char *target)
-{
-    xfree (m_default_target);
-    m_default_target = 0;
-    if (target)
-       m_default_target = (char *) xstrdup (target);
-}
-
-void Yaz_Proxy::set_proxy_authentication (const char *auth)
-{
-    xfree (m_proxy_authentication);
-    m_proxy_authentication = 0;
-    if (auth)
-       m_proxy_authentication = (char *) xstrdup (auth);
-}
-
-Yaz_ProxyConfig *Yaz_Proxy::check_reconfigure()
-{
-    if (m_parent)
-       return m_parent->check_reconfigure();
-
-    Yaz_ProxyConfig *cfg = m_config;
-    if (m_reconfig_flag)
-    {
-       yaz_log(LOG_LOG, "reconfigure");
-       yaz_log_reopen();
-       if (m_config_fname && cfg)
-       {
-           yaz_log(LOG_LOG, "reconfigure config %s", m_config_fname);
-           int r = cfg->read_xml(m_config_fname);
-           if (r)
-               yaz_log(LOG_WARN, "reconfigure failed");
-           else
-           {
-               m_log_mask = 0;
-               cfg->get_generic_info(&m_log_mask, &m_max_clients);
-           }
-       }
-       else
-           yaz_log(LOG_LOG, "reconfigure");
-       m_reconfig_flag = 0;
-    }
-    return cfg;
-}
-
-IYaz_PDU_Observer *Yaz_Proxy::sessionNotify(IYaz_PDU_Observable
-                                           *the_PDU_Observable, int fd)
-{
-    check_reconfigure();
-    Yaz_Proxy *new_proxy = new Yaz_Proxy(the_PDU_Observable, this);
-    new_proxy->m_config = 0;
-    new_proxy->m_config_fname = 0;
-    new_proxy->timeout(m_client_idletime);
-    new_proxy->m_target_idletime = m_target_idletime;
-    new_proxy->set_default_target(m_default_target);
-    new_proxy->m_max_clients = m_max_clients;
-    new_proxy->m_log_mask = m_log_mask;
-    new_proxy->set_APDU_log(get_APDU_log());
-    if (m_log_mask & PROXY_LOG_APDU_CLIENT)
-       new_proxy->set_APDU_yazlog(1);
-    else
-       new_proxy->set_APDU_yazlog(0);
-    new_proxy->set_proxy_authentication(m_proxy_authentication);
-    sprintf(new_proxy->m_session_str, "%ld:%d ", (long) time(0), m_session_no);
-    m_session_no++;
-    yaz_log (LOG_LOG, "%sNew session %s", new_proxy->m_session_str,
-            the_PDU_Observable->getpeername());
-    return new_proxy;
-}
-
-char *Yaz_Proxy::get_cookie(Z_OtherInformation **otherInfo)
-{
-    int oid[OID_SIZE];
-    Z_OtherInformationUnit *oi;
-    struct oident ent;
-    ent.proto = PROTO_Z3950;
-    ent.oclass = CLASS_USERINFO;
-    ent.value = (oid_value) VAL_COOKIE;
-    assert (oid_ent_to_oid (&ent, oid));
-
-    if (oid_ent_to_oid (&ent, oid) && 
-       (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
-       oi->which == Z_OtherInfo_characterInfo)
-       return oi->information.characterInfo;
-    return 0;
-}
-
-char *Yaz_Proxy::get_proxy(Z_OtherInformation **otherInfo)
-{
-    int oid[OID_SIZE];
-    Z_OtherInformationUnit *oi;
-    struct oident ent;
-    ent.proto = PROTO_Z3950;
-    ent.oclass = CLASS_USERINFO;
-    ent.value = (oid_value) VAL_PROXY;
-    if (oid_ent_to_oid (&ent, oid) &&
-       (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
-       oi->which == Z_OtherInfo_characterInfo)
-       return oi->information.characterInfo;
-    return 0;
-}
-
-const char *Yaz_Proxy::load_balance(const char **url)
-{
-    int zurl_in_use[MAX_ZURL_PLEX];
-    int zurl_in_spare[MAX_ZURL_PLEX];
-    Yaz_ProxyClient *c;
-    int i;
-
-    for (i = 0; i<MAX_ZURL_PLEX; i++)
-    {
-       zurl_in_use[i] = 0;
-       zurl_in_spare[i] = 0;
-    }
-    for (c = m_parent->m_clientPool; c; c = c->m_next)
-    {
-       for (i = 0; url[i]; i++)
-           if (!strcmp(url[i], c->get_hostname()))
-           {
-               zurl_in_use[i]++;
-               if (c->m_cookie == 0 && c->m_server == 0 && c->m_waiting == 0)
-                   zurl_in_spare[i]++;
-           }
-    }
-    int min_use = 100000;
-    int spare_for_min = 0;
-    int max_spare = 0;
-    const char *ret_min = 0;
-    const char *ret_spare = 0;
-    for (i = 0; url[i]; i++)
-    {
-       yaz_log(LOG_DEBUG, "%szurl=%s use=%d spare=%d",
-               m_session_str, url[i], zurl_in_use[i], zurl_in_spare[i]);
-       if (min_use > zurl_in_use[i])
-       {
-           ret_min = url[i];
-           min_use = zurl_in_use[i];
-           spare_for_min = zurl_in_spare[i];
-       }
-       if (max_spare < zurl_in_spare[i]) 
-       {
-           ret_spare = url[i];
-           max_spare = zurl_in_spare[i];
-       }
-    }
-    // use the one with minimum connections if spare is > 3
-    if (spare_for_min > 3)
-       return ret_min;
-    // use one with most spares (if any)
-    if (max_spare > 0)
-       return ret_spare;
-    return ret_min;
-}
-
-Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
-                                      const char *proxy_host)
-{
-    assert (m_parent);
-    Yaz_Proxy *parent = m_parent;
-    Yaz_ProxyClient *c = m_client;
-    
-    if (!m_proxyTarget)
-    {
-       const char *url[MAX_ZURL_PLEX];
-       Yaz_ProxyConfig *cfg = check_reconfigure();
-       if (proxy_host)
-       {
-#if 1
-/* only to be enabled for debugging... */
-           if (!strcmp(proxy_host, "stop"))
-               exit(0);
-#endif
-           xfree(m_default_target);
-           m_default_target = xstrdup(proxy_host);
-           proxy_host = m_default_target;
-       }
-       int client_idletime = -1;
-       const char *cql2rpn_fname = 0;
-       url[0] = m_default_target;
-       url[1] = 0;
-       if (cfg)
-       {
-           int pre_init = 0;
-           cfg->get_target_info(proxy_host, url, &m_bw_max,
-                                &m_pdu_max, &m_max_record_retrieve,
-                                &m_target_idletime, &client_idletime,
-                                &parent->m_max_clients,
-                                &m_keepalive_limit_bw,
-                                &m_keepalive_limit_pdu,
-                                &pre_init,
-                                &cql2rpn_fname);
-       }
-       if (client_idletime != -1)
-       {
-           m_client_idletime = client_idletime;
-           timeout(m_client_idletime);
-       }
-       if (cql2rpn_fname)
-           m_cql2rpn.set_pqf_file(cql2rpn_fname);
-       if (!url[0])
-       {
-           yaz_log(LOG_LOG, "%sNo default target", m_session_str);
-           return 0;
-       }
-       // we don't handle multiplexing for cookie session, so we just
-       // pick the first one in this case (anonymous users will be able
-       // to use any backend)
-       if (cookie && *cookie)
-           m_proxyTarget = (char*) xstrdup(url[0]);
-       else
-           m_proxyTarget = (char*) xstrdup(load_balance(url));
-    }
-    if (cookie && *cookie)
-    {   // search in sessions with a cookie
-       for (c = parent->m_clientPool; c; c = c->m_next)
-       {
-           assert (c->m_prev);
-           assert (*c->m_prev == c);
-           if (c->m_cookie && !strcmp(cookie,c->m_cookie) &&
-               !strcmp(m_proxyTarget, c->get_hostname()))
-           {
-               // Found it in cache
-               // The following handles "cancel"
-               // If connection is busy (waiting for PDU) and
-               // we have an initRequest we can safely do re-open
-               if (c->m_waiting && apdu->which == Z_APDU_initRequest)
-               {
-                   yaz_log (LOG_LOG, "%s REOPEN target=%s", m_session_str,
-                            c->get_hostname());
-                   c->close();
-                   c->m_init_flag = 0;
-                   
-                   c->m_last_ok = 0;
-                   c->m_cache.clear();
-                   c->m_last_resultCount = 0;
-                   c->m_sr_transform = 0;
-                   c->m_waiting = 0;
-                   c->m_resultSetStartPoint = 0;
-                   c->m_target_idletime = m_target_idletime;
-                   if (c->client(m_proxyTarget))
-                   {
-                       delete c;
-                       return 0;
-                   }
-                   c->timeout(30); 
-               }
-               c->m_seqno = parent->m_seqno;
-               if (c->m_server && c->m_server != this)
-                   c->m_server->m_client = 0;
-               c->m_server = this;
-               (parent->m_seqno)++;
-               yaz_log (LOG_DEBUG, "get_client 1 %p %p", this, c);
-               return c;
-           }
-       }
-    }
-    else if (!c)
-    {
-       // don't have a client session yet. Search in session w/o cookie
-       for (c = parent->m_clientPool; c; c = c->m_next)
-       {
-           assert (c->m_prev);
-           assert (*c->m_prev == c);
-           if (c->m_server == 0 && c->m_cookie == 0 && 
-               c->m_waiting == 0 &&
-               !strcmp(m_proxyTarget, c->get_hostname()))
-           {
-               // found it in cache
-               yaz_log (LOG_LOG, "%sREUSE %d %s",
-                        m_session_str, parent->m_seqno, c->get_hostname());
-               
-               c->m_seqno = parent->m_seqno;
-               assert(c->m_server == 0);
-               c->m_server = this;
-
-               if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
-                   c->set_APDU_yazlog(1);
-               else
-                   c->set_APDU_yazlog(0);
-
-               (parent->m_seqno)++;
-               
-               parent->pre_init();
-               
-               return c;
-           }
-       }
-    }
-    if (!m_client)
-    {
-       if (apdu->which != Z_APDU_initRequest)
-       {
-           yaz_log (LOG_LOG, "%sno init request as first PDU", m_session_str);
-           return 0;
-       }
-        Z_InitRequest *initRequest = apdu->u.initRequest;
-
-        if (!initRequest->idAuthentication)
-        {
-            if (m_proxy_authentication)
-            {
-                initRequest->idAuthentication =
-                    (Z_IdAuthentication *)
-                    odr_malloc (odr_encode(),
-                                sizeof(*initRequest->idAuthentication));
-                initRequest->idAuthentication->which =
-                    Z_IdAuthentication_open;
-                initRequest->idAuthentication->u.open =
-                    odr_strdup (odr_encode(), m_proxy_authentication);
-            }
-        }
-       // go through list of clients - and find the lowest/oldest one.
-       Yaz_ProxyClient *c_min = 0;
-       int min_seq = -1;
-       int no_of_clients = 0;
-       if (parent->m_clientPool)
-           yaz_log (LOG_DEBUG, "Existing sessions");
-       for (c = parent->m_clientPool; c; c = c->m_next)
-       {
-           yaz_log (LOG_DEBUG, " Session %-3d wait=%d %s cookie=%s", c->m_seqno,
-                              c->m_waiting, c->get_hostname(),
-                              c->m_cookie ? c->m_cookie : "");
-           no_of_clients++;
-           if (min_seq < 0 || c->m_seqno < min_seq)
-           {
-               min_seq = c->m_seqno;
-               c_min = c;
-           }
-       }
-       if (no_of_clients >= parent->m_max_clients)
-       {
-           c = c_min;
-           if (c->m_waiting || strcmp(m_proxyTarget, c->get_hostname()))
-           {
-               yaz_log (LOG_LOG, "%sMAXCLIENTS %d Destroy %d",
-                        m_session_str, parent->m_max_clients, c->m_seqno);
-               if (c->m_server && c->m_server != this)
-                   delete c->m_server;
-               c->m_server = 0;
-           }
-           else
-           {
-               yaz_log (LOG_LOG, "%sMAXCLIENTS %d Reuse %d %d %s",
-                        m_session_str, parent->m_max_clients,
-                        c->m_seqno, parent->m_seqno, c->get_hostname());
-               xfree (c->m_cookie);
-               c->m_cookie = 0;
-               if (cookie)
-                   c->m_cookie = xstrdup(cookie);
-               c->m_seqno = parent->m_seqno;
-               if (c->m_server && c->m_server != this)
-               {
-                   c->m_server->m_client = 0;
-                   delete c->m_server;
-               }
-               (parent->m_seqno)++;
-               c->m_target_idletime = m_target_idletime;
-               c->timeout(m_target_idletime);
-               
-               if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
-                   c->set_APDU_yazlog(1);
-               else
-                   c->set_APDU_yazlog(0);
-
-               return c;
-           }
-       }
-       else
-       {
-           yaz_log (LOG_LOG, "%sNEW %d %s",
-                    m_session_str, parent->m_seqno, m_proxyTarget);
-           c = new Yaz_ProxyClient(m_PDU_Observable->clone(), parent);
-           c->m_next = parent->m_clientPool;
-           if (c->m_next)
-               c->m_next->m_prev = &c->m_next;
-           parent->m_clientPool = c;
-           c->m_prev = &parent->m_clientPool;
-       }
-
-       xfree (c->m_cookie);
-       c->m_cookie = 0;
-       if (cookie)
-           c->m_cookie = xstrdup(cookie);
-
-       c->m_seqno = parent->m_seqno;
-       c->m_init_flag = 0;
-       c->m_last_resultCount = 0;
-        c->m_last_ok = 0;
-       c->m_cache.clear();
-       c->m_sr_transform = 0;
-       c->m_waiting = 0;
-       c->m_resultSetStartPoint = 0;
-       (parent->m_seqno)++;
-       if (c->client(m_proxyTarget))
-       {
-           delete c;
-           return 0;
-        }
-       c->m_target_idletime = m_target_idletime;
-       c->timeout(30);
-
-       if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
-           c->set_APDU_yazlog(1);
-       else
-           c->set_APDU_yazlog(0);
-    }
-    yaz_log (LOG_DEBUG, "get_client 3 %p %p", this, c);
-    return c;
-}
-
-void Yaz_Proxy::display_diagrecs(Z_DiagRec **pp, int num)
-{
-    int i;
-    for (i = 0; i<num; i++)
-    {
-       oident *ent;
-       Z_DefaultDiagFormat *r;
-        Z_DiagRec *p = pp[i];
-        if (p->which != Z_DiagRec_defaultFormat)
-        {
-           yaz_log(LOG_LOG, "%sError no diagnostics", m_session_str);
-            return;
-        }
-        else
-            r = p->u.defaultFormat;
-        if (!(ent = oid_getentbyoid(r->diagnosticSetId)) ||
-            ent->oclass != CLASS_DIAGSET || ent->value != VAL_BIB1)
-           yaz_log(LOG_LOG, "%sError unknown diagnostic set", m_session_str);
-        switch (r->which)
-        {
-        case Z_DefaultDiagFormat_v2Addinfo:
-           yaz_log(LOG_LOG, "%sError %d %s:%s",
-                   m_session_str,
-                   *r->condition, diagbib1_str(*r->condition),
-                   r->u.v2Addinfo);
-            break;
-        case Z_DefaultDiagFormat_v3Addinfo:
-           yaz_log(LOG_LOG, "%sError %d %s:%s",
-                   m_session_str,
-                   *r->condition, diagbib1_str(*r->condition),
-                   r->u.v3Addinfo);
-            break;
-        }
-    }
-}
-
-int Yaz_Proxy::convert_xsl(Z_NamePlusRecordList *p, Z_APDU *apdu)
-{
-    if (!m_stylesheet_xsp || p->num_records <= 0)
-       return 0;  /* no XSLT to be done ... */
-
-    m_stylesheet_offset = 0;
-    m_stylesheet_nprl = p;
-    m_stylesheet_apdu = apdu;
-    timeout(0);
-    return 1;
-}
-
-void Yaz_Proxy::convert_xsl_delay()
-{
-    Z_NamePlusRecord *npr = m_stylesheet_nprl->records[m_stylesheet_offset];
-    if (npr->which == Z_NamePlusRecord_databaseRecord)
-    {
-       Z_External *r = npr->u.databaseRecord;
-       if (r->which == Z_External_octet)
-       {
-#if 0
-           fwrite((char*) r->u.octet_aligned->buf, 1, r->u.octet_aligned->len, stdout);
-#endif
-           xmlDocPtr res, doc = xmlParseMemory(
-               (char*) r->u.octet_aligned->buf,
-               r->u.octet_aligned->len);
-
-           
-           yaz_log(LOG_LOG, "%sXSLT convert %d",
-                   m_session_str, m_stylesheet_offset);
-           res = xsltApplyStylesheet(m_stylesheet_xsp, doc, 0);
-
-           if (res)
-           {
-               xmlChar *out_buf;
-               int out_len;
-               xmlDocDumpFormatMemory (res, &out_buf, &out_len, 1);
-               
-               m_stylesheet_nprl->records[m_stylesheet_offset]->
-                   u.databaseRecord = 
-                   z_ext_record(odr_encode(), VAL_TEXT_XML,
-                                (char*) out_buf, out_len);
-               xmlFree(out_buf);
-               xmlFreeDoc(res);
-           }
-
-           xmlFreeDoc(doc);
-       }
-    }
-    m_stylesheet_offset++;
-    if (m_stylesheet_offset == m_stylesheet_nprl->num_records)
-    {
-       m_stylesheet_nprl = 0;
-       if (m_stylesheet_xsp)
-           xsltFreeStylesheet(m_stylesheet_xsp);
-       m_stylesheet_xsp = 0;
-       timeout(m_client_idletime);
-       send_PDU_convert(m_stylesheet_apdu);
-    }
-    else
-       timeout(0);
-}
-
-void Yaz_Proxy::convert_to_marcxml(Z_NamePlusRecordList *p)
-{
-    int i;
-
-    yaz_iconv_t cd = yaz_iconv_open("UTF-8", "MARC-8");
-    yaz_marc_t mt = yaz_marc_create();
-    yaz_marc_xml(mt, YAZ_MARC_MARCXML);
-    yaz_marc_iconv(mt, cd);
-    for (i = 0; i < p->num_records; i++)
-    {
-       Z_NamePlusRecord *npr = p->records[i];
-       if (npr->which == Z_NamePlusRecord_databaseRecord)
-       {
-           Z_External *r = npr->u.databaseRecord;
-           if (r->which == Z_External_octet)
-           {
-               int rlen;
-               char *result;
-               if (yaz_marc_decode_buf(mt, (char*) r->u.octet_aligned->buf,
-                                       r->u.octet_aligned->len,
-                                       &result, &rlen))
-               {
-                   npr->u.databaseRecord = z_ext_record(odr_encode(),
-                                                        VAL_TEXT_XML,
-                                                        result, rlen);
-               }
-           }
-       }
-    }
-    if (cd)
-       yaz_iconv_close(cd);
-    yaz_marc_destroy(mt);
-}
-
-void Yaz_Proxy::logtime()
-{
-    if (m_time_tv.tv_sec)
-    {
-       struct timeval tv;
-       gettimeofday(&tv, 0);
-       long diff = (tv.tv_sec - m_time_tv.tv_sec)*1000000 +
-           (tv.tv_usec - m_time_tv.tv_usec);
-       if (diff >= 0)
-           yaz_log(LOG_LOG, "%sElapsed %ld.%03ld", m_session_str,
-                   diff/1000000, (diff/1000)%1000);
-    }
-    m_time_tv.tv_sec = 0;
-    m_time_tv.tv_usec = 0;
-}
-
-int Yaz_Proxy::send_http_response(int code)
-{
-    ODR o = odr_encode();
-    Z_GDU *gdu = z_get_HTTP_Response(o, code);
-    Z_HTTP_Response *hres = gdu->u.HTTP_Response;
-    if (m_http_version)
-       hres->version = odr_strdup(o, m_http_version);
-    if (m_http_keepalive)
-        z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
-    else
-       timeout(0);
-    
-    if (m_log_mask & PROXY_LOG_REQ_CLIENT)
-    {
-       yaz_log (LOG_LOG, "%sSending %s to client", m_session_str,
-                gdu_name(gdu));
-    }
-    int len;
-    int r = send_GDU(gdu, &len);
-    m_bytes_sent += len;
-    m_bw_stat.add_bytes(len);
-    logtime();
-    return r;
-}
-
-int Yaz_Proxy::send_srw_response(Z_SRW_PDU *srw_pdu)
-{
-    ODR o = odr_encode();
-    const char *ctype = "text/xml";
-    Z_GDU *gdu = z_get_HTTP_Response(o, 200);
-    Z_HTTP_Response *hres = gdu->u.HTTP_Response;
-    if (m_http_version)
-       hres->version = odr_strdup(o, m_http_version);
-    z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
-    if (m_http_keepalive)
-        z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
-    else
-       timeout(0);
-
-    static Z_SOAP_Handler soap_handlers[2] = {
-#if HAVE_XSLT
-       {"http://www.loc.gov/zing/srw/", 0,
-        (Z_SOAP_fun) yaz_srw_codec},
-#endif
-       {0, 0, 0}
-    };
-    
-    Z_SOAP *soap_package = (Z_SOAP*) odr_malloc(o, sizeof(Z_SOAP));
-    soap_package->which = Z_SOAP_generic;
-    soap_package->u.generic = 
-       (Z_SOAP_Generic *) odr_malloc(o,  sizeof(*soap_package->u.generic));
-    soap_package->u.generic->no = 0;
-    soap_package->u.generic->ns = soap_handlers[0].ns;
-    soap_package->u.generic->p = (void *) srw_pdu;
-    soap_package->ns = m_soap_ns;
-    z_soap_codec_enc_xsl(o, &soap_package,
-                        &hres->content_buf, &hres->content_len,
-                        soap_handlers, 0, m_s2z_stylesheet);
-    if (m_log_mask & PROXY_LOG_REQ_CLIENT)
-    {
-       yaz_log (LOG_LOG, "%sSending %s to client", m_session_str,
-                gdu_name(gdu));
-    }
-    int len;
-    int r = send_GDU(gdu, &len);
-    m_bytes_sent += len;
-    m_bw_stat.add_bytes(len);
-    logtime();
-    return r;
-}
-
-int Yaz_Proxy::send_to_srw_client_error(int srw_error, const char *add)
-{
-    ODR o = odr_encode();
-    Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
-    Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
-
-    srw_res->num_diagnostics = 1;
-    srw_res->diagnostics = (Z_SRW_diagnostic *)
-       odr_malloc(o, sizeof(*srw_res->diagnostics));
-    yaz_mk_std_diagnostic(o, srw_res->diagnostics, srw_error, add);
-    return send_srw_response(srw_pdu);
-}
-
-int Yaz_Proxy::z_to_srw_diag(ODR o, Z_SRW_searchRetrieveResponse *srw_res,
-                            Z_DefaultDiagFormat *ddf)
-{
-    int bib1_code = *ddf->condition;
-    if (bib1_code == 109)
-       return 404;
-    srw_res->num_diagnostics = 1;
-    srw_res->diagnostics = (Z_SRW_diagnostic *)
-       odr_malloc(o, sizeof(*srw_res->diagnostics));
-    yaz_mk_std_diagnostic(o, srw_res->diagnostics,
-                         yaz_diag_bib1_to_srw(*ddf->condition), 
-                         ddf->u.v2Addinfo);
-    return 0;
-}
-
-int Yaz_Proxy::send_to_srw_client_ok(int hits, Z_Records *records, int start)
-{
-    ODR o = odr_encode();
-    Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
-    Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
-
-    srw_res->numberOfRecords = odr_intdup (o, hits);
-    if (records && records->which == Z_Records_DBOSD)
-    {
-       srw_res->num_records =
-           records->u.databaseOrSurDiagnostics->num_records;
-       int i;
-       srw_res->records = (Z_SRW_record *)
-           odr_malloc(o, srw_res->num_records * sizeof(Z_SRW_record));
-       for (i = 0; i < srw_res->num_records; i++)
-       {
-           Z_NamePlusRecord *npr = records->u.databaseOrSurDiagnostics->records[i];
-           if (npr->which != Z_NamePlusRecord_databaseRecord)
-           {
-               srw_res->records[i].recordSchema = "diagnostic";
-               srw_res->records[i].recordPacking = m_s2z_packing;
-               srw_res->records[i].recordData_buf = "67";
-               srw_res->records[i].recordData_len = 2;
-               srw_res->records[i].recordPosition = odr_intdup(o, i+start);
-               continue;
-           }
-           Z_External *r = npr->u.databaseRecord;
-           oident *ent = oid_getentbyoid(r->direct_reference);
-           if (r->which == Z_External_octet && ent->value == VAL_TEXT_XML)
-           {
-               srw_res->records[i].recordSchema = m_schema;
-               srw_res->records[i].recordPacking = m_s2z_packing;
-               srw_res->records[i].recordData_buf = (char*) 
-                   r->u.octet_aligned->buf;
-               srw_res->records[i].recordData_len = r->u.octet_aligned->len;
-               srw_res->records[i].recordPosition = odr_intdup(o, i+start);
-           }
-           else
-           {
-               srw_res->records[i].recordSchema = "diagnostic";
-               srw_res->records[i].recordPacking = m_s2z_packing;
-               srw_res->records[i].recordData_buf = "67";
-               srw_res->records[i].recordData_len = 2;
-               srw_res->records[i].recordPosition = odr_intdup(o, i+start);
-           }
-       }
-    }
-    if (records && records->which == Z_Records_NSD)
-    {
-       int http_code;
-       http_code = z_to_srw_diag(odr_encode(), srw_res,
-                                  records->u.nonSurrogateDiagnostic);
-       if (http_code)
-           return send_http_response(http_code);
-    }
-    return send_srw_response(srw_pdu);
-    
-}
-
-int Yaz_Proxy::send_srw_explain_response(Z_SRW_diagnostic *diagnostics,
-                                       int num_diagnostics)
-{
-    Yaz_ProxyConfig *cfg = check_reconfigure();
-    if (cfg)
-    {
-       int len;
-       char *b = cfg->get_explain(odr_encode(), 0 /* target */,
-                                  m_s2z_database, &len);
-       if (b)
-       {
-           Z_SRW_PDU *res = yaz_srw_get(odr_encode(), Z_SRW_explain_response);
-           Z_SRW_explainResponse *er = res->u.explain_response;
-
-           er->record.recordData_buf = b;
-           er->record.recordData_len = len;
-           er->record.recordPacking = m_s2z_packing;
-           er->record.recordSchema = "http://explain.z3950.org/dtd/2.0/";
-
-           er->diagnostics = diagnostics;
-           er->num_diagnostics = num_diagnostics;
-           return send_srw_response(res);
-       }
-    }
-    return send_http_response(404);
-}
-
-int Yaz_Proxy::send_PDU_convert(Z_APDU *apdu)
-{
-    if (m_http_version)
-    {
-       if (apdu->which == Z_APDU_initResponse)
-       {
-           Z_InitResponse *res = apdu->u.initResponse;
-           if (*res->result == 0)
-           {
-               send_to_srw_client_error(3, 0);
-           }
-           else if (!m_s2z_search_apdu)
-           {
-               send_srw_explain_response(0, 0);
-           }
-           else
-           {
-               handle_incoming_Z_PDU(m_s2z_search_apdu);
-           }
-       }
-       else if (m_s2z_search_apdu && apdu->which == Z_APDU_searchResponse)
-       {
-           m_s2z_search_apdu = 0;
-           Z_SearchResponse *res = apdu->u.searchResponse;
-           m_s2z_hit_count = *res->resultCount;
-           if (res->records && res->records->which == Z_Records_NSD)
-           {
-               send_to_srw_client_ok(0, res->records, 1);
-           }
-           else if (m_s2z_present_apdu && m_s2z_hit_count > 0)
-           {
-               // adjust 
-               Z_PresentRequest *pr = m_s2z_present_apdu->u.presentRequest;
-               
-               if (*pr->resultSetStartPoint <= m_s2z_hit_count)
-               {
-                   if (*pr->numberOfRecordsRequested+ *pr->resultSetStartPoint
-                       > m_s2z_hit_count)
-                       *pr->numberOfRecordsRequested =
-                           1 + m_s2z_hit_count - *pr->resultSetStartPoint;
-               }
-               handle_incoming_Z_PDU(m_s2z_present_apdu);
-           }
-           else
-           {
-               m_s2z_present_apdu = 0;
-               send_to_srw_client_ok(m_s2z_hit_count, res->records, 1);
-           }
-       }
-       else if (m_s2z_present_apdu && apdu->which == Z_APDU_presentResponse)
-       {
-           int start = 
-               *m_s2z_present_apdu->u.presentRequest->resultSetStartPoint;
-
-           m_s2z_present_apdu = 0;
-           Z_PresentResponse *res = apdu->u.presentResponse;
-           send_to_srw_client_ok(m_s2z_hit_count, res->records, start);
-       }
-    }
-    else
-    {
-       int len = 0;
-       if (m_log_mask & PROXY_LOG_REQ_CLIENT)
-           yaz_log (LOG_LOG, "%sSending %s to client", m_session_str,
-                    apdu_name(apdu));
-       int r = send_Z_PDU(apdu, &len);
-       m_bytes_sent += len;
-       m_bw_stat.add_bytes(len);
-       logtime();
-       return r;
-    }
-    return 0;
-}
-
-int Yaz_Proxy::send_to_client(Z_APDU *apdu)
-{
-    int kill_session = 0;
-    Z_ReferenceId **new_id = get_referenceIdP(apdu);
-
-    if (new_id)
-       *new_id = m_referenceId;
-    
-    if (apdu->which == Z_APDU_searchResponse)
-    {
-       Z_SearchResponse *sr = apdu->u.searchResponse;
-       Z_Records *p = sr->records;
-       if (p && p->which == Z_Records_NSD)
-       {
-           Z_DiagRec dr, *dr_p = &dr;
-           dr.which = Z_DiagRec_defaultFormat;
-           dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
-
-           *sr->searchStatus = 0;
-           display_diagrecs(&dr_p, 1);
-       }
-       else
-       {
-           if (p && p->which == Z_Records_DBOSD)
-           {
-               if (m_marcxml_flag)
-                   convert_to_marcxml(p->u.databaseOrSurDiagnostics);
-               if (convert_xsl(p->u.databaseOrSurDiagnostics, apdu))
-                   return 0;
-                   
-           }
-           if (sr->resultCount)
-           {
-               yaz_log(LOG_LOG, "%s%d hits", m_session_str,
-                       *sr->resultCount);
-               if (*sr->resultCount < 0)
-               {
-                   m_invalid_session = 1;
-                   kill_session = 1;
-
-                   *sr->searchStatus = 0;
-                   sr->records =
-                       create_nonSurrogateDiagnostics(odr_encode(), 2, 0);
-                   *sr->resultCount = 0;
-               }
-           }
-       }
-    }
-    else if (apdu->which == Z_APDU_presentResponse)
-    {
-       Z_PresentResponse *sr = apdu->u.presentResponse;
-       Z_Records *p = sr->records;
-       if (p && p->which == Z_Records_NSD)
-       {
-           Z_DiagRec dr, *dr_p = &dr;
-           dr.which = Z_DiagRec_defaultFormat;
-           dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
-           if (*sr->presentStatus == Z_PresentStatus_success)
-               *sr->presentStatus = Z_PresentStatus_failure;
-           display_diagrecs(&dr_p, 1);
-       }
-       if (p && p->which == Z_Records_DBOSD)
-       {
-           if (m_marcxml_flag)
-               convert_to_marcxml(p->u.databaseOrSurDiagnostics);
-           if (convert_xsl(p->u.databaseOrSurDiagnostics, apdu))
-               return 0;
-       }
-    }
-    else if (apdu->which == Z_APDU_initResponse)
-    {
-       if (m_initRequest_options)
-       {
-           Z_Options *nopt = 
-               (Odr_bitmask *)odr_malloc(odr_encode(),
-                                         sizeof(Odr_bitmask));
-           ODR_MASK_ZERO(nopt);
-
-           int i;
-           for (i = 0; i<24; i++)
-               if (ODR_MASK_GET(m_initRequest_options, i) &&
-                   ODR_MASK_GET(apdu->u.initResponse->options, i))
-                   ODR_MASK_SET(nopt, i);
-           apdu->u.initResponse->options = nopt;           
-       }
-       if (m_initRequest_version)
-       {
-           Z_ProtocolVersion *nopt = 
-               (Odr_bitmask *)odr_malloc(odr_encode(),
-                                         sizeof(Odr_bitmask));
-           ODR_MASK_ZERO(nopt);
-
-           int i;
-           for (i = 0; i<8; i++)
-               if (ODR_MASK_GET(m_initRequest_version, i) &&
-                   ODR_MASK_GET(apdu->u.initResponse->protocolVersion, i))
-                   ODR_MASK_SET(nopt, i);
-           apdu->u.initResponse->protocolVersion = nopt;           
-       }
-       apdu->u.initResponse->preferredMessageSize =
-           odr_intdup(odr_encode(),
-                      m_client->m_initResponse_preferredMessageSize >
-                      m_initRequest_preferredMessageSize ?
-                      m_initRequest_preferredMessageSize :
-                      m_client->m_initResponse_preferredMessageSize);
-       apdu->u.initResponse->maximumRecordSize =
-           odr_intdup(odr_encode(),
-                      m_client->m_initResponse_maximumRecordSize >
-                      m_initRequest_maximumRecordSize ?
-                      m_initRequest_maximumRecordSize :
-                      m_client->m_initResponse_maximumRecordSize);
-    }
-    int r = send_PDU_convert(apdu);
-    if (r)
-       return r;
-    if (kill_session)
-    {
-       delete m_client;
-       m_client = 0;
-       m_parent->pre_init();
-    }
-    return r;
-}
-
-int Yaz_ProxyClient::send_to_target(Z_APDU *apdu)
-{
-    int len = 0;
-    const char *apdu_name_tmp = apdu_name(apdu);
-    int r = send_Z_PDU(apdu, &len);
-    if (m_root->get_log_mask() & PROXY_LOG_REQ_SERVER)
-       yaz_log (LOG_LOG, "%sSending %s to %s %d bytes",
-                get_session_str(),
-                apdu_name_tmp, get_hostname(), len);
-    m_bytes_sent += len;
-    return r;
-}
-
-Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
-{
-    if (apdu->which == Z_APDU_presentRequest)
-    {
-       Z_PresentRequest *pr = apdu->u.presentRequest;
-       int toget = *pr->numberOfRecordsRequested;
-       int start = *pr->resultSetStartPoint;
-
-       yaz_log(LOG_LOG, "%sPresent %s %d+%d", m_session_str,
-               pr->resultSetId, start, toget);
-
-       if (*m_parent->m_optimize == '0')
-           return apdu;
-
-       if (!m_client->m_last_resultSetId)
-       {
-           Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
-           new_apdu->u.presentResponse->records =
-               create_nonSurrogateDiagnostics(odr_encode(), 30,
-                                              pr->resultSetId);
-           send_to_client(new_apdu);
-           return 0;
-       }
-       if (!strcmp(m_client->m_last_resultSetId, pr->resultSetId))
-       {
-           if (start+toget-1 > m_client->m_last_resultCount)
-           {
-               Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
-               new_apdu->u.presentResponse->records =
-                   create_nonSurrogateDiagnostics(odr_encode(), 13, 0);
-               send_to_client(new_apdu);
-               return 0;
-           }
-           Z_NamePlusRecordList *npr;
-           if (m_client->m_cache.lookup (odr_encode(), &npr, start, toget,
-                                         pr->preferredRecordSyntax,
-                                         pr->recordComposition))
-           {
-               yaz_log (LOG_LOG, "%sReturned cached records for present request", 
-                        m_session_str);
-               Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
-               new_apdu->u.presentResponse->referenceId = pr->referenceId;
-               
-               new_apdu->u.presentResponse->numberOfRecordsReturned
-                   = odr_intdup(odr_encode(), toget);
-                                                                
-               new_apdu->u.presentResponse->records = (Z_Records*)
-                   odr_malloc(odr_encode(), sizeof(Z_Records));
-               new_apdu->u.presentResponse->records->which = Z_Records_DBOSD;
-               new_apdu->u.presentResponse->records->u.databaseOrSurDiagnostics = npr;
-               new_apdu->u.presentResponse->nextResultSetPosition =
-                   odr_intdup(odr_encode(), start+toget);
-
-               send_to_client(new_apdu);
-               return 0;
-           }
-       }
-    }
-
-    if (apdu->which != Z_APDU_searchRequest)
-       return apdu;
-    Z_SearchRequest *sr = apdu->u.searchRequest;
-    Yaz_Z_Query *this_query = new Yaz_Z_Query;
-    Yaz_Z_Databases this_databases;
-
-    this_databases.set(sr->num_databaseNames, (const char **)
-                       sr->databaseNames);
-    
-    this_query->set_Z_Query(sr->query);
-
-    char query_str[120];
-    this_query->print(query_str, sizeof(query_str)-1);
-    yaz_log(LOG_LOG, "%sSearch %s", m_session_str, query_str);
-
-    if (*m_parent->m_optimize != '0' &&
-       m_client->m_last_ok && m_client->m_last_query &&
-       m_client->m_last_query->match(this_query) &&
-        !strcmp(m_client->m_last_resultSetId, sr->resultSetName) &&
-        m_client->m_last_databases.match(this_databases))
-    {
-       delete this_query;
-       if (m_client->m_last_resultCount > *sr->smallSetUpperBound &&
-           m_client->m_last_resultCount < *sr->largeSetLowerBound)
-       {
-           Z_NamePlusRecordList *npr;
-           int toget = *sr->mediumSetPresentNumber;
-           Z_RecordComposition *comp = 0;
-
-           if (toget > m_client->m_last_resultCount)
-               toget = m_client->m_last_resultCount;
-           
-           if (sr->mediumSetElementSetNames)
-           {
-               comp = (Z_RecordComposition *)
-                   odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
-               comp->which = Z_RecordComp_simple;
-               comp->u.simple = sr->mediumSetElementSetNames;
-           }
-           if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget,
-                                         sr->preferredRecordSyntax, comp))
-           {
-               yaz_log (LOG_LOG, "%sReturned cached records for medium set",
-                        m_session_str);
-               Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
-               new_apdu->u.searchResponse->referenceId = sr->referenceId;
-               new_apdu->u.searchResponse->resultCount =
-                   &m_client->m_last_resultCount;
-               
-               new_apdu->u.searchResponse->numberOfRecordsReturned
-                   = odr_intdup(odr_encode(), toget);
-                                                       
-               new_apdu->u.searchResponse->presentStatus =
-                   odr_intdup(odr_encode(), Z_PresentStatus_success);
-               new_apdu->u.searchResponse->records = (Z_Records*)
-                   odr_malloc(odr_encode(), sizeof(Z_Records));
-               new_apdu->u.searchResponse->records->which = Z_Records_DBOSD;
-               new_apdu->u.searchResponse->records->u.databaseOrSurDiagnostics = npr;
-               new_apdu->u.searchResponse->nextResultSetPosition =
-                   odr_intdup(odr_encode(), toget+1);
-               send_to_client(new_apdu);
-               return 0;
-           }
-           else
-           {
-               // medium Set
-               // send present request (medium size)
-               yaz_log (LOG_LOG, "%sOptimizing search for medium set",
-                        m_session_str);
-
-               Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
-               Z_PresentRequest *pr = new_apdu->u.presentRequest;
-               pr->referenceId = sr->referenceId;
-               pr->resultSetId = sr->resultSetName;
-               pr->preferredRecordSyntax = sr->preferredRecordSyntax;
-               *pr->numberOfRecordsRequested = toget;
-               pr->recordComposition = comp;
-               m_client->m_sr_transform = 1;
-               return new_apdu;
-           }
-       }
-       else if (m_client->m_last_resultCount >= *sr->largeSetLowerBound ||
-           m_client->m_last_resultCount <= 0)
-       {
-            // large set. Return pseudo-search response immediately
-           yaz_log (LOG_LOG, "%sOptimizing search for large set",
-                    m_session_str);
-           Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
-           new_apdu->u.searchResponse->referenceId = sr->referenceId;
-           new_apdu->u.searchResponse->resultCount =
-               &m_client->m_last_resultCount;
-           send_to_client(new_apdu);
-           return 0;
-       }
-       else
-       {
-           Z_NamePlusRecordList *npr;
-           int toget = m_client->m_last_resultCount;
-           Z_RecordComposition *comp = 0;
-           // small set
-            // send a present request (small set)
-           
-           if (sr->smallSetElementSetNames)
-           {
-               comp = (Z_RecordComposition *)
-                   odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
-               comp->which = Z_RecordComp_simple;
-               comp->u.simple = sr->smallSetElementSetNames;
-           }
-
-           if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget,
-                                         sr->preferredRecordSyntax, comp))
-           {
-               yaz_log (LOG_LOG, "%sReturned cached records for small set",
-                        m_session_str);
-               Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
-               new_apdu->u.searchResponse->referenceId = sr->referenceId;
-               new_apdu->u.searchResponse->resultCount =
-                   &m_client->m_last_resultCount;
-               
-               new_apdu->u.searchResponse->numberOfRecordsReturned
-                   = odr_intdup(odr_encode(), toget);
-                                                                
-               new_apdu->u.searchResponse->presentStatus =
-                   odr_intdup(odr_encode(), Z_PresentStatus_success);
-               new_apdu->u.searchResponse->records = (Z_Records*)
-                   odr_malloc(odr_encode(), sizeof(Z_Records));
-               new_apdu->u.searchResponse->records->which = Z_Records_DBOSD;
-               new_apdu->u.searchResponse->records->u.databaseOrSurDiagnostics = npr;
-               new_apdu->u.searchResponse->nextResultSetPosition =
-                   odr_intdup(odr_encode(), toget+1);
-               send_to_client(new_apdu);
-               return 0;
-           }
-           else
-           {
-               yaz_log (LOG_LOG, "%sOptimizing search for small set",
-                        m_session_str);
-               Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
-               Z_PresentRequest *pr = new_apdu->u.presentRequest;
-               pr->referenceId = sr->referenceId;
-               pr->resultSetId = sr->resultSetName;
-               pr->preferredRecordSyntax = sr->preferredRecordSyntax;
-               *pr->numberOfRecordsRequested = toget;
-               pr->recordComposition = comp;
-               m_client->m_sr_transform = 1;
-               return new_apdu;
-           }
-       }
-    }
-    else  // query doesn't match
-    {
-       delete m_client->m_last_query;
-       m_client->m_last_query = this_query;
-        m_client->m_last_ok = 0;
-       m_client->m_cache.clear();
-       m_client->m_resultSetStartPoint = 0;
-
-        xfree (m_client->m_last_resultSetId);
-        m_client->m_last_resultSetId = xstrdup (sr->resultSetName);
-
-        m_client->m_last_databases.set(sr->num_databaseNames,
-                                       (const char **) sr->databaseNames);
-    }
-    return apdu;
-}
-
-
-void Yaz_Proxy::inc_request_no()
-{
-    char *cp = strchr(m_session_str, ' ');
-    m_request_no++;
-    if (cp)
-       sprintf(cp+1, "%d ", m_request_no);
-}
-
-void Yaz_Proxy::recv_GDU(Z_GDU *apdu, int len)
-{
-    inc_request_no();
-
-    m_bytes_recv += len;
-    
-    if (m_log_mask & PROXY_LOG_REQ_CLIENT)
-       yaz_log (LOG_LOG, "%sReceiving %s from client %d bytes",
-                m_session_str, gdu_name(apdu), len);
-
-    if (m_bw_hold_PDU)     // double incoming PDU. shutdown now.
-       shutdown();
-
-    m_bw_stat.add_bytes(len);
-    m_pdu_stat.add_bytes(1);
-
-    gettimeofday(&m_time_tv, 0);
-
-    int bw_total = m_bw_stat.get_total();
-    int pdu_total = m_pdu_stat.get_total();
-
-    int reduce = 0;
-    if (m_bw_max)
-    {
-       if (bw_total > m_bw_max)
-       {
-           reduce = (bw_total/m_bw_max);
-       }
-    }
-    if (m_pdu_max)
-    {
-       if (pdu_total > m_pdu_max)
-       {
-           int nreduce = (m_pdu_max >= 60) ? 1 : 60/m_pdu_max;
-           reduce = (reduce > nreduce) ? reduce : nreduce;
-       }
-    }
-    if (reduce)  
-    {
-       yaz_log(LOG_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);
-       
-       m_bw_hold_PDU = apdu;  // save PDU and signal "on hold"
-       timeout(reduce);       // call us reduce seconds later
-    }
-    else if (apdu->which == Z_GDU_Z3950)
-       handle_incoming_Z_PDU(apdu->u.z3950);
-    else if (apdu->which == Z_GDU_HTTP_Request)
-       handle_incoming_HTTP(apdu->u.HTTP_Request);
-}
-
-void Yaz_Proxy::handle_max_record_retrieve(Z_APDU *apdu)
-{
-    if (m_max_record_retrieve)
-    {
-       if (apdu->which == Z_APDU_presentRequest)
-       {
-           Z_PresentRequest *pr = apdu->u.presentRequest;
-           if (pr->numberOfRecordsRequested && 
-               *pr->numberOfRecordsRequested > m_max_record_retrieve)
-               *pr->numberOfRecordsRequested = m_max_record_retrieve;
-       }
-    }
-}
-
-Z_Records *Yaz_Proxy::create_nonSurrogateDiagnostics(ODR odr,
-                                                    int error,
-                                                    const char *addinfo)
-{
-    Z_Records *rec = (Z_Records *)
-        odr_malloc (odr, sizeof(*rec));
-    int *err = (int *)
-        odr_malloc (odr, sizeof(*err));
-    Z_DiagRec *drec = (Z_DiagRec *)
-        odr_malloc (odr, sizeof(*drec));
-    Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)
-        odr_malloc (odr, sizeof(*dr));
-    *err = error;
-    rec->which = Z_Records_NSD;
-    rec->u.nonSurrogateDiagnostic = dr;
-    dr->diagnosticSetId =
-        yaz_oidval_to_z3950oid (odr, CLASS_DIAGSET, VAL_BIB1);
-    dr->condition = err;
-    dr->which = Z_DefaultDiagFormat_v2Addinfo;
-    dr->u.v2Addinfo = odr_strdup (odr, addinfo ? addinfo : "");
-    return rec;
-}
-
-Z_APDU *Yaz_Proxy::handle_query_transformation(Z_APDU *apdu)
-{
-    if (apdu->which == Z_APDU_searchRequest &&
-       apdu->u.searchRequest->query &&
-       apdu->u.searchRequest->query->which == Z_Query_type_104 &&
-       apdu->u.searchRequest->query->u.type_104->which == Z_External_CQL)
-    {
-       Z_RPNQuery *rpnquery = 0;
-       Z_SearchRequest *sr = apdu->u.searchRequest;
-       char *addinfo = 0;
-       
-       yaz_log(LOG_LOG, "%sCQL: %s", m_session_str,
-               sr->query->u.type_104->u.cql);
-
-       int r = m_cql2rpn.query_transform(sr->query->u.type_104->u.cql,
-                                         &rpnquery, odr_encode(),
-                                         &addinfo);
-       if (r == -3)
-           yaz_log(LOG_LOG, "%sNo CQL to RPN table", m_session_str);
-       else if (r)
-       {
-           yaz_log(LOG_LOG, "%sCQL Conversion error %d", m_session_str, r);
-           Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
-
-           new_apdu->u.searchResponse->referenceId = sr->referenceId;
-           new_apdu->u.searchResponse->records =
-               create_nonSurrogateDiagnostics(odr_encode(),
-                                              yaz_diag_srw_to_bib1(r),
-                                              addinfo);
-           *new_apdu->u.searchResponse->searchStatus = 0;
-
-           send_to_client(new_apdu);
-
-           return 0;
-       }
-       else
-       {
-           sr->query->which = Z_Query_type_1;
-           sr->query->u.type_1 = rpnquery;
-       }
-       return apdu;
-    }
-    return apdu;
-}
-
-Z_APDU *Yaz_Proxy::handle_query_validation(Z_APDU *apdu)
-{
-    if (apdu->which == Z_APDU_searchRequest)
-    {
-       Z_SearchRequest *sr = apdu->u.searchRequest;
-       int err = 0;
-       char *addinfo = 0;
-
-       Yaz_ProxyConfig *cfg = check_reconfigure();
-       if (cfg)
-           err = cfg->check_query(odr_encode(), m_default_target,
-                                  sr->query, &addinfo);
-       if (err)
-       {
-           Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
-
-           new_apdu->u.searchResponse->referenceId = sr->referenceId;
-           new_apdu->u.searchResponse->records =
-               create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
-           *new_apdu->u.searchResponse->searchStatus = 0;
-
-           send_to_client(new_apdu);
-
-           return 0;
-       }
-    }
-    return apdu;
-}
-
-Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu)
-{
-    m_marcxml_flag = 0;
-    if (apdu->which == Z_APDU_searchRequest)
-    {
-       Z_SearchRequest *sr = apdu->u.searchRequest;
-       int err = 0;
-       char *addinfo = 0;
-       Yaz_ProxyConfig *cfg = check_reconfigure();
-
-       Z_RecordComposition rc_temp, *rc = 0;
-       if (sr->smallSetElementSetNames)
-       {
-           rc_temp.which = Z_RecordComp_simple;
-           rc_temp.u.simple = sr->smallSetElementSetNames;
-           rc = &rc_temp;
-       }
-
-       char *stylesheet_name = 0;
-       if (cfg)
-           err = cfg->check_syntax(odr_encode(),
-                                   m_default_target,
-                                   sr->preferredRecordSyntax, rc,
-                                   &addinfo, &stylesheet_name, &m_schema);
-       if (stylesheet_name)
-       {
-           m_parent->low_socket_close();
-
-           if (m_stylesheet_xsp)
-               xsltFreeStylesheet(m_stylesheet_xsp);
-
-           m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*)
-                                                      stylesheet_name);
-           m_stylesheet_offset = 0;
-           xfree(stylesheet_name);
-
-           m_parent->low_socket_open();
-       }
-       if (err == -1)
-       {
-           sr->preferredRecordSyntax =
-               yaz_oidval_to_z3950oid(odr_encode(), CLASS_RECSYN, VAL_USMARC);
-           m_marcxml_flag = 1;
-       }
-       else if (err)
-       {
-           Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
-           
-           new_apdu->u.searchResponse->referenceId = sr->referenceId;
-           new_apdu->u.searchResponse->records =
-               create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
-           *new_apdu->u.searchResponse->searchStatus = 0;
-           
-           send_to_client(new_apdu);
-           
-           return 0;
-       }
-    }
-    else if (apdu->which == Z_APDU_presentRequest)
-    {
-       Z_PresentRequest *pr = apdu->u.presentRequest;
-       int err = 0;
-       char *addinfo = 0;
-       Yaz_ProxyConfig *cfg = check_reconfigure();
-
-       char *stylesheet_name = 0;
-       if (cfg)
-           err = cfg->check_syntax(odr_encode(), m_default_target,
-                                   pr->preferredRecordSyntax,
-                                   pr->recordComposition,
-                                   &addinfo, &stylesheet_name, &m_schema);
-       if (stylesheet_name)
-       {
-           m_parent->low_socket_close();
-
-           if (m_stylesheet_xsp)
-               xsltFreeStylesheet(m_stylesheet_xsp);
-
-           m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*)
-                                                      stylesheet_name);
-           m_stylesheet_offset = 0;
-           xfree(stylesheet_name);
-
-           m_parent->low_socket_open();
-       }
-       if (err == -1)
-       {
-           pr->preferredRecordSyntax =
-               yaz_oidval_to_z3950oid(odr_decode(), CLASS_RECSYN, VAL_USMARC);
-           m_marcxml_flag = 1;
-       }
-       else if (err)
-       {
-           Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
-           
-           new_apdu->u.presentResponse->referenceId = pr->referenceId;
-           new_apdu->u.presentResponse->records =
-               create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
-           *new_apdu->u.presentResponse->presentStatus =
-               Z_PresentStatus_failure;
-           
-           send_to_client(new_apdu);
-           
-           return 0;
-       }
-    }
-    return apdu;
-}
-
-Z_ElementSetNames *Yaz_Proxy::mk_esn_from_schema(ODR o, const char *schema)
-{
-    if (!schema)
-       return 0;
-    Z_ElementSetNames *esn = (Z_ElementSetNames *)
-       odr_malloc(o, sizeof(Z_ElementSetNames));
-    esn->which = Z_ElementSetNames_generic;
-    esn->u.generic = odr_strdup(o, schema);
-    return esn;
-}
-
-void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
-{
-    if (m_s2z_odr_init)
-    {
-       odr_destroy(m_s2z_odr_init);
-       m_s2z_odr_init = 0;
-    }
-    if (m_s2z_odr_search)
-    {
-       odr_destroy(m_s2z_odr_search);
-       m_s2z_odr_search = 0;
-    }
-
-    m_http_keepalive = 0;
-    m_http_version = 0;
-    if (!strcmp(hreq->version, "1.0")) 
-    {
-        const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
-        if (v && !strcmp(v, "Keep-Alive"))
-            m_http_keepalive = 1;
-        else
-            m_http_keepalive = 0;
-        m_http_version = "1.0";
-    }
-    else
-    {
-        const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
-        if (v && !strcmp(v, "close"))
-            m_http_keepalive = 0;
-        else
-            m_http_keepalive = 1;
-        m_http_version = "1.1";
-    }
-
-    Z_SRW_PDU *srw_pdu = 0;
-    Z_SOAP *soap_package = 0;
-    char *charset = 0;
-    Z_SRW_diagnostic *diagnostic = 0;
-    int num_diagnostic = 0;
-    if (yaz_srw_decode(hreq, &srw_pdu, &soap_package, odr_decode(),
-                      &charset) == 0
-       || yaz_sru_decode(hreq, &srw_pdu, &soap_package, odr_decode(),
-                         &charset, &diagnostic, &num_diagnostic) == 0)
-    {
-       m_s2z_odr_init = odr_createmem(ODR_ENCODE);
-       m_s2z_odr_search = odr_createmem(ODR_ENCODE);
-       m_soap_ns = odr_strdup(m_s2z_odr_search, soap_package->ns);
-       m_s2z_init_apdu = 0;
-       m_s2z_search_apdu = 0;
-       m_s2z_present_apdu = 0;
-
-       m_s2z_stylesheet = 0;
-       
-       if (srw_pdu->which == Z_SRW_searchRetrieve_request)
-       {
-           Z_SRW_searchRetrieveRequest *srw_req = srw_pdu->u.request;
-
-           m_s2z_database = odr_strdup(m_s2z_odr_init, srw_req->database);
-           // recordXPath unsupported.
-           if (srw_req->recordXPath)
-            {
-               yaz_add_srw_diagnostic(odr_decode(),
-                                      &diagnostic, &num_diagnostic,
-                                      72, 0);
-            }
-           // must have a query
-           if (!srw_req->query.cql)
-           {
-               yaz_add_srw_diagnostic(odr_decode(),
-                                      &diagnostic, &num_diagnostic,
-                                      7, "query");
-           }
-           // sort unsupported
-           if (srw_req->sort_type != Z_SRW_sort_type_none)
-           {
-               yaz_add_srw_diagnostic(odr_decode(),
-                                      &diagnostic, &num_diagnostic,
-                                      80, 0);
-           }
-           // save stylesheet
-           if (srw_req->stylesheet)
-               m_s2z_stylesheet =
-                   odr_strdup(m_s2z_odr_init, srw_req->stylesheet);
-                                             
-           // set packing for response records ..
-           if (srw_req->recordPacking &&
-               !strcmp(srw_req->recordPacking, "xml"))
-               m_s2z_packing = Z_SRW_recordPacking_XML;
-           else
-               m_s2z_packing = Z_SRW_recordPacking_string;
-
-           if (num_diagnostic)
-           {
-               Z_SRW_PDU *srw_pdu =
-                   yaz_srw_get(odr_encode(),
-                               Z_SRW_searchRetrieve_response);
-               Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
-               
-               srw_res->diagnostics = diagnostic;
-               srw_res->num_diagnostics = num_diagnostic;
-               send_srw_response(srw_pdu);
-               return;
-           }
-
-           // prepare search PDU
-           m_s2z_search_apdu = zget_APDU(m_s2z_odr_search,
-                                         Z_APDU_searchRequest);
-           Z_SearchRequest *z_searchRequest =
-               m_s2z_search_apdu->u.searchRequest;
-
-           z_searchRequest->num_databaseNames = 1;
-           z_searchRequest->databaseNames = (char**)
-               odr_malloc(m_s2z_odr_search, sizeof(char *));
-           z_searchRequest->databaseNames[0] = odr_strdup(m_s2z_odr_search,
-                                                          srw_req->database);
-           
-           // query transformation
-           Z_Query *query = (Z_Query *)
-               odr_malloc(m_s2z_odr_search, sizeof(Z_Query));
-           z_searchRequest->query = query;
-           
-           if (srw_req->query_type == Z_SRW_query_type_cql)
-           {
-               Z_External *ext = (Z_External *) 
-                   odr_malloc(m_s2z_odr_search, sizeof(*ext));
-               ext->direct_reference = 
-                   odr_getoidbystr(m_s2z_odr_search, "1.2.840.10003.16.2");
-               ext->indirect_reference = 0;
-               ext->descriptor = 0;
-               ext->which = Z_External_CQL;
-               ext->u.cql = srw_req->query.cql;
-               
-               query->which = Z_Query_type_104;
-               query->u.type_104 =  ext;
-           }
-           else if (srw_req->query_type == Z_SRW_query_type_pqf)
-           {
-               Z_RPNQuery *RPNquery;
-               YAZ_PQF_Parser pqf_parser;
-               
-               pqf_parser = yaz_pqf_create ();
-               
-               RPNquery = yaz_pqf_parse (pqf_parser, m_s2z_odr_search,
-                                         srw_req->query.pqf);
-               if (!RPNquery)
-               {
-                   const char *pqf_msg;
-                   size_t off;
-                   int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
-                   yaz_log(LOG_LOG, "%*s^\n", off+4, "");
-                   yaz_log(LOG_LOG, "Bad PQF: %s (code %d)\n", pqf_msg, code);
-                   
-                   send_to_srw_client_error(10, 0);
-                   return;
-               }
-               query->which = Z_Query_type_1;
-               query->u.type_1 =  RPNquery;
-               
-               yaz_pqf_destroy (pqf_parser);
-           }
-           else
-           {
-               send_to_srw_client_error(7, "query");
-               return;
-           }
-
-           // present
-           m_s2z_present_apdu = 0;
-           int max = 0;
-           if (srw_req->maximumRecords)
-               max = *srw_req->maximumRecords;
-           int start = 1;
-           if (srw_req->startRecord)
-               start = *srw_req->startRecord;
-           if (max > 0)
-           {
-                // Some backend, such as Voyager doesn't honor piggyback
-               // So we use present always (0 &&).
-               if (0 && start <= 1)  // Z39.50 piggyback
-               {
-                   *z_searchRequest->smallSetUpperBound = max;
-                   *z_searchRequest->mediumSetPresentNumber = max;
-                   *z_searchRequest->largeSetLowerBound = 2000000000; // 2e9
-
-                   z_searchRequest->preferredRecordSyntax =
-                       yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN,
-                                              VAL_TEXT_XML);
-                   if (srw_req->recordSchema)
-                   {
-                       z_searchRequest->smallSetElementSetNames =
-                           z_searchRequest->mediumSetElementSetNames =
-                           mk_esn_from_schema(m_s2z_odr_search,
-                                              srw_req->recordSchema);
-                   }
-               }
-               else   // Z39.50 present
-               {
-                   m_s2z_present_apdu = zget_APDU(m_s2z_odr_search, 
-                                                  Z_APDU_presentRequest);
-                   Z_PresentRequest *z_presentRequest = 
-                       m_s2z_present_apdu->u.presentRequest;
-                   *z_presentRequest->resultSetStartPoint = start;
-                   *z_presentRequest->numberOfRecordsRequested = max;
-                   z_presentRequest->preferredRecordSyntax =
-                       yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN,
-                                              VAL_TEXT_XML);
-                   if (srw_req->recordSchema)
-                   {
-                       z_presentRequest->recordComposition =
-                           (Z_RecordComposition *)
-                           odr_malloc(m_s2z_odr_search,
-                                      sizeof(Z_RecordComposition));
-                       z_presentRequest->recordComposition->which = 
-                           Z_RecordComp_simple;                    
-                       z_presentRequest->recordComposition->u.simple =
-                           mk_esn_from_schema(m_s2z_odr_search,
-                                              srw_req->recordSchema);
-                   }
-               }
-           }
-           if (!m_client)
-           {
-               m_s2z_init_apdu = zget_APDU(m_s2z_odr_init,
-                                           Z_APDU_initRequest);
-               
-               // prevent m_initRequest_apdu memory from being grabbed
-               // in Yaz_Proxy::handle_incoming_Z_PDU
-               m_initRequest_apdu = m_s2z_init_apdu;
-               handle_incoming_Z_PDU(m_s2z_init_apdu);
-               return;
-           }
-           else
-           {
-               handle_incoming_Z_PDU(m_s2z_search_apdu);
-               return;
-           }
-       }
-       else if (srw_pdu->which == Z_SRW_explain_request)
-       {
-           Z_SRW_explainRequest *srw_req = srw_pdu->u.explain_request;
-
-           m_s2z_database = odr_strdup(m_s2z_odr_init, srw_req->database);
-
-           // save stylesheet
-           if (srw_req->stylesheet)
-               m_s2z_stylesheet =
-                   odr_strdup(m_s2z_odr_init, srw_req->stylesheet);
-
-           if (srw_req->recordPacking &&
-               !strcmp(srw_req->recordPacking, "xml"))
-               m_s2z_packing = Z_SRW_recordPacking_XML;
-           else
-               m_s2z_packing = Z_SRW_recordPacking_string;
-
-           if (num_diagnostic)
-           {
-               send_srw_explain_response(diagnostic, num_diagnostic);
-               return;
-           }
-
-           if (!m_client)
-           {
-               m_s2z_init_apdu = zget_APDU(m_s2z_odr_init,
-                                           Z_APDU_initRequest);
-               
-               // prevent m_initRequest_apdu memory from being grabbed
-               // in Yaz_Proxy::handle_incoming_Z_PDU
-               m_initRequest_apdu = m_s2z_init_apdu;
-               handle_incoming_Z_PDU(m_s2z_init_apdu);
-           }
-           else
-               send_srw_explain_response(0, 0);
-           return;
-       }
-       else if (srw_pdu->which == Z_SRW_scan_request)
-        {
-           m_s2z_database = odr_strdup(m_s2z_odr_init,
-                                       srw_pdu->u.scan_request->database);
-
-           yaz_add_srw_diagnostic(odr_decode(),
-                                  &diagnostic, &num_diagnostic,
-                                  4, "scan");
-           Z_SRW_PDU *srw_pdu =
-               yaz_srw_get(odr_encode(),
-                           Z_SRW_scan_response);
-           Z_SRW_scanResponse *srw_res = srw_pdu->u.scan_response;
-           
-           srw_res->diagnostics = diagnostic;
-           srw_res->num_diagnostics = num_diagnostic;
-           send_srw_response(srw_pdu);
-           return;
-        }
-       else
-        {
-           m_s2z_database = 0;
-
-           send_to_srw_client_error(4, 0);
-        }
-    }
-    int len = 0;
-    Z_GDU *p = z_get_HTTP_Response(odr_encode(), 400);
-    timeout(0);
-    send_GDU(p, &len);
-}
-
-void Yaz_Proxy::handle_incoming_Z_PDU(Z_APDU *apdu)
-{
-    Z_ReferenceId **refid = get_referenceIdP(apdu);
-    nmem_reset(m_referenceId_mem);
-    if (refid && *refid)
-    {
-       m_referenceId = (Z_ReferenceId *)
-           nmem_malloc(m_referenceId_mem, sizeof(*m_referenceId));
-       m_referenceId->len = m_referenceId->size = (*refid)->len;
-       m_referenceId->buf = (unsigned char *)
-           nmem_malloc(m_referenceId_mem, (*refid)->len);
-       memcpy(m_referenceId->buf, (*refid)->buf, (*refid)->len);
-    }
-    else
-       m_referenceId = 0;
-
-    if (!m_client && m_invalid_session)
-    {
-       m_apdu_invalid_session = apdu;
-       m_mem_invalid_session = odr_extract_mem(odr_decode());
-       apdu = m_initRequest_apdu;
-    }
-    
-    // Determine our client.
-    Z_OtherInformation **oi;
-    get_otherInfoAPDU(apdu, &oi);
-    m_client = get_client(apdu, get_cookie(oi), get_proxy(oi));
-    if (!m_client)
-    {
-       delete this;
-       return;
-    }
-    m_client->m_server = this;
-
-    if (apdu->which == Z_APDU_initRequest)
-    {
-       if (apdu->u.initRequest->implementationId)
-           yaz_log(LOG_LOG, "%simplementationId: %s",
-                   m_session_str, apdu->u.initRequest->implementationId);
-       if (apdu->u.initRequest->implementationName)
-           yaz_log(LOG_LOG, "%simplementationName: %s",
-                   m_session_str, apdu->u.initRequest->implementationName);
-       if (apdu->u.initRequest->implementationVersion)
-           yaz_log(LOG_LOG, "%simplementationVersion: %s",
-                   m_session_str, apdu->u.initRequest->implementationVersion);
-       if (m_initRequest_apdu == 0)
-       {
-           if (m_initRequest_mem)
-               nmem_destroy(m_initRequest_mem);
-           m_initRequest_apdu = apdu;
-           m_initRequest_mem = odr_extract_mem(odr_decode());
-
-           m_initRequest_preferredMessageSize = *apdu->u.initRequest->
-               preferredMessageSize;
-           *apdu->u.initRequest->preferredMessageSize = 1024*1024;
-           m_initRequest_maximumRecordSize = *apdu->u.initRequest->
-               maximumRecordSize;
-           *apdu->u.initRequest->maximumRecordSize = 1024*1024;
-
-           // save init options for the response..
-           m_initRequest_options = apdu->u.initRequest->options;
-           
-           apdu->u.initRequest->options = 
-               (Odr_bitmask *)nmem_malloc(m_initRequest_mem,
-                                          sizeof(Odr_bitmask));
-           ODR_MASK_ZERO(apdu->u.initRequest->options);
-           int i;
-           for (i = 0; i<= 24; i++)
-               ODR_MASK_SET(apdu->u.initRequest->options, i);
-           ODR_MASK_CLEAR(apdu->u.initRequest->options,
-                          Z_Options_negotiationModel);
-           ODR_MASK_CLEAR(apdu->u.initRequest->options,
-                          Z_Options_concurrentOperations);
-
-           // make new version
-           m_initRequest_version = apdu->u.initRequest->protocolVersion;
-           apdu->u.initRequest->protocolVersion = 
-               (Odr_bitmask *)nmem_malloc(m_initRequest_mem,
-                                          sizeof(Odr_bitmask));
-           ODR_MASK_ZERO(apdu->u.initRequest->protocolVersion);
-
-           for (i = 0; i<= 8; i++)
-               ODR_MASK_SET(apdu->u.initRequest->protocolVersion, i);
-       }
-       if (m_client->m_init_flag)
-       {
-           if (handle_init_response_for_invalid_session(apdu))
-               return;
-           if (m_client->m_initResponse)
-           {
-               Z_APDU *apdu2 = m_client->m_initResponse;
-               apdu2->u.initResponse->otherInfo = 0;
-               if (m_client->m_cookie && *m_client->m_cookie)
-                   set_otherInformationString(apdu2, VAL_COOKIE, 1,
-                                              m_client->m_cookie);
-               apdu2->u.initResponse->referenceId =
-                   apdu->u.initRequest->referenceId;
-               apdu2->u.initResponse->options = m_client->m_initResponse_options;
-               apdu2->u.initResponse->protocolVersion = 
-                   m_client->m_initResponse_version;
-               
-               send_to_client(apdu2);
-               return;
-           }
-       }
-       m_client->m_init_flag = 1;
-    }
-    handle_max_record_retrieve(apdu);
-
-    if (apdu)
-       apdu = handle_syntax_validation(apdu);
-
-    if (apdu)
-       apdu = handle_query_transformation(apdu);
-
-    if (apdu)
-       apdu = handle_query_validation(apdu);
-
-    if (apdu)
-       apdu = result_set_optimize(apdu);
-    if (!apdu)
-    {
-       m_client->timeout(m_target_idletime);  // mark it active even 
-       // though we didn't use it
-       return;
-    }
-
-    // delete other info part from PDU before sending to target
-    get_otherInfoAPDU(apdu, &oi);
-    if (oi)
-        *oi = 0;
-
-    if (apdu->which == Z_APDU_presentRequest &&
-       m_client->m_resultSetStartPoint == 0)
-    {
-       Z_PresentRequest *pr = apdu->u.presentRequest;
-       m_client->m_resultSetStartPoint = *pr->resultSetStartPoint;
-       m_client->m_cache.copy_presentRequest(apdu->u.presentRequest);
-    } else {
-       m_client->m_resultSetStartPoint = 0;
-    }
-    if (m_client->send_to_target(apdu) < 0)
-    {
-       delete m_client;
-       m_client = 0;
-       delete this;
-    }
-    else
-       m_client->m_waiting = 1;
-}
-
-void Yaz_Proxy::connectNotify()
-{
-}
-
-void Yaz_Proxy::shutdown()
-{
-    m_invalid_session = 0;
-    // only keep if keep_alive flag is set...
-    if (m_client && 
-       m_client->m_pdu_recv < m_keepalive_limit_pdu &&
-       m_client->m_bytes_recv+m_client->m_bytes_sent < m_keepalive_limit_bw &&
-       m_client->m_waiting == 0)
-    {
-        yaz_log(LOG_LOG, "%sShutdown (client to proxy) keepalive %s",
-                m_session_str,
-                 m_client->get_hostname());
-       yaz_log(LOG_LOG, "%sbw=%d pdu=%d limit-bw=%d limit-pdu=%d",
-               m_session_str, m_client->m_pdu_recv,
-               m_client->m_bytes_sent + m_client->m_bytes_recv,
-               m_keepalive_limit_bw, m_keepalive_limit_pdu);
-        assert (m_client->m_waiting != 2);
-       // Tell client (if any) that no server connection is there..
-       m_client->m_server = 0;
-       m_invalid_session = 0;
-    }
-    else if (m_client)
-    {
-        yaz_log (LOG_LOG, "%sShutdown (client to proxy) close %s",
-                m_session_str,
-                 m_client->get_hostname());
-        assert (m_client->m_waiting != 2);
-       delete m_client;
-    }
-    else if (!m_parent)
-    {
-        yaz_log (LOG_LOG, "%sshutdown (client to proxy) bad state",
-                m_session_str);
-        assert (m_parent);
-    }
-    else 
-    {
-        yaz_log (LOG_LOG, "%sShutdown (client to proxy)",
-                m_session_str);
-    }
-    if (m_parent)
-       m_parent->pre_init();
-    delete this;
-}
-
-const char *Yaz_ProxyClient::get_session_str() 
-{
-    if (!m_server)
-       return "0 ";
-    return m_server->get_session_str();
-}
-
-void Yaz_ProxyClient::shutdown()
-{
-    yaz_log (LOG_LOG, "%sShutdown (proxy to target) %s", get_session_str(),
-            get_hostname());
-    delete m_server;
-    delete this;
-}
-
-void Yaz_Proxy::failNotify()
-{
-    inc_request_no();
-    yaz_log (LOG_LOG, "%sConnection closed by client",
-            get_session_str());
-    shutdown();
-}
-
-void Yaz_ProxyClient::failNotify()
-{
-    if (m_server)
-       m_server->inc_request_no();
-    yaz_log (LOG_LOG, "%sConnection closed by target %s", 
-            get_session_str(), get_hostname());
-    shutdown();
-}
-
-void Yaz_ProxyClient::connectNotify()
-{
-    const char *s = get_session_str();
-    const char *h = get_hostname();
-    yaz_log (LOG_LOG, "%sConnection accepted by %s timeout=%d", s, h,
-            m_target_idletime);
-    timeout(m_target_idletime);
-    if (!m_server)
-       pre_init_client();
-}
-
-IYaz_PDU_Observer *Yaz_ProxyClient::sessionNotify(IYaz_PDU_Observable
-                                                 *the_PDU_Observable, int fd)
-{
-    return new Yaz_ProxyClient(the_PDU_Observable, 0);
-}
-
-Yaz_ProxyClient::~Yaz_ProxyClient()
-{
-    if (m_prev)
-       *m_prev = m_next;
-    if (m_next)
-       m_next->m_prev = m_prev;
-    m_waiting = 2;     // for debugging purposes only.
-    odr_destroy(m_init_odr);
-    delete m_last_query;
-    xfree (m_last_resultSetId);
-    xfree (m_cookie);
-}
-
-void Yaz_ProxyClient::pre_init_client()
-{
-    Z_APDU *apdu = create_Z_PDU(Z_APDU_initRequest);
-    Z_InitRequest *req = apdu->u.initRequest;
-    
-    int i;
-    for (i = 0; i<= 24; i++)
-       ODR_MASK_SET(req->options, i);
-    ODR_MASK_CLEAR(apdu->u.initRequest->options,
-                  Z_Options_negotiationModel);
-    ODR_MASK_CLEAR(apdu->u.initRequest->options,
-                  Z_Options_concurrentOperations);
-    for (i = 0; i<= 10; i++)
-       ODR_MASK_SET(req->protocolVersion, i);
-
-    if (send_to_target(apdu) < 0)
-    {
-       delete this;
-    }
-    else
-    {
-       m_waiting = 1;
-       m_init_flag = 1;
-    }
-}
-
-void Yaz_Proxy::pre_init()
-{
-    int i;
-    const char *name = 0;
-    const char *zurl_in_use[MAX_ZURL_PLEX];
-    int limit_bw, limit_pdu, limit_req;
-    int target_idletime, client_idletime;
-    int max_clients;
-    int keepalive_limit_bw, keepalive_limit_pdu;
-    int pre_init;
-    const char *cql2rpn = 0;
-
-    Yaz_ProxyConfig *cfg = check_reconfigure();
-
-    zurl_in_use[0] = 0;
-
-    if (m_log_mask & PROXY_LOG_APDU_CLIENT)
-       set_APDU_yazlog(1);
-    else
-       set_APDU_yazlog(0);
-
-    for (i = 0; cfg && cfg->get_target_no(i, &name, zurl_in_use,
-                                         &limit_bw, &limit_pdu, &limit_req,
-                                         &target_idletime, &client_idletime,
-                                         &max_clients, 
-                                         &keepalive_limit_bw,
-                                         &keepalive_limit_pdu,
-                                         &pre_init,
-                                         &cql2rpn) ; i++)
-    {
-       if (pre_init)
-       {
-           int j;
-           for (j = 0; zurl_in_use[j]; j++)
-           {
-               Yaz_ProxyClient *c;
-               int spare = 0;
-               int spare_waiting = 0;
-               int in_use = 0;
-               int other = 0;
-               for (c = m_clientPool; c; c = c->m_next)
-               {
-                   if (!strcmp(zurl_in_use[j], c->get_hostname()))
-                   {
-                       if (c->m_cookie == 0)
-                       {
-                           if (c->m_server == 0)
-                               if (c->m_waiting)
-                                   spare_waiting++;
-                               else
-                                   spare++;
-                           else
-                               in_use++;
-                       }
-                       else
-                           other++;
-                   }
-               }
-               yaz_log(LOG_LOG, "%spre-init %s %s use=%d other=%d spare=%d "
-                       "sparew=%d preinit=%d",m_session_str,
-                       name, zurl_in_use[j], in_use, other,
-                       spare, spare_waiting, pre_init);
-               if (spare + spare_waiting < pre_init)
-               {
-                   c = new Yaz_ProxyClient(m_PDU_Observable->clone(), this);
-                   c->m_next = m_clientPool;
-                   if (c->m_next)
-                       c->m_next->m_prev = &c->m_next;
-                   m_clientPool = c;
-                   c->m_prev = &m_clientPool;
-                   
-                   if (m_log_mask & PROXY_LOG_APDU_SERVER)
-                       c->set_APDU_yazlog(1);
-                   else
-                       c->set_APDU_yazlog(0);
-
-                   if (c->client(zurl_in_use[j]))
-                   {
-                       timeout(60);
-                       delete c;
-                       return;
-                   }
-                   c->timeout(30);
-                   c->m_waiting = 1;
-                   c->m_target_idletime = target_idletime;
-                   c->m_seqno = m_seqno++;
-               }
-           }
-       }
-    }
-}
-
-void Yaz_Proxy::timeoutNotify()
-{
-    if (m_parent)
-    {
-       if (m_bw_hold_PDU)
-       {
-           timeout(m_client_idletime);
-           Z_GDU *apdu = m_bw_hold_PDU;
-           m_bw_hold_PDU = 0;
-           
-           if (apdu->which == Z_GDU_Z3950)
-               handle_incoming_Z_PDU(apdu->u.z3950);
-           else if (apdu->which == Z_GDU_HTTP_Request)
-               handle_incoming_HTTP(apdu->u.HTTP_Request);
-       }
-       else if (m_stylesheet_nprl)
-           convert_xsl_delay();
-       else
-       {
-           inc_request_no();
-
-           yaz_log (LOG_LOG, "%sTimeout (client to proxy)", m_session_str);
-           shutdown();
-       }
-    }
-    else
-    {
-       timeout(600);
-       pre_init();
-    }
-}
-
-void Yaz_Proxy::markInvalid()
-{
-    m_client = 0;
-    m_invalid_session = 1;
-}
-
-void Yaz_ProxyClient::timeoutNotify()
-{
-    if (m_server)
-       m_server->inc_request_no();
-
-    yaz_log (LOG_LOG, "%sTimeout (proxy to target) %s", get_session_str(),
-            get_hostname());
-    m_waiting = 1;
-    m_root->pre_init();
-    if (m_server && m_init_flag)
-    {
-       // target timed out in a session that was properly initialized
-       // server object stay alive but we mark it as invalid so it
-       // gets initialized again
-       m_server->markInvalid();
-       m_server = 0;
-    }
-    shutdown();
-}
-
-Yaz_ProxyClient::Yaz_ProxyClient(IYaz_PDU_Observable *the_PDU_Observable,
-                                Yaz_Proxy *parent) :
-    Yaz_Z_Assoc (the_PDU_Observable)
-{
-    m_cookie = 0;
-    m_next = 0;
-    m_prev = 0;
-    m_init_flag = 0;
-    m_last_query = 0;
-    m_last_resultSetId = 0;
-    m_last_resultCount = 0;
-    m_last_ok = 0;
-    m_sr_transform = 0;
-    m_waiting = 0;
-    m_init_odr = odr_createmem (ODR_DECODE);
-    m_initResponse = 0;
-    m_initResponse_options = 0;
-    m_initResponse_version = 0;
-    m_initResponse_preferredMessageSize = 0;
-    m_initResponse_maximumRecordSize = 0;
-    m_resultSetStartPoint = 0;
-    m_bytes_sent = m_bytes_recv = 0;
-    m_pdu_recv = 0;
-    m_server = 0;
-    m_seqno = 0;
-    m_target_idletime = 600;
-    m_root = parent;
-}
-
-const char *Yaz_Proxy::option(const char *name, const char *value)
-{
-    if (!strcmp (name, "optimize")) {
-       if (value) {
-            xfree (m_optimize);        
-           m_optimize = xstrdup (value);
-        }
-       return m_optimize;
-    }
-    return 0;
-}
-
-void Yaz_ProxyClient::recv_HTTP_response(Z_HTTP_Response *apdu, int len)
-{
-
-}
-
-void Yaz_ProxyClient::recv_GDU(Z_GDU *apdu, int len)
-{
-    if (apdu->which == Z_GDU_Z3950)
-       recv_Z_PDU(apdu->u.z3950, len);
-    else if (apdu->which == Z_GDU_HTTP_Response)
-       recv_HTTP_response(apdu->u.HTTP_Response, len);
-    else
-       shutdown();
-}
-
-int Yaz_Proxy::handle_init_response_for_invalid_session(Z_APDU *apdu)
-{
-    if (!m_invalid_session)
-       return 0;
-    m_invalid_session = 0;
-    handle_incoming_Z_PDU(m_apdu_invalid_session);
-    assert (m_mem_invalid_session);
-    nmem_destroy(m_mem_invalid_session);
-    m_mem_invalid_session = 0;
-    return 1;
-}
-
-void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len)
-{
-    m_bytes_recv += len;
-
-    m_pdu_recv++;
-    m_waiting = 0;
-    if (m_root->get_log_mask() & PROXY_LOG_REQ_SERVER)
-       yaz_log (LOG_LOG, "%sReceiving %s from %s %d bytes", get_session_str(),
-                apdu_name(apdu), get_hostname(), len);
-    if (apdu->which == Z_APDU_initResponse)
-    {
-       if (!m_server)  // if this is a pre init session , check for more
-           m_root->pre_init();
-        NMEM nmem = odr_extract_mem (odr_decode());
-       odr_reset (m_init_odr);
-        nmem_transfer (m_init_odr->mem, nmem);
-        m_initResponse = apdu;
-       m_initResponse_options = apdu->u.initResponse->options;
-       m_initResponse_version = apdu->u.initResponse->protocolVersion;
-       m_initResponse_preferredMessageSize = 
-           *apdu->u.initResponse->preferredMessageSize;
-       m_initResponse_maximumRecordSize = 
-           *apdu->u.initResponse->maximumRecordSize;
-
-       Z_InitResponse *ir = apdu->u.initResponse;
-       char *im0 = ir->implementationName;
-       
-       char *im1 = (char*) 
-           odr_malloc(m_init_odr, 20 + (im0 ? strlen(im0) : 0));
-       *im1 = '\0';
-       if (im0)
-       {
-           strcat(im1, im0);
-           strcat(im1, " ");
-       }
-       strcat(im1, "(YAZ Proxy)");
-       ir->implementationName = im1;
-
-        nmem_destroy (nmem);
-
-       if (m_server && m_server->handle_init_response_for_invalid_session(apdu))
-           return;
-    }
-    if (apdu->which == Z_APDU_searchResponse)
-    {
-       Z_SearchResponse *sr = apdu->u.searchResponse;
-       m_last_resultCount = *sr->resultCount;
-       int status = *sr->searchStatus;
-       if (status && (!sr->records || sr->records->which == Z_Records_DBOSD))
-       {
-            m_last_ok = 1;
-           
-           if (sr->records && sr->records->which == Z_Records_DBOSD)
-           {
-               m_cache.add(odr_decode(),
-                           sr->records->u.databaseOrSurDiagnostics, 1,
-                           *sr->resultCount);
-           }
-       }
-    }
-    if (apdu->which == Z_APDU_presentResponse)
-    {
-       Z_PresentResponse *pr = apdu->u.presentResponse;
-       if (m_sr_transform)
-       {
-           m_sr_transform = 0;
-           Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
-           Z_SearchResponse *sr = new_apdu->u.searchResponse;
-           sr->referenceId = pr->referenceId;
-           *sr->resultCount = m_last_resultCount;
-           sr->records = pr->records;
-           sr->nextResultSetPosition = pr->nextResultSetPosition;
-           sr->numberOfRecordsReturned = pr->numberOfRecordsReturned;
-           apdu = new_apdu;
-       }
-       if (pr->records && 
-           pr->records->which == Z_Records_DBOSD && m_resultSetStartPoint)
-       {
-           m_cache.add(odr_decode(),
-                       pr->records->u.databaseOrSurDiagnostics,
-                       m_resultSetStartPoint, -1);
-           m_resultSetStartPoint = 0;
-       }
-    }
-    if (m_cookie)
-       set_otherInformationString (apdu, VAL_COOKIE, 1, m_cookie);
-    if (m_server)
-    {
-       m_server->send_to_client(apdu);
-    }
-    if (apdu->which == Z_APDU_close)
-    {
-       shutdown();
-    }
-}
-
-void Yaz_Proxy::low_socket_close()
-{
-    int i;
-    for (i = 0; i<NO_SPARE_SOLARIS_FD; i++)
-       if  (m_lo_fd[i] >= 0)
-           ::close(m_lo_fd[i]);
-}
-
-void Yaz_Proxy::low_socket_open()
-{
-    int i;
-    for (i = 0; i<NO_SPARE_SOLARIS_FD; i++)
-       m_lo_fd[i] = open("/dev/null", O_RDONLY);
-}
-
-int Yaz_Proxy::server(const char *addr)
-{
-    int r = Yaz_Z_Assoc::server(addr);
-    if (!r)
-    {
-       yaz_log(LOG_LOG, "%sStarted proxy " VERSION " on %s", m_session_str, addr);
-       timeout(1);
-    }
-    return r;
-}
-
index b0729a4..cb2fac0 100644 (file)
@@ -2,11 +2,12 @@
  * Copyright (c) 2002-2004, Index Data.
  * See the file LICENSE for details.
  * 
- * $Id: yaz-z-cache.cpp,v 1.8 2004-01-24 21:32:31 adam Exp $
+ * $Id: yaz-z-cache.cpp,v 1.9 2004-03-29 22:46:51 adam Exp $
  */
 
 #include <yaz/log.h>
-#include <yaz++/proxy.h>
+#include <yaz/proto.h>
+#include <yaz++/record-cache.h>
 
 struct Yaz_RecordCache_Entry {
     int m_offset;
index 29d92b3..052e142 100644 (file)
@@ -1,5 +1,5 @@
 #!/bin/sh
-# $Id: yaz++-config.in,v 1.6 2004-02-11 10:05:19 adam Exp $
+# $Id: yaz++-config.in,v 1.7 2004-03-29 22:46:50 adam Exp $
 yazppprefix=@prefix@
 yaz_echo_cflags=no
 yaz_echo_libs=no
@@ -28,9 +28,9 @@ EOF
        exit $1
 }
 
-#if test $# -eq 0; then
-#      yaz_echo_help=yes
-#fi
+if test $# -eq 0; then
+       yaz_echo_help=yes
+fi
 
 while test $# -gt 0; do
   case "$1" in