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