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