ef6c0ccdfb08e5e234784b20097ebc4584b2566d
[yazproxy-moved-to-github.git] / src / yaz-proxy.cpp
1 /* $Id: yaz-proxy.cpp,v 1.1 2004-04-11 11:36:52 adam Exp $
2    Copyright (c) 1998-2004, Index Data.
3
4 This file is part of the yaz-proxy.
5
6 Zebra is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2, or (at your option) any later
9 version.
10
11 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Zebra; see the file LICENSE.proxy.  If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20  */
21
22 #ifdef WIN32
23 #else
24 #include <unistd.h>
25 #endif
26
27 #include <assert.h>
28 #include <time.h>
29 #include <sys/types.h>
30 #include <fcntl.h>
31
32 #include <yaz/srw.h>
33 #include <yaz/marcdisp.h>
34 #include <yaz/yaz-iconv.h>
35 #include <yaz/log.h>
36 #include <yaz/diagbib1.h>
37 #include <yaz++/proxy/proxy.h>
38 #include <yaz/pquery.h>
39
40 static const char *apdu_name(Z_APDU *apdu)
41 {
42     switch (apdu->which)
43     {
44     case Z_APDU_initRequest:
45         return "initRequest";
46     case Z_APDU_initResponse:
47         return "initResponse";
48     case Z_APDU_searchRequest:
49         return "searchRequest";
50     case Z_APDU_searchResponse:
51         return "searchResponse";
52     case Z_APDU_presentRequest:
53         return "presentRequest";
54     case Z_APDU_presentResponse:
55         return "presentResponse";
56     case Z_APDU_deleteResultSetRequest:
57         return "deleteResultSetRequest";
58     case Z_APDU_deleteResultSetResponse:
59         return "deleteResultSetResponse";
60     case Z_APDU_scanRequest:
61         return "scanRequest";
62     case Z_APDU_scanResponse:
63         return "scanResponse";
64     case Z_APDU_sortRequest:
65         return "sortRequest";
66     case Z_APDU_sortResponse:
67         return "sortResponse";
68     case Z_APDU_extendedServicesRequest:
69         return "extendedServicesRequest";
70     case Z_APDU_extendedServicesResponse:
71         return "extendedServicesResponse";
72     case Z_APDU_close:
73         return "close";
74     }
75     return "other";
76 }
77
78 static const char *gdu_name(Z_GDU *gdu)
79 {
80     switch(gdu->which)
81     {
82     case Z_GDU_Z3950:
83         return apdu_name(gdu->u.z3950);
84     case Z_GDU_HTTP_Request:
85         return "HTTP Request";
86     case Z_GDU_HTTP_Response:
87         return "HTTP Response";
88     }
89     return "Unknown request/response";
90 }
91
92 Yaz_Proxy::Yaz_Proxy(IYaz_PDU_Observable *the_PDU_Observable,
93                      Yaz_Proxy *parent) :
94     Yaz_Z_Assoc(the_PDU_Observable), m_bw_stat(60), m_pdu_stat(60)
95 {
96     m_PDU_Observable = the_PDU_Observable;
97     m_client = 0;
98     m_parent = parent;
99     m_clientPool = 0;
100     m_seqno = 1;
101     m_keepalive_limit_bw = 500000;
102     m_keepalive_limit_pdu = 1000;
103     m_proxyTarget = 0;
104     m_default_target = 0;
105     m_proxy_authentication = 0;
106     m_max_clients = 150;
107     m_log_mask = 0;
108     m_seed = time(0);
109     m_client_idletime = 600;
110     m_target_idletime = 600;
111     m_optimize = xstrdup ("1");
112     strcpy(m_session_str, "0 ");
113     m_session_no=0;
114     m_bytes_sent = 0;
115     m_bytes_recv = 0;
116     m_bw_hold_PDU = 0;
117     m_bw_max = 0;
118     m_pdu_max = 0;
119     m_max_record_retrieve = 0;
120     m_reconfig_flag = 0;
121     m_config_fname = 0;
122     m_request_no = 0;
123     m_invalid_session = 0;
124     m_referenceId = 0;
125     m_referenceId_mem = nmem_create();
126     m_config = 0;
127     m_marcxml_flag = 0;
128     m_stylesheet_xsp = 0;
129     m_stylesheet_nprl = 0;
130     m_s2z_stylesheet = 0;
131     m_s2z_database = 0;
132     m_schema = 0;
133     m_initRequest_apdu = 0;
134     m_initRequest_mem = 0;
135     m_initRequest_preferredMessageSize = 0;
136     m_initRequest_maximumRecordSize = 0;
137     m_initRequest_options = 0;
138     m_initRequest_version = 0;
139     m_apdu_invalid_session = 0;
140     m_mem_invalid_session = 0;
141     m_s2z_odr_init = 0;
142     m_s2z_odr_search = 0;
143     m_s2z_init_apdu = 0;
144     m_s2z_search_apdu = 0;
145     m_s2z_present_apdu = 0;
146     m_http_keepalive = 0;
147     m_http_version = 0;
148     m_soap_ns = 0;
149     m_s2z_packing = Z_SRW_recordPacking_string;
150 #if HAVE_GETTIMEOFDAY
151     m_time_tv.tv_sec = 0;
152     m_time_tv.tv_usec = 0;
153 #endif
154     if (!m_parent)
155         low_socket_open();
156 }
157
158 Yaz_Proxy::~Yaz_Proxy()
159 {
160     yaz_log(LOG_LOG, "%sClosed %d/%d sent/recv bytes total", m_session_str,
161             m_bytes_sent, m_bytes_recv);
162     nmem_destroy(m_initRequest_mem);
163     nmem_destroy(m_mem_invalid_session);
164     nmem_destroy(m_referenceId_mem);
165
166     xfree (m_proxyTarget);
167     xfree (m_default_target);
168     xfree (m_proxy_authentication);
169     xfree (m_optimize);
170
171 #if HAVE_XSLT
172     if (m_stylesheet_xsp)
173         xsltFreeStylesheet(m_stylesheet_xsp);
174 #endif
175     xfree (m_schema);
176     if (m_s2z_odr_init)
177         odr_destroy(m_s2z_odr_init);
178     if (m_s2z_odr_search)
179         odr_destroy(m_s2z_odr_search);
180     if (!m_parent)
181         low_socket_close();
182     delete m_config;
183 }
184
185 int Yaz_Proxy::set_config(const char *config)
186 {
187     delete m_config;
188     m_config = new Yaz_ProxyConfig();
189     xfree(m_config_fname);
190     m_config_fname = xstrdup(config);
191     int r = m_config->read_xml(config);
192     if (!r)
193         m_config->get_generic_info(&m_log_mask, &m_max_clients);
194     return r;
195 }
196
197 void Yaz_Proxy::set_default_target(const char *target)
198 {
199     xfree (m_default_target);
200     m_default_target = 0;
201     if (target)
202         m_default_target = (char *) xstrdup (target);
203 }
204
205 void Yaz_Proxy::set_proxy_authentication (const char *auth)
206 {
207     xfree (m_proxy_authentication);
208     m_proxy_authentication = 0;
209     if (auth)
210         m_proxy_authentication = (char *) xstrdup (auth);
211 }
212
213 Yaz_ProxyConfig *Yaz_Proxy::check_reconfigure()
214 {
215     if (m_parent)
216         return m_parent->check_reconfigure();
217
218     Yaz_ProxyConfig *cfg = m_config;
219     if (m_reconfig_flag)
220     {
221         yaz_log(LOG_LOG, "reconfigure");
222         yaz_log_reopen();
223         if (m_config_fname && cfg)
224         {
225             yaz_log(LOG_LOG, "reconfigure config %s", m_config_fname);
226             int r = cfg->read_xml(m_config_fname);
227             if (r)
228                 yaz_log(LOG_WARN, "reconfigure failed");
229             else
230             {
231                 m_log_mask = 0;
232                 cfg->get_generic_info(&m_log_mask, &m_max_clients);
233             }
234         }
235         else
236             yaz_log(LOG_LOG, "reconfigure");
237         m_reconfig_flag = 0;
238     }
239     return cfg;
240 }
241
242 IYaz_PDU_Observer *Yaz_Proxy::sessionNotify(IYaz_PDU_Observable
243                                             *the_PDU_Observable, int fd)
244 {
245     check_reconfigure();
246     Yaz_Proxy *new_proxy = new Yaz_Proxy(the_PDU_Observable, this);
247     new_proxy->m_config = 0;
248     new_proxy->m_config_fname = 0;
249     new_proxy->timeout(m_client_idletime);
250     new_proxy->m_target_idletime = m_target_idletime;
251     new_proxy->set_default_target(m_default_target);
252     new_proxy->m_max_clients = m_max_clients;
253     new_proxy->m_log_mask = m_log_mask;
254     new_proxy->set_APDU_log(get_APDU_log());
255     if (m_log_mask & PROXY_LOG_APDU_CLIENT)
256         new_proxy->set_APDU_yazlog(1);
257     else
258         new_proxy->set_APDU_yazlog(0);
259     new_proxy->set_proxy_authentication(m_proxy_authentication);
260     sprintf(new_proxy->m_session_str, "%ld:%d ", (long) time(0), m_session_no);
261     m_session_no++;
262     yaz_log (LOG_LOG, "%sNew session %s", new_proxy->m_session_str,
263              the_PDU_Observable->getpeername());
264     return new_proxy;
265 }
266
267 char *Yaz_Proxy::get_cookie(Z_OtherInformation **otherInfo)
268 {
269     int oid[OID_SIZE];
270     Z_OtherInformationUnit *oi;
271     struct oident ent;
272     ent.proto = PROTO_Z3950;
273     ent.oclass = CLASS_USERINFO;
274     ent.value = (oid_value) VAL_COOKIE;
275     assert (oid_ent_to_oid (&ent, oid));
276
277     if (oid_ent_to_oid (&ent, oid) && 
278         (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
279         oi->which == Z_OtherInfo_characterInfo)
280         return oi->information.characterInfo;
281     return 0;
282 }
283
284 char *Yaz_Proxy::get_proxy(Z_OtherInformation **otherInfo)
285 {
286     int oid[OID_SIZE];
287     Z_OtherInformationUnit *oi;
288     struct oident ent;
289     ent.proto = PROTO_Z3950;
290     ent.oclass = CLASS_USERINFO;
291     ent.value = (oid_value) VAL_PROXY;
292     if (oid_ent_to_oid (&ent, oid) &&
293         (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
294         oi->which == Z_OtherInfo_characterInfo)
295         return oi->information.characterInfo;
296     return 0;
297 }
298
299 const char *Yaz_Proxy::load_balance(const char **url)
300 {
301     int zurl_in_use[MAX_ZURL_PLEX];
302     int zurl_in_spare[MAX_ZURL_PLEX];
303     Yaz_ProxyClient *c;
304     int i;
305
306     for (i = 0; i<MAX_ZURL_PLEX; i++)
307     {
308         zurl_in_use[i] = 0;
309         zurl_in_spare[i] = 0;
310     }
311     for (c = m_parent->m_clientPool; c; c = c->m_next)
312     {
313         for (i = 0; url[i]; i++)
314             if (!strcmp(url[i], c->get_hostname()))
315             {
316                 zurl_in_use[i]++;
317                 if (c->m_cookie == 0 && c->m_server == 0 && c->m_waiting == 0)
318                     zurl_in_spare[i]++;
319             }
320     }
321     int min_use = 100000;
322     int spare_for_min = 0;
323     int max_spare = 0;
324     const char *ret_min = 0;
325     const char *ret_spare = 0;
326     for (i = 0; url[i]; i++)
327     {
328         yaz_log(LOG_DEBUG, "%szurl=%s use=%d spare=%d",
329                 m_session_str, url[i], zurl_in_use[i], zurl_in_spare[i]);
330         if (min_use > zurl_in_use[i])
331         {
332             ret_min = url[i];
333             min_use = zurl_in_use[i];
334             spare_for_min = zurl_in_spare[i];
335         }
336         if (max_spare < zurl_in_spare[i]) 
337         {
338             ret_spare = url[i];
339             max_spare = zurl_in_spare[i];
340         }
341     }
342     // use the one with minimum connections if spare is > 3
343     if (spare_for_min > 3)
344         return ret_min;
345     // use one with most spares (if any)
346     if (max_spare > 0)
347         return ret_spare;
348     return ret_min;
349 }
350
351 Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
352                                        const char *proxy_host)
353 {
354     assert (m_parent);
355     Yaz_Proxy *parent = m_parent;
356     Yaz_ProxyClient *c = m_client;
357     
358     if (!m_proxyTarget)
359     {
360         const char *url[MAX_ZURL_PLEX];
361         Yaz_ProxyConfig *cfg = check_reconfigure();
362         if (proxy_host)
363         {
364 #if 0
365 /* only to be enabled for debugging... */
366             if (!strcmp(proxy_host, "stop"))
367                 exit(0);
368 #endif
369             xfree(m_default_target);
370             m_default_target = xstrdup(proxy_host);
371             proxy_host = m_default_target;
372         }
373         int client_idletime = -1;
374         const char *cql2rpn_fname = 0;
375         url[0] = m_default_target;
376         url[1] = 0;
377         if (cfg)
378         {
379             int pre_init = 0;
380             cfg->get_target_info(proxy_host, url, &m_bw_max,
381                                  &m_pdu_max, &m_max_record_retrieve,
382                                  &m_target_idletime, &client_idletime,
383                                  &parent->m_max_clients,
384                                  &m_keepalive_limit_bw,
385                                  &m_keepalive_limit_pdu,
386                                  &pre_init,
387                                  &cql2rpn_fname);
388         }
389         if (client_idletime != -1)
390         {
391             m_client_idletime = client_idletime;
392             timeout(m_client_idletime);
393         }
394         if (cql2rpn_fname)
395             m_cql2rpn.set_pqf_file(cql2rpn_fname);
396         if (!url[0])
397         {
398             yaz_log(LOG_LOG, "%sNo default target", m_session_str);
399             return 0;
400         }
401         // we don't handle multiplexing for cookie session, so we just
402         // pick the first one in this case (anonymous users will be able
403         // to use any backend)
404         if (cookie && *cookie)
405             m_proxyTarget = (char*) xstrdup(url[0]);
406         else
407             m_proxyTarget = (char*) xstrdup(load_balance(url));
408     }
409     if (cookie && *cookie)
410     {   // search in sessions with a cookie
411         for (c = parent->m_clientPool; c; c = c->m_next)
412         {
413             assert (c->m_prev);
414             assert (*c->m_prev == c);
415             if (c->m_cookie && !strcmp(cookie,c->m_cookie) &&
416                 !strcmp(m_proxyTarget, c->get_hostname()))
417             {
418                 // Found it in cache
419                 // The following handles "cancel"
420                 // If connection is busy (waiting for PDU) and
421                 // we have an initRequest we can safely do re-open
422                 if (c->m_waiting && apdu->which == Z_APDU_initRequest)
423                 {
424                     yaz_log (LOG_LOG, "%s REOPEN target=%s", m_session_str,
425                              c->get_hostname());
426                     c->close();
427                     c->m_init_flag = 0;
428                     
429                     c->m_last_ok = 0;
430                     c->m_cache.clear();
431                     c->m_last_resultCount = 0;
432                     c->m_sr_transform = 0;
433                     c->m_waiting = 0;
434                     c->m_resultSetStartPoint = 0;
435                     c->m_target_idletime = m_target_idletime;
436                     if (c->client(m_proxyTarget))
437                     {
438                         delete c;
439                         return 0;
440                     }
441                     c->timeout(30); 
442                 }
443                 c->m_seqno = parent->m_seqno;
444                 if (c->m_server && c->m_server != this)
445                     c->m_server->m_client = 0;
446                 c->m_server = this;
447                 (parent->m_seqno)++;
448                 yaz_log (LOG_DEBUG, "get_client 1 %p %p", this, c);
449                 return c;
450             }
451         }
452     }
453     else if (!c)
454     {
455         // don't have a client session yet. Search in session w/o cookie
456         for (c = parent->m_clientPool; c; c = c->m_next)
457         {
458             assert (c->m_prev);
459             assert (*c->m_prev == c);
460             if (c->m_server == 0 && c->m_cookie == 0 && 
461                 c->m_waiting == 0 &&
462                 !strcmp(m_proxyTarget, c->get_hostname()))
463             {
464                 // found it in cache
465                 yaz_log (LOG_LOG, "%sREUSE %d %s",
466                          m_session_str, parent->m_seqno, c->get_hostname());
467                 
468                 c->m_seqno = parent->m_seqno;
469                 assert(c->m_server == 0);
470                 c->m_server = this;
471
472                 if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
473                     c->set_APDU_yazlog(1);
474                 else
475                     c->set_APDU_yazlog(0);
476
477                 (parent->m_seqno)++;
478                 
479                 parent->pre_init();
480                 
481                 return c;
482             }
483         }
484     }
485     if (!m_client)
486     {
487         if (apdu->which != Z_APDU_initRequest)
488         {
489             yaz_log (LOG_LOG, "%sno init request as first PDU", m_session_str);
490             return 0;
491         }
492         Z_InitRequest *initRequest = apdu->u.initRequest;
493
494         if (!initRequest->idAuthentication)
495         {
496             if (m_proxy_authentication)
497             {
498                 initRequest->idAuthentication =
499                     (Z_IdAuthentication *)
500                     odr_malloc (odr_encode(),
501                                 sizeof(*initRequest->idAuthentication));
502                 initRequest->idAuthentication->which =
503                     Z_IdAuthentication_open;
504                 initRequest->idAuthentication->u.open =
505                     odr_strdup (odr_encode(), m_proxy_authentication);
506             }
507         }
508         // go through list of clients - and find the lowest/oldest one.
509         Yaz_ProxyClient *c_min = 0;
510         int min_seq = -1;
511         int no_of_clients = 0;
512         if (parent->m_clientPool)
513             yaz_log (LOG_DEBUG, "Existing sessions");
514         for (c = parent->m_clientPool; c; c = c->m_next)
515         {
516             yaz_log (LOG_DEBUG, " Session %-3d wait=%d %s cookie=%s", c->m_seqno,
517                                c->m_waiting, c->get_hostname(),
518                                c->m_cookie ? c->m_cookie : "");
519             no_of_clients++;
520             if (min_seq < 0 || c->m_seqno < min_seq)
521             {
522                 min_seq = c->m_seqno;
523                 c_min = c;
524             }
525         }
526         if (no_of_clients >= parent->m_max_clients)
527         {
528             c = c_min;
529             if (c->m_waiting || strcmp(m_proxyTarget, c->get_hostname()))
530             {
531                 yaz_log (LOG_LOG, "%sMAXCLIENTS %d Destroy %d",
532                          m_session_str, parent->m_max_clients, c->m_seqno);
533                 if (c->m_server && c->m_server != this)
534                     delete c->m_server;
535                 c->m_server = 0;
536             }
537             else
538             {
539                 yaz_log (LOG_LOG, "%sMAXCLIENTS %d Reuse %d %d %s",
540                          m_session_str, parent->m_max_clients,
541                          c->m_seqno, parent->m_seqno, c->get_hostname());
542                 xfree (c->m_cookie);
543                 c->m_cookie = 0;
544                 if (cookie)
545                     c->m_cookie = xstrdup(cookie);
546                 c->m_seqno = parent->m_seqno;
547                 if (c->m_server && c->m_server != this)
548                 {
549                     c->m_server->m_client = 0;
550                     delete c->m_server;
551                 }
552                 (parent->m_seqno)++;
553                 c->m_target_idletime = m_target_idletime;
554                 c->timeout(m_target_idletime);
555                 
556                 if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
557                     c->set_APDU_yazlog(1);
558                 else
559                     c->set_APDU_yazlog(0);
560
561                 return c;
562             }
563         }
564         else
565         {
566             yaz_log (LOG_LOG, "%sNEW %d %s",
567                      m_session_str, parent->m_seqno, m_proxyTarget);
568             c = new Yaz_ProxyClient(m_PDU_Observable->clone(), parent);
569             c->m_next = parent->m_clientPool;
570             if (c->m_next)
571                 c->m_next->m_prev = &c->m_next;
572             parent->m_clientPool = c;
573             c->m_prev = &parent->m_clientPool;
574         }
575
576         xfree (c->m_cookie);
577         c->m_cookie = 0;
578         if (cookie)
579             c->m_cookie = xstrdup(cookie);
580
581         c->m_seqno = parent->m_seqno;
582         c->m_init_flag = 0;
583         c->m_last_resultCount = 0;
584         c->m_last_ok = 0;
585         c->m_cache.clear();
586         c->m_sr_transform = 0;
587         c->m_waiting = 0;
588         c->m_resultSetStartPoint = 0;
589         (parent->m_seqno)++;
590         if (c->client(m_proxyTarget))
591         {
592             delete c;
593             return 0;
594         }
595         c->m_target_idletime = m_target_idletime;
596         c->timeout(30);
597
598         if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
599             c->set_APDU_yazlog(1);
600         else
601             c->set_APDU_yazlog(0);
602     }
603     yaz_log (LOG_DEBUG, "get_client 3 %p %p", this, c);
604     return c;
605 }
606
607 void Yaz_Proxy::display_diagrecs(Z_DiagRec **pp, int num)
608 {
609     int i;
610     for (i = 0; i<num; i++)
611     {
612         oident *ent;
613         Z_DefaultDiagFormat *r;
614         Z_DiagRec *p = pp[i];
615         if (p->which != Z_DiagRec_defaultFormat)
616         {
617             yaz_log(LOG_LOG, "%sError no diagnostics", m_session_str);
618             return;
619         }
620         else
621             r = p->u.defaultFormat;
622         if (!(ent = oid_getentbyoid(r->diagnosticSetId)) ||
623             ent->oclass != CLASS_DIAGSET || ent->value != VAL_BIB1)
624             yaz_log(LOG_LOG, "%sError unknown diagnostic set", m_session_str);
625         switch (r->which)
626         {
627         case Z_DefaultDiagFormat_v2Addinfo:
628             yaz_log(LOG_LOG, "%sError %d %s:%s",
629                     m_session_str,
630                     *r->condition, diagbib1_str(*r->condition),
631                     r->u.v2Addinfo);
632             break;
633         case Z_DefaultDiagFormat_v3Addinfo:
634             yaz_log(LOG_LOG, "%sError %d %s:%s",
635                     m_session_str,
636                     *r->condition, diagbib1_str(*r->condition),
637                     r->u.v3Addinfo);
638             break;
639         }
640     }
641 }
642
643 int Yaz_Proxy::convert_xsl(Z_NamePlusRecordList *p, Z_APDU *apdu)
644 {
645     if (!m_stylesheet_xsp || p->num_records <= 0)
646         return 0;  /* no XSLT to be done ... */
647
648     m_stylesheet_offset = 0;
649     m_stylesheet_nprl = p;
650     m_stylesheet_apdu = apdu;
651     timeout(0);
652     return 1;
653 }
654
655 void Yaz_Proxy::convert_xsl_delay()
656 {
657     Z_NamePlusRecord *npr = m_stylesheet_nprl->records[m_stylesheet_offset];
658 #if HAVE_XSLT
659     if (npr->which == Z_NamePlusRecord_databaseRecord)
660     {
661         Z_External *r = npr->u.databaseRecord;
662         if (r->which == Z_External_octet)
663         {
664 #if 0
665             fwrite((char*) r->u.octet_aligned->buf, 1, r->u.octet_aligned->len, stdout);
666 #endif
667             xmlDocPtr res, doc = xmlParseMemory(
668                 (char*) r->u.octet_aligned->buf,
669                 r->u.octet_aligned->len);
670
671             
672             yaz_log(LOG_LOG, "%sXSLT convert %d",
673                     m_session_str, m_stylesheet_offset);
674             res = xsltApplyStylesheet(m_stylesheet_xsp, doc, 0);
675
676             if (res)
677             {
678                 xmlChar *out_buf;
679                 int out_len;
680                 xmlDocDumpFormatMemory (res, &out_buf, &out_len, 1);
681                 
682                 m_stylesheet_nprl->records[m_stylesheet_offset]->
683                     u.databaseRecord = 
684                     z_ext_record(odr_encode(), VAL_TEXT_XML,
685                                  (char*) out_buf, out_len);
686                 xmlFree(out_buf);
687                 xmlFreeDoc(res);
688             }
689
690             xmlFreeDoc(doc);
691         }
692     }
693 #endif
694     m_stylesheet_offset++;
695     if (m_stylesheet_offset == m_stylesheet_nprl->num_records)
696     {
697         m_stylesheet_nprl = 0;
698 #if HAVE_XSLT
699         if (m_stylesheet_xsp)
700             xsltFreeStylesheet(m_stylesheet_xsp);
701 #endif
702         m_stylesheet_xsp = 0;
703         timeout(m_client_idletime);
704         send_PDU_convert(m_stylesheet_apdu);
705     }
706     else
707         timeout(0);
708 }
709
710 void Yaz_Proxy::convert_to_marcxml(Z_NamePlusRecordList *p)
711 {
712     int i;
713
714     yaz_iconv_t cd = yaz_iconv_open("UTF-8", "MARC-8");
715     yaz_marc_t mt = yaz_marc_create();
716     yaz_marc_xml(mt, YAZ_MARC_MARCXML);
717     yaz_marc_iconv(mt, cd);
718     for (i = 0; i < p->num_records; i++)
719     {
720         Z_NamePlusRecord *npr = p->records[i];
721         if (npr->which == Z_NamePlusRecord_databaseRecord)
722         {
723             Z_External *r = npr->u.databaseRecord;
724             if (r->which == Z_External_octet)
725             {
726                 int rlen;
727                 char *result;
728                 if (yaz_marc_decode_buf(mt, (char*) r->u.octet_aligned->buf,
729                                         r->u.octet_aligned->len,
730                                         &result, &rlen))
731                 {
732                     npr->u.databaseRecord = z_ext_record(odr_encode(),
733                                                          VAL_TEXT_XML,
734                                                          result, rlen);
735                 }
736             }
737         }
738     }
739     if (cd)
740         yaz_iconv_close(cd);
741     yaz_marc_destroy(mt);
742 }
743
744 void Yaz_Proxy::logtime()
745 {
746 #if HAVE_GETTIMEOFDAY
747     if (m_time_tv.tv_sec)
748     {
749         struct timeval tv;
750         gettimeofday(&tv, 0);
751         long diff = (tv.tv_sec - m_time_tv.tv_sec)*1000000 +
752             (tv.tv_usec - m_time_tv.tv_usec);
753         if (diff >= 0)
754             yaz_log(LOG_LOG, "%sElapsed %ld.%03ld", m_session_str,
755                     diff/1000000, (diff/1000)%1000);
756     }
757     m_time_tv.tv_sec = 0;
758     m_time_tv.tv_usec = 0;
759 #endif
760 }
761
762 int Yaz_Proxy::send_http_response(int code)
763 {
764     ODR o = odr_encode();
765     Z_GDU *gdu = z_get_HTTP_Response(o, code);
766     Z_HTTP_Response *hres = gdu->u.HTTP_Response;
767     if (m_http_version)
768         hres->version = odr_strdup(o, m_http_version);
769     if (m_http_keepalive)
770         z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
771     else
772         timeout(0);
773     
774     if (m_log_mask & PROXY_LOG_REQ_CLIENT)
775     {
776         yaz_log (LOG_LOG, "%sSending %s to client", m_session_str,
777                  gdu_name(gdu));
778     }
779     int len;
780     int r = send_GDU(gdu, &len);
781     m_bytes_sent += len;
782     m_bw_stat.add_bytes(len);
783     logtime();
784     return r;
785 }
786
787 int Yaz_Proxy::send_srw_response(Z_SRW_PDU *srw_pdu)
788 {
789     ODR o = odr_encode();
790     const char *ctype = "text/xml";
791     Z_GDU *gdu = z_get_HTTP_Response(o, 200);
792     Z_HTTP_Response *hres = gdu->u.HTTP_Response;
793     if (m_http_version)
794         hres->version = odr_strdup(o, m_http_version);
795     z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
796     if (m_http_keepalive)
797         z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
798     else
799         timeout(0);
800
801     static Z_SOAP_Handler soap_handlers[2] = {
802 #if HAVE_XSLT
803         {"http://www.loc.gov/zing/srw/", 0,
804          (Z_SOAP_fun) yaz_srw_codec},
805 #endif
806         {0, 0, 0}
807     };
808     
809     Z_SOAP *soap_package = (Z_SOAP*) odr_malloc(o, sizeof(Z_SOAP));
810     soap_package->which = Z_SOAP_generic;
811     soap_package->u.generic = 
812         (Z_SOAP_Generic *) odr_malloc(o,  sizeof(*soap_package->u.generic));
813     soap_package->u.generic->no = 0;
814     soap_package->u.generic->ns = soap_handlers[0].ns;
815     soap_package->u.generic->p = (void *) srw_pdu;
816     soap_package->ns = m_soap_ns;
817     z_soap_codec_enc_xsl(o, &soap_package,
818                          &hres->content_buf, &hres->content_len,
819                          soap_handlers, 0, m_s2z_stylesheet);
820     if (m_log_mask & PROXY_LOG_REQ_CLIENT)
821     {
822         yaz_log (LOG_LOG, "%sSending %s to client", m_session_str,
823                  gdu_name(gdu));
824     }
825     int len;
826     int r = send_GDU(gdu, &len);
827     m_bytes_sent += len;
828     m_bw_stat.add_bytes(len);
829     logtime();
830     return r;
831 }
832
833 int Yaz_Proxy::send_to_srw_client_error(int srw_error, const char *add)
834 {
835     ODR o = odr_encode();
836     Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
837     Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
838
839     srw_res->num_diagnostics = 1;
840     srw_res->diagnostics = (Z_SRW_diagnostic *)
841         odr_malloc(o, sizeof(*srw_res->diagnostics));
842     yaz_mk_std_diagnostic(o, srw_res->diagnostics, srw_error, add);
843     return send_srw_response(srw_pdu);
844 }
845
846 int Yaz_Proxy::z_to_srw_diag(ODR o, Z_SRW_searchRetrieveResponse *srw_res,
847                              Z_DefaultDiagFormat *ddf)
848 {
849     int bib1_code = *ddf->condition;
850     if (bib1_code == 109)
851         return 404;
852     srw_res->num_diagnostics = 1;
853     srw_res->diagnostics = (Z_SRW_diagnostic *)
854         odr_malloc(o, sizeof(*srw_res->diagnostics));
855     yaz_mk_std_diagnostic(o, srw_res->diagnostics,
856                           yaz_diag_bib1_to_srw(*ddf->condition), 
857                           ddf->u.v2Addinfo);
858     return 0;
859 }
860
861 int Yaz_Proxy::send_to_srw_client_ok(int hits, Z_Records *records, int start)
862 {
863     ODR o = odr_encode();
864     Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
865     Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
866
867     srw_res->numberOfRecords = odr_intdup (o, hits);
868     if (records && records->which == Z_Records_DBOSD)
869     {
870         srw_res->num_records =
871             records->u.databaseOrSurDiagnostics->num_records;
872         int i;
873         srw_res->records = (Z_SRW_record *)
874             odr_malloc(o, srw_res->num_records * sizeof(Z_SRW_record));
875         for (i = 0; i < srw_res->num_records; i++)
876         {
877             Z_NamePlusRecord *npr = records->u.databaseOrSurDiagnostics->records[i];
878             if (npr->which != Z_NamePlusRecord_databaseRecord)
879             {
880                 srw_res->records[i].recordSchema = "diagnostic";
881                 srw_res->records[i].recordPacking = m_s2z_packing;
882                 srw_res->records[i].recordData_buf = "67";
883                 srw_res->records[i].recordData_len = 2;
884                 srw_res->records[i].recordPosition = odr_intdup(o, i+start);
885                 continue;
886             }
887             Z_External *r = npr->u.databaseRecord;
888             oident *ent = oid_getentbyoid(r->direct_reference);
889             if (r->which == Z_External_octet && ent->value == VAL_TEXT_XML)
890             {
891                 srw_res->records[i].recordSchema = m_schema;
892                 srw_res->records[i].recordPacking = m_s2z_packing;
893                 srw_res->records[i].recordData_buf = (char*) 
894                     r->u.octet_aligned->buf;
895                 srw_res->records[i].recordData_len = r->u.octet_aligned->len;
896                 srw_res->records[i].recordPosition = odr_intdup(o, i+start);
897             }
898             else
899             {
900                 srw_res->records[i].recordSchema = "diagnostic";
901                 srw_res->records[i].recordPacking = m_s2z_packing;
902                 srw_res->records[i].recordData_buf = "67";
903                 srw_res->records[i].recordData_len = 2;
904                 srw_res->records[i].recordPosition = odr_intdup(o, i+start);
905             }
906         }
907     }
908     if (records && records->which == Z_Records_NSD)
909     {
910         int http_code;
911         http_code = z_to_srw_diag(odr_encode(), srw_res,
912                                    records->u.nonSurrogateDiagnostic);
913         if (http_code)
914             return send_http_response(http_code);
915     }
916     return send_srw_response(srw_pdu);
917     
918 }
919
920 int Yaz_Proxy::send_srw_explain_response(Z_SRW_diagnostic *diagnostics,
921                                         int num_diagnostics)
922 {
923     Yaz_ProxyConfig *cfg = check_reconfigure();
924     if (cfg)
925     {
926         int len;
927         char *b = cfg->get_explain(odr_encode(), 0 /* target */,
928                                    m_s2z_database, &len);
929         if (b)
930         {
931             Z_SRW_PDU *res = yaz_srw_get(odr_encode(), Z_SRW_explain_response);
932             Z_SRW_explainResponse *er = res->u.explain_response;
933
934             er->record.recordData_buf = b;
935             er->record.recordData_len = len;
936             er->record.recordPacking = m_s2z_packing;
937             er->record.recordSchema = "http://explain.z3950.org/dtd/2.0/";
938
939             er->diagnostics = diagnostics;
940             er->num_diagnostics = num_diagnostics;
941             return send_srw_response(res);
942         }
943     }
944     return send_http_response(404);
945 }
946
947 int Yaz_Proxy::send_PDU_convert(Z_APDU *apdu)
948 {
949     if (m_http_version)
950     {
951         if (apdu->which == Z_APDU_initResponse)
952         {
953             Z_InitResponse *res = apdu->u.initResponse;
954             if (*res->result == 0)
955             {
956                 send_to_srw_client_error(3, 0);
957             }
958             else if (!m_s2z_search_apdu)
959             {
960                 send_srw_explain_response(0, 0);
961             }
962             else
963             {
964                 handle_incoming_Z_PDU(m_s2z_search_apdu);
965             }
966         }
967         else if (m_s2z_search_apdu && apdu->which == Z_APDU_searchResponse)
968         {
969             m_s2z_search_apdu = 0;
970             Z_SearchResponse *res = apdu->u.searchResponse;
971             m_s2z_hit_count = *res->resultCount;
972             if (res->records && res->records->which == Z_Records_NSD)
973             {
974                 send_to_srw_client_ok(0, res->records, 1);
975             }
976             else if (m_s2z_present_apdu && m_s2z_hit_count > 0)
977             {
978                 // adjust 
979                 Z_PresentRequest *pr = m_s2z_present_apdu->u.presentRequest;
980                 
981                 if (*pr->resultSetStartPoint <= m_s2z_hit_count)
982                 {
983                     if (*pr->numberOfRecordsRequested+ *pr->resultSetStartPoint
984                         > m_s2z_hit_count)
985                         *pr->numberOfRecordsRequested =
986                             1 + m_s2z_hit_count - *pr->resultSetStartPoint;
987                 }
988                 handle_incoming_Z_PDU(m_s2z_present_apdu);
989             }
990             else
991             {
992                 m_s2z_present_apdu = 0;
993                 send_to_srw_client_ok(m_s2z_hit_count, res->records, 1);
994             }
995         }
996         else if (m_s2z_present_apdu && apdu->which == Z_APDU_presentResponse)
997         {
998             int start = 
999                 *m_s2z_present_apdu->u.presentRequest->resultSetStartPoint;
1000
1001             m_s2z_present_apdu = 0;
1002             Z_PresentResponse *res = apdu->u.presentResponse;
1003             send_to_srw_client_ok(m_s2z_hit_count, res->records, start);
1004         }
1005     }
1006     else
1007     {
1008         int len = 0;
1009         if (m_log_mask & PROXY_LOG_REQ_CLIENT)
1010             yaz_log (LOG_LOG, "%sSending %s to client", m_session_str,
1011                      apdu_name(apdu));
1012         int r = send_Z_PDU(apdu, &len);
1013         m_bytes_sent += len;
1014         m_bw_stat.add_bytes(len);
1015         logtime();
1016         return r;
1017     }
1018     return 0;
1019 }
1020
1021 int Yaz_Proxy::send_to_client(Z_APDU *apdu)
1022 {
1023     int kill_session = 0;
1024     Z_ReferenceId **new_id = get_referenceIdP(apdu);
1025
1026     if (new_id)
1027         *new_id = m_referenceId;
1028     
1029     if (apdu->which == Z_APDU_searchResponse)
1030     {
1031         Z_SearchResponse *sr = apdu->u.searchResponse;
1032         Z_Records *p = sr->records;
1033         if (p && p->which == Z_Records_NSD)
1034         {
1035             Z_DiagRec dr, *dr_p = &dr;
1036             dr.which = Z_DiagRec_defaultFormat;
1037             dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
1038
1039             *sr->searchStatus = 0;
1040             display_diagrecs(&dr_p, 1);
1041         }
1042         else
1043         {
1044             if (p && p->which == Z_Records_DBOSD)
1045             {
1046                 if (m_marcxml_flag)
1047                     convert_to_marcxml(p->u.databaseOrSurDiagnostics);
1048                 if (convert_xsl(p->u.databaseOrSurDiagnostics, apdu))
1049                     return 0;
1050                     
1051             }
1052             if (sr->resultCount)
1053             {
1054                 yaz_log(LOG_LOG, "%s%d hits", m_session_str,
1055                         *sr->resultCount);
1056                 if (*sr->resultCount < 0)
1057                 {
1058                     m_invalid_session = 1;
1059                     kill_session = 1;
1060
1061                     *sr->searchStatus = 0;
1062                     sr->records =
1063                         create_nonSurrogateDiagnostics(odr_encode(), 2, 0);
1064                     *sr->resultCount = 0;
1065                 }
1066             }
1067         }
1068     }
1069     else if (apdu->which == Z_APDU_presentResponse)
1070     {
1071         Z_PresentResponse *sr = apdu->u.presentResponse;
1072         Z_Records *p = sr->records;
1073         if (p && p->which == Z_Records_NSD)
1074         {
1075             Z_DiagRec dr, *dr_p = &dr;
1076             dr.which = Z_DiagRec_defaultFormat;
1077             dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
1078             if (*sr->presentStatus == Z_PresentStatus_success)
1079                 *sr->presentStatus = Z_PresentStatus_failure;
1080             display_diagrecs(&dr_p, 1);
1081         }
1082         if (p && p->which == Z_Records_DBOSD)
1083         {
1084             if (m_marcxml_flag)
1085                 convert_to_marcxml(p->u.databaseOrSurDiagnostics);
1086             if (convert_xsl(p->u.databaseOrSurDiagnostics, apdu))
1087                 return 0;
1088         }
1089     }
1090     else if (apdu->which == Z_APDU_initResponse)
1091     {
1092         if (m_initRequest_options)
1093         {
1094             Z_Options *nopt = 
1095                 (Odr_bitmask *)odr_malloc(odr_encode(),
1096                                           sizeof(Odr_bitmask));
1097             ODR_MASK_ZERO(nopt);
1098
1099             int i;
1100             for (i = 0; i<24; i++)
1101                 if (ODR_MASK_GET(m_initRequest_options, i) &&
1102                     ODR_MASK_GET(apdu->u.initResponse->options, i))
1103                     ODR_MASK_SET(nopt, i);
1104             apdu->u.initResponse->options = nopt;           
1105         }
1106         if (m_initRequest_version)
1107         {
1108             Z_ProtocolVersion *nopt = 
1109                 (Odr_bitmask *)odr_malloc(odr_encode(),
1110                                           sizeof(Odr_bitmask));
1111             ODR_MASK_ZERO(nopt);
1112
1113             int i;
1114             for (i = 0; i<8; i++)
1115                 if (ODR_MASK_GET(m_initRequest_version, i) &&
1116                     ODR_MASK_GET(apdu->u.initResponse->protocolVersion, i))
1117                     ODR_MASK_SET(nopt, i);
1118             apdu->u.initResponse->protocolVersion = nopt;           
1119         }
1120         apdu->u.initResponse->preferredMessageSize =
1121             odr_intdup(odr_encode(),
1122                        m_client->m_initResponse_preferredMessageSize >
1123                        m_initRequest_preferredMessageSize ?
1124                        m_initRequest_preferredMessageSize :
1125                        m_client->m_initResponse_preferredMessageSize);
1126         apdu->u.initResponse->maximumRecordSize =
1127             odr_intdup(odr_encode(),
1128                        m_client->m_initResponse_maximumRecordSize >
1129                        m_initRequest_maximumRecordSize ?
1130                        m_initRequest_maximumRecordSize :
1131                        m_client->m_initResponse_maximumRecordSize);
1132     }
1133     int r = send_PDU_convert(apdu);
1134     if (r)
1135         return r;
1136     if (kill_session)
1137     {
1138         delete m_client;
1139         m_client = 0;
1140         m_parent->pre_init();
1141     }
1142     return r;
1143 }
1144
1145 int Yaz_ProxyClient::send_to_target(Z_APDU *apdu)
1146 {
1147     int len = 0;
1148     const char *apdu_name_tmp = apdu_name(apdu);
1149     int r = send_Z_PDU(apdu, &len);
1150     if (m_root->get_log_mask() & PROXY_LOG_REQ_SERVER)
1151         yaz_log (LOG_LOG, "%sSending %s to %s %d bytes",
1152                  get_session_str(),
1153                  apdu_name_tmp, get_hostname(), len);
1154     m_bytes_sent += len;
1155     return r;
1156 }
1157
1158 Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
1159 {
1160     if (apdu->which == Z_APDU_presentRequest)
1161     {
1162         Z_PresentRequest *pr = apdu->u.presentRequest;
1163         int toget = *pr->numberOfRecordsRequested;
1164         int start = *pr->resultSetStartPoint;
1165
1166         yaz_log(LOG_LOG, "%sPresent %s %d+%d", m_session_str,
1167                 pr->resultSetId, start, toget);
1168
1169         if (*m_parent->m_optimize == '0')
1170             return apdu;
1171
1172         if (!m_client->m_last_resultSetId)
1173         {
1174             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1175             new_apdu->u.presentResponse->records =
1176                 create_nonSurrogateDiagnostics(odr_encode(), 30,
1177                                                pr->resultSetId);
1178             send_to_client(new_apdu);
1179             return 0;
1180         }
1181         if (!strcmp(m_client->m_last_resultSetId, pr->resultSetId))
1182         {
1183             if (start+toget-1 > m_client->m_last_resultCount)
1184             {
1185                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1186                 new_apdu->u.presentResponse->records =
1187                     create_nonSurrogateDiagnostics(odr_encode(), 13, 0);
1188                 send_to_client(new_apdu);
1189                 return 0;
1190             }
1191             Z_NamePlusRecordList *npr;
1192             if (m_client->m_cache.lookup (odr_encode(), &npr, start, toget,
1193                                           pr->preferredRecordSyntax,
1194                                           pr->recordComposition))
1195             {
1196                 yaz_log (LOG_LOG, "%sReturned cached records for present request", 
1197                          m_session_str);
1198                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1199                 new_apdu->u.presentResponse->referenceId = pr->referenceId;
1200                 
1201                 new_apdu->u.presentResponse->numberOfRecordsReturned
1202                     = odr_intdup(odr_encode(), toget);
1203                                                                  
1204                 new_apdu->u.presentResponse->records = (Z_Records*)
1205                     odr_malloc(odr_encode(), sizeof(Z_Records));
1206                 new_apdu->u.presentResponse->records->which = Z_Records_DBOSD;
1207                 new_apdu->u.presentResponse->records->u.databaseOrSurDiagnostics = npr;
1208                 new_apdu->u.presentResponse->nextResultSetPosition =
1209                     odr_intdup(odr_encode(), start+toget);
1210
1211                 send_to_client(new_apdu);
1212                 return 0;
1213             }
1214         }
1215     }
1216
1217     if (apdu->which != Z_APDU_searchRequest)
1218         return apdu;
1219     Z_SearchRequest *sr = apdu->u.searchRequest;
1220     Yaz_Z_Query *this_query = new Yaz_Z_Query;
1221     Yaz_Z_Databases this_databases;
1222
1223     this_databases.set(sr->num_databaseNames, (const char **)
1224                        sr->databaseNames);
1225     
1226     this_query->set_Z_Query(sr->query);
1227
1228     char query_str[120];
1229     this_query->print(query_str, sizeof(query_str)-1);
1230     yaz_log(LOG_LOG, "%sSearch %s", m_session_str, query_str);
1231
1232     if (*m_parent->m_optimize != '0' &&
1233         m_client->m_last_ok && m_client->m_last_query &&
1234         m_client->m_last_query->match(this_query) &&
1235         !strcmp(m_client->m_last_resultSetId, sr->resultSetName) &&
1236         m_client->m_last_databases.match(this_databases))
1237     {
1238         delete this_query;
1239         if (m_client->m_last_resultCount > *sr->smallSetUpperBound &&
1240             m_client->m_last_resultCount < *sr->largeSetLowerBound)
1241         {
1242             Z_NamePlusRecordList *npr;
1243             int toget = *sr->mediumSetPresentNumber;
1244             Z_RecordComposition *comp = 0;
1245
1246             if (toget > m_client->m_last_resultCount)
1247                 toget = m_client->m_last_resultCount;
1248             
1249             if (sr->mediumSetElementSetNames)
1250             {
1251                 comp = (Z_RecordComposition *)
1252                     odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
1253                 comp->which = Z_RecordComp_simple;
1254                 comp->u.simple = sr->mediumSetElementSetNames;
1255             }
1256  
1257             if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget,
1258                                           sr->preferredRecordSyntax, comp))
1259             {
1260                 yaz_log (LOG_LOG, "%sReturned cached records for medium set",
1261                          m_session_str);
1262                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1263                 new_apdu->u.searchResponse->referenceId = sr->referenceId;
1264                 new_apdu->u.searchResponse->resultCount =
1265                     &m_client->m_last_resultCount;
1266                 
1267                 new_apdu->u.searchResponse->numberOfRecordsReturned
1268                     = odr_intdup(odr_encode(), toget);
1269                                                         
1270                 new_apdu->u.searchResponse->presentStatus =
1271                     odr_intdup(odr_encode(), Z_PresentStatus_success);
1272                 new_apdu->u.searchResponse->records = (Z_Records*)
1273                     odr_malloc(odr_encode(), sizeof(Z_Records));
1274                 new_apdu->u.searchResponse->records->which = Z_Records_DBOSD;
1275                 new_apdu->u.searchResponse->records->u.databaseOrSurDiagnostics = npr;
1276                 new_apdu->u.searchResponse->nextResultSetPosition =
1277                     odr_intdup(odr_encode(), toget+1);
1278                 send_to_client(new_apdu);
1279                 return 0;
1280             }
1281             else
1282             {
1283                 // medium Set
1284                 // send present request (medium size)
1285                 yaz_log (LOG_LOG, "%sOptimizing search for medium set",
1286                          m_session_str);
1287
1288                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
1289                 Z_PresentRequest *pr = new_apdu->u.presentRequest;
1290                 pr->referenceId = sr->referenceId;
1291                 pr->resultSetId = sr->resultSetName;
1292                 pr->preferredRecordSyntax = sr->preferredRecordSyntax;
1293                 *pr->numberOfRecordsRequested = toget;
1294                 pr->recordComposition = comp;
1295                 m_client->m_sr_transform = 1;
1296                 return new_apdu;
1297             }
1298         }
1299         else if (m_client->m_last_resultCount >= *sr->largeSetLowerBound ||
1300             m_client->m_last_resultCount <= 0)
1301         {
1302             // large set. Return pseudo-search response immediately
1303             yaz_log (LOG_LOG, "%sOptimizing search for large set",
1304                      m_session_str);
1305             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1306             new_apdu->u.searchResponse->referenceId = sr->referenceId;
1307             new_apdu->u.searchResponse->resultCount =
1308                 &m_client->m_last_resultCount;
1309             send_to_client(new_apdu);
1310             return 0;
1311         }
1312         else
1313         {
1314             Z_NamePlusRecordList *npr;
1315             int toget = m_client->m_last_resultCount;
1316             Z_RecordComposition *comp = 0;
1317             // small set
1318             // send a present request (small set)
1319             
1320             if (sr->smallSetElementSetNames)
1321             {
1322                 comp = (Z_RecordComposition *)
1323                     odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
1324                 comp->which = Z_RecordComp_simple;
1325                 comp->u.simple = sr->smallSetElementSetNames;
1326             }
1327
1328             if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget,
1329                                           sr->preferredRecordSyntax, comp))
1330             {
1331                 yaz_log (LOG_LOG, "%sReturned cached records for small set",
1332                          m_session_str);
1333                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1334                 new_apdu->u.searchResponse->referenceId = sr->referenceId;
1335                 new_apdu->u.searchResponse->resultCount =
1336                     &m_client->m_last_resultCount;
1337                 
1338                 new_apdu->u.searchResponse->numberOfRecordsReturned
1339                     = odr_intdup(odr_encode(), toget);
1340                                                                  
1341                 new_apdu->u.searchResponse->presentStatus =
1342                     odr_intdup(odr_encode(), Z_PresentStatus_success);
1343                 new_apdu->u.searchResponse->records = (Z_Records*)
1344                     odr_malloc(odr_encode(), sizeof(Z_Records));
1345                 new_apdu->u.searchResponse->records->which = Z_Records_DBOSD;
1346                 new_apdu->u.searchResponse->records->u.databaseOrSurDiagnostics = npr;
1347                 new_apdu->u.searchResponse->nextResultSetPosition =
1348                     odr_intdup(odr_encode(), toget+1);
1349                 send_to_client(new_apdu);
1350                 return 0;
1351             }
1352             else
1353             {
1354                 yaz_log (LOG_LOG, "%sOptimizing search for small set",
1355                          m_session_str);
1356                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
1357                 Z_PresentRequest *pr = new_apdu->u.presentRequest;
1358                 pr->referenceId = sr->referenceId;
1359                 pr->resultSetId = sr->resultSetName;
1360                 pr->preferredRecordSyntax = sr->preferredRecordSyntax;
1361                 *pr->numberOfRecordsRequested = toget;
1362                 pr->recordComposition = comp;
1363                 m_client->m_sr_transform = 1;
1364                 return new_apdu;
1365             }
1366         }
1367     }
1368     else  // query doesn't match
1369     {
1370         delete m_client->m_last_query;
1371         m_client->m_last_query = this_query;
1372         m_client->m_last_ok = 0;
1373         m_client->m_cache.clear();
1374         m_client->m_resultSetStartPoint = 0;
1375
1376         xfree (m_client->m_last_resultSetId);
1377         m_client->m_last_resultSetId = xstrdup (sr->resultSetName);
1378
1379         m_client->m_last_databases.set(sr->num_databaseNames,
1380                                        (const char **) sr->databaseNames);
1381     }
1382     return apdu;
1383 }
1384
1385
1386 void Yaz_Proxy::inc_request_no()
1387 {
1388     char *cp = strchr(m_session_str, ' ');
1389     m_request_no++;
1390     if (cp)
1391         sprintf(cp+1, "%d ", m_request_no);
1392 }
1393
1394 void Yaz_Proxy::recv_GDU(Z_GDU *apdu, int len)
1395 {
1396     inc_request_no();
1397
1398     m_bytes_recv += len;
1399     
1400     if (m_log_mask & PROXY_LOG_REQ_CLIENT)
1401         yaz_log (LOG_LOG, "%sReceiving %s from client %d bytes",
1402                  m_session_str, gdu_name(apdu), len);
1403
1404     if (m_bw_hold_PDU)     // double incoming PDU. shutdown now.
1405         shutdown();
1406
1407     m_bw_stat.add_bytes(len);
1408     m_pdu_stat.add_bytes(1);
1409
1410 #if HAVE_GETTIMEOFDAY
1411     gettimeofday(&m_time_tv, 0);
1412 #endif
1413
1414     int bw_total = m_bw_stat.get_total();
1415     int pdu_total = m_pdu_stat.get_total();
1416
1417     int reduce = 0;
1418     if (m_bw_max)
1419     {
1420         if (bw_total > m_bw_max)
1421         {
1422             reduce = (bw_total/m_bw_max);
1423         }
1424     }
1425     if (m_pdu_max)
1426     {
1427         if (pdu_total > m_pdu_max)
1428         {
1429             int nreduce = (m_pdu_max >= 60) ? 1 : 60/m_pdu_max;
1430             reduce = (reduce > nreduce) ? reduce : nreduce;
1431         }
1432     }
1433     if (reduce)  
1434     {
1435         yaz_log(LOG_LOG, "%sdelay=%d bw=%d pdu=%d limit-bw=%d limit-pdu=%d",
1436                 m_session_str, reduce, bw_total, pdu_total,
1437                 m_bw_max, m_pdu_max);
1438         
1439         m_bw_hold_PDU = apdu;  // save PDU and signal "on hold"
1440         timeout(reduce);       // call us reduce seconds later
1441     }
1442     else if (apdu->which == Z_GDU_Z3950)
1443         handle_incoming_Z_PDU(apdu->u.z3950);
1444     else if (apdu->which == Z_GDU_HTTP_Request)
1445         handle_incoming_HTTP(apdu->u.HTTP_Request);
1446 }
1447
1448 void Yaz_Proxy::handle_max_record_retrieve(Z_APDU *apdu)
1449 {
1450     if (m_max_record_retrieve)
1451     {
1452         if (apdu->which == Z_APDU_presentRequest)
1453         {
1454             Z_PresentRequest *pr = apdu->u.presentRequest;
1455             if (pr->numberOfRecordsRequested && 
1456                 *pr->numberOfRecordsRequested > m_max_record_retrieve)
1457                 *pr->numberOfRecordsRequested = m_max_record_retrieve;
1458         }
1459     }
1460 }
1461
1462 Z_Records *Yaz_Proxy::create_nonSurrogateDiagnostics(ODR odr,
1463                                                      int error,
1464                                                      const char *addinfo)
1465 {
1466     Z_Records *rec = (Z_Records *)
1467         odr_malloc (odr, sizeof(*rec));
1468     int *err = (int *)
1469         odr_malloc (odr, sizeof(*err));
1470     Z_DiagRec *drec = (Z_DiagRec *)
1471         odr_malloc (odr, sizeof(*drec));
1472     Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)
1473         odr_malloc (odr, sizeof(*dr));
1474     *err = error;
1475     rec->which = Z_Records_NSD;
1476     rec->u.nonSurrogateDiagnostic = dr;
1477     dr->diagnosticSetId =
1478         yaz_oidval_to_z3950oid (odr, CLASS_DIAGSET, VAL_BIB1);
1479     dr->condition = err;
1480     dr->which = Z_DefaultDiagFormat_v2Addinfo;
1481     dr->u.v2Addinfo = odr_strdup (odr, addinfo ? addinfo : "");
1482     return rec;
1483 }
1484
1485 Z_APDU *Yaz_Proxy::handle_query_transformation(Z_APDU *apdu)
1486 {
1487     if (apdu->which == Z_APDU_searchRequest &&
1488         apdu->u.searchRequest->query &&
1489         apdu->u.searchRequest->query->which == Z_Query_type_104 &&
1490         apdu->u.searchRequest->query->u.type_104->which == Z_External_CQL)
1491     {
1492         Z_RPNQuery *rpnquery = 0;
1493         Z_SearchRequest *sr = apdu->u.searchRequest;
1494         char *addinfo = 0;
1495         
1496         yaz_log(LOG_LOG, "%sCQL: %s", m_session_str,
1497                 sr->query->u.type_104->u.cql);
1498
1499         int r = m_cql2rpn.query_transform(sr->query->u.type_104->u.cql,
1500                                           &rpnquery, odr_encode(),
1501                                           &addinfo);
1502         if (r == -3)
1503             yaz_log(LOG_LOG, "%sNo CQL to RPN table", m_session_str);
1504         else if (r)
1505         {
1506             yaz_log(LOG_LOG, "%sCQL Conversion error %d", m_session_str, r);
1507             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1508
1509             new_apdu->u.searchResponse->referenceId = sr->referenceId;
1510             new_apdu->u.searchResponse->records =
1511                 create_nonSurrogateDiagnostics(odr_encode(),
1512                                                yaz_diag_srw_to_bib1(r),
1513                                                addinfo);
1514             *new_apdu->u.searchResponse->searchStatus = 0;
1515
1516             send_to_client(new_apdu);
1517
1518             return 0;
1519         }
1520         else
1521         {
1522             sr->query->which = Z_Query_type_1;
1523             sr->query->u.type_1 = rpnquery;
1524         }
1525         return apdu;
1526     }
1527     return apdu;
1528 }
1529
1530 Z_APDU *Yaz_Proxy::handle_query_validation(Z_APDU *apdu)
1531 {
1532     if (apdu->which == Z_APDU_searchRequest)
1533     {
1534         Z_SearchRequest *sr = apdu->u.searchRequest;
1535         int err = 0;
1536         char *addinfo = 0;
1537
1538         Yaz_ProxyConfig *cfg = check_reconfigure();
1539         if (cfg)
1540             err = cfg->check_query(odr_encode(), m_default_target,
1541                                    sr->query, &addinfo);
1542         if (err)
1543         {
1544             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1545
1546             new_apdu->u.searchResponse->referenceId = sr->referenceId;
1547             new_apdu->u.searchResponse->records =
1548                 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
1549             *new_apdu->u.searchResponse->searchStatus = 0;
1550
1551             send_to_client(new_apdu);
1552
1553             return 0;
1554         }
1555     }
1556     return apdu;
1557 }
1558
1559 Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu)
1560 {
1561     m_marcxml_flag = 0;
1562     if (apdu->which == Z_APDU_searchRequest)
1563     {
1564         Z_SearchRequest *sr = apdu->u.searchRequest;
1565         int err = 0;
1566         char *addinfo = 0;
1567         Yaz_ProxyConfig *cfg = check_reconfigure();
1568
1569         Z_RecordComposition rc_temp, *rc = 0;
1570         if (sr->smallSetElementSetNames)
1571         {
1572             rc_temp.which = Z_RecordComp_simple;
1573             rc_temp.u.simple = sr->smallSetElementSetNames;
1574             rc = &rc_temp;
1575         }
1576
1577         char *stylesheet_name = 0;
1578         if (cfg)
1579             err = cfg->check_syntax(odr_encode(),
1580                                     m_default_target,
1581                                     sr->preferredRecordSyntax, rc,
1582                                     &addinfo, &stylesheet_name, &m_schema);
1583         if (stylesheet_name)
1584         {
1585             m_parent->low_socket_close();
1586
1587 #if HAVE_XSLT
1588             if (m_stylesheet_xsp)
1589                 xsltFreeStylesheet(m_stylesheet_xsp);
1590             m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*)
1591                                                        stylesheet_name);
1592 #endif
1593             m_stylesheet_offset = 0;
1594             xfree(stylesheet_name);
1595
1596             m_parent->low_socket_open();
1597         }
1598         if (err == -1)
1599         {
1600             sr->preferredRecordSyntax =
1601                 yaz_oidval_to_z3950oid(odr_encode(), CLASS_RECSYN, VAL_USMARC);
1602             m_marcxml_flag = 1;
1603         }
1604         else if (err)
1605         {
1606             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1607             
1608             new_apdu->u.searchResponse->referenceId = sr->referenceId;
1609             new_apdu->u.searchResponse->records =
1610                 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
1611             *new_apdu->u.searchResponse->searchStatus = 0;
1612             
1613             send_to_client(new_apdu);
1614             
1615             return 0;
1616         }
1617     }
1618     else if (apdu->which == Z_APDU_presentRequest)
1619     {
1620         Z_PresentRequest *pr = apdu->u.presentRequest;
1621         int err = 0;
1622         char *addinfo = 0;
1623         Yaz_ProxyConfig *cfg = check_reconfigure();
1624
1625         char *stylesheet_name = 0;
1626         if (cfg)
1627             err = cfg->check_syntax(odr_encode(), m_default_target,
1628                                     pr->preferredRecordSyntax,
1629                                     pr->recordComposition,
1630                                     &addinfo, &stylesheet_name, &m_schema);
1631         if (stylesheet_name)
1632         {
1633             m_parent->low_socket_close();
1634
1635 #if HAVE_XSLT
1636             if (m_stylesheet_xsp)
1637                 xsltFreeStylesheet(m_stylesheet_xsp);
1638
1639             m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*)
1640                                                        stylesheet_name);
1641 #endif
1642             m_stylesheet_offset = 0;
1643             xfree(stylesheet_name);
1644
1645             m_parent->low_socket_open();
1646         }
1647         if (err == -1)
1648         {
1649             pr->preferredRecordSyntax =
1650                 yaz_oidval_to_z3950oid(odr_decode(), CLASS_RECSYN, VAL_USMARC);
1651             m_marcxml_flag = 1;
1652         }
1653         else if (err)
1654         {
1655             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1656             
1657             new_apdu->u.presentResponse->referenceId = pr->referenceId;
1658             new_apdu->u.presentResponse->records =
1659                 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
1660             *new_apdu->u.presentResponse->presentStatus =
1661                 Z_PresentStatus_failure;
1662             
1663             send_to_client(new_apdu);
1664             
1665             return 0;
1666         }
1667     }
1668     return apdu;
1669 }
1670
1671 Z_ElementSetNames *Yaz_Proxy::mk_esn_from_schema(ODR o, const char *schema)
1672 {
1673     if (!schema)
1674         return 0;
1675     Z_ElementSetNames *esn = (Z_ElementSetNames *)
1676         odr_malloc(o, sizeof(Z_ElementSetNames));
1677     esn->which = Z_ElementSetNames_generic;
1678     esn->u.generic = odr_strdup(o, schema);
1679     return esn;
1680 }
1681
1682 void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
1683 {
1684     if (m_s2z_odr_init)
1685     {
1686         odr_destroy(m_s2z_odr_init);
1687         m_s2z_odr_init = 0;
1688     }
1689     if (m_s2z_odr_search)
1690     {
1691         odr_destroy(m_s2z_odr_search);
1692         m_s2z_odr_search = 0;
1693     }
1694
1695     m_http_keepalive = 0;
1696     m_http_version = 0;
1697     if (!strcmp(hreq->version, "1.0")) 
1698     {
1699         const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
1700         if (v && !strcmp(v, "Keep-Alive"))
1701             m_http_keepalive = 1;
1702         else
1703             m_http_keepalive = 0;
1704         m_http_version = "1.0";
1705     }
1706     else
1707     {
1708         const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
1709         if (v && !strcmp(v, "close"))
1710             m_http_keepalive = 0;
1711         else
1712             m_http_keepalive = 1;
1713         m_http_version = "1.1";
1714     }
1715
1716     Z_SRW_PDU *srw_pdu = 0;
1717     Z_SOAP *soap_package = 0;
1718     char *charset = 0;
1719     Z_SRW_diagnostic *diagnostic = 0;
1720     int num_diagnostic = 0;
1721     if (yaz_srw_decode(hreq, &srw_pdu, &soap_package, odr_decode(),
1722                        &charset) == 0
1723         || yaz_sru_decode(hreq, &srw_pdu, &soap_package, odr_decode(),
1724                           &charset, &diagnostic, &num_diagnostic) == 0)
1725     {
1726         m_s2z_odr_init = odr_createmem(ODR_ENCODE);
1727         m_s2z_odr_search = odr_createmem(ODR_ENCODE);
1728         m_soap_ns = odr_strdup(m_s2z_odr_search, soap_package->ns);
1729         m_s2z_init_apdu = 0;
1730         m_s2z_search_apdu = 0;
1731         m_s2z_present_apdu = 0;
1732
1733         m_s2z_stylesheet = 0;
1734         
1735         if (srw_pdu->which == Z_SRW_searchRetrieve_request)
1736         {
1737             Z_SRW_searchRetrieveRequest *srw_req = srw_pdu->u.request;
1738
1739             m_s2z_database = odr_strdup(m_s2z_odr_init, srw_req->database);
1740             // recordXPath unsupported.
1741             if (srw_req->recordXPath)
1742             {
1743                 yaz_add_srw_diagnostic(odr_decode(),
1744                                        &diagnostic, &num_diagnostic,
1745                                        72, 0);
1746             }
1747             // sort unsupported
1748             if (srw_req->sort_type != Z_SRW_sort_type_none)
1749             {
1750                 yaz_add_srw_diagnostic(odr_decode(),
1751                                        &diagnostic, &num_diagnostic,
1752                                        80, 0);
1753             }
1754             // save stylesheet
1755             if (srw_req->stylesheet)
1756                 m_s2z_stylesheet =
1757                     odr_strdup(m_s2z_odr_init, srw_req->stylesheet);
1758                                               
1759             // set packing for response records ..
1760             if (srw_req->recordPacking &&
1761                 !strcmp(srw_req->recordPacking, "xml"))
1762                 m_s2z_packing = Z_SRW_recordPacking_XML;
1763             else
1764                 m_s2z_packing = Z_SRW_recordPacking_string;
1765
1766             if (num_diagnostic)
1767             {
1768                 Z_SRW_PDU *srw_pdu =
1769                     yaz_srw_get(odr_encode(),
1770                                 Z_SRW_searchRetrieve_response);
1771                 Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
1772                 
1773                 srw_res->diagnostics = diagnostic;
1774                 srw_res->num_diagnostics = num_diagnostic;
1775                 send_srw_response(srw_pdu);
1776                 return;
1777             }
1778
1779             // prepare search PDU
1780             m_s2z_search_apdu = zget_APDU(m_s2z_odr_search,
1781                                           Z_APDU_searchRequest);
1782             Z_SearchRequest *z_searchRequest =
1783                 m_s2z_search_apdu->u.searchRequest;
1784
1785             z_searchRequest->num_databaseNames = 1;
1786             z_searchRequest->databaseNames = (char**)
1787                 odr_malloc(m_s2z_odr_search, sizeof(char *));
1788             z_searchRequest->databaseNames[0] = odr_strdup(m_s2z_odr_search,
1789                                                            srw_req->database);
1790             
1791             // query transformation
1792             Z_Query *query = (Z_Query *)
1793                 odr_malloc(m_s2z_odr_search, sizeof(Z_Query));
1794             z_searchRequest->query = query;
1795             
1796             if (srw_req->query_type == Z_SRW_query_type_cql)
1797             {
1798                 Z_External *ext = (Z_External *) 
1799                     odr_malloc(m_s2z_odr_search, sizeof(*ext));
1800                 ext->direct_reference = 
1801                     odr_getoidbystr(m_s2z_odr_search, "1.2.840.10003.16.2");
1802                 ext->indirect_reference = 0;
1803                 ext->descriptor = 0;
1804                 ext->which = Z_External_CQL;
1805                 ext->u.cql = srw_req->query.cql;
1806                 
1807                 query->which = Z_Query_type_104;
1808                 query->u.type_104 =  ext;
1809             }
1810             else if (srw_req->query_type == Z_SRW_query_type_pqf)
1811             {
1812                 Z_RPNQuery *RPNquery;
1813                 YAZ_PQF_Parser pqf_parser;
1814                 
1815                 pqf_parser = yaz_pqf_create ();
1816                 
1817                 RPNquery = yaz_pqf_parse (pqf_parser, m_s2z_odr_search,
1818                                           srw_req->query.pqf);
1819                 if (!RPNquery)
1820                 {
1821                     const char *pqf_msg;
1822                     size_t off;
1823                     int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
1824                     yaz_log(LOG_LOG, "%*s^\n", off+4, "");
1825                     yaz_log(LOG_LOG, "Bad PQF: %s (code %d)\n", pqf_msg, code);
1826                     
1827                     send_to_srw_client_error(10, 0);
1828                     return;
1829                 }
1830                 query->which = Z_Query_type_1;
1831                 query->u.type_1 =  RPNquery;
1832                 
1833                 yaz_pqf_destroy (pqf_parser);
1834             }
1835             else
1836             {
1837                 send_to_srw_client_error(7, "query");
1838                 return;
1839             }
1840
1841             // present
1842             m_s2z_present_apdu = 0;
1843             int max = 0;
1844             if (srw_req->maximumRecords)
1845                 max = *srw_req->maximumRecords;
1846             int start = 1;
1847             if (srw_req->startRecord)
1848                 start = *srw_req->startRecord;
1849             if (max > 0)
1850             {
1851                 // Some backend, such as Voyager doesn't honor piggyback
1852                 // So we use present always (0 &&).
1853                 if (0 && start <= 1)  // Z39.50 piggyback
1854                 {
1855                     *z_searchRequest->smallSetUpperBound = max;
1856                     *z_searchRequest->mediumSetPresentNumber = max;
1857                     *z_searchRequest->largeSetLowerBound = 2000000000; // 2e9
1858
1859                     z_searchRequest->preferredRecordSyntax =
1860                         yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN,
1861                                                VAL_TEXT_XML);
1862                     if (srw_req->recordSchema)
1863                     {
1864                         z_searchRequest->smallSetElementSetNames =
1865                             z_searchRequest->mediumSetElementSetNames =
1866                             mk_esn_from_schema(m_s2z_odr_search,
1867                                                srw_req->recordSchema);
1868                     }
1869                 }
1870                 else   // Z39.50 present
1871                 {
1872                     m_s2z_present_apdu = zget_APDU(m_s2z_odr_search, 
1873                                                    Z_APDU_presentRequest);
1874                     Z_PresentRequest *z_presentRequest = 
1875                         m_s2z_present_apdu->u.presentRequest;
1876                     *z_presentRequest->resultSetStartPoint = start;
1877                     *z_presentRequest->numberOfRecordsRequested = max;
1878                     z_presentRequest->preferredRecordSyntax =
1879                         yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN,
1880                                                VAL_TEXT_XML);
1881                     if (srw_req->recordSchema)
1882                     {
1883                         z_presentRequest->recordComposition =
1884                             (Z_RecordComposition *)
1885                             odr_malloc(m_s2z_odr_search,
1886                                        sizeof(Z_RecordComposition));
1887                         z_presentRequest->recordComposition->which = 
1888                             Z_RecordComp_simple;                    
1889                         z_presentRequest->recordComposition->u.simple =
1890                             mk_esn_from_schema(m_s2z_odr_search,
1891                                                srw_req->recordSchema);
1892                     }
1893                 }
1894             }
1895             if (!m_client)
1896             {
1897                 m_s2z_init_apdu = zget_APDU(m_s2z_odr_init,
1898                                             Z_APDU_initRequest);
1899                 
1900                 // prevent m_initRequest_apdu memory from being grabbed
1901                 // in Yaz_Proxy::handle_incoming_Z_PDU
1902                 m_initRequest_apdu = m_s2z_init_apdu;
1903                 handle_incoming_Z_PDU(m_s2z_init_apdu);
1904                 return;
1905             }
1906             else
1907             {
1908                 handle_incoming_Z_PDU(m_s2z_search_apdu);
1909                 return;
1910             }
1911         }
1912         else if (srw_pdu->which == Z_SRW_explain_request)
1913         {
1914             Z_SRW_explainRequest *srw_req = srw_pdu->u.explain_request;
1915
1916             m_s2z_database = odr_strdup(m_s2z_odr_init, srw_req->database);
1917
1918             // save stylesheet
1919             if (srw_req->stylesheet)
1920                 m_s2z_stylesheet =
1921                     odr_strdup(m_s2z_odr_init, srw_req->stylesheet);
1922
1923             if (srw_req->recordPacking &&
1924                 !strcmp(srw_req->recordPacking, "xml"))
1925                 m_s2z_packing = Z_SRW_recordPacking_XML;
1926             else
1927                 m_s2z_packing = Z_SRW_recordPacking_string;
1928
1929             if (num_diagnostic)
1930             {
1931                 send_srw_explain_response(diagnostic, num_diagnostic);
1932                 return;
1933             }
1934
1935             if (!m_client)
1936             {
1937                 m_s2z_init_apdu = zget_APDU(m_s2z_odr_init,
1938                                             Z_APDU_initRequest);
1939                 
1940                 // prevent m_initRequest_apdu memory from being grabbed
1941                 // in Yaz_Proxy::handle_incoming_Z_PDU
1942                 m_initRequest_apdu = m_s2z_init_apdu;
1943                 handle_incoming_Z_PDU(m_s2z_init_apdu);
1944             }
1945             else
1946                 send_srw_explain_response(0, 0);
1947             return;
1948         }
1949         else if (srw_pdu->which == Z_SRW_scan_request)
1950         {
1951             m_s2z_database = odr_strdup(m_s2z_odr_init,
1952                                         srw_pdu->u.scan_request->database);
1953
1954             yaz_add_srw_diagnostic(odr_decode(),
1955                                    &diagnostic, &num_diagnostic,
1956                                    4, "scan");
1957             Z_SRW_PDU *srw_pdu =
1958                 yaz_srw_get(odr_encode(),
1959                             Z_SRW_scan_response);
1960             Z_SRW_scanResponse *srw_res = srw_pdu->u.scan_response;
1961             
1962             srw_res->diagnostics = diagnostic;
1963             srw_res->num_diagnostics = num_diagnostic;
1964             send_srw_response(srw_pdu);
1965             return;
1966         }
1967         else
1968         {
1969             m_s2z_database = 0;
1970
1971             send_to_srw_client_error(4, 0);
1972         }
1973     }
1974     int len = 0;
1975     Z_GDU *p = z_get_HTTP_Response(odr_encode(), 400);
1976     timeout(0);
1977     send_GDU(p, &len);
1978 }
1979
1980 void Yaz_Proxy::handle_incoming_Z_PDU(Z_APDU *apdu)
1981 {
1982     Z_ReferenceId **refid = get_referenceIdP(apdu);
1983     nmem_reset(m_referenceId_mem);
1984     if (refid && *refid)
1985     {
1986         m_referenceId = (Z_ReferenceId *)
1987             nmem_malloc(m_referenceId_mem, sizeof(*m_referenceId));
1988         m_referenceId->len = m_referenceId->size = (*refid)->len;
1989         m_referenceId->buf = (unsigned char *)
1990             nmem_malloc(m_referenceId_mem, (*refid)->len);
1991         memcpy(m_referenceId->buf, (*refid)->buf, (*refid)->len);
1992     }
1993     else
1994         m_referenceId = 0;
1995
1996     if (!m_client && m_invalid_session)
1997     {
1998         m_apdu_invalid_session = apdu;
1999         m_mem_invalid_session = odr_extract_mem(odr_decode());
2000         apdu = m_initRequest_apdu;
2001     }
2002     
2003     // Determine our client.
2004     Z_OtherInformation **oi;
2005     get_otherInfoAPDU(apdu, &oi);
2006     m_client = get_client(apdu, get_cookie(oi), get_proxy(oi));
2007     if (!m_client)
2008     {
2009         delete this;
2010         return;
2011     }
2012     m_client->m_server = this;
2013
2014     if (apdu->which == Z_APDU_initRequest)
2015     {
2016         if (apdu->u.initRequest->implementationId)
2017             yaz_log(LOG_LOG, "%simplementationId: %s",
2018                     m_session_str, apdu->u.initRequest->implementationId);
2019         if (apdu->u.initRequest->implementationName)
2020             yaz_log(LOG_LOG, "%simplementationName: %s",
2021                     m_session_str, apdu->u.initRequest->implementationName);
2022         if (apdu->u.initRequest->implementationVersion)
2023             yaz_log(LOG_LOG, "%simplementationVersion: %s",
2024                     m_session_str, apdu->u.initRequest->implementationVersion);
2025         if (m_initRequest_apdu == 0)
2026         {
2027             if (m_initRequest_mem)
2028                 nmem_destroy(m_initRequest_mem);
2029             m_initRequest_apdu = apdu;
2030             m_initRequest_mem = odr_extract_mem(odr_decode());
2031
2032             m_initRequest_preferredMessageSize = *apdu->u.initRequest->
2033                 preferredMessageSize;
2034             *apdu->u.initRequest->preferredMessageSize = 1024*1024;
2035             m_initRequest_maximumRecordSize = *apdu->u.initRequest->
2036                 maximumRecordSize;
2037             *apdu->u.initRequest->maximumRecordSize = 1024*1024;
2038
2039             // save init options for the response..
2040             m_initRequest_options = apdu->u.initRequest->options;
2041             
2042             apdu->u.initRequest->options = 
2043                 (Odr_bitmask *)nmem_malloc(m_initRequest_mem,
2044                                            sizeof(Odr_bitmask));
2045             ODR_MASK_ZERO(apdu->u.initRequest->options);
2046             int i;
2047             for (i = 0; i<= 24; i++)
2048                 ODR_MASK_SET(apdu->u.initRequest->options, i);
2049             ODR_MASK_CLEAR(apdu->u.initRequest->options,
2050                            Z_Options_negotiationModel);
2051             ODR_MASK_CLEAR(apdu->u.initRequest->options,
2052                            Z_Options_concurrentOperations);
2053
2054             // make new version
2055             m_initRequest_version = apdu->u.initRequest->protocolVersion;
2056             apdu->u.initRequest->protocolVersion = 
2057                 (Odr_bitmask *)nmem_malloc(m_initRequest_mem,
2058                                            sizeof(Odr_bitmask));
2059             ODR_MASK_ZERO(apdu->u.initRequest->protocolVersion);
2060
2061             for (i = 0; i<= 8; i++)
2062                 ODR_MASK_SET(apdu->u.initRequest->protocolVersion, i);
2063         }
2064         if (m_client->m_init_flag)
2065         {
2066             if (handle_init_response_for_invalid_session(apdu))
2067                 return;
2068             if (m_client->m_initResponse)
2069             {
2070                 Z_APDU *apdu2 = m_client->m_initResponse;
2071                 apdu2->u.initResponse->otherInfo = 0;
2072                 if (m_client->m_cookie && *m_client->m_cookie)
2073                     set_otherInformationString(apdu2, VAL_COOKIE, 1,
2074                                                m_client->m_cookie);
2075                 apdu2->u.initResponse->referenceId =
2076                     apdu->u.initRequest->referenceId;
2077                 apdu2->u.initResponse->options = m_client->m_initResponse_options;
2078                 apdu2->u.initResponse->protocolVersion = 
2079                     m_client->m_initResponse_version;
2080                 
2081                 send_to_client(apdu2);
2082                 return;
2083             }
2084         }
2085         m_client->m_init_flag = 1;
2086     }
2087     handle_max_record_retrieve(apdu);
2088
2089     if (apdu)
2090         apdu = handle_syntax_validation(apdu);
2091
2092     if (apdu)
2093         apdu = handle_query_transformation(apdu);
2094
2095     if (apdu)
2096         apdu = handle_query_validation(apdu);
2097
2098     if (apdu)
2099         apdu = result_set_optimize(apdu);
2100     if (!apdu)
2101     {
2102         m_client->timeout(m_target_idletime);  // mark it active even 
2103         // though we didn't use it
2104         return;
2105     }
2106
2107     // delete other info part from PDU before sending to target
2108     get_otherInfoAPDU(apdu, &oi);
2109     if (oi)
2110         *oi = 0;
2111
2112     if (apdu->which == Z_APDU_presentRequest &&
2113         m_client->m_resultSetStartPoint == 0)
2114     {
2115         Z_PresentRequest *pr = apdu->u.presentRequest;
2116         m_client->m_resultSetStartPoint = *pr->resultSetStartPoint;
2117         m_client->m_cache.copy_presentRequest(apdu->u.presentRequest);
2118     } else {
2119         m_client->m_resultSetStartPoint = 0;
2120     }
2121     if (m_client->send_to_target(apdu) < 0)
2122     {
2123         delete m_client;
2124         m_client = 0;
2125         delete this;
2126     }
2127     else
2128         m_client->m_waiting = 1;
2129 }
2130
2131 void Yaz_Proxy::connectNotify()
2132 {
2133 }
2134
2135 void Yaz_Proxy::shutdown()
2136 {
2137     m_invalid_session = 0;
2138     // only keep if keep_alive flag is set...
2139     if (m_client && 
2140         m_client->m_pdu_recv < m_keepalive_limit_pdu &&
2141         m_client->m_bytes_recv+m_client->m_bytes_sent < m_keepalive_limit_bw &&
2142         m_client->m_waiting == 0)
2143     {
2144         yaz_log(LOG_LOG, "%sShutdown (client to proxy) keepalive %s",
2145                  m_session_str,
2146                  m_client->get_hostname());
2147         yaz_log(LOG_LOG, "%sbw=%d pdu=%d limit-bw=%d limit-pdu=%d",
2148                 m_session_str, m_client->m_pdu_recv,
2149                 m_client->m_bytes_sent + m_client->m_bytes_recv,
2150                 m_keepalive_limit_bw, m_keepalive_limit_pdu);
2151         assert (m_client->m_waiting != 2);
2152         // Tell client (if any) that no server connection is there..
2153         m_client->m_server = 0;
2154         m_invalid_session = 0;
2155     }
2156     else if (m_client)
2157     {
2158         yaz_log (LOG_LOG, "%sShutdown (client to proxy) close %s",
2159                  m_session_str,
2160                  m_client->get_hostname());
2161         assert (m_client->m_waiting != 2);
2162         delete m_client;
2163     }
2164     else if (!m_parent)
2165     {
2166         yaz_log (LOG_LOG, "%sshutdown (client to proxy) bad state",
2167                  m_session_str);
2168         assert (m_parent);
2169     }
2170     else 
2171     {
2172         yaz_log (LOG_LOG, "%sShutdown (client to proxy)",
2173                  m_session_str);
2174     }
2175     if (m_parent)
2176         m_parent->pre_init();
2177     delete this;
2178 }
2179
2180 const char *Yaz_ProxyClient::get_session_str() 
2181 {
2182     if (!m_server)
2183         return "0 ";
2184     return m_server->get_session_str();
2185 }
2186
2187 void Yaz_ProxyClient::shutdown()
2188 {
2189     yaz_log (LOG_LOG, "%sShutdown (proxy to target) %s", get_session_str(),
2190              get_hostname());
2191     delete m_server;
2192     delete this;
2193 }
2194
2195 void Yaz_Proxy::failNotify()
2196 {
2197     inc_request_no();
2198     yaz_log (LOG_LOG, "%sConnection closed by client",
2199              get_session_str());
2200     shutdown();
2201 }
2202
2203 void Yaz_ProxyClient::failNotify()
2204 {
2205     if (m_server)
2206         m_server->inc_request_no();
2207     yaz_log (LOG_LOG, "%sConnection closed by target %s", 
2208              get_session_str(), get_hostname());
2209     shutdown();
2210 }
2211
2212 void Yaz_ProxyClient::connectNotify()
2213 {
2214     const char *s = get_session_str();
2215     const char *h = get_hostname();
2216     yaz_log (LOG_LOG, "%sConnection accepted by %s timeout=%d", s, h,
2217              m_target_idletime);
2218     timeout(m_target_idletime);
2219     if (!m_server)
2220         pre_init_client();
2221 }
2222
2223 IYaz_PDU_Observer *Yaz_ProxyClient::sessionNotify(IYaz_PDU_Observable
2224                                                   *the_PDU_Observable, int fd)
2225 {
2226     return new Yaz_ProxyClient(the_PDU_Observable, 0);
2227 }
2228
2229 Yaz_ProxyClient::~Yaz_ProxyClient()
2230 {
2231     if (m_prev)
2232         *m_prev = m_next;
2233     if (m_next)
2234         m_next->m_prev = m_prev;
2235     m_waiting = 2;     // for debugging purposes only.
2236     odr_destroy(m_init_odr);
2237     delete m_last_query;
2238     xfree (m_last_resultSetId);
2239     xfree (m_cookie);
2240 }
2241
2242 void Yaz_ProxyClient::pre_init_client()
2243 {
2244     Z_APDU *apdu = create_Z_PDU(Z_APDU_initRequest);
2245     Z_InitRequest *req = apdu->u.initRequest;
2246     
2247     int i;
2248     for (i = 0; i<= 24; i++)
2249         ODR_MASK_SET(req->options, i);
2250     ODR_MASK_CLEAR(apdu->u.initRequest->options,
2251                    Z_Options_negotiationModel);
2252     ODR_MASK_CLEAR(apdu->u.initRequest->options,
2253                    Z_Options_concurrentOperations);
2254     for (i = 0; i<= 10; i++)
2255         ODR_MASK_SET(req->protocolVersion, i);
2256
2257     if (send_to_target(apdu) < 0)
2258     {
2259         delete this;
2260     }
2261     else
2262     {
2263         m_waiting = 1;
2264         m_init_flag = 1;
2265     }
2266 }
2267
2268 void Yaz_Proxy::pre_init()
2269 {
2270     int i;
2271     const char *name = 0;
2272     const char *zurl_in_use[MAX_ZURL_PLEX];
2273     int limit_bw, limit_pdu, limit_req;
2274     int target_idletime, client_idletime;
2275     int max_clients;
2276     int keepalive_limit_bw, keepalive_limit_pdu;
2277     int pre_init;
2278     const char *cql2rpn = 0;
2279
2280     Yaz_ProxyConfig *cfg = check_reconfigure();
2281
2282     zurl_in_use[0] = 0;
2283
2284     if (m_log_mask & PROXY_LOG_APDU_CLIENT)
2285         set_APDU_yazlog(1);
2286     else
2287         set_APDU_yazlog(0);
2288
2289     for (i = 0; cfg && cfg->get_target_no(i, &name, zurl_in_use,
2290                                           &limit_bw, &limit_pdu, &limit_req,
2291                                           &target_idletime, &client_idletime,
2292                                           &max_clients, 
2293                                           &keepalive_limit_bw,
2294                                           &keepalive_limit_pdu,
2295                                           &pre_init,
2296                                           &cql2rpn) ; i++)
2297     {
2298         if (pre_init)
2299         {
2300             int j;
2301             for (j = 0; zurl_in_use[j]; j++)
2302             {
2303                 Yaz_ProxyClient *c;
2304                 int spare = 0;
2305                 int spare_waiting = 0;
2306                 int in_use = 0;
2307                 int other = 0;
2308                 for (c = m_clientPool; c; c = c->m_next)
2309                 {
2310                     if (!strcmp(zurl_in_use[j], c->get_hostname()))
2311                     {
2312                         if (c->m_cookie == 0)
2313                         {
2314                             if (c->m_server == 0)
2315                                 if (c->m_waiting)
2316                                     spare_waiting++;
2317                                 else
2318                                     spare++;
2319                             else
2320                                 in_use++;
2321                         }
2322                         else
2323                             other++;
2324                     }
2325                 }
2326                 yaz_log(LOG_LOG, "%spre-init %s %s use=%d other=%d spare=%d "
2327                         "sparew=%d preinit=%d",m_session_str,
2328                         name, zurl_in_use[j], in_use, other,
2329                         spare, spare_waiting, pre_init);
2330                 if (spare + spare_waiting < pre_init)
2331                 {
2332                     c = new Yaz_ProxyClient(m_PDU_Observable->clone(), this);
2333                     c->m_next = m_clientPool;
2334                     if (c->m_next)
2335                         c->m_next->m_prev = &c->m_next;
2336                     m_clientPool = c;
2337                     c->m_prev = &m_clientPool;
2338                     
2339                     if (m_log_mask & PROXY_LOG_APDU_SERVER)
2340                         c->set_APDU_yazlog(1);
2341                     else
2342                         c->set_APDU_yazlog(0);
2343
2344                     if (c->client(zurl_in_use[j]))
2345                     {
2346                         timeout(60);
2347                         delete c;
2348                         return;
2349                     }
2350                     c->timeout(30);
2351                     c->m_waiting = 1;
2352                     c->m_target_idletime = target_idletime;
2353                     c->m_seqno = m_seqno++;
2354                 }
2355             }
2356         }
2357     }
2358 }
2359
2360 void Yaz_Proxy::timeoutNotify()
2361 {
2362     if (m_parent)
2363     {
2364         if (m_bw_hold_PDU)
2365         {
2366             timeout(m_client_idletime);
2367             Z_GDU *apdu = m_bw_hold_PDU;
2368             m_bw_hold_PDU = 0;
2369             
2370             if (apdu->which == Z_GDU_Z3950)
2371                 handle_incoming_Z_PDU(apdu->u.z3950);
2372             else if (apdu->which == Z_GDU_HTTP_Request)
2373                 handle_incoming_HTTP(apdu->u.HTTP_Request);
2374         }
2375         else if (m_stylesheet_nprl)
2376             convert_xsl_delay();
2377         else
2378         {
2379             inc_request_no();
2380
2381             yaz_log (LOG_LOG, "%sTimeout (client to proxy)", m_session_str);
2382             shutdown();
2383         }
2384     }
2385     else
2386     {
2387         timeout(600);
2388         pre_init();
2389     }
2390 }
2391
2392 void Yaz_Proxy::markInvalid()
2393 {
2394     m_client = 0;
2395     m_invalid_session = 1;
2396 }
2397
2398 void Yaz_ProxyClient::timeoutNotify()
2399 {
2400     if (m_server)
2401         m_server->inc_request_no();
2402
2403     yaz_log (LOG_LOG, "%sTimeout (proxy to target) %s", get_session_str(),
2404              get_hostname());
2405     m_waiting = 1;
2406     m_root->pre_init();
2407     if (m_server && m_init_flag)
2408     {
2409         // target timed out in a session that was properly initialized
2410         // server object stay alive but we mark it as invalid so it
2411         // gets initialized again
2412         m_server->markInvalid();
2413         m_server = 0;
2414     }
2415     shutdown();
2416 }
2417
2418 Yaz_ProxyClient::Yaz_ProxyClient(IYaz_PDU_Observable *the_PDU_Observable,
2419                                  Yaz_Proxy *parent) :
2420     Yaz_Z_Assoc (the_PDU_Observable)
2421 {
2422     m_cookie = 0;
2423     m_next = 0;
2424     m_prev = 0;
2425     m_init_flag = 0;
2426     m_last_query = 0;
2427     m_last_resultSetId = 0;
2428     m_last_resultCount = 0;
2429     m_last_ok = 0;
2430     m_sr_transform = 0;
2431     m_waiting = 0;
2432     m_init_odr = odr_createmem (ODR_DECODE);
2433     m_initResponse = 0;
2434     m_initResponse_options = 0;
2435     m_initResponse_version = 0;
2436     m_initResponse_preferredMessageSize = 0;
2437     m_initResponse_maximumRecordSize = 0;
2438     m_resultSetStartPoint = 0;
2439     m_bytes_sent = m_bytes_recv = 0;
2440     m_pdu_recv = 0;
2441     m_server = 0;
2442     m_seqno = 0;
2443     m_target_idletime = 600;
2444     m_root = parent;
2445 }
2446
2447 const char *Yaz_Proxy::option(const char *name, const char *value)
2448 {
2449     if (!strcmp (name, "optimize")) {
2450         if (value) {
2451             xfree (m_optimize); 
2452             m_optimize = xstrdup (value);
2453         }
2454         return m_optimize;
2455     }
2456     return 0;
2457 }
2458
2459 void Yaz_ProxyClient::recv_HTTP_response(Z_HTTP_Response *apdu, int len)
2460 {
2461
2462 }
2463
2464 void Yaz_ProxyClient::recv_GDU(Z_GDU *apdu, int len)
2465 {
2466     if (apdu->which == Z_GDU_Z3950)
2467         recv_Z_PDU(apdu->u.z3950, len);
2468     else if (apdu->which == Z_GDU_HTTP_Response)
2469         recv_HTTP_response(apdu->u.HTTP_Response, len);
2470     else
2471         shutdown();
2472 }
2473
2474 int Yaz_Proxy::handle_init_response_for_invalid_session(Z_APDU *apdu)
2475 {
2476     if (!m_invalid_session)
2477         return 0;
2478     m_invalid_session = 0;
2479     handle_incoming_Z_PDU(m_apdu_invalid_session);
2480     assert (m_mem_invalid_session);
2481     nmem_destroy(m_mem_invalid_session);
2482     m_mem_invalid_session = 0;
2483     return 1;
2484 }
2485
2486 void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len)
2487 {
2488     m_bytes_recv += len;
2489
2490     m_pdu_recv++;
2491     m_waiting = 0;
2492     if (m_root->get_log_mask() & PROXY_LOG_REQ_SERVER)
2493         yaz_log (LOG_LOG, "%sReceiving %s from %s %d bytes", get_session_str(),
2494                  apdu_name(apdu), get_hostname(), len);
2495     if (apdu->which == Z_APDU_initResponse)
2496     {
2497         if (!m_server)  // if this is a pre init session , check for more
2498             m_root->pre_init();
2499         NMEM nmem = odr_extract_mem (odr_decode());
2500         odr_reset (m_init_odr);
2501         nmem_transfer (m_init_odr->mem, nmem);
2502         m_initResponse = apdu;
2503         m_initResponse_options = apdu->u.initResponse->options;
2504         m_initResponse_version = apdu->u.initResponse->protocolVersion;
2505         m_initResponse_preferredMessageSize = 
2506             *apdu->u.initResponse->preferredMessageSize;
2507         m_initResponse_maximumRecordSize = 
2508             *apdu->u.initResponse->maximumRecordSize;
2509
2510         Z_InitResponse *ir = apdu->u.initResponse;
2511         char *im0 = ir->implementationName;
2512         
2513         char *im1 = (char*) 
2514             odr_malloc(m_init_odr, 20 + (im0 ? strlen(im0) : 0));
2515         *im1 = '\0';
2516         if (im0)
2517         {
2518             strcat(im1, im0);
2519             strcat(im1, " ");
2520         }
2521         strcat(im1, "(YAZ Proxy)");
2522         ir->implementationName = im1;
2523
2524         nmem_destroy (nmem);
2525
2526         if (m_server && m_server->handle_init_response_for_invalid_session(apdu))
2527             return;
2528     }
2529     if (apdu->which == Z_APDU_searchResponse)
2530     {
2531         Z_SearchResponse *sr = apdu->u.searchResponse;
2532         m_last_resultCount = *sr->resultCount;
2533         int status = *sr->searchStatus;
2534         if (status && (!sr->records || sr->records->which == Z_Records_DBOSD))
2535         {
2536             m_last_ok = 1;
2537             
2538             if (sr->records && sr->records->which == Z_Records_DBOSD)
2539             {
2540                 m_cache.add(odr_decode(),
2541                             sr->records->u.databaseOrSurDiagnostics, 1,
2542                             *sr->resultCount);
2543             }
2544         }
2545     }
2546     if (apdu->which == Z_APDU_presentResponse)
2547     {
2548         Z_PresentResponse *pr = apdu->u.presentResponse;
2549         if (m_sr_transform)
2550         {
2551             m_sr_transform = 0;
2552             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
2553             Z_SearchResponse *sr = new_apdu->u.searchResponse;
2554             sr->referenceId = pr->referenceId;
2555             *sr->resultCount = m_last_resultCount;
2556             sr->records = pr->records;
2557             sr->nextResultSetPosition = pr->nextResultSetPosition;
2558             sr->numberOfRecordsReturned = pr->numberOfRecordsReturned;
2559             apdu = new_apdu;
2560         }
2561         if (pr->records && 
2562             pr->records->which == Z_Records_DBOSD && m_resultSetStartPoint)
2563         {
2564             m_cache.add(odr_decode(),
2565                         pr->records->u.databaseOrSurDiagnostics,
2566                         m_resultSetStartPoint, -1);
2567             m_resultSetStartPoint = 0;
2568         }
2569     }
2570     if (m_cookie)
2571         set_otherInformationString (apdu, VAL_COOKIE, 1, m_cookie);
2572     if (m_server)
2573     {
2574         m_server->send_to_client(apdu);
2575     }
2576     if (apdu->which == Z_APDU_close)
2577     {
2578         shutdown();
2579     }
2580 }
2581
2582 void Yaz_Proxy::low_socket_close()
2583 {
2584 #if WIN32
2585 #else
2586     int i;
2587     for (i = 0; i<NO_SPARE_SOLARIS_FD; i++)
2588         if  (m_lo_fd[i] >= 0)
2589             ::close(m_lo_fd[i]);
2590 #endif
2591 }
2592
2593 void Yaz_Proxy::low_socket_open()
2594 {
2595 #if WIN32
2596 #else
2597     int i;
2598     for (i = 0; i<NO_SPARE_SOLARIS_FD; i++)
2599         m_lo_fd[i] = open("/dev/null", O_RDONLY);
2600 #endif
2601 }
2602
2603 int Yaz_Proxy::server(const char *addr)
2604 {
2605     int r = Yaz_Z_Assoc::server(addr);
2606     if (!r)
2607     {
2608         yaz_log(LOG_LOG, "%sStarted proxy " 
2609 #ifdef VERSION
2610             VERSION 
2611 #endif
2612             " on %s", m_session_str, addr);
2613         timeout(1);
2614     }
2615     return r;
2616 }
2617