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