USMARC to MARCXML conversion. Check for reconfigure in more places
[yazpp-moved-to-github.git] / src / yaz-proxy.cpp
1 /*
2  * Copyright (c) 1998-2003, Index Data.
3  * See the file LICENSE for details.
4  * 
5  * $Id: yaz-proxy.cpp,v 1.56 2003-10-10 17:58:29 adam Exp $
6  */
7
8 #include <assert.h>
9 #include <time.h>
10
11 #include <yaz/marcdisp.h>
12 #include <yaz/yaz-iconv.h>
13 #include <yaz/log.h>
14 #include <yaz/diagbib1.h>
15 #include <yaz++/proxy.h>
16
17 static const char *apdu_name(Z_APDU *apdu)
18 {
19     switch (apdu->which)
20     {
21     case Z_APDU_initRequest:
22         return "initRequest";
23     case Z_APDU_initResponse:
24         return "initResponse";
25     case Z_APDU_searchRequest:
26         return "searchRequest";
27     case Z_APDU_searchResponse:
28         return "searchResponse";
29     case Z_APDU_presentRequest:
30         return "presentRequest";
31     case Z_APDU_presentResponse:
32         return "presentResponse";
33     case Z_APDU_deleteResultSetRequest:
34         return "deleteResultSetRequest";
35     case Z_APDU_deleteResultSetResponse:
36         return "deleteResultSetResponse";
37     case Z_APDU_scanRequest:
38         return "scanRequest";
39     case Z_APDU_scanResponse:
40         return "scanResponse";
41     case Z_APDU_sortRequest:
42         return "sortRequest";
43     case Z_APDU_sortResponse:
44         return "sortResponse";
45     case Z_APDU_extendedServicesRequest:
46         return "extendedServicesRequest";
47     case Z_APDU_extendedServicesResponse:
48         return "extendedServicesResponse";
49     case Z_APDU_close:
50         return "close";
51     }
52     return "other";
53 }
54
55 Yaz_Proxy::Yaz_Proxy(IYaz_PDU_Observable *the_PDU_Observable) :
56     Yaz_Z_Assoc(the_PDU_Observable), m_bw_stat(60), m_pdu_stat(60)
57 {
58     m_PDU_Observable = the_PDU_Observable;
59     m_client = 0;
60     m_parent = 0;
61     m_clientPool = 0;
62     m_seqno = 1;
63     m_keepalive_limit_bw = 500000;
64     m_keepalive_limit_pdu = 1000;
65     m_proxyTarget = 0;
66     m_default_target = 0;
67     m_proxy_authentication = 0;
68     m_max_clients = 150;
69     m_seed = time(0);
70     m_client_idletime = 600;
71     m_target_idletime = 600;
72     m_optimize = xstrdup ("1");
73     strcpy(m_session_str, "x");
74     m_session_no=0;
75     m_bytes_sent = m_bytes_recv = 0;
76     m_bw_hold_PDU = 0;
77     m_bw_max = 0;
78     m_pdu_max = 0;
79     m_max_record_retrieve = 0;
80     m_reconfig_flag = 0;
81     m_config_fname = 0;
82     m_request_no = 0;
83     m_invalid_session = 0;
84     m_config = 0;
85     m_marcxml_flag = 0;
86 }
87
88 Yaz_Proxy::~Yaz_Proxy()
89 {
90     yaz_log(LOG_LOG, "%sClosed %d/%d sent/recv bytes total", m_session_str,
91             m_bytes_sent, m_bytes_recv);
92     xfree (m_proxyTarget);
93     xfree (m_default_target);
94     xfree (m_proxy_authentication);
95     xfree (m_optimize);
96     delete m_config;
97 }
98
99 int Yaz_Proxy::set_config(const char *config)
100 {
101     delete m_config;
102     m_config = new Yaz_ProxyConfig();
103     xfree(m_config_fname);
104     m_config_fname = xstrdup(config);
105     int r = m_config->read_xml(config);
106     return r;
107 }
108
109 void Yaz_Proxy::set_default_target(const char *target)
110 {
111     xfree (m_default_target);
112     m_default_target = 0;
113     if (target)
114         m_default_target = (char *) xstrdup (target);
115 }
116
117 void Yaz_Proxy::set_proxy_authentication (const char *auth)
118 {
119     xfree (m_proxy_authentication);
120     m_proxy_authentication = 0;
121     if (auth)
122         m_proxy_authentication = (char *) xstrdup (auth);
123 }
124
125 Yaz_ProxyConfig *Yaz_Proxy::check_reconfigure()
126 {
127     if (m_parent)
128         return m_parent->check_reconfigure();
129
130     Yaz_ProxyConfig *cfg = m_config;
131     if (m_reconfig_flag)
132     {
133         yaz_log(LOG_LOG, "reconfigure");
134         yaz_log_reopen();
135         if (m_config_fname && cfg)
136         {
137             yaz_log(LOG_LOG, "reconfigure config %s", m_config_fname);
138             int r = cfg->read_xml(m_config_fname);
139             if (r)
140                 yaz_log(LOG_WARN, "reconfigure failed");
141         }
142         else
143             yaz_log(LOG_LOG, "reconfigure");
144         m_reconfig_flag = 0;
145     }
146     return cfg;
147 }
148
149 IYaz_PDU_Observer *Yaz_Proxy::sessionNotify(IYaz_PDU_Observable
150                                             *the_PDU_Observable, int fd)
151 {
152     check_reconfigure();
153     Yaz_Proxy *new_proxy = new Yaz_Proxy(the_PDU_Observable);
154     new_proxy->m_parent = this;
155     new_proxy->m_config = 0;
156     new_proxy->m_config_fname = 0;
157     new_proxy->timeout(m_client_idletime);
158     new_proxy->m_target_idletime = m_target_idletime;
159     new_proxy->set_default_target(m_default_target);
160     new_proxy->set_APDU_log(get_APDU_log());
161     new_proxy->set_proxy_authentication(m_proxy_authentication);
162     sprintf(new_proxy->m_session_str, "%ld:%d ", (long) time(0), m_session_no);
163     m_session_no++;
164     yaz_log (LOG_LOG, "%sNew session %s", new_proxy->m_session_str,
165              the_PDU_Observable->getpeername());
166     return new_proxy;
167 }
168
169 char *Yaz_Proxy::get_cookie(Z_OtherInformation **otherInfo)
170 {
171     int oid[OID_SIZE];
172     Z_OtherInformationUnit *oi;
173     struct oident ent;
174     ent.proto = PROTO_Z3950;
175     ent.oclass = CLASS_USERINFO;
176     ent.value = (oid_value) VAL_COOKIE;
177     assert (oid_ent_to_oid (&ent, oid));
178
179     if (oid_ent_to_oid (&ent, oid) && 
180         (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
181         oi->which == Z_OtherInfo_characterInfo)
182         return oi->information.characterInfo;
183     return 0;
184 }
185
186 char *Yaz_Proxy::get_proxy(Z_OtherInformation **otherInfo)
187 {
188     int oid[OID_SIZE];
189     Z_OtherInformationUnit *oi;
190     struct oident ent;
191     ent.proto = PROTO_Z3950;
192     ent.oclass = CLASS_USERINFO;
193     ent.value = (oid_value) VAL_PROXY;
194     if (oid_ent_to_oid (&ent, oid) &&
195         (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
196         oi->which == Z_OtherInfo_characterInfo)
197         return oi->information.characterInfo;
198     return 0;
199 }
200
201 const char *Yaz_Proxy::load_balance(const char **url)
202 {
203     int zurl_in_use[MAX_ZURL_PLEX];
204     Yaz_ProxyClient *c;
205     int i;
206
207     for (i = 0; i<MAX_ZURL_PLEX; i++)
208         zurl_in_use[i] = 0;
209     for (c = m_parent->m_clientPool; c; c = c->m_next)
210     {
211         for (i = 0; url[i]; i++)
212             if (!strcmp(url[i], c->get_hostname()))
213                 zurl_in_use[i]++;
214     }
215     int min = 100000;
216     const char *ret = 0;
217     for (i = 0; url[i]; i++)
218     {
219         yaz_log(LOG_DEBUG, "%szurl=%s use=%d",
220                 m_session_str, url[i], zurl_in_use[i]);
221         if (min > zurl_in_use[i])
222         {
223             ret = url[i];
224             min = zurl_in_use[i];
225         }
226     }
227     return ret;
228 }
229
230 Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu)
231 {
232     assert (m_parent);
233     Yaz_Proxy *parent = m_parent;
234     Z_OtherInformation **oi;
235     Yaz_ProxyClient *c = m_client;
236     
237     get_otherInfoAPDU(apdu, &oi);
238     char *cookie = get_cookie(oi);
239
240     if (!m_proxyTarget)
241     {
242         const char *url[MAX_ZURL_PLEX];
243         const char *proxy_host = get_proxy(oi);
244         Yaz_ProxyConfig *cfg = check_reconfigure();
245         if (proxy_host)
246         {
247             xfree(m_default_target);
248             m_default_target = xstrdup(proxy_host);
249             proxy_host = m_default_target;
250         }
251         int client_idletime = -1;
252         if (cfg)
253             cfg->get_target_info(proxy_host, url, &m_bw_max,
254                                  &m_pdu_max, &m_max_record_retrieve,
255                                  &m_target_idletime, &client_idletime,
256                                  &parent->m_max_clients,
257                                  &m_keepalive_limit_bw,
258                                  &m_keepalive_limit_pdu);
259         if (client_idletime != -1)
260         {
261             m_client_idletime = client_idletime;
262             timeout(m_client_idletime);
263         }
264         if (!url[0])
265         {
266             yaz_log(LOG_LOG, "%sNo default target", m_session_str);
267             return 0;
268         }
269         // we don't handle multiplexing for cookie session, so we just
270         // pick the first one in this case (anonymous users will be able
271         // to use any backend)
272         if (cookie && *cookie)
273             m_proxyTarget = (char*) xstrdup(url[0]);
274         else
275             m_proxyTarget = (char*) xstrdup(load_balance(url));
276     }
277     if (cookie && *cookie)
278     {
279         Yaz_ProxyClient *cc = 0;
280         
281         for (c = parent->m_clientPool; c; c = c->m_next)
282         {
283             assert (c->m_prev);
284             assert (*c->m_prev == c);
285             if (c->m_cookie && !strcmp(cookie,c->m_cookie) &&
286                 !strcmp(m_proxyTarget, c->get_hostname()))
287             {
288                 cc = c;
289             }
290         }
291         if (cc)
292         {
293             // found it in cache
294             c = cc;
295             // The following handles "cancel"
296             // If connection is busy (waiting for PDU) and
297             // we have an initRequest we can safely do re-open
298             if (c->m_waiting && apdu->which == Z_APDU_initRequest)
299             {
300                 yaz_log (LOG_LOG, "%s REOPEN target=%s", m_session_str,
301                          c->get_hostname());
302                 c->close();
303                 c->m_init_flag = 0;
304
305                 c->m_last_ok = 0;
306                 c->m_cache.clear();
307                 c->m_last_resultCount = 0;
308                 c->m_sr_transform = 0;
309                 c->m_waiting = 0;
310                 c->m_resultSetStartPoint = 0;
311                 if (c->client(m_proxyTarget))
312                 {
313                     delete c;
314                     return 0;
315                 }
316                 c->timeout(m_target_idletime); 
317             }
318             c->m_seqno = parent->m_seqno;
319             if (c->m_server && c->m_server != this)
320                 c->m_server->m_client = 0;
321             c->m_server = this;
322             (parent->m_seqno)++;
323             yaz_log (LOG_DEBUG, "get_client 1 %p %p", this, c);
324             return c;
325         }
326     }
327     else if (!c)
328     {
329         Yaz_ProxyClient *cc = 0;
330         
331         for (c = parent->m_clientPool; c; c = c->m_next)
332         {
333             assert (c->m_prev);
334             assert (*c->m_prev == c);
335             if (c->m_server == 0 && c->m_cookie == 0 && 
336                 !strcmp(m_proxyTarget, c->get_hostname()))
337             {
338                 cc = c;
339             }
340         }
341         if (cc)
342         {
343             // found it in cache
344             c = cc;
345
346             yaz_log (LOG_LOG, "%sREUSE %d %d %s",
347                      m_session_str,
348                      c->m_seqno, parent->m_seqno, c->get_hostname());
349
350             c->m_seqno = parent->m_seqno;
351             assert(c->m_server == 0);
352             c->m_server = this;
353             
354             (parent->m_seqno)++;
355             return c;
356         }
357     }
358     if (!m_client)
359     {
360         if (apdu->which != Z_APDU_initRequest)
361         {
362             yaz_log (LOG_LOG, "no first INIT!");
363             return 0;
364         }
365         Z_InitRequest *initRequest = apdu->u.initRequest;
366
367         if (!initRequest->idAuthentication)
368         {
369             if (m_proxy_authentication)
370             {
371                 initRequest->idAuthentication =
372                     (Z_IdAuthentication *)
373                     odr_malloc (odr_encode(),
374                                 sizeof(*initRequest->idAuthentication));
375                 initRequest->idAuthentication->which =
376                     Z_IdAuthentication_open;
377                 initRequest->idAuthentication->u.open =
378                     odr_strdup (odr_encode(), m_proxy_authentication);
379             }
380         }
381
382         // go through list of clients - and find the lowest/oldest one.
383         Yaz_ProxyClient *c_min = 0;
384         int min_seq = -1;
385         int no_of_clients = 0;
386         if (parent->m_clientPool)
387             yaz_log (LOG_DEBUG, "Existing sessions");
388         for (c = parent->m_clientPool; c; c = c->m_next)
389         {
390             yaz_log (LOG_DEBUG, " Session %-3d wait=%d %s cookie=%s", c->m_seqno,
391                                c->m_waiting, c->get_hostname(),
392                                c->m_cookie ? c->m_cookie : "");
393             no_of_clients++;
394             if (min_seq < 0 || c->m_seqno < min_seq)
395             {
396                 min_seq = c->m_seqno;
397                 c_min = c;
398             }
399         }
400         if (no_of_clients >= parent->m_max_clients)
401         {
402             c = c_min;
403             if (c->m_waiting || strcmp(m_proxyTarget, c->get_hostname()))
404             {
405                 yaz_log (LOG_LOG, "%sMAXCLIENTS Destroy %d",
406                          m_session_str, c->m_seqno);
407                 if (c->m_server && c->m_server != this)
408                     delete c->m_server;
409                 c->m_server = 0;
410             }
411             else
412             {
413                 yaz_log (LOG_LOG, "%sMAXCLIENTS Reuse %d %d %s",
414                          m_session_str,
415                          c->m_seqno, parent->m_seqno, c->get_hostname());
416                 xfree (c->m_cookie);
417                 c->m_cookie = 0;
418                 if (cookie)
419                     c->m_cookie = xstrdup(cookie);
420                 c->m_seqno = parent->m_seqno;
421                 if (c->m_server && c->m_server != this)
422                 {
423                     c->m_server->m_client = 0;
424                     delete c->m_server;
425                 }
426                 (parent->m_seqno)++;
427                 return c;
428             }
429         }
430         else
431         {
432             yaz_log (LOG_LOG, "%sNEW %d %s",
433                      m_session_str, parent->m_seqno, m_proxyTarget);
434             c = new Yaz_ProxyClient(m_PDU_Observable->clone());
435             c->m_next = parent->m_clientPool;
436             if (c->m_next)
437                 c->m_next->m_prev = &c->m_next;
438             parent->m_clientPool = c;
439             c->m_prev = &parent->m_clientPool;
440         }
441
442         xfree (c->m_cookie);
443         c->m_cookie = 0;
444         if (cookie)
445             c->m_cookie = xstrdup(cookie);
446
447         c->m_seqno = parent->m_seqno;
448         c->m_init_flag = 0;
449         c->m_last_resultCount = 0;
450         c->m_last_ok = 0;
451         c->m_cache.clear();
452         c->m_sr_transform = 0;
453         c->m_waiting = 0;
454         c->m_resultSetStartPoint = 0;
455         (parent->m_seqno)++;
456         if (c->client(m_proxyTarget))
457         {
458             delete c;
459             return 0;
460         }
461         c->timeout(30);
462
463     }
464     yaz_log (LOG_DEBUG, "get_client 3 %p %p", this, c);
465     return c;
466 }
467
468 void Yaz_Proxy::display_diagrecs(Z_DiagRec **pp, int num)
469 {
470     int i;
471     for (i = 0; i<num; i++)
472     {
473         oident *ent;
474         Z_DefaultDiagFormat *r;
475         Z_DiagRec *p = pp[i];
476         if (p->which != Z_DiagRec_defaultFormat)
477         {
478             yaz_log(LOG_LOG, "%sError no diagnostics", m_session_str);
479             return;
480         }
481         else
482             r = p->u.defaultFormat;
483         if (!(ent = oid_getentbyoid(r->diagnosticSetId)) ||
484             ent->oclass != CLASS_DIAGSET || ent->value != VAL_BIB1)
485             yaz_log(LOG_LOG, "%sError unknown diagnostic set", m_session_str);
486         switch (r->which)
487         {
488         case Z_DefaultDiagFormat_v2Addinfo:
489             yaz_log(LOG_LOG, "%sError %d %s:%s",
490                     m_session_str,
491                     *r->condition, diagbib1_str(*r->condition),
492                     r->u.v2Addinfo);
493             break;
494         case Z_DefaultDiagFormat_v3Addinfo:
495             yaz_log(LOG_LOG, "%sError %d %s:%s",
496                     m_session_str,
497                     *r->condition, diagbib1_str(*r->condition),
498                     r->u.v3Addinfo);
499             break;
500         }
501     }
502 }
503
504 void Yaz_Proxy::convert_to_marcxml(Z_NamePlusRecordList *p)
505 {
506     int i;
507
508     yaz_marc_t mt = yaz_marc_create();
509     yaz_marc_xml(mt, YAZ_MARC_MARCXML);
510     for (i = 0; i < p->num_records; i++)
511     {
512         Z_NamePlusRecord *npr = p->records[i];
513         if (npr->which == Z_NamePlusRecord_databaseRecord)
514         {
515             Z_External *r = npr->u.databaseRecord;
516             if (r->which == Z_External_octet)
517             {
518                 int rlen;
519                 char *result;
520                 if (yaz_marc_decode_buf(mt, (char*) r->u.octet_aligned->buf,
521                                         r->u.octet_aligned->len,
522                                         &result, &rlen))
523                 {
524                     yaz_iconv_t cd = yaz_iconv_open("UTF-8", "MARC-8");
525                     WRBUF wrbuf = wrbuf_alloc();
526                     
527                     char outbuf[120];
528                     size_t inbytesleft = rlen;
529                     const char *inp = result;
530                     while (cd && inbytesleft)
531                     {
532                         size_t outbytesleft = sizeof(outbuf);
533                         char *outp = outbuf;
534                         size_t r;
535                         
536                         r = yaz_iconv (cd, (char**) &inp,
537                                        &inbytesleft,
538                                        &outp, &outbytesleft);
539                         if (r == (size_t) (-1))
540                         {
541                             int e = yaz_iconv_error(cd);
542                             if (e != YAZ_ICONV_E2BIG)
543                             {
544                                 yaz_log(LOG_WARN, "conversion failure");
545                                 break;
546                             }
547                         }
548                         wrbuf_write(wrbuf, outbuf, outp - outbuf);
549                     }
550                     if (cd)
551                         yaz_iconv_close(cd);
552
553                     npr->u.databaseRecord = z_ext_record(odr_encode(),
554                                                          VAL_TEXT_XML,
555                                                          wrbuf_buf(wrbuf),
556                                                          wrbuf_len(wrbuf));
557                     wrbuf_free(wrbuf, 1);
558                 }
559             }
560         }
561     }
562     yaz_marc_destroy(mt);
563 }
564
565 int Yaz_Proxy::send_to_client(Z_APDU *apdu)
566 {
567     int len = 0;
568     if (apdu->which == Z_APDU_searchResponse)
569     {
570         Z_SearchResponse *sr = apdu->u.searchResponse;
571         Z_Records *p = sr->records;
572         if (p && p->which == Z_Records_NSD)
573         {
574             Z_DiagRec dr, *dr_p = &dr;
575             dr.which = Z_DiagRec_defaultFormat;
576             dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
577
578             display_diagrecs(&dr_p, 1);
579         }
580         else
581         {
582             if (m_marcxml_flag && p && p->which == Z_Records_DBOSD)
583                 convert_to_marcxml(p->u.databaseOrSurDiagnostics);
584             if (sr->resultCount)
585             {
586                 yaz_log(LOG_LOG, "%s%d hits", m_session_str,
587                         *sr->resultCount);
588                 if (*sr->resultCount < 0)
589                     m_invalid_session = 1;
590             }
591         }
592     }
593     else if (apdu->which == Z_APDU_presentResponse)
594     {
595         Z_PresentResponse *sr = apdu->u.presentResponse;
596         Z_Records *p = sr->records;
597         if (p && p->which == Z_Records_NSD)
598         {
599             Z_DiagRec dr, *dr_p = &dr;
600             dr.which = Z_DiagRec_defaultFormat;
601             dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
602
603             display_diagrecs(&dr_p, 1);
604         }
605         if (m_marcxml_flag && p && p->which == Z_Records_DBOSD)
606             convert_to_marcxml(p->u.databaseOrSurDiagnostics);
607     }
608     int r = send_Z_PDU(apdu, &len);
609     yaz_log (LOG_DEBUG, "%sSending %s to client %d bytes", m_session_str,
610              apdu_name(apdu), len);
611     m_bytes_sent += len;
612     m_bw_stat.add_bytes(len);
613     return r;
614 }
615
616 int Yaz_ProxyClient::send_to_target(Z_APDU *apdu)
617 {
618     int len = 0;
619     int r = send_Z_PDU(apdu, &len);
620     yaz_log (LOG_DEBUG, "%sSending %s to %s %d bytes",
621              get_session_str(),
622              apdu_name(apdu), get_hostname(), len);
623     m_bytes_sent += len;
624     return r;
625 }
626
627 Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
628 {
629     if (*m_parent->m_optimize == '0')
630         return apdu;      // don't optimize result sets..
631     if (apdu->which == Z_APDU_presentRequest)
632     {
633         Z_PresentRequest *pr = apdu->u.presentRequest;
634         Z_NamePlusRecordList *npr;
635         int toget = *pr->numberOfRecordsRequested;
636         int start = *pr->resultSetStartPoint;
637
638         if (m_client->m_last_resultSetId &&
639             !strcmp(m_client->m_last_resultSetId, pr->resultSetId))
640         {
641             if (m_client->m_cache.lookup (odr_encode(), &npr, start, toget,
642                                           pr->preferredRecordSyntax,
643                                           pr->recordComposition))
644             {
645                 yaz_log (LOG_LOG, "%sReturned cached records for present request", 
646                          m_session_str);
647                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
648                 new_apdu->u.presentResponse->referenceId = pr->referenceId;
649                 
650                 new_apdu->u.presentResponse->numberOfRecordsReturned
651                     = odr_intdup(odr_encode(), toget);
652                                                                  
653                 new_apdu->u.presentResponse->records = (Z_Records*)
654                     odr_malloc(odr_encode(), sizeof(Z_Records));
655                 new_apdu->u.presentResponse->records->which = Z_Records_DBOSD;
656                 new_apdu->u.presentResponse->records->u.databaseOrSurDiagnostics = npr;
657                 new_apdu->u.presentResponse->nextResultSetPosition =
658                     odr_intdup(odr_encode(), start+toget);
659
660                 send_to_client(new_apdu);
661                 return 0;
662             }
663         }
664     }
665
666     if (apdu->which != Z_APDU_searchRequest)
667         return apdu;
668     Z_SearchRequest *sr = apdu->u.searchRequest;
669     Yaz_Z_Query *this_query = new Yaz_Z_Query;
670     Yaz_Z_Databases this_databases;
671
672     this_databases.set(sr->num_databaseNames, (const char **)
673                        sr->databaseNames);
674     
675     this_query->set_Z_Query(sr->query);
676
677     char query_str[120];
678     this_query->print(query_str, sizeof(query_str)-1);
679     yaz_log(LOG_LOG, "%sQuery %s", m_session_str, query_str);
680
681     if (m_client->m_last_ok && m_client->m_last_query &&
682         m_client->m_last_query->match(this_query) &&
683         !strcmp(m_client->m_last_resultSetId, sr->resultSetName) &&
684         m_client->m_last_databases.match(this_databases))
685     {
686         delete this_query;
687         if (m_client->m_last_resultCount > *sr->smallSetUpperBound &&
688             m_client->m_last_resultCount < *sr->largeSetLowerBound)
689         {
690             Z_NamePlusRecordList *npr;
691             int toget = *sr->mediumSetPresentNumber;
692             Z_RecordComposition *comp = 0;
693
694             if (toget > m_client->m_last_resultCount)
695                 toget = m_client->m_last_resultCount;
696             
697             if (sr->mediumSetElementSetNames)
698             {
699                 comp = (Z_RecordComposition *)
700                     odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
701                 comp->which = Z_RecordComp_simple;
702                 comp->u.simple = sr->mediumSetElementSetNames;
703             }
704  
705             if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget,
706                                           sr->preferredRecordSyntax, comp))
707             {
708                 yaz_log (LOG_LOG, "%sReturned cached records for medium set",
709                          m_session_str);
710                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
711                 new_apdu->u.searchResponse->referenceId = sr->referenceId;
712                 new_apdu->u.searchResponse->resultCount =
713                     &m_client->m_last_resultCount;
714                 
715                 new_apdu->u.searchResponse->numberOfRecordsReturned
716                     = odr_intdup(odr_encode(), toget);
717                                                         
718                 new_apdu->u.searchResponse->presentStatus =
719                     odr_intdup(odr_encode(), Z_PresentStatus_success);
720                 new_apdu->u.searchResponse->records = (Z_Records*)
721                     odr_malloc(odr_encode(), sizeof(Z_Records));
722                 new_apdu->u.searchResponse->records->which = Z_Records_DBOSD;
723                 new_apdu->u.searchResponse->records->u.databaseOrSurDiagnostics = npr;
724                 new_apdu->u.searchResponse->nextResultSetPosition =
725                     odr_intdup(odr_encode(), toget+1);
726                 send_to_client(new_apdu);
727                 return 0;
728             }
729             else
730             {
731                 // medium Set
732                 // send present request (medium size)
733                 yaz_log (LOG_LOG, "%sOptimizing search for medium set",
734                          m_session_str);
735
736                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
737                 Z_PresentRequest *pr = new_apdu->u.presentRequest;
738                 pr->referenceId = sr->referenceId;
739                 pr->resultSetId = sr->resultSetName;
740                 pr->preferredRecordSyntax = sr->preferredRecordSyntax;
741                 *pr->numberOfRecordsRequested = toget;
742                 pr->recordComposition = comp;
743                 m_client->m_sr_transform = 1;
744                 return new_apdu;
745             }
746         }
747         else if (m_client->m_last_resultCount >= *sr->largeSetLowerBound ||
748             m_client->m_last_resultCount <= 0)
749         {
750             // large set. Return pseudo-search response immediately
751             yaz_log (LOG_LOG, "%sOptimizing search for large set",
752                      m_session_str);
753             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
754             new_apdu->u.searchResponse->referenceId = sr->referenceId;
755             new_apdu->u.searchResponse->resultCount =
756                 &m_client->m_last_resultCount;
757             send_to_client(new_apdu);
758             return 0;
759         }
760         else
761         {
762             Z_NamePlusRecordList *npr;
763             int toget = m_client->m_last_resultCount;
764             Z_RecordComposition *comp = 0;
765             // small set
766             // send a present request (small set)
767             
768             if (sr->smallSetElementSetNames)
769             {
770                 comp = (Z_RecordComposition *)
771                     odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
772                 comp->which = Z_RecordComp_simple;
773                 comp->u.simple = sr->smallSetElementSetNames;
774             }
775
776             if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget,
777                                           sr->preferredRecordSyntax, comp))
778             {
779                 yaz_log (LOG_LOG, "%sReturned cached records for small set",
780                          m_session_str);
781                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
782                 new_apdu->u.searchResponse->referenceId = sr->referenceId;
783                 new_apdu->u.searchResponse->resultCount =
784                     &m_client->m_last_resultCount;
785                 
786                 new_apdu->u.searchResponse->numberOfRecordsReturned
787                     = odr_intdup(odr_encode(), toget);
788                                                                  
789                 new_apdu->u.searchResponse->presentStatus =
790                     odr_intdup(odr_encode(), Z_PresentStatus_success);
791                 new_apdu->u.searchResponse->records = (Z_Records*)
792                     odr_malloc(odr_encode(), sizeof(Z_Records));
793                 new_apdu->u.searchResponse->records->which = Z_Records_DBOSD;
794                 new_apdu->u.searchResponse->records->u.databaseOrSurDiagnostics = npr;
795                 new_apdu->u.searchResponse->nextResultSetPosition =
796                     odr_intdup(odr_encode(), toget+1);
797                 send_to_client(new_apdu);
798                 return 0;
799             }
800             else
801             {
802                 yaz_log (LOG_LOG, "%sOptimizing search for small set",
803                          m_session_str);
804                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
805                 Z_PresentRequest *pr = new_apdu->u.presentRequest;
806                 pr->referenceId = sr->referenceId;
807                 pr->resultSetId = sr->resultSetName;
808                 pr->preferredRecordSyntax = sr->preferredRecordSyntax;
809                 *pr->numberOfRecordsRequested = toget;
810                 pr->recordComposition = comp;
811                 m_client->m_sr_transform = 1;
812                 return new_apdu;
813             }
814         }
815     }
816     else  // query doesn't match
817     {
818         delete m_client->m_last_query;
819         m_client->m_last_query = this_query;
820         m_client->m_last_ok = 0;
821         m_client->m_cache.clear();
822         m_client->m_resultSetStartPoint = 0;
823
824         xfree (m_client->m_last_resultSetId);
825         m_client->m_last_resultSetId = xstrdup (sr->resultSetName);
826
827         m_client->m_last_databases.set(sr->num_databaseNames,
828                                        (const char **) sr->databaseNames);
829     }
830     return apdu;
831 }
832
833
834 void Yaz_Proxy::recv_Z_PDU(Z_APDU *apdu, int len)
835 {
836     char *cp = strchr(m_session_str, ' ');
837     m_request_no++;
838     if (cp)
839         sprintf(cp+1, "%d ", m_request_no);
840
841     int reduce = 0;
842     m_bytes_recv += len;
843     
844     yaz_log (LOG_DEBUG, "%sReceiving %s from client %d bytes", m_session_str,
845              apdu_name(apdu), len);
846
847     if (m_bw_hold_PDU)     // double incoming PDU. shutdown now.
848         shutdown();
849
850     m_bw_stat.add_bytes(len);
851     m_pdu_stat.add_bytes(1);
852
853     int bw_total = m_bw_stat.get_total();
854     int pdu_total = m_pdu_stat.get_total();
855
856     yaz_log(LOG_LOG, "%sstat bw=%d pdu=%d limit-bw=%d limit-pdu=%d",
857             m_session_str, bw_total, pdu_total, m_bw_max, m_pdu_max);
858     if (m_bw_max)
859     {
860         if (bw_total > m_bw_max)
861         {
862             reduce = (bw_total/m_bw_max);
863         }
864     }
865     if (m_pdu_max)
866     {
867         if (pdu_total > m_pdu_max)
868         {
869             int nreduce = (60/m_pdu_max);
870             reduce = (reduce > nreduce) ? reduce : nreduce;
871         }
872     }
873     if (reduce)  
874     {
875         yaz_log(LOG_LOG, "%sLimit delay=%d", m_session_str, reduce);
876         m_bw_hold_PDU = apdu;  // save PDU and signal "on hold"
877         timeout(reduce);       // call us reduce seconds later
878     }
879     else
880         recv_Z_PDU_0(apdu);    // all fine. Proceed receive PDU as usual
881 }
882
883 void Yaz_Proxy::handle_max_record_retrieve(Z_APDU *apdu)
884 {
885     if (m_max_record_retrieve)
886     {
887         if (apdu->which == Z_APDU_presentRequest)
888         {
889             Z_PresentRequest *pr = apdu->u.presentRequest;
890             if (pr->numberOfRecordsRequested && 
891                 *pr->numberOfRecordsRequested > m_max_record_retrieve)
892                 *pr->numberOfRecordsRequested = m_max_record_retrieve;
893         }
894     }
895 }
896
897 Z_Records *Yaz_Proxy::create_nonSurrogateDiagnostics(ODR odr,
898                                                      int error,
899                                                      const char *addinfo)
900 {
901     Z_Records *rec = (Z_Records *)
902         odr_malloc (odr, sizeof(*rec));
903     int *err = (int *)
904         odr_malloc (odr, sizeof(*err));
905     Z_DiagRec *drec = (Z_DiagRec *)
906         odr_malloc (odr, sizeof(*drec));
907     Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)
908         odr_malloc (odr, sizeof(*dr));
909     *err = error;
910     rec->which = Z_Records_NSD;
911     rec->u.nonSurrogateDiagnostic = dr;
912     dr->diagnosticSetId =
913         yaz_oidval_to_z3950oid (odr, CLASS_DIAGSET, VAL_BIB1);
914     dr->condition = err;
915     dr->which = Z_DefaultDiagFormat_v2Addinfo;
916     dr->u.v2Addinfo = odr_strdup (odr, addinfo ? addinfo : "");
917     return rec;
918 }
919
920 Z_APDU *Yaz_Proxy::handle_query_validation(Z_APDU *apdu)
921 {
922     if (apdu->which == Z_APDU_searchRequest)
923     {
924         Z_SearchRequest *sr = apdu->u.searchRequest;
925         int err = 0;
926         char *addinfo = 0;
927
928         Yaz_ProxyConfig *cfg = check_reconfigure();
929         if (cfg)
930             err = cfg->check_query(odr_encode(), m_default_target,
931                                    sr->query, &addinfo);
932         if (err)
933         {
934             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
935
936             new_apdu->u.searchResponse->referenceId = sr->referenceId;
937             new_apdu->u.searchResponse->records =
938                 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
939             *new_apdu->u.searchResponse->searchStatus = 0;
940
941             send_to_client(new_apdu);
942
943             return 0;
944         }
945     }
946     return apdu;
947 }
948
949 Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu)
950 {
951     m_marcxml_flag = 0;
952     if (apdu->which == Z_APDU_searchRequest)
953     {
954         Z_SearchRequest *sr = apdu->u.searchRequest;
955         if (*sr->smallSetUpperBound > 0 || *sr->largeSetLowerBound > 1)
956         {
957             int err = 0;
958             char *addinfo = 0;
959             Yaz_ProxyConfig *cfg = check_reconfigure();
960
961             if (cfg)
962                 err = cfg->check_syntax(odr_encode(),
963                                         m_default_target,
964                                         sr->preferredRecordSyntax,
965                                         &addinfo);
966             if (err == -1)
967             {
968                 sr->preferredRecordSyntax =
969                     yaz_oidval_to_z3950oid(odr_decode(), CLASS_RECSYN,
970                                            VAL_USMARC);
971                 m_marcxml_flag = 1;
972             }
973             else if (err)
974             {
975                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
976                 
977                 new_apdu->u.searchResponse->referenceId = sr->referenceId;
978                 new_apdu->u.searchResponse->records =
979                     create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
980                 *new_apdu->u.searchResponse->searchStatus = 0;
981                 
982                 send_to_client(new_apdu);
983                 
984                 return 0;
985             }
986         }
987     }
988     else if (apdu->which == Z_APDU_presentRequest)
989     {
990         Z_PresentRequest *pr = apdu->u.presentRequest;
991         int err = 0;
992         char *addinfo = 0;
993         Yaz_ProxyConfig *cfg = check_reconfigure();
994
995         if (cfg)
996             err = cfg->check_syntax(odr_encode(), m_default_target,
997                                     pr->preferredRecordSyntax,
998                                     &addinfo);
999         if (err == -1)
1000         {
1001             pr->preferredRecordSyntax =
1002                 yaz_oidval_to_z3950oid(odr_decode(), CLASS_RECSYN,
1003                                        VAL_USMARC);
1004             m_marcxml_flag = 1;
1005         }
1006         else if (err)
1007         {
1008             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1009             
1010             new_apdu->u.presentResponse->referenceId = pr->referenceId;
1011             new_apdu->u.presentResponse->records =
1012                 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
1013             *new_apdu->u.presentResponse->presentStatus =
1014                 Z_PresentStatus_failure;
1015             
1016             send_to_client(new_apdu);
1017             
1018             return 0;
1019         }
1020     }
1021     return apdu;
1022 }
1023
1024 void Yaz_Proxy::recv_Z_PDU_0(Z_APDU *apdu)
1025 {
1026     // Determine our client.
1027     m_client = get_client(apdu);
1028     if (!m_client)
1029     {
1030         delete this;
1031         return;
1032     }
1033     m_client->m_server = this;
1034
1035     if (apdu->which == Z_APDU_initRequest)
1036     {
1037         if (apdu->u.initRequest->implementationId)
1038             yaz_log(LOG_LOG, "%simplementationId: %s",
1039                     m_session_str, apdu->u.initRequest->implementationId);
1040         if (apdu->u.initRequest->implementationName)
1041             yaz_log(LOG_LOG, "%simplementationName: %s",
1042                     m_session_str, apdu->u.initRequest->implementationName);
1043         if (apdu->u.initRequest->implementationVersion)
1044             yaz_log(LOG_LOG, "%simplementationVersion: %s",
1045                     m_session_str, apdu->u.initRequest->implementationVersion);
1046         if (m_client->m_init_flag)
1047         {
1048             Z_APDU *apdu = m_client->m_initResponse;
1049             apdu->u.initResponse->otherInfo = 0;
1050             if (m_client->m_cookie && *m_client->m_cookie)
1051                 set_otherInformationString(apdu, VAL_COOKIE, 1,
1052                                            m_client->m_cookie);
1053             send_to_client(apdu);
1054             return;
1055         }
1056         m_client->m_init_flag = 1;
1057     }
1058     handle_max_record_retrieve(apdu);
1059
1060     if (apdu)
1061         apdu = handle_syntax_validation(apdu);
1062
1063     if (apdu)
1064         apdu = handle_query_validation(apdu);
1065
1066     if (apdu)
1067         apdu = result_set_optimize(apdu);
1068     if (!apdu)
1069     {
1070         m_client->timeout(m_target_idletime);  // mark it active even 
1071         // though we didn't use it
1072         return;
1073     }
1074
1075     // delete other info part from PDU before sending to target
1076     Z_OtherInformation **oi;
1077     get_otherInfoAPDU(apdu, &oi);
1078     if (oi)
1079         *oi = 0;
1080
1081     if (apdu->which == Z_APDU_presentRequest &&
1082         m_client->m_resultSetStartPoint == 0)
1083     {
1084         Z_PresentRequest *pr = apdu->u.presentRequest;
1085         m_client->m_resultSetStartPoint = *pr->resultSetStartPoint;
1086         m_client->m_cache.copy_presentRequest(apdu->u.presentRequest);
1087     } else {
1088         m_client->m_resultSetStartPoint = 0;
1089     }
1090     if (m_client->send_to_target(apdu) < 0)
1091     {
1092         delete m_client;
1093         m_client = 0;
1094         delete this;
1095     }
1096     else
1097         m_client->m_waiting = 1;
1098 }
1099
1100 void Yaz_Proxy::connectNotify()
1101 {
1102 }
1103
1104 void Yaz_Proxy::shutdown()
1105 {
1106     // only keep if keep_alive flag is set...
1107     if (m_client && 
1108         !m_invalid_session &&
1109         m_client->m_pdu_recv < m_keepalive_limit_pdu &&
1110         m_client->m_bytes_recv+m_client->m_bytes_sent < m_keepalive_limit_bw &&
1111         m_client->m_waiting == 0)
1112     {
1113         yaz_log(LOG_LOG, "%sShutdown (client to proxy) keepalive %s",
1114                  m_session_str,
1115                  m_client->get_hostname());
1116         yaz_log(LOG_LOG, "%sbw=%d pdu=%d limit-bw=%d limit-pdu=%d",
1117                 m_session_str, m_client->m_pdu_recv,
1118                 m_client->m_bytes_sent + m_client->m_bytes_recv,
1119                 m_keepalive_limit_bw, m_keepalive_limit_pdu);
1120         assert (m_client->m_waiting != 2);
1121         // Tell client (if any) that no server connection is there..
1122         m_client->m_server = 0;
1123     }
1124     else if (m_client)
1125     {
1126         yaz_log (LOG_LOG, "%sShutdown (client to proxy) close %s",
1127                  m_session_str,
1128                  m_client->get_hostname());
1129         assert (m_client->m_waiting != 2);
1130         delete m_client;
1131     }
1132     else if (!m_parent)
1133     {
1134         yaz_log (LOG_LOG, "%sshutdown (client to proxy) bad state",
1135                  m_session_str);
1136         assert (m_parent);
1137     }
1138     else 
1139     {
1140         yaz_log (LOG_LOG, "%sShutdown (client to proxy)",
1141                  m_session_str);
1142     }
1143     delete this;
1144 }
1145
1146 const char *Yaz_ProxyClient::get_session_str() 
1147 {
1148     if (!m_server)
1149         return "0 ";
1150     return m_server->get_session_str();
1151 }
1152
1153 void Yaz_ProxyClient::shutdown()
1154 {
1155     yaz_log (LOG_LOG, "%sShutdown (proxy to target) %s", get_session_str(),
1156              get_hostname());
1157     delete m_server;
1158     delete this;
1159 }
1160
1161 void Yaz_Proxy::failNotify()
1162 {
1163     yaz_log (LOG_LOG, "%sConnection closed by client",
1164              get_session_str());
1165     shutdown();
1166 }
1167
1168 void Yaz_ProxyClient::failNotify()
1169 {
1170     yaz_log (LOG_LOG, "%sConnection closed by target %s", 
1171              get_session_str(), get_hostname());
1172     shutdown();
1173 }
1174
1175 void Yaz_ProxyClient::connectNotify()
1176 {
1177     yaz_log (LOG_LOG, "%sConnection accepted by %s", get_session_str(),
1178              get_hostname());
1179     int to;
1180     if (m_server)
1181         to = m_server->get_target_idletime();
1182     else
1183         to = 600;
1184     timeout(to);
1185 }
1186
1187 IYaz_PDU_Observer *Yaz_ProxyClient::sessionNotify(IYaz_PDU_Observable
1188                                                   *the_PDU_Observable, int fd)
1189 {
1190     return new Yaz_ProxyClient(the_PDU_Observable);
1191 }
1192
1193 Yaz_ProxyClient::~Yaz_ProxyClient()
1194 {
1195     if (m_prev)
1196         *m_prev = m_next;
1197     if (m_next)
1198         m_next->m_prev = m_prev;
1199     m_waiting = 2;     // for debugging purposes only.
1200     odr_destroy(m_init_odr);
1201     delete m_last_query;
1202     xfree (m_last_resultSetId);
1203     xfree (m_cookie);
1204 }
1205
1206 void Yaz_Proxy::timeoutNotify()
1207 {
1208     if (m_bw_hold_PDU)
1209     {
1210         timeout(m_client_idletime);
1211         Z_APDU *apdu = m_bw_hold_PDU;
1212         m_bw_hold_PDU = 0;
1213         recv_Z_PDU_0(apdu);
1214     }
1215     else
1216     {
1217         yaz_log (LOG_LOG, "%sTimeout (client to proxy)", m_session_str);
1218         shutdown();
1219     }
1220 }
1221
1222 void Yaz_ProxyClient::timeoutNotify()
1223 {
1224     yaz_log (LOG_LOG, "%sTimeout (proxy to target) %s", get_session_str(),
1225              get_hostname());
1226     shutdown();
1227 }
1228
1229 Yaz_ProxyClient::Yaz_ProxyClient(IYaz_PDU_Observable *the_PDU_Observable) :
1230     Yaz_Z_Assoc (the_PDU_Observable)
1231 {
1232     m_cookie = 0;
1233     m_next = 0;
1234     m_prev = 0;
1235     m_init_flag = 0;
1236     m_last_query = 0;
1237     m_last_resultSetId = 0;
1238     m_last_resultCount = 0;
1239     m_last_ok = 0;
1240     m_sr_transform = 0;
1241     m_waiting = 0;
1242     m_init_odr = odr_createmem (ODR_DECODE);
1243     m_initResponse = 0;
1244     m_resultSetStartPoint = 0;
1245     m_bytes_sent = m_bytes_recv = 0;
1246     m_pdu_recv = 0;
1247 }
1248
1249 const char *Yaz_Proxy::option(const char *name, const char *value)
1250 {
1251     if (!strcmp (name, "optimize")) {
1252         if (value) {
1253             xfree (m_optimize); 
1254             m_optimize = xstrdup (value);
1255         }
1256         return m_optimize;
1257     }
1258     return 0;
1259 }
1260
1261 void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len)
1262 {
1263     m_bytes_recv += len;
1264     m_pdu_recv++;
1265     m_waiting = 0;
1266     yaz_log (LOG_DEBUG, "%sReceiving %s from %s %d bytes", get_session_str(),
1267              apdu_name(apdu), get_hostname(), len);
1268     if (apdu->which == Z_APDU_initResponse)
1269     {
1270         NMEM nmem = odr_extract_mem (odr_decode());
1271         odr_reset (m_init_odr);
1272         nmem_transfer (m_init_odr->mem, nmem);
1273         m_initResponse = apdu;
1274
1275         Z_InitResponse *ir = apdu->u.initResponse;
1276         char *im0 = ir->implementationName;
1277         
1278         char *im1 = (char*) 
1279             odr_malloc(m_init_odr, 20 + (im0 ? strlen(im0) : 0));
1280         *im1 = '\0';
1281         if (im0)
1282         {
1283             strcat(im1, im0);
1284             strcat(im1, " ");
1285         }
1286         strcat(im1, "(YAZ Proxy)");
1287         ir->implementationName = im1;
1288
1289         nmem_destroy (nmem);
1290     }
1291     if (apdu->which == Z_APDU_searchResponse)
1292     {
1293         Z_SearchResponse *sr = apdu->u.searchResponse;
1294         m_last_resultCount = *sr->resultCount;
1295         int status = *sr->searchStatus;
1296         if (status && (!sr->records || sr->records->which == Z_Records_DBOSD))
1297         {
1298             m_last_ok = 1;
1299             
1300             if (sr->records && sr->records->which == Z_Records_DBOSD)
1301             {
1302                 m_cache.add(odr_decode(),
1303                             sr->records->u.databaseOrSurDiagnostics, 1,
1304                             *sr->resultCount);
1305             }
1306         }
1307     }
1308     if (apdu->which == Z_APDU_presentResponse)
1309     {
1310         Z_PresentResponse *pr = apdu->u.presentResponse;
1311         if (m_sr_transform)
1312         {
1313             m_sr_transform = 0;
1314             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1315             Z_SearchResponse *sr = new_apdu->u.searchResponse;
1316             sr->referenceId = pr->referenceId;
1317             *sr->resultCount = m_last_resultCount;
1318             sr->records = pr->records;
1319             sr->nextResultSetPosition = pr->nextResultSetPosition;
1320             sr->numberOfRecordsReturned = pr->numberOfRecordsReturned;
1321             apdu = new_apdu;
1322         }
1323         if (pr->records && 
1324             pr->records->which == Z_Records_DBOSD && m_resultSetStartPoint)
1325         {
1326             m_cache.add(odr_decode(),
1327                         pr->records->u.databaseOrSurDiagnostics,
1328                         m_resultSetStartPoint, -1);
1329             m_resultSetStartPoint = 0;
1330         }
1331     }
1332     if (m_cookie)
1333         set_otherInformationString (apdu, VAL_COOKIE, 1, m_cookie);
1334     if (m_server)
1335     {
1336         m_server->send_to_client(apdu);
1337     }
1338     if (apdu->which == Z_APDU_close)
1339     {
1340         shutdown();
1341     }
1342 }