2 * Copyright (c) 1998-2003, Index Data.
3 * See the file LICENSE for details.
5 * $Id: yaz-proxy.cpp,v 1.52 2003-10-08 09:32:49 adam Exp $
12 #include <yaz/diagbib1.h>
13 #include <yaz++/proxy.h>
15 static const char *apdu_name(Z_APDU *apdu)
19 case Z_APDU_initRequest:
21 case Z_APDU_initResponse:
22 return "initResponse";
23 case Z_APDU_searchRequest:
24 return "searchRequest";
25 case Z_APDU_searchResponse:
26 return "searchResponse";
27 case Z_APDU_presentRequest:
28 return "presentRequest";
29 case Z_APDU_presentResponse:
30 return "presentResponse";
31 case Z_APDU_deleteResultSetRequest:
32 return "deleteResultSetRequest";
33 case Z_APDU_deleteResultSetResponse:
34 return "deleteResultSetResponse";
35 case Z_APDU_scanRequest:
37 case Z_APDU_scanResponse:
38 return "scanResponse";
39 case Z_APDU_sortRequest:
41 case Z_APDU_sortResponse:
42 return "sortResponse";
43 case Z_APDU_extendedServicesRequest:
44 return "extendedServicesRequest";
45 case Z_APDU_extendedServicesResponse:
46 return "extendedServicesResponse";
53 Yaz_Proxy::Yaz_Proxy(IYaz_PDU_Observable *the_PDU_Observable) :
54 Yaz_Z_Assoc(the_PDU_Observable), m_bw_stat(60), m_pdu_stat(60)
56 m_PDU_Observable = the_PDU_Observable;
64 m_proxy_authentication = 0;
67 m_client_idletime = 600;
68 m_target_idletime = 600;
69 m_optimize = xstrdup ("1");
70 strcpy(m_session_str, "x");
72 m_bytes_sent = m_bytes_recv = 0;
76 m_max_record_retrieve = 0;
79 Yaz_Proxy::~Yaz_Proxy()
81 yaz_log(LOG_LOG, "%s Closed %d/%d sent/recv bytes total", m_session_str,
82 m_bytes_sent, m_bytes_recv);
83 xfree (m_proxyTarget);
84 xfree (m_default_target);
85 xfree (m_proxy_authentication);
89 int Yaz_Proxy::set_config(const char *config)
91 int r = m_config.read_xml(config);
95 void Yaz_Proxy::set_default_target(const char *target)
97 xfree (m_default_target);
100 m_default_target = (char *) xstrdup (target);
103 void Yaz_Proxy::set_proxy_authentication (const char *auth)
105 xfree (m_proxy_authentication);
106 m_proxy_authentication = 0;
108 m_proxy_authentication = (char *) xstrdup (auth);
111 IYaz_PDU_Observer *Yaz_Proxy::sessionNotify(IYaz_PDU_Observable
112 *the_PDU_Observable, int fd)
114 Yaz_Proxy *new_proxy = new Yaz_Proxy(the_PDU_Observable);
115 new_proxy->m_parent = this;
116 new_proxy->m_config = m_config;
117 new_proxy->timeout(m_client_idletime);
118 new_proxy->m_target_idletime = m_target_idletime;
119 new_proxy->set_default_target(m_default_target);
120 new_proxy->set_APDU_log(get_APDU_log());
121 new_proxy->set_proxy_authentication(m_proxy_authentication);
122 sprintf(new_proxy->m_session_str, "%ld:%d", (long) time(0), m_session_no);
124 yaz_log (LOG_LOG, "%s New session %s", new_proxy->m_session_str,
125 the_PDU_Observable->getpeername());
129 char *Yaz_Proxy::get_cookie(Z_OtherInformation **otherInfo)
132 Z_OtherInformationUnit *oi;
134 ent.proto = PROTO_Z3950;
135 ent.oclass = CLASS_USERINFO;
136 ent.value = (oid_value) VAL_COOKIE;
137 assert (oid_ent_to_oid (&ent, oid));
139 if (oid_ent_to_oid (&ent, oid) &&
140 (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
141 oi->which == Z_OtherInfo_characterInfo)
142 return oi->information.characterInfo;
146 char *Yaz_Proxy::get_proxy(Z_OtherInformation **otherInfo)
149 Z_OtherInformationUnit *oi;
151 ent.proto = PROTO_Z3950;
152 ent.oclass = CLASS_USERINFO;
153 ent.value = (oid_value) VAL_PROXY;
154 if (oid_ent_to_oid (&ent, oid) &&
155 (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
156 oi->which == Z_OtherInfo_characterInfo)
157 return oi->information.characterInfo;
161 const char *Yaz_Proxy::load_balance(const char **url)
163 int zurl_in_use[MAX_ZURL_PLEX];
167 for (i = 0; i<MAX_ZURL_PLEX; i++)
169 for (c = m_parent->m_clientPool; c; c = c->m_next)
171 for (i = 0; url[i]; i++)
172 if (!strcmp(url[i], c->get_hostname()))
177 for (i = 0; url[i]; i++)
179 yaz_log(LOG_LOG, "%s zurl=%s use=%d",
180 m_session_str, url[i], zurl_in_use[i]);
181 if (min > zurl_in_use[i])
184 min = zurl_in_use[i];
190 Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu)
193 Yaz_Proxy *parent = m_parent;
194 Z_OtherInformation **oi;
195 Yaz_ProxyClient *c = m_client;
197 get_otherInfoAPDU(apdu, &oi);
198 char *cookie = get_cookie(oi);
202 const char *url[MAX_ZURL_PLEX];
203 const char *proxy_host = get_proxy(oi);
206 xfree(m_default_target);
207 m_default_target = xstrdup(proxy_host);
208 proxy_host = m_default_target;
211 int client_idletime = -1;
212 m_config.get_target_info(proxy_host, url, &m_keepalive, &m_bw_max,
213 &m_pdu_max, &m_max_record_retrieve,
214 &m_target_idletime, &client_idletime,
215 &parent->m_max_clients);
216 if (client_idletime != -1)
218 m_client_idletime = client_idletime;
219 timeout(m_client_idletime);
223 yaz_log(LOG_LOG, "%s No default target", m_session_str);
226 m_proxyTarget = (char*) xstrdup(load_balance(url));
228 if (cookie && *cookie)
230 Yaz_ProxyClient *cc = 0;
232 for (c = parent->m_clientPool; c; c = c->m_next)
235 assert (*c->m_prev == c);
236 if (c->m_cookie && !strcmp(cookie,c->m_cookie) &&
237 !strcmp(m_proxyTarget, c->get_hostname()))
246 // The following handles "cancel"
247 // If connection is busy (waiting for PDU) and
248 // we have an initRequest we can safely do re-open
249 if (c->m_waiting && apdu->which == Z_APDU_initRequest)
251 yaz_log (LOG_LOG, "%s REOPEN target=%s", m_session_str,
254 c->client(m_proxyTarget);
259 c->m_last_resultCount = 0;
260 c->m_sr_transform = 0;
262 c->m_resultSetStartPoint = 0;
263 c->timeout(m_target_idletime);
265 c->m_seqno = parent->m_seqno;
266 if (c->m_server && c->m_server != this)
267 c->m_server->m_client = 0;
270 yaz_log (LOG_DEBUG, "get_client 1 %p %p", this, c);
276 Yaz_ProxyClient *cc = 0;
278 for (c = parent->m_clientPool; c; c = c->m_next)
281 assert (*c->m_prev == c);
282 if (c->m_server == 0 && c->m_cookie == 0 &&
283 !strcmp(m_proxyTarget, c->get_hostname()))
293 yaz_log (LOG_LOG, "%s REUSE %d %d %s",
295 c->m_seqno, parent->m_seqno, c->get_hostname());
297 c->m_seqno = parent->m_seqno;
298 assert(c->m_server == 0);
307 if (apdu->which != Z_APDU_initRequest)
309 yaz_log (LOG_LOG, "no first INIT!");
312 Z_InitRequest *initRequest = apdu->u.initRequest;
314 if (!initRequest->idAuthentication)
316 if (m_proxy_authentication)
318 initRequest->idAuthentication =
319 (Z_IdAuthentication *)
320 odr_malloc (odr_encode(),
321 sizeof(*initRequest->idAuthentication));
322 initRequest->idAuthentication->which =
323 Z_IdAuthentication_open;
324 initRequest->idAuthentication->u.open =
325 odr_strdup (odr_encode(), m_proxy_authentication);
329 // go through list of clients - and find the lowest/oldest one.
330 Yaz_ProxyClient *c_min = 0;
332 int no_of_clients = 0;
333 if (parent->m_clientPool)
334 yaz_log (LOG_LOG, "Existing sessions");
335 for (c = parent->m_clientPool; c; c = c->m_next)
337 yaz_log (LOG_LOG, " Session %-3d wait=%d %s cookie=%s", c->m_seqno,
338 c->m_waiting, c->get_hostname(),
339 c->m_cookie ? c->m_cookie : "");
341 if (min_seq < 0 || c->m_seqno < min_seq)
343 min_seq = c->m_seqno;
347 if (no_of_clients >= parent->m_max_clients)
350 if (c->m_waiting || strcmp(m_proxyTarget, c->get_hostname()))
352 yaz_log (LOG_LOG, "%s MAXCLIENTS Destroy %d",
353 m_session_str, c->m_seqno);
354 if (c->m_server && c->m_server != this)
360 yaz_log (LOG_LOG, "%s MAXCLIENTS Reuse %d %d %s",
362 c->m_seqno, parent->m_seqno, c->get_hostname());
366 c->m_cookie = xstrdup(cookie);
367 c->m_seqno = parent->m_seqno;
368 if (c->m_server && c->m_server != this)
370 c->m_server->m_client = 0;
379 yaz_log (LOG_LOG, "%s NEW %d %s",
380 m_session_str, parent->m_seqno, m_proxyTarget);
381 c = new Yaz_ProxyClient(m_PDU_Observable->clone());
382 c->m_next = parent->m_clientPool;
384 c->m_next->m_prev = &c->m_next;
385 parent->m_clientPool = c;
386 c->m_prev = &parent->m_clientPool;
392 c->m_cookie = xstrdup(cookie);
394 c->m_seqno = parent->m_seqno;
395 c->client(m_proxyTarget);
397 c->m_last_resultCount = 0;
400 c->m_sr_transform = 0;
402 c->m_resultSetStartPoint = 0;
407 yaz_log (LOG_DEBUG, "get_client 3 %p %p", this, c);
411 void Yaz_Proxy::display_diagrecs(Z_DiagRec **pp, int num)
414 for (i = 0; i<num; i++)
417 Z_DefaultDiagFormat *r;
418 Z_DiagRec *p = pp[i];
419 if (p->which != Z_DiagRec_defaultFormat)
421 yaz_log(LOG_LOG, "%s Error no diagnostics", m_session_str);
425 r = p->u.defaultFormat;
426 if (!(ent = oid_getentbyoid(r->diagnosticSetId)) ||
427 ent->oclass != CLASS_DIAGSET || ent->value != VAL_BIB1)
428 yaz_log(LOG_LOG, "%s Error unknown diagnostic set", m_session_str);
431 case Z_DefaultDiagFormat_v2Addinfo:
432 yaz_log(LOG_LOG, "%s Error %d %s:%s",
434 *r->condition, diagbib1_str(*r->condition),
437 case Z_DefaultDiagFormat_v3Addinfo:
438 yaz_log(LOG_LOG, "%s Error %d %s:%s",
440 *r->condition, diagbib1_str(*r->condition),
447 int Yaz_Proxy::send_to_client(Z_APDU *apdu)
450 if (apdu->which == Z_APDU_searchResponse)
452 Z_SearchResponse *sr = apdu->u.searchResponse;
453 Z_Records *p = sr->records;
454 if (p && p->which == Z_Records_NSD)
456 Z_DiagRec dr, *dr_p = &dr;
457 dr.which = Z_DiagRec_defaultFormat;
458 dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
460 display_diagrecs(&dr_p, 1);
465 yaz_log(LOG_LOG, "%s %d hits", m_session_str,
469 else if (apdu->which == Z_APDU_presentResponse)
471 Z_PresentResponse *sr = apdu->u.presentResponse;
472 Z_Records *p = sr->records;
473 if (p && p->which == Z_Records_NSD)
475 Z_DiagRec dr, *dr_p = &dr;
476 dr.which = Z_DiagRec_defaultFormat;
477 dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
479 display_diagrecs(&dr_p, 1);
482 int r = send_Z_PDU(apdu, &len);
483 yaz_log (LOG_LOG, "%s Sending %s to client %d bytes", m_session_str,
484 apdu_name(apdu), len);
486 m_bw_stat.add_bytes(len);
490 int Yaz_ProxyClient::send_to_target(Z_APDU *apdu)
493 int r = send_Z_PDU(apdu, &len);
494 yaz_log (LOG_LOG, "%s Sending %s to %s %d bytes",
496 apdu_name(apdu), get_hostname(), len);
501 Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
503 if (*m_parent->m_optimize == '0')
504 return apdu; // don't optimize result sets..
505 if (apdu->which == Z_APDU_presentRequest)
507 Z_PresentRequest *pr = apdu->u.presentRequest;
508 Z_NamePlusRecordList *npr;
509 int toget = *pr->numberOfRecordsRequested;
510 int start = *pr->resultSetStartPoint;
512 if (m_client->m_last_resultSetId &&
513 !strcmp(m_client->m_last_resultSetId, pr->resultSetId))
515 if (m_client->m_cache.lookup (odr_encode(), &npr, start, toget,
516 pr->preferredRecordSyntax,
517 pr->recordComposition))
519 yaz_log (LOG_LOG, "%s Returned cache records for present request",
521 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
522 new_apdu->u.presentResponse->referenceId = pr->referenceId;
524 new_apdu->u.presentResponse->numberOfRecordsReturned
525 = odr_intdup(odr_encode(), toget);
527 new_apdu->u.presentResponse->records = (Z_Records*)
528 odr_malloc(odr_encode(), sizeof(Z_Records));
529 new_apdu->u.presentResponse->records->which = Z_Records_DBOSD;
530 new_apdu->u.presentResponse->records->u.databaseOrSurDiagnostics = npr;
531 new_apdu->u.presentResponse->nextResultSetPosition =
532 odr_intdup(odr_encode(), start+toget);
534 send_to_client(new_apdu);
540 if (apdu->which != Z_APDU_searchRequest)
542 Z_SearchRequest *sr = apdu->u.searchRequest;
543 Yaz_Z_Query *this_query = new Yaz_Z_Query;
544 Yaz_Z_Databases this_databases;
546 this_databases.set(sr->num_databaseNames, (const char **)
549 this_query->set_Z_Query(sr->query);
552 this_query->print(query_str, sizeof(query_str)-1);
553 yaz_log(LOG_LOG, "%s Query %s", m_session_str, query_str);
555 if (m_client->m_last_ok && m_client->m_last_query &&
556 m_client->m_last_query->match(this_query) &&
557 !strcmp(m_client->m_last_resultSetId, sr->resultSetName) &&
558 m_client->m_last_databases.match(this_databases))
561 if (m_client->m_last_resultCount > *sr->smallSetUpperBound &&
562 m_client->m_last_resultCount < *sr->largeSetLowerBound)
564 Z_NamePlusRecordList *npr;
565 int toget = *sr->mediumSetPresentNumber;
566 Z_RecordComposition *comp = 0;
568 if (toget > m_client->m_last_resultCount)
569 toget = m_client->m_last_resultCount;
571 if (sr->mediumSetElementSetNames)
573 comp = (Z_RecordComposition *)
574 odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
575 comp->which = Z_RecordComp_simple;
576 comp->u.simple = sr->mediumSetElementSetNames;
579 if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget,
580 sr->preferredRecordSyntax, comp))
582 yaz_log (LOG_LOG, "%s Returned cache records for medium set",
584 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
585 new_apdu->u.searchResponse->referenceId = sr->referenceId;
586 new_apdu->u.searchResponse->resultCount =
587 &m_client->m_last_resultCount;
589 new_apdu->u.searchResponse->numberOfRecordsReturned
590 = odr_intdup(odr_encode(), toget);
592 new_apdu->u.searchResponse->presentStatus =
593 odr_intdup(odr_encode(), Z_PresentStatus_success);
594 new_apdu->u.searchResponse->records = (Z_Records*)
595 odr_malloc(odr_encode(), sizeof(Z_Records));
596 new_apdu->u.searchResponse->records->which = Z_Records_DBOSD;
597 new_apdu->u.searchResponse->records->u.databaseOrSurDiagnostics = npr;
598 new_apdu->u.searchResponse->nextResultSetPosition =
599 odr_intdup(odr_encode(), toget+1);
600 send_to_client(new_apdu);
606 // send present request (medium size)
607 yaz_log (LOG_LOG, "%s Optimizing search for medium set",
610 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
611 Z_PresentRequest *pr = new_apdu->u.presentRequest;
612 pr->referenceId = sr->referenceId;
613 pr->resultSetId = sr->resultSetName;
614 pr->preferredRecordSyntax = sr->preferredRecordSyntax;
615 *pr->numberOfRecordsRequested = toget;
616 pr->recordComposition = comp;
617 m_client->m_sr_transform = 1;
621 else if (m_client->m_last_resultCount >= *sr->largeSetLowerBound ||
622 m_client->m_last_resultCount <= 0)
624 // large set. Return pseudo-search response immediately
625 yaz_log (LOG_LOG, "%s Optimizing search for large set",
627 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
628 new_apdu->u.searchResponse->referenceId = sr->referenceId;
629 new_apdu->u.searchResponse->resultCount =
630 &m_client->m_last_resultCount;
631 send_to_client(new_apdu);
636 Z_NamePlusRecordList *npr;
637 int toget = m_client->m_last_resultCount;
638 Z_RecordComposition *comp = 0;
640 // send a present request (small set)
642 if (sr->smallSetElementSetNames)
644 comp = (Z_RecordComposition *)
645 odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
646 comp->which = Z_RecordComp_simple;
647 comp->u.simple = sr->smallSetElementSetNames;
650 if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget,
651 sr->preferredRecordSyntax, comp))
653 yaz_log (LOG_LOG, "%s Returned cache records for small set",
655 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
656 new_apdu->u.searchResponse->referenceId = sr->referenceId;
657 new_apdu->u.searchResponse->resultCount =
658 &m_client->m_last_resultCount;
660 new_apdu->u.searchResponse->numberOfRecordsReturned
661 = odr_intdup(odr_encode(), toget);
663 new_apdu->u.searchResponse->presentStatus =
664 odr_intdup(odr_encode(), Z_PresentStatus_success);
665 new_apdu->u.searchResponse->records = (Z_Records*)
666 odr_malloc(odr_encode(), sizeof(Z_Records));
667 new_apdu->u.searchResponse->records->which = Z_Records_DBOSD;
668 new_apdu->u.searchResponse->records->u.databaseOrSurDiagnostics = npr;
669 new_apdu->u.searchResponse->nextResultSetPosition =
670 odr_intdup(odr_encode(), toget+1);
671 send_to_client(new_apdu);
676 yaz_log (LOG_LOG, "%s Optimizing search for small set",
678 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
679 Z_PresentRequest *pr = new_apdu->u.presentRequest;
680 pr->referenceId = sr->referenceId;
681 pr->resultSetId = sr->resultSetName;
682 pr->preferredRecordSyntax = sr->preferredRecordSyntax;
683 *pr->numberOfRecordsRequested = toget;
684 pr->recordComposition = comp;
685 m_client->m_sr_transform = 1;
690 else // query doesn't match
692 delete m_client->m_last_query;
693 m_client->m_last_query = this_query;
694 m_client->m_last_ok = 0;
695 m_client->m_cache.clear();
696 m_client->m_resultSetStartPoint = 0;
698 xfree (m_client->m_last_resultSetId);
699 m_client->m_last_resultSetId = xstrdup (sr->resultSetName);
701 m_client->m_last_databases.set(sr->num_databaseNames,
702 (const char **) sr->databaseNames);
708 void Yaz_Proxy::recv_Z_PDU(Z_APDU *apdu, int len)
713 yaz_log (LOG_LOG, "%s Receiving %s from client %d bytes", m_session_str,
714 apdu_name(apdu), len);
716 if (m_bw_hold_PDU) // double incoming PDU. shutdown now.
719 m_bw_stat.add_bytes(len);
720 m_pdu_stat.add_bytes(1);
722 int bw_total = m_bw_stat.get_total();
723 int pdu_total = m_pdu_stat.get_total();
725 yaz_log(LOG_LOG, "%s stat bw=%d pdu=%d limit-bw=%d limit-pdu=%d",
726 m_session_str, bw_total, pdu_total, m_bw_max, m_pdu_max);
729 if (bw_total > m_bw_max)
731 reduce = (bw_total/m_bw_max);
736 if (pdu_total > m_pdu_max)
738 int nreduce = (60/m_pdu_max);
739 reduce = (reduce > nreduce) ? reduce : nreduce;
744 yaz_log(LOG_LOG, "%s Limit delay=%d", m_session_str, reduce);
745 m_bw_hold_PDU = apdu; // save PDU and signal "on hold"
746 timeout(reduce); // call us reduce seconds later
749 recv_Z_PDU_0(apdu); // all fine. Proceed receive PDU as usual
752 void Yaz_Proxy::handle_max_record_retrieve(Z_APDU *apdu)
754 if (m_max_record_retrieve)
756 if (apdu->which == Z_APDU_presentRequest)
758 Z_PresentRequest *pr = apdu->u.presentRequest;
759 if (pr->numberOfRecordsRequested &&
760 *pr->numberOfRecordsRequested > m_max_record_retrieve)
761 *pr->numberOfRecordsRequested = m_max_record_retrieve;
766 Z_Records *Yaz_Proxy::create_nonSurrogateDiagnostics(ODR odr,
770 Z_Records *rec = (Z_Records *)
771 odr_malloc (odr, sizeof(*rec));
773 odr_malloc (odr, sizeof(*err));
774 Z_DiagRec *drec = (Z_DiagRec *)
775 odr_malloc (odr, sizeof(*drec));
776 Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)
777 odr_malloc (odr, sizeof(*dr));
779 rec->which = Z_Records_NSD;
780 rec->u.nonSurrogateDiagnostic = dr;
781 dr->diagnosticSetId =
782 yaz_oidval_to_z3950oid (odr, CLASS_DIAGSET, VAL_BIB1);
784 dr->which = Z_DefaultDiagFormat_v2Addinfo;
785 dr->u.v2Addinfo = odr_strdup (odr, addinfo ? addinfo : "");
789 Z_APDU *Yaz_Proxy::handle_query_validation(Z_APDU *apdu)
791 if (apdu->which == Z_APDU_searchRequest)
793 Z_SearchRequest *sr = apdu->u.searchRequest;
796 err = m_config.check_query(odr_encode(), m_default_target, sr->query,
800 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
802 new_apdu->u.searchResponse->referenceId = sr->referenceId;
803 new_apdu->u.searchResponse->records =
804 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
805 *new_apdu->u.searchResponse->searchStatus = 0;
807 send_to_client(new_apdu);
815 Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu)
817 if (apdu->which == Z_APDU_searchRequest)
819 Z_SearchRequest *sr = apdu->u.searchRequest;
820 if (*sr->smallSetUpperBound > 0 || *sr->largeSetLowerBound > 1)
824 err = m_config.check_syntax(odr_encode(), m_default_target,
825 sr->preferredRecordSyntax,
829 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
831 new_apdu->u.searchResponse->referenceId = sr->referenceId;
832 new_apdu->u.searchResponse->records =
833 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
834 *new_apdu->u.searchResponse->searchStatus = 0;
836 send_to_client(new_apdu);
842 else if (apdu->which == Z_APDU_presentRequest)
844 Z_PresentRequest *pr = apdu->u.presentRequest;
847 err = m_config.check_syntax(odr_encode(), m_default_target,
848 pr->preferredRecordSyntax,
852 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
854 new_apdu->u.presentResponse->referenceId = pr->referenceId;
855 new_apdu->u.presentResponse->records =
856 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
857 *new_apdu->u.presentResponse->presentStatus =
858 Z_PresentStatus_failure;
860 send_to_client(new_apdu);
868 void Yaz_Proxy::recv_Z_PDU_0(Z_APDU *apdu)
870 // Determine our client.
871 m_client = get_client(apdu);
877 m_client->m_server = this;
879 if (apdu->which == Z_APDU_initRequest)
881 if (apdu->u.initRequest->implementationId)
882 yaz_log(LOG_LOG, "%s implementationId: %s",
883 m_session_str, apdu->u.initRequest->implementationId);
884 if (apdu->u.initRequest->implementationName)
885 yaz_log(LOG_LOG, "%s implementationName: %s",
886 m_session_str, apdu->u.initRequest->implementationName);
887 if (apdu->u.initRequest->implementationVersion)
888 yaz_log(LOG_LOG, "%s implementationVersion: %s",
889 m_session_str, apdu->u.initRequest->implementationVersion);
890 if (m_client->m_init_flag)
892 Z_APDU *apdu = m_client->m_initResponse;
893 apdu->u.initResponse->otherInfo = 0;
894 if (m_client->m_cookie && *m_client->m_cookie)
895 set_otherInformationString(apdu, VAL_COOKIE, 1,
897 send_to_client(apdu);
900 m_client->m_init_flag = 1;
902 handle_max_record_retrieve(apdu);
905 apdu = handle_syntax_validation(apdu);
908 apdu = handle_query_validation(apdu);
911 apdu = result_set_optimize(apdu);
914 m_client->timeout(m_target_idletime); // mark it active even
915 // though we didn't use it
919 // delete other info part from PDU before sending to target
920 Z_OtherInformation **oi;
921 get_otherInfoAPDU(apdu, &oi);
925 if (apdu->which == Z_APDU_presentRequest &&
926 m_client->m_resultSetStartPoint == 0)
928 Z_PresentRequest *pr = apdu->u.presentRequest;
929 m_client->m_resultSetStartPoint = *pr->resultSetStartPoint;
930 m_client->m_cache.copy_presentRequest(apdu->u.presentRequest);
932 m_client->m_resultSetStartPoint = 0;
934 if (m_client->send_to_target(apdu) < 0)
941 m_client->m_waiting = 1;
944 void Yaz_Proxy::connectNotify()
948 void Yaz_Proxy::shutdown()
950 // only keep if keep_alive flag is set...
951 if (m_keepalive && m_client && m_client->m_waiting == 0)
953 yaz_log (LOG_LOG, "%s Shutdown (client to proxy) keepalive %s",
955 m_client->get_hostname());
956 assert (m_client->m_waiting != 2);
957 // Tell client (if any) that no server connection is there..
958 m_client->m_server = 0;
962 yaz_log (LOG_LOG, "%s Shutdown (client to proxy) close %s",
964 m_client->get_hostname());
965 assert (m_client->m_waiting != 2);
970 yaz_log (LOG_LOG, "%s shutdown (client to proxy) bad state",
976 yaz_log (LOG_LOG, "%s Shutdown (client to proxy)",
982 const char *Yaz_ProxyClient::get_session_str()
986 return m_server->get_session_str();
989 void Yaz_ProxyClient::shutdown()
991 yaz_log (LOG_LOG, "%s Shutdown (proxy to target) %s", get_session_str(),
997 void Yaz_Proxy::failNotify()
999 yaz_log (LOG_LOG, "%s Connection closed by client",
1004 void Yaz_ProxyClient::failNotify()
1006 yaz_log (LOG_LOG, "%s Connection closed by target %s",
1007 get_session_str(), get_hostname());
1011 void Yaz_ProxyClient::connectNotify()
1013 yaz_log (LOG_LOG, "%s Connection accepted by %s", get_session_str(),
1017 to = m_server->get_target_idletime();
1023 IYaz_PDU_Observer *Yaz_ProxyClient::sessionNotify(IYaz_PDU_Observable
1024 *the_PDU_Observable, int fd)
1026 return new Yaz_ProxyClient(the_PDU_Observable);
1029 Yaz_ProxyClient::~Yaz_ProxyClient()
1034 m_next->m_prev = m_prev;
1035 m_waiting = 2; // for debugging purposes only.
1036 odr_destroy(m_init_odr);
1037 delete m_last_query;
1038 xfree (m_last_resultSetId);
1042 void Yaz_Proxy::timeoutNotify()
1046 timeout(m_client_idletime);
1047 Z_APDU *apdu = m_bw_hold_PDU;
1053 yaz_log (LOG_LOG, "%s Timeout (client to proxy)", m_session_str);
1058 void Yaz_ProxyClient::timeoutNotify()
1060 yaz_log (LOG_LOG, "%s Timeout (proxy to target) %s", get_session_str(),
1065 Yaz_ProxyClient::Yaz_ProxyClient(IYaz_PDU_Observable *the_PDU_Observable) :
1066 Yaz_Z_Assoc (the_PDU_Observable)
1073 m_last_resultSetId = 0;
1074 m_last_resultCount = 0;
1078 m_init_odr = odr_createmem (ODR_DECODE);
1080 m_resultSetStartPoint = 0;
1081 m_bytes_sent = m_bytes_recv = 0;
1084 const char *Yaz_Proxy::option(const char *name, const char *value)
1086 if (!strcmp (name, "optimize")) {
1089 m_optimize = xstrdup (value);
1096 void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len)
1098 m_bytes_recv += len;
1100 yaz_log (LOG_LOG, "%s Receiving %s from %s %d bytes", get_session_str(),
1101 apdu_name(apdu), get_hostname(), len);
1102 if (apdu->which == Z_APDU_initResponse)
1104 NMEM nmem = odr_extract_mem (odr_decode());
1105 odr_reset (m_init_odr);
1106 nmem_transfer (m_init_odr->mem, nmem);
1107 m_initResponse = apdu;
1109 Z_InitResponse *ir = apdu->u.initResponse;
1110 char *im0 = ir->implementationName;
1113 odr_malloc(m_init_odr, 20 + (im0 ? strlen(im0) : 0));
1120 strcat(im1, "(YAZ Proxy)");
1121 ir->implementationName = im1;
1123 nmem_destroy (nmem);
1125 if (apdu->which == Z_APDU_searchResponse)
1127 Z_SearchResponse *sr = apdu->u.searchResponse;
1128 m_last_resultCount = *sr->resultCount;
1129 int status = *sr->searchStatus;
1130 if (status && (!sr->records || sr->records->which == Z_Records_DBOSD))
1134 if (sr->records && sr->records->which == Z_Records_DBOSD)
1136 m_cache.add(odr_decode(),
1137 sr->records->u.databaseOrSurDiagnostics, 1,
1142 if (apdu->which == Z_APDU_presentResponse)
1144 Z_PresentResponse *pr = apdu->u.presentResponse;
1148 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1149 Z_SearchResponse *sr = new_apdu->u.searchResponse;
1150 sr->referenceId = pr->referenceId;
1151 *sr->resultCount = m_last_resultCount;
1152 sr->records = pr->records;
1153 sr->nextResultSetPosition = pr->nextResultSetPosition;
1154 sr->numberOfRecordsReturned = pr->numberOfRecordsReturned;
1158 pr->records->which == Z_Records_DBOSD && m_resultSetStartPoint)
1160 m_cache.add(odr_decode(),
1161 pr->records->u.databaseOrSurDiagnostics,
1162 m_resultSetStartPoint, -1);
1163 m_resultSetStartPoint = 0;
1167 set_otherInformationString (apdu, VAL_COOKIE, 1, m_cookie);
1170 m_server->send_to_client(apdu);
1172 if (apdu->which == Z_APDU_close)