1 /* $Id: yaz-proxy.cpp,v 1.24 2005-02-22 10:08:20 adam Exp $
2 Copyright (c) 1998-2005, Index Data.
4 This file is part of the yaz-proxy.
6 YAZ proxy is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2, or (at your option) any later
11 YAZ proxy is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 You should have received a copy of the GNU General Public License
17 along with YAZ proxy; see the file LICENSE. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23 #define HAVE_SYS_STAT_H 1
24 #define HAVE_SYS_TYPES_H 1
34 #include <sys/types.h>
46 #include <yaz/marcdisp.h>
47 #include <yaz/yaz-iconv.h>
49 #include <yaz/diagbib1.h>
51 #include <yaz/pquery.h>
52 #include <yaz/otherinfo.h>
53 #include <yaz/charneg.h>
56 static const char *apdu_name(Z_APDU *apdu)
60 case Z_APDU_initRequest:
62 case Z_APDU_initResponse:
63 return "initResponse";
64 case Z_APDU_searchRequest:
65 return "searchRequest";
66 case Z_APDU_searchResponse:
67 return "searchResponse";
68 case Z_APDU_presentRequest:
69 return "presentRequest";
70 case Z_APDU_presentResponse:
71 return "presentResponse";
72 case Z_APDU_deleteResultSetRequest:
73 return "deleteResultSetRequest";
74 case Z_APDU_deleteResultSetResponse:
75 return "deleteResultSetResponse";
76 case Z_APDU_scanRequest:
78 case Z_APDU_scanResponse:
79 return "scanResponse";
80 case Z_APDU_sortRequest:
82 case Z_APDU_sortResponse:
83 return "sortResponse";
84 case Z_APDU_extendedServicesRequest:
85 return "extendedServicesRequest";
86 case Z_APDU_extendedServicesResponse:
87 return "extendedServicesResponse";
94 static const char *gdu_name(Z_GDU *gdu)
99 return apdu_name(gdu->u.z3950);
100 case Z_GDU_HTTP_Request:
101 return "HTTP Request";
102 case Z_GDU_HTTP_Response:
103 return "HTTP Response";
105 return "Unknown request/response";
107 Yaz_Proxy::Yaz_Proxy(IYaz_PDU_Observable *the_PDU_Observable,
109 Yaz_Z_Assoc(the_PDU_Observable), m_bw_stat(60), m_pdu_stat(60)
111 m_PDU_Observable = the_PDU_Observable;
116 m_keepalive_limit_bw = 500000;
117 m_keepalive_limit_pdu = 1000;
119 m_default_target = 0;
120 m_proxy_negotiation_charset = 0;
121 m_proxy_negotiation_lang = 0;
125 m_client_idletime = 600;
126 m_target_idletime = 600;
127 m_optimize = xstrdup ("1");
128 strcpy(m_session_str, "0 ");
135 m_max_record_retrieve = 0;
139 m_invalid_session = 0;
141 m_referenceId_mem = nmem_create();
143 m_marcxml_mode = none;
144 m_stylesheet_xsp = 0;
145 m_stylesheet_nprl = 0;
146 m_s2z_stylesheet = 0;
150 m_backend_charset = 0;
152 m_initRequest_apdu = 0;
153 m_initRequest_mem = 0;
154 m_initRequest_preferredMessageSize = 0;
155 m_initRequest_maximumRecordSize = 0;
156 m_initRequest_options = 0;
157 m_initRequest_version = 0;
158 m_initRequest_oi_negotiation_charsets = 0;
159 m_initRequest_oi_negotiation_num_charsets = 0;
160 m_initRequest_oi_negotiation_langs = 0;
161 m_initRequest_oi_negotiation_num_langs = 0;
162 m_initRequest_oi_negotiation_selected = 0;
163 m_apdu_invalid_session = 0;
164 m_mem_invalid_session = 0;
166 m_s2z_odr_search = 0;
168 m_s2z_search_apdu = 0;
169 m_s2z_present_apdu = 0;
170 m_http_keepalive = 0;
173 m_s2z_packing = Z_SRW_recordPacking_string;
174 #if HAVE_GETTIMEOFDAY
175 m_time_tv = xmalloc(sizeof(struct timeval));
176 struct timeval *tv = (struct timeval *) m_time_tv;
182 m_usemarcon_ini_stage1 = 0;
183 m_usemarcon_ini_stage2 = 0;
184 m_usemarcon = new Yaz_usemarcon();
189 Yaz_Proxy::~Yaz_Proxy()
191 yaz_log(YLOG_LOG, "%sClosed %d/%d sent/recv bytes total", m_session_str,
192 m_bytes_sent, m_bytes_recv);
193 nmem_destroy(m_initRequest_mem);
194 nmem_destroy(m_mem_invalid_session);
195 nmem_destroy(m_referenceId_mem);
197 xfree(m_proxyTarget);
198 xfree(m_default_target);
199 xfree(m_proxy_negotiation_charset);
200 xfree(m_proxy_negotiation_lang);
204 if (m_stylesheet_xsp)
205 xsltFreeStylesheet((xsltStylesheetPtr) m_stylesheet_xsp);
210 xfree (m_backend_type);
211 xfree (m_backend_charset);
212 xfree (m_usemarcon_ini_stage1);
213 xfree (m_usemarcon_ini_stage2);
216 odr_destroy(m_s2z_odr_init);
217 if (m_s2z_odr_search)
218 odr_destroy(m_s2z_odr_search);
224 void Yaz_Proxy::set_debug_mode(int mode)
229 int Yaz_Proxy::set_config(const char *config)
232 m_config = new Yaz_ProxyConfig();
233 xfree(m_config_fname);
234 m_config_fname = xstrdup(config);
235 int r = m_config->read_xml(config);
237 m_config->get_generic_info(&m_log_mask, &m_max_clients);
241 void Yaz_Proxy::set_default_target(const char *target)
243 xfree (m_default_target);
244 m_default_target = 0;
246 m_default_target = (char *) xstrdup (target);
249 void Yaz_Proxy::set_proxy_negotiation (const char *charset, const char *lang)
251 yaz_log(YLOG_LOG, "%sSet the proxy negotiation: charset to '%s', "
252 "language to '%s'", m_session_str, charset?charset:"none",
254 xfree (m_proxy_negotiation_charset);
255 xfree (m_proxy_negotiation_lang);
256 m_proxy_negotiation_charset = m_proxy_negotiation_lang = 0;
258 m_proxy_negotiation_charset = (char *) xstrdup (charset);
260 m_proxy_negotiation_lang = (char *) xstrdup (lang);
263 Yaz_ProxyConfig *Yaz_Proxy::check_reconfigure()
266 return m_parent->check_reconfigure();
268 Yaz_ProxyConfig *cfg = m_config;
271 yaz_log(YLOG_LOG, "reconfigure");
273 if (m_config_fname && cfg)
275 yaz_log(YLOG_LOG, "reconfigure config %s", m_config_fname);
276 int r = cfg->read_xml(m_config_fname);
278 yaz_log(YLOG_WARN, "reconfigure failed");
282 cfg->get_generic_info(&m_log_mask, &m_max_clients);
286 yaz_log(YLOG_LOG, "reconfigure");
292 IYaz_PDU_Observer *Yaz_Proxy::sessionNotify(IYaz_PDU_Observable
293 *the_PDU_Observable, int fd)
296 Yaz_Proxy *new_proxy = new Yaz_Proxy(the_PDU_Observable, this);
297 new_proxy->m_config = 0;
298 new_proxy->m_config_fname = 0;
299 new_proxy->timeout(m_client_idletime);
300 new_proxy->m_target_idletime = m_target_idletime;
301 new_proxy->set_default_target(m_default_target);
302 new_proxy->m_max_clients = m_max_clients;
303 new_proxy->m_log_mask = m_log_mask;
304 new_proxy->set_APDU_log(get_APDU_log());
305 if (m_log_mask & PROXY_LOG_APDU_CLIENT)
306 new_proxy->set_APDU_yazlog(1);
308 new_proxy->set_APDU_yazlog(0);
309 new_proxy->set_proxy_negotiation(m_proxy_negotiation_charset,
310 m_proxy_negotiation_lang);
311 sprintf(new_proxy->m_session_str, "%ld:%d ", (long) time(0), m_session_no);
313 yaz_log (YLOG_LOG, "%sNew session %s", new_proxy->m_session_str,
314 the_PDU_Observable->getpeername());
318 char *Yaz_Proxy::get_cookie(Z_OtherInformation **otherInfo)
321 Z_OtherInformationUnit *oi;
323 ent.proto = PROTO_Z3950;
324 ent.oclass = CLASS_USERINFO;
325 ent.value = (oid_value) VAL_COOKIE;
326 assert (oid_ent_to_oid (&ent, oid));
328 if (oid_ent_to_oid (&ent, oid) &&
329 (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
330 oi->which == Z_OtherInfo_characterInfo)
331 return oi->information.characterInfo;
334 char *Yaz_Proxy::get_proxy(Z_OtherInformation **otherInfo)
337 Z_OtherInformationUnit *oi;
339 ent.proto = PROTO_Z3950;
340 ent.oclass = CLASS_USERINFO;
341 ent.value = (oid_value) VAL_PROXY;
342 if (oid_ent_to_oid (&ent, oid) &&
343 (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
344 oi->which == Z_OtherInfo_characterInfo)
345 return oi->information.characterInfo;
348 const char *Yaz_Proxy::load_balance(const char **url)
350 int zurl_in_use[MAX_ZURL_PLEX];
351 int zurl_in_spare[MAX_ZURL_PLEX];
355 for (i = 0; i<MAX_ZURL_PLEX; i++)
358 zurl_in_spare[i] = 0;
360 for (c = m_parent->m_clientPool; c; c = c->m_next)
362 for (i = 0; url[i]; i++)
363 if (!strcmp(url[i], c->get_hostname()))
366 if (c->m_cookie == 0 && c->m_server == 0 && c->m_waiting == 0)
370 int min_use = 100000;
371 int spare_for_min = 0;
373 const char *ret_min = 0;
374 const char *ret_spare = 0;
375 for (i = 0; url[i]; i++)
377 yaz_log(YLOG_DEBUG, "%szurl=%s use=%d spare=%d",
378 m_session_str, url[i], zurl_in_use[i], zurl_in_spare[i]);
379 if (min_use > zurl_in_use[i])
382 min_use = zurl_in_use[i];
383 spare_for_min = zurl_in_spare[i];
385 if (max_spare < zurl_in_spare[i])
388 max_spare = zurl_in_spare[i];
391 // use the one with minimum connections if spare is > 3
392 if (spare_for_min > 3)
394 // use one with most spares (if any)
400 Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
401 const char *proxy_host)
404 Yaz_Proxy *parent = m_parent;
405 Yaz_ProxyClient *c = m_client;
409 const char *url[MAX_ZURL_PLEX];
410 Yaz_ProxyConfig *cfg = check_reconfigure();
413 if (parent && parent->m_debug_mode)
415 // only to be enabled for debugging...
416 if (!strcmp(proxy_host, "stop"))
419 xfree(m_default_target);
420 m_default_target = xstrdup(proxy_host);
422 proxy_host = m_default_target;
423 int client_idletime = -1;
424 const char *cql2rpn_fname = 0;
425 const char *negotiation_charset = 0;
426 const char *negotiation_lang = 0;
427 url[0] = m_default_target;
432 cfg->get_target_info(proxy_host, url, &m_bw_max,
433 &m_pdu_max, &m_max_record_retrieve,
434 &m_target_idletime, &client_idletime,
435 &parent->m_max_clients,
436 &m_keepalive_limit_bw,
437 &m_keepalive_limit_pdu,
440 &negotiation_charset,
443 if (client_idletime != -1)
445 m_client_idletime = client_idletime;
446 timeout(m_client_idletime);
449 m_cql2rpn.set_pqf_file(cql2rpn_fname);
450 if (negotiation_charset || negotiation_lang)
452 yaz_log(YLOG_LOG, "set_proxy_negotiation...");
453 set_proxy_negotiation(negotiation_charset,
458 yaz_log(YLOG_LOG, "%sNo default target", m_session_str);
461 // we don't handle multiplexing for cookie session, so we just
462 // pick the first one in this case (anonymous users will be able
463 // to use any backend)
464 if (cookie && *cookie)
465 m_proxyTarget = (char*) xstrdup(url[0]);
467 m_proxyTarget = (char*) xstrdup(load_balance(url));
469 if (cookie && *cookie)
470 { // search in sessions with a cookie
471 for (c = parent->m_clientPool; c; c = c->m_next)
474 assert (*c->m_prev == c);
475 if (c->m_cookie && !strcmp(cookie,c->m_cookie) &&
476 !strcmp(m_proxyTarget, c->get_hostname()))
479 // The following handles "cancel"
480 // If connection is busy (waiting for PDU) and
481 // we have an initRequest we can safely do re-open
482 if (c->m_waiting && apdu->which == Z_APDU_initRequest)
484 yaz_log (YLOG_LOG, "%s REOPEN target=%s", m_session_str,
491 c->m_last_resultCount = 0;
492 c->m_sr_transform = 0;
494 c->m_resultSetStartPoint = 0;
495 c->m_target_idletime = m_target_idletime;
496 if (c->client(m_proxyTarget))
503 c->m_seqno = parent->m_seqno;
504 if (c->m_server && c->m_server != this)
505 c->m_server->m_client = 0;
508 yaz_log (YLOG_DEBUG, "get_client 1 %p %p", this, c);
514 apdu->which == Z_APDU_initRequest &&
515 apdu->u.initRequest->idAuthentication == 0 &&
516 !ODR_MASK_GET(apdu->u.initRequest->options, Z_Options_negotiationModel))
518 // anonymous sessions without cookie.
519 // if authentication is set it is NOT anonymous se we can't share them.
520 // If charset and lang negotiation is use it is NOT anonymous session too.
521 for (c = parent->m_clientPool; c; c = c->m_next)
524 assert(*c->m_prev == c);
525 if (c->m_server == 0 && c->m_cookie == 0 &&
527 !strcmp(m_proxyTarget, c->get_hostname()))
530 yaz_log (YLOG_LOG, "%sREUSE %d %s",
531 m_session_str, parent->m_seqno, c->get_hostname());
533 c->m_seqno = parent->m_seqno;
534 assert(c->m_server == 0);
537 if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
538 c->set_APDU_yazlog(1);
540 c->set_APDU_yazlog(0);
552 if (apdu->which != Z_APDU_initRequest)
554 yaz_log (YLOG_LOG, "%sno init request as first PDU", m_session_str);
557 Z_InitRequest *initRequest = apdu->u.initRequest;
559 if (initRequest->idAuthentication)
561 // the client uses authentication. We set the keepalive PDU
562 // to 0 so we don't cache it in releaseClient
563 m_keepalive_limit_pdu = 0;
565 // go through list of clients - and find the lowest/oldest one.
566 Yaz_ProxyClient *c_min = 0;
568 int no_of_clients = 0;
569 if (parent->m_clientPool)
570 yaz_log (YLOG_DEBUG, "Existing sessions");
571 for (c = parent->m_clientPool; c; c = c->m_next)
573 yaz_log (YLOG_DEBUG, " Session %-3d wait=%d %s cookie=%s", c->m_seqno,
574 c->m_waiting, c->get_hostname(),
575 c->m_cookie ? c->m_cookie : "");
577 if (min_seq < 0 || c->m_seqno < min_seq)
579 min_seq = c->m_seqno;
583 if (no_of_clients >= parent->m_max_clients)
586 if (c->m_waiting || strcmp(m_proxyTarget, c->get_hostname()))
588 yaz_log (YLOG_LOG, "%sMAXCLIENTS %d Destroy %d",
589 m_session_str, parent->m_max_clients, c->m_seqno);
590 if (c->m_server && c->m_server != this)
596 yaz_log (YLOG_LOG, "%sMAXCLIENTS %d Reuse %d %d %s",
597 m_session_str, parent->m_max_clients,
598 c->m_seqno, parent->m_seqno, c->get_hostname());
602 c->m_cookie = xstrdup(cookie);
603 c->m_seqno = parent->m_seqno;
604 if (c->m_server && c->m_server != this)
606 c->m_server->m_client = 0;
610 c->m_target_idletime = m_target_idletime;
611 c->timeout(m_target_idletime);
613 if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
614 c->set_APDU_yazlog(1);
616 c->set_APDU_yazlog(0);
623 yaz_log (YLOG_LOG, "%sNEW %d %s",
624 m_session_str, parent->m_seqno, m_proxyTarget);
625 c = new Yaz_ProxyClient(m_PDU_Observable->clone(), parent);
626 c->m_next = parent->m_clientPool;
628 c->m_next->m_prev = &c->m_next;
629 parent->m_clientPool = c;
630 c->m_prev = &parent->m_clientPool;
636 c->m_cookie = xstrdup(cookie);
638 c->m_seqno = parent->m_seqno;
640 c->m_last_resultCount = 0;
643 c->m_sr_transform = 0;
645 c->m_resultSetStartPoint = 0;
647 if (c->client(m_proxyTarget))
652 c->m_target_idletime = m_target_idletime;
655 if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
656 c->set_APDU_yazlog(1);
658 c->set_APDU_yazlog(0);
660 yaz_log (YLOG_DEBUG, "get_client 3 %p %p", this, c);
663 void Yaz_Proxy::display_diagrecs(Z_DiagRec **pp, int num)
666 for (i = 0; i<num; i++)
669 Z_DefaultDiagFormat *r;
670 Z_DiagRec *p = pp[i];
671 if (p->which != Z_DiagRec_defaultFormat)
673 yaz_log(YLOG_LOG, "%sError no diagnostics", m_session_str);
677 r = p->u.defaultFormat;
678 if (!(ent = oid_getentbyoid(r->diagnosticSetId)) ||
679 ent->oclass != CLASS_DIAGSET || ent->value != VAL_BIB1)
680 yaz_log(YLOG_LOG, "%sError unknown diagnostic set", m_session_str);
683 case Z_DefaultDiagFormat_v2Addinfo:
684 yaz_log(YLOG_LOG, "%sError %d %s:%s",
686 *r->condition, diagbib1_str(*r->condition),
689 case Z_DefaultDiagFormat_v3Addinfo:
690 yaz_log(YLOG_LOG, "%sError %d %s:%s",
692 *r->condition, diagbib1_str(*r->condition),
699 int Yaz_Proxy::convert_xsl(Z_NamePlusRecordList *p, Z_APDU *apdu)
701 if (!m_stylesheet_xsp || p->num_records <= 0)
702 return 0; /* no XSLT to be done ... */
704 m_stylesheet_offset = 0;
705 m_stylesheet_nprl = p;
706 m_stylesheet_apdu = apdu;
711 void Yaz_Proxy::convert_xsl_delay()
714 Z_NamePlusRecord *npr = m_stylesheet_nprl->records[m_stylesheet_offset];
715 if (npr->which == Z_NamePlusRecord_databaseRecord)
717 Z_External *r = npr->u.databaseRecord;
718 if (r->which == Z_External_octet)
721 fwrite((char*) r->u.octet_aligned->buf, 1, r->u.octet_aligned->len, stdout);
723 xmlDocPtr res, doc = xmlParseMemory(
724 (char*) r->u.octet_aligned->buf,
725 r->u.octet_aligned->len);
728 yaz_log(YLOG_LOG, "%sXSLT convert %d",
729 m_session_str, m_stylesheet_offset);
730 res = xsltApplyStylesheet((xsltStylesheetPtr) m_stylesheet_xsp,
737 xmlDocDumpFormatMemory (res, &out_buf, &out_len, 1);
739 m_stylesheet_nprl->records[m_stylesheet_offset]->
741 z_ext_record(odr_encode(), VAL_TEXT_XML,
742 (char*) out_buf, out_len);
751 m_stylesheet_offset++;
752 if (m_stylesheet_offset == m_stylesheet_nprl->num_records)
754 m_stylesheet_nprl = 0;
756 if (m_stylesheet_xsp)
757 xsltFreeStylesheet((xsltStylesheetPtr) m_stylesheet_xsp);
759 m_stylesheet_xsp = 0;
760 timeout(m_client_idletime);
761 send_PDU_convert(m_stylesheet_apdu);
767 void Yaz_Proxy::convert_to_frontend_type(Z_NamePlusRecordList *p)
769 if (m_frontend_type != VAL_NONE)
772 for (i = 0; i < p->num_records; i++)
774 Z_NamePlusRecord *npr = p->records[i];
775 if (npr->which == Z_NamePlusRecord_databaseRecord)
777 Z_External *r = npr->u.databaseRecord;
778 if (r->which == Z_External_octet)
781 if (m_usemarcon_ini_stage1 && *m_usemarcon_ini_stage1)
783 if (!m_usemarcon->m_stage1)
785 m_usemarcon->m_stage1 = new CDetails();
787 m_usemarcon->m_stage1->SetIniFileName(m_usemarcon_ini_stage1);
788 m_usemarcon->m_stage1->SetMarcRecord((char*) r->u.octet_aligned->buf, r->u.octet_aligned->len);
789 int res = m_usemarcon->m_stage1->Start();
794 m_usemarcon->m_stage1->GetMarcRecord(converted, convlen);
795 if (m_usemarcon_ini_stage2 && *m_usemarcon_ini_stage2)
797 if (!m_usemarcon->m_stage2)
799 m_usemarcon->m_stage2 = new CDetails();
801 m_usemarcon->m_stage2->SetIniFileName(m_usemarcon_ini_stage2);
802 m_usemarcon->m_stage2->SetMarcRecord(converted, convlen);
803 res = m_usemarcon->m_stage2->Start();
807 m_usemarcon->m_stage2->GetMarcRecord(converted, convlen);
811 yaz_log(YLOG_LOG, "%sUSEMARCON stage 2 error %d", m_session_str, res);
814 npr->u.databaseRecord =
815 z_ext_record(odr_encode(),
823 yaz_log(YLOG_LOG, "%sUSEMARCON stage 1 error %d", m_session_str, res);
829 npr->u.databaseRecord =
830 z_ext_record(odr_encode(),
832 (char*) r->u.octet_aligned->buf,
833 r->u.octet_aligned->len);
840 void Yaz_Proxy::convert_to_marcxml(Z_NamePlusRecordList *p,
841 const char *backend_charset)
844 if (!backend_charset)
845 backend_charset = "MARC-8";
846 yaz_iconv_t cd = yaz_iconv_open("UTF-8", backend_charset);
847 yaz_marc_t mt = yaz_marc_create();
848 yaz_marc_xml(mt, YAZ_MARC_MARCXML);
849 yaz_marc_iconv(mt, cd);
850 for (i = 0; i < p->num_records; i++)
852 Z_NamePlusRecord *npr = p->records[i];
853 if (npr->which == Z_NamePlusRecord_databaseRecord)
855 Z_External *r = npr->u.databaseRecord;
856 if (r->which == Z_External_OPAC)
858 WRBUF w = wrbuf_alloc();
860 yaz_display_OPAC(w, r->u.opac, 0);
861 npr->u.databaseRecord = z_ext_record(
862 odr_encode(), VAL_TEXT_XML,
863 wrbuf_buf(w), wrbuf_len(w)
867 else if (r->which == Z_External_octet)
871 if (yaz_marc_decode_buf(mt, (char*) r->u.octet_aligned->buf,
872 r->u.octet_aligned->len,
875 npr->u.databaseRecord =
876 z_ext_record(odr_encode(), VAL_TEXT_XML, result, rlen);
883 yaz_marc_destroy(mt);
886 void Yaz_Proxy::logtime()
888 #if HAVE_GETTIMEOFDAY
889 struct timeval *tv = (struct timeval*) m_time_tv;
893 gettimeofday(&tv1, 0);
894 long diff = (tv1.tv_sec - tv->tv_sec)*1000000 +
895 (tv1.tv_usec - tv->tv_usec);
897 yaz_log(YLOG_LOG, "%sElapsed %ld.%03ld", m_session_str,
898 diff/1000000, (diff/1000)%1000);
905 int Yaz_Proxy::send_http_response(int code)
907 ODR o = odr_encode();
908 Z_GDU *gdu = z_get_HTTP_Response(o, code);
909 Z_HTTP_Response *hres = gdu->u.HTTP_Response;
911 hres->version = odr_strdup(o, m_http_version);
912 if (m_http_keepalive)
913 z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
917 if (m_log_mask & PROXY_LOG_REQ_CLIENT)
919 yaz_log (YLOG_LOG, "%sSending %s to client", m_session_str,
923 int r = send_GDU(gdu, &len);
925 m_bw_stat.add_bytes(len);
930 int Yaz_Proxy::send_srw_response(Z_SRW_PDU *srw_pdu)
932 ODR o = odr_encode();
933 const char *ctype = "text/xml";
934 Z_GDU *gdu = z_get_HTTP_Response(o, 200);
935 Z_HTTP_Response *hres = gdu->u.HTTP_Response;
937 hres->version = odr_strdup(o, m_http_version);
938 z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
939 if (m_http_keepalive)
940 z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
944 static Z_SOAP_Handler soap_handlers[2] = {
946 {"http://www.loc.gov/zing/srw/", 0,
947 (Z_SOAP_fun) yaz_srw_codec},
952 Z_SOAP *soap_package = (Z_SOAP*) odr_malloc(o, sizeof(Z_SOAP));
953 soap_package->which = Z_SOAP_generic;
954 soap_package->u.generic =
955 (Z_SOAP_Generic *) odr_malloc(o, sizeof(*soap_package->u.generic));
956 soap_package->u.generic->no = 0;
957 soap_package->u.generic->ns = soap_handlers[0].ns;
958 soap_package->u.generic->p = (void *) srw_pdu;
959 soap_package->ns = m_soap_ns;
960 z_soap_codec_enc_xsl(o, &soap_package,
961 &hres->content_buf, &hres->content_len,
962 soap_handlers, 0, m_s2z_stylesheet);
963 if (m_log_mask & PROXY_LOG_REQ_CLIENT)
965 yaz_log (YLOG_LOG, "%sSending %s to client", m_session_str,
969 int r = send_GDU(gdu, &len);
971 m_bw_stat.add_bytes(len);
976 int Yaz_Proxy::send_to_srw_client_error(int srw_error, const char *add)
978 ODR o = odr_encode();
979 Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
980 Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
982 srw_res->num_diagnostics = 1;
983 srw_res->diagnostics = (Z_SRW_diagnostic *)
984 odr_malloc(o, sizeof(*srw_res->diagnostics));
985 yaz_mk_std_diagnostic(o, srw_res->diagnostics, srw_error, add);
986 return send_srw_response(srw_pdu);
989 int Yaz_Proxy::z_to_srw_diag(ODR o, Z_SRW_searchRetrieveResponse *srw_res,
990 Z_DefaultDiagFormat *ddf)
992 int bib1_code = *ddf->condition;
993 if (bib1_code == 109)
995 srw_res->num_diagnostics = 1;
996 srw_res->diagnostics = (Z_SRW_diagnostic *)
997 odr_malloc(o, sizeof(*srw_res->diagnostics));
998 yaz_mk_std_diagnostic(o, srw_res->diagnostics,
999 yaz_diag_bib1_to_srw(*ddf->condition),
1004 int Yaz_Proxy::send_to_srw_client_ok(int hits, Z_Records *records, int start)
1006 ODR o = odr_encode();
1007 Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
1008 Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
1010 srw_res->numberOfRecords = odr_intdup (o, hits);
1011 if (records && records->which == Z_Records_DBOSD)
1013 srw_res->num_records =
1014 records->u.databaseOrSurDiagnostics->num_records;
1016 srw_res->records = (Z_SRW_record *)
1017 odr_malloc(o, srw_res->num_records * sizeof(Z_SRW_record));
1018 for (i = 0; i < srw_res->num_records; i++)
1020 Z_NamePlusRecord *npr = records->u.databaseOrSurDiagnostics->records[i];
1021 if (npr->which != Z_NamePlusRecord_databaseRecord)
1023 srw_res->records[i].recordSchema = "diagnostic";
1024 srw_res->records[i].recordPacking = m_s2z_packing;
1025 srw_res->records[i].recordData_buf = "67";
1026 srw_res->records[i].recordData_len = 2;
1027 srw_res->records[i].recordPosition = odr_intdup(o, i+start);
1030 Z_External *r = npr->u.databaseRecord;
1031 oident *ent = oid_getentbyoid(r->direct_reference);
1032 if (r->which == Z_External_octet && ent->value == VAL_TEXT_XML)
1034 srw_res->records[i].recordSchema = m_schema;
1035 srw_res->records[i].recordPacking = m_s2z_packing;
1036 srw_res->records[i].recordData_buf = (char*)
1037 r->u.octet_aligned->buf;
1038 srw_res->records[i].recordData_len = r->u.octet_aligned->len;
1039 srw_res->records[i].recordPosition = odr_intdup(o, i+start);
1043 srw_res->records[i].recordSchema = "diagnostic";
1044 srw_res->records[i].recordPacking = m_s2z_packing;
1045 srw_res->records[i].recordData_buf = "67";
1046 srw_res->records[i].recordData_len = 2;
1047 srw_res->records[i].recordPosition = odr_intdup(o, i+start);
1051 if (records && records->which == Z_Records_NSD)
1054 http_code = z_to_srw_diag(odr_encode(), srw_res,
1055 records->u.nonSurrogateDiagnostic);
1057 return send_http_response(http_code);
1059 return send_srw_response(srw_pdu);
1063 int Yaz_Proxy::send_srw_explain_response(Z_SRW_diagnostic *diagnostics,
1064 int num_diagnostics)
1066 Yaz_ProxyConfig *cfg = check_reconfigure();
1070 char *b = cfg->get_explain_doc(odr_encode(), 0 /* target */,
1071 m_s2z_database, &len);
1074 Z_SRW_PDU *res = yaz_srw_get(odr_encode(), Z_SRW_explain_response);
1075 Z_SRW_explainResponse *er = res->u.explain_response;
1077 er->record.recordData_buf = b;
1078 er->record.recordData_len = len;
1079 er->record.recordPacking = m_s2z_packing;
1080 er->record.recordSchema = "http://explain.z3950.org/dtd/2.0/";
1082 er->diagnostics = diagnostics;
1083 er->num_diagnostics = num_diagnostics;
1084 return send_srw_response(res);
1087 return send_http_response(404);
1090 int Yaz_Proxy::send_PDU_convert(Z_APDU *apdu)
1094 if (apdu->which == Z_APDU_initResponse)
1096 Z_InitResponse *res = apdu->u.initResponse;
1097 if (*res->result == 0)
1099 send_to_srw_client_error(3, 0);
1101 else if (!m_s2z_search_apdu)
1103 send_srw_explain_response(0, 0);
1107 handle_incoming_Z_PDU(m_s2z_search_apdu);
1110 else if (m_s2z_search_apdu && apdu->which == Z_APDU_searchResponse)
1112 m_s2z_search_apdu = 0;
1113 Z_SearchResponse *res = apdu->u.searchResponse;
1114 m_s2z_hit_count = *res->resultCount;
1115 if (res->records && res->records->which == Z_Records_NSD)
1117 send_to_srw_client_ok(0, res->records, 1);
1119 else if (m_s2z_present_apdu && m_s2z_hit_count > 0)
1122 Z_PresentRequest *pr = m_s2z_present_apdu->u.presentRequest;
1124 if (*pr->resultSetStartPoint <= m_s2z_hit_count)
1126 if (*pr->numberOfRecordsRequested+ *pr->resultSetStartPoint
1128 *pr->numberOfRecordsRequested =
1129 1 + m_s2z_hit_count - *pr->resultSetStartPoint;
1131 handle_incoming_Z_PDU(m_s2z_present_apdu);
1135 m_s2z_present_apdu = 0;
1136 send_to_srw_client_ok(m_s2z_hit_count, res->records, 1);
1139 else if (m_s2z_present_apdu && apdu->which == Z_APDU_presentResponse)
1142 *m_s2z_present_apdu->u.presentRequest->resultSetStartPoint;
1144 m_s2z_present_apdu = 0;
1145 Z_PresentResponse *res = apdu->u.presentResponse;
1146 send_to_srw_client_ok(m_s2z_hit_count, res->records, start);
1152 if (m_log_mask & PROXY_LOG_REQ_CLIENT)
1153 yaz_log (YLOG_LOG, "%sSending %s to client", m_session_str,
1155 int r = send_Z_PDU(apdu, &len);
1156 m_bytes_sent += len;
1157 m_bw_stat.add_bytes(len);
1164 int Yaz_Proxy::send_to_client(Z_APDU *apdu)
1166 int kill_session = 0;
1167 Z_ReferenceId **new_id = get_referenceIdP(apdu);
1170 *new_id = m_referenceId;
1172 if (apdu->which == Z_APDU_searchResponse)
1174 Z_SearchResponse *sr = apdu->u.searchResponse;
1175 Z_Records *p = sr->records;
1176 if (p && p->which == Z_Records_NSD)
1178 Z_DiagRec dr, *dr_p = &dr;
1179 dr.which = Z_DiagRec_defaultFormat;
1180 dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
1182 *sr->searchStatus = 0;
1183 display_diagrecs(&dr_p, 1);
1187 if (p && p->which == Z_Records_DBOSD)
1191 || m_usemarcon_ini_stage1 || m_usemarcon_ini_stage2
1194 convert_to_frontend_type(p->u.databaseOrSurDiagnostics);
1195 if (m_marcxml_mode == marcxml)
1196 convert_to_marcxml(p->u.databaseOrSurDiagnostics,
1198 if (convert_xsl(p->u.databaseOrSurDiagnostics, apdu))
1202 if (sr->resultCount)
1204 yaz_log(YLOG_LOG, "%s%d hits", m_session_str,
1206 if (*sr->resultCount < 0)
1208 m_invalid_session = 1;
1211 *sr->searchStatus = 0;
1213 create_nonSurrogateDiagnostics(odr_encode(), 2, 0);
1214 *sr->resultCount = 0;
1219 else if (apdu->which == Z_APDU_presentResponse)
1221 Z_PresentResponse *sr = apdu->u.presentResponse;
1222 Z_Records *p = sr->records;
1223 if (p && p->which == Z_Records_NSD)
1225 Z_DiagRec dr, *dr_p = &dr;
1226 dr.which = Z_DiagRec_defaultFormat;
1227 dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
1228 if (*sr->presentStatus == Z_PresentStatus_success)
1229 *sr->presentStatus = Z_PresentStatus_failure;
1230 display_diagrecs(&dr_p, 1);
1232 if (p && p->which == Z_Records_DBOSD)
1236 || m_usemarcon_ini_stage1 || m_usemarcon_ini_stage2
1239 convert_to_frontend_type(p->u.databaseOrSurDiagnostics);
1240 if (m_marcxml_mode == marcxml)
1241 convert_to_marcxml(p->u.databaseOrSurDiagnostics,
1243 if (convert_xsl(p->u.databaseOrSurDiagnostics, apdu))
1247 else if (apdu->which == Z_APDU_initResponse)
1249 //Get and check negotiation record
1250 //from init response.
1251 handle_charset_lang_negotiation(apdu);
1253 if (m_initRequest_options)
1256 (Odr_bitmask *)odr_malloc(odr_encode(),
1257 sizeof(Odr_bitmask));
1258 ODR_MASK_ZERO(nopt);
1261 for (i = 0; i<24; i++)
1262 if (ODR_MASK_GET(m_initRequest_options, i) &&
1263 ODR_MASK_GET(apdu->u.initResponse->options, i))
1264 ODR_MASK_SET(nopt, i);
1265 apdu->u.initResponse->options = nopt;
1267 if (m_initRequest_version)
1269 Z_ProtocolVersion *nopt =
1270 (Odr_bitmask *)odr_malloc(odr_encode(),
1271 sizeof(Odr_bitmask));
1272 ODR_MASK_ZERO(nopt);
1275 for (i = 0; i<8; i++)
1276 if (ODR_MASK_GET(m_initRequest_version, i) &&
1277 ODR_MASK_GET(apdu->u.initResponse->protocolVersion, i))
1278 ODR_MASK_SET(nopt, i);
1279 apdu->u.initResponse->protocolVersion = nopt;
1281 apdu->u.initResponse->preferredMessageSize =
1282 odr_intdup(odr_encode(),
1283 m_client->m_initResponse_preferredMessageSize >
1284 m_initRequest_preferredMessageSize ?
1285 m_initRequest_preferredMessageSize :
1286 m_client->m_initResponse_preferredMessageSize);
1287 apdu->u.initResponse->maximumRecordSize =
1288 odr_intdup(odr_encode(),
1289 m_client->m_initResponse_maximumRecordSize >
1290 m_initRequest_maximumRecordSize ?
1291 m_initRequest_maximumRecordSize :
1292 m_client->m_initResponse_maximumRecordSize);
1295 int r = send_PDU_convert(apdu);
1302 m_parent->pre_init();
1307 int Yaz_ProxyClient::send_to_target(Z_APDU *apdu)
1310 const char *apdu_name_tmp = apdu_name(apdu);
1311 int r = send_Z_PDU(apdu, &len);
1312 if (m_root->get_log_mask() & PROXY_LOG_REQ_SERVER)
1313 yaz_log (YLOG_LOG, "%sSending %s to %s %d bytes",
1315 apdu_name_tmp, get_hostname(), len);
1316 m_bytes_sent += len;
1320 Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
1322 if (apdu->which == Z_APDU_presentRequest)
1324 Z_PresentRequest *pr = apdu->u.presentRequest;
1325 int toget = *pr->numberOfRecordsRequested;
1326 int start = *pr->resultSetStartPoint;
1328 yaz_log(YLOG_LOG, "%sPresent %s %d+%d", m_session_str,
1329 pr->resultSetId, start, toget);
1331 if (*m_parent->m_optimize == '0')
1334 if (!m_client->m_last_resultSetId)
1336 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1337 new_apdu->u.presentResponse->records =
1338 create_nonSurrogateDiagnostics(odr_encode(), 30,
1340 send_to_client(new_apdu);
1343 if (!strcmp(m_client->m_last_resultSetId, pr->resultSetId))
1345 if (start+toget-1 > m_client->m_last_resultCount)
1347 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1348 new_apdu->u.presentResponse->records =
1349 create_nonSurrogateDiagnostics(odr_encode(), 13, 0);
1350 send_to_client(new_apdu);
1353 Z_NamePlusRecordList *npr;
1355 yaz_log(YLOG_LOG, "%sCache lookup %d+%d syntax=%s",
1356 m_session_str, start, toget, yaz_z3950oid_to_str(
1357 pr->preferredRecordSyntax, &oclass));
1359 if (m_client->m_cache.lookup (odr_encode(), &npr, start, toget,
1360 pr->preferredRecordSyntax,
1361 pr->recordComposition))
1363 yaz_log (YLOG_LOG, "%sReturned cached records for present request",
1365 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1366 new_apdu->u.presentResponse->referenceId = pr->referenceId;
1368 new_apdu->u.presentResponse->numberOfRecordsReturned
1369 = odr_intdup(odr_encode(), toget);
1371 new_apdu->u.presentResponse->records = (Z_Records*)
1372 odr_malloc(odr_encode(), sizeof(Z_Records));
1373 new_apdu->u.presentResponse->records->which = Z_Records_DBOSD;
1374 new_apdu->u.presentResponse->records->u.databaseOrSurDiagnostics = npr;
1375 new_apdu->u.presentResponse->nextResultSetPosition =
1376 odr_intdup(odr_encode(), start+toget);
1378 send_to_client(new_apdu);
1384 if (apdu->which != Z_APDU_searchRequest)
1386 Z_SearchRequest *sr = apdu->u.searchRequest;
1387 Yaz_Z_Query *this_query = new Yaz_Z_Query;
1388 Yaz_Z_Databases this_databases;
1390 this_databases.set(sr->num_databaseNames, (const char **)
1393 this_query->set_Z_Query(sr->query);
1395 char query_str[120];
1396 this_query->print(query_str, sizeof(query_str)-1);
1397 yaz_log(YLOG_LOG, "%sSearch %s", m_session_str, query_str);
1399 if (*m_parent->m_optimize != '0' &&
1400 m_client->m_last_ok && m_client->m_last_query &&
1401 m_client->m_last_query->match(this_query) &&
1402 !strcmp(m_client->m_last_resultSetId, sr->resultSetName) &&
1403 m_client->m_last_databases.match(this_databases))
1406 if (m_client->m_last_resultCount > *sr->smallSetUpperBound &&
1407 m_client->m_last_resultCount < *sr->largeSetLowerBound)
1409 Z_NamePlusRecordList *npr;
1410 int toget = *sr->mediumSetPresentNumber;
1411 Z_RecordComposition *comp = 0;
1413 if (toget > m_client->m_last_resultCount)
1414 toget = m_client->m_last_resultCount;
1416 if (sr->mediumSetElementSetNames)
1418 comp = (Z_RecordComposition *)
1419 odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
1420 comp->which = Z_RecordComp_simple;
1421 comp->u.simple = sr->mediumSetElementSetNames;
1424 if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget,
1425 sr->preferredRecordSyntax, comp))
1427 yaz_log (YLOG_LOG, "%sReturned cached records for medium set",
1429 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1430 new_apdu->u.searchResponse->referenceId = sr->referenceId;
1431 new_apdu->u.searchResponse->resultCount =
1432 &m_client->m_last_resultCount;
1434 new_apdu->u.searchResponse->numberOfRecordsReturned
1435 = odr_intdup(odr_encode(), toget);
1437 new_apdu->u.searchResponse->presentStatus =
1438 odr_intdup(odr_encode(), Z_PresentStatus_success);
1439 new_apdu->u.searchResponse->records = (Z_Records*)
1440 odr_malloc(odr_encode(), sizeof(Z_Records));
1441 new_apdu->u.searchResponse->records->which = Z_Records_DBOSD;
1442 new_apdu->u.searchResponse->records->u.databaseOrSurDiagnostics = npr;
1443 new_apdu->u.searchResponse->nextResultSetPosition =
1444 odr_intdup(odr_encode(), toget+1);
1445 send_to_client(new_apdu);
1451 // send present request (medium size)
1452 yaz_log (YLOG_LOG, "%sOptimizing search for medium set",
1455 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
1456 Z_PresentRequest *pr = new_apdu->u.presentRequest;
1457 pr->referenceId = sr->referenceId;
1458 pr->resultSetId = sr->resultSetName;
1459 pr->preferredRecordSyntax = sr->preferredRecordSyntax;
1460 *pr->numberOfRecordsRequested = toget;
1461 pr->recordComposition = comp;
1462 m_client->m_sr_transform = 1;
1466 else if (m_client->m_last_resultCount >= *sr->largeSetLowerBound ||
1467 m_client->m_last_resultCount <= 0)
1469 // large set. Return pseudo-search response immediately
1470 yaz_log (YLOG_LOG, "%sOptimizing search for large set",
1472 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1473 new_apdu->u.searchResponse->referenceId = sr->referenceId;
1474 new_apdu->u.searchResponse->resultCount =
1475 &m_client->m_last_resultCount;
1476 send_to_client(new_apdu);
1481 Z_NamePlusRecordList *npr;
1482 int toget = m_client->m_last_resultCount;
1483 Z_RecordComposition *comp = 0;
1485 // send a present request (small set)
1487 if (sr->smallSetElementSetNames)
1489 comp = (Z_RecordComposition *)
1490 odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
1491 comp->which = Z_RecordComp_simple;
1492 comp->u.simple = sr->smallSetElementSetNames;
1495 if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget,
1496 sr->preferredRecordSyntax, comp))
1498 yaz_log (YLOG_LOG, "%sReturned cached records for small set",
1500 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1501 new_apdu->u.searchResponse->referenceId = sr->referenceId;
1502 new_apdu->u.searchResponse->resultCount =
1503 &m_client->m_last_resultCount;
1505 new_apdu->u.searchResponse->numberOfRecordsReturned
1506 = odr_intdup(odr_encode(), toget);
1508 new_apdu->u.searchResponse->presentStatus =
1509 odr_intdup(odr_encode(), Z_PresentStatus_success);
1510 new_apdu->u.searchResponse->records = (Z_Records*)
1511 odr_malloc(odr_encode(), sizeof(Z_Records));
1512 new_apdu->u.searchResponse->records->which = Z_Records_DBOSD;
1513 new_apdu->u.searchResponse->records->u.databaseOrSurDiagnostics = npr;
1514 new_apdu->u.searchResponse->nextResultSetPosition =
1515 odr_intdup(odr_encode(), toget+1);
1516 send_to_client(new_apdu);
1521 yaz_log (YLOG_LOG, "%sOptimizing search for small set",
1523 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
1524 Z_PresentRequest *pr = new_apdu->u.presentRequest;
1525 pr->referenceId = sr->referenceId;
1526 pr->resultSetId = sr->resultSetName;
1527 pr->preferredRecordSyntax = sr->preferredRecordSyntax;
1528 *pr->numberOfRecordsRequested = toget;
1529 pr->recordComposition = comp;
1530 m_client->m_sr_transform = 1;
1535 else // query doesn't match
1537 delete m_client->m_last_query;
1538 m_client->m_last_query = this_query;
1539 m_client->m_last_ok = 0;
1540 m_client->m_cache.clear();
1541 m_client->m_resultSetStartPoint = 0;
1543 xfree (m_client->m_last_resultSetId);
1544 m_client->m_last_resultSetId = xstrdup (sr->resultSetName);
1546 m_client->m_last_databases.set(sr->num_databaseNames,
1547 (const char **) sr->databaseNames);
1553 void Yaz_Proxy::inc_request_no()
1555 char *cp = strchr(m_session_str, ' ');
1558 sprintf(cp+1, "%d ", m_request_no);
1561 void Yaz_Proxy::recv_GDU(Z_GDU *apdu, int len)
1565 m_bytes_recv += len;
1567 if (m_log_mask & PROXY_LOG_REQ_CLIENT)
1568 yaz_log (YLOG_LOG, "%sReceiving %s from client %d bytes",
1569 m_session_str, gdu_name(apdu), len);
1571 if (m_bw_hold_PDU) // double incoming PDU. shutdown now.
1574 m_bw_stat.add_bytes(len);
1575 m_pdu_stat.add_bytes(1);
1577 #if HAVE_GETTIMEOFDAY
1578 gettimeofday((struct timeval *) m_time_tv, 0);
1581 int bw_total = m_bw_stat.get_total();
1582 int pdu_total = m_pdu_stat.get_total();
1587 if (bw_total > m_bw_max)
1589 reduce = (bw_total/m_bw_max);
1594 if (pdu_total > m_pdu_max)
1596 int nreduce = (m_pdu_max >= 60) ? 1 : 60/m_pdu_max;
1597 reduce = (reduce > nreduce) ? reduce : nreduce;
1603 yaz_log(YLOG_LOG, "%sdelay=%d bw=%d pdu=%d limit-bw=%d limit-pdu=%d",
1604 m_session_str, reduce, bw_total, pdu_total,
1605 m_bw_max, m_pdu_max);
1607 m_bw_hold_PDU = apdu; // save PDU and signal "on hold"
1608 timeout(reduce); // call us reduce seconds later
1610 else if (apdu->which == Z_GDU_Z3950)
1611 handle_incoming_Z_PDU(apdu->u.z3950);
1612 else if (apdu->which == Z_GDU_HTTP_Request)
1613 handle_incoming_HTTP(apdu->u.HTTP_Request);
1616 void Yaz_Proxy::handle_max_record_retrieve(Z_APDU *apdu)
1618 if (m_max_record_retrieve)
1620 if (apdu->which == Z_APDU_presentRequest)
1622 Z_PresentRequest *pr = apdu->u.presentRequest;
1623 if (pr->numberOfRecordsRequested &&
1624 *pr->numberOfRecordsRequested > m_max_record_retrieve)
1625 *pr->numberOfRecordsRequested = m_max_record_retrieve;
1629 void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu)
1631 if (apdu->which == Z_APDU_initRequest)
1633 if (m_initRequest_options &&
1634 !ODR_MASK_GET(m_initRequest_options, Z_Options_negotiationModel) &&
1635 (m_proxy_negotiation_charset || m_proxy_negotiation_lang))
1637 // There is not negotiation proposal from
1638 // client's side. OK. The proxy negotiation
1640 Z_InitRequest *initRequest = apdu->u.initRequest;
1641 Z_OtherInformation **otherInfo;
1642 Z_OtherInformationUnit *oi;
1643 get_otherInfoAPDU(apdu, &otherInfo);
1644 oi = update_otherInformation(otherInfo, 1, NULL, 0, 0);
1647 ODR_MASK_SET(initRequest->options,
1648 Z_Options_negotiationModel);
1649 oi->which = Z_OtherInfo_externallyDefinedInfo;
1650 oi->information.externallyDefinedInfo =
1651 yaz_set_proposal_charneg(odr_encode(),
1652 (const char**)&m_proxy_negotiation_charset,
1653 m_proxy_negotiation_charset ? 1:0,
1654 (const char**)&m_proxy_negotiation_lang,
1655 m_proxy_negotiation_lang ? 1:0,
1660 else if (apdu->which == Z_APDU_initResponse)
1662 Z_InitResponse *initResponse = apdu->u.initResponse;
1663 Z_OtherInformation **otherInfo;
1665 if (ODR_MASK_GET(initResponse->options, Z_Options_negotiationModel))
1671 get_otherInfoAPDU(apdu, &otherInfo);
1673 if (!otherInfo && !(*otherInfo))
1676 Z_CharSetandLanguageNegotiation *charneg =
1677 yaz_get_charneg_record(*otherInfo);
1682 yaz_get_response_charneg(m_referenceId_mem, charneg,
1683 &charset, &lang, &selected);
1685 yaz_log(YLOG_LOG, "%sAccepted charset - '%s' and lang - '%s'",
1686 m_session_str, (charset)?charset:"none", (lang)?lang:"none");
1688 if (m_initRequest_options &&
1689 ODR_MASK_GET(m_initRequest_options, Z_Options_negotiationModel))
1691 yaz_log(YLOG_LOG, "%sClient's negotiation record in use",
1694 else if (m_proxy_negotiation_charset || m_proxy_negotiation_lang)
1696 // negotiation-charset, negotiation-lang
1697 // elements of config file in use.
1699 yaz_log(YLOG_LOG, "%sProxy's negotiation record in use",
1702 // clear negotiation option.
1703 ODR_MASK_CLEAR(initResponse->options, Z_Options_negotiationModel);
1705 // Delete negotiation (charneg-3) entry.
1706 Z_OtherInformation *p = *otherInfo;
1707 for (int i=0; i<p->num_elements; i++)
1709 if (p->list[i]->which == Z_OtherInfo_externallyDefinedInfo)
1712 p->list[i]->information.externallyDefinedInfo;
1713 struct oident *e = oid_getentbyoid(pext->direct_reference);
1715 if (e && e->value == VAL_CHARNEG3 && e->oclass == CLASS_NEGOT &&
1716 pext->which == Z_External_charSetandLanguageNegotiation)
1718 (p->num_elements)--;
1719 if(p->num_elements == 0)
1725 for (int j=i; j<p->num_elements;j++)
1726 p->list[j] = p->list[j+1];
1735 Z_Records *Yaz_Proxy::create_nonSurrogateDiagnostics(ODR odr,
1737 const char *addinfo)
1739 Z_Records *rec = (Z_Records *)
1740 odr_malloc (odr, sizeof(*rec));
1742 odr_malloc (odr, sizeof(*err));
1743 Z_DiagRec *drec = (Z_DiagRec *)
1744 odr_malloc (odr, sizeof(*drec));
1745 Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)
1746 odr_malloc (odr, sizeof(*dr));
1748 rec->which = Z_Records_NSD;
1749 rec->u.nonSurrogateDiagnostic = dr;
1750 dr->diagnosticSetId =
1751 yaz_oidval_to_z3950oid (odr, CLASS_DIAGSET, VAL_BIB1);
1752 dr->condition = err;
1753 dr->which = Z_DefaultDiagFormat_v2Addinfo;
1754 dr->u.v2Addinfo = odr_strdup (odr, addinfo ? addinfo : "");
1758 Z_APDU *Yaz_Proxy::handle_query_transformation(Z_APDU *apdu)
1760 if (apdu->which == Z_APDU_searchRequest &&
1761 apdu->u.searchRequest->query &&
1762 apdu->u.searchRequest->query->which == Z_Query_type_104 &&
1763 apdu->u.searchRequest->query->u.type_104->which == Z_External_CQL)
1765 Z_RPNQuery *rpnquery = 0;
1766 Z_SearchRequest *sr = apdu->u.searchRequest;
1769 yaz_log(YLOG_LOG, "%sCQL: %s", m_session_str,
1770 sr->query->u.type_104->u.cql);
1772 int r = m_cql2rpn.query_transform(sr->query->u.type_104->u.cql,
1773 &rpnquery, odr_encode(),
1776 yaz_log(YLOG_LOG, "%sNo CQL to RPN table", m_session_str);
1779 yaz_log(YLOG_LOG, "%sCQL Conversion error %d", m_session_str, r);
1780 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1782 new_apdu->u.searchResponse->referenceId = sr->referenceId;
1783 new_apdu->u.searchResponse->records =
1784 create_nonSurrogateDiagnostics(odr_encode(),
1785 yaz_diag_srw_to_bib1(r),
1787 *new_apdu->u.searchResponse->searchStatus = 0;
1789 send_to_client(new_apdu);
1795 sr->query->which = Z_Query_type_1;
1796 sr->query->u.type_1 = rpnquery;
1803 Z_APDU *Yaz_Proxy::handle_query_validation(Z_APDU *apdu)
1805 if (apdu->which == Z_APDU_searchRequest)
1807 Z_SearchRequest *sr = apdu->u.searchRequest;
1811 Yaz_ProxyConfig *cfg = check_reconfigure();
1813 err = cfg->check_query(odr_encode(), m_default_target,
1814 sr->query, &addinfo);
1817 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1819 new_apdu->u.searchResponse->referenceId = sr->referenceId;
1820 new_apdu->u.searchResponse->records =
1821 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
1822 *new_apdu->u.searchResponse->searchStatus = 0;
1824 send_to_client(new_apdu);
1832 int Yaz_Proxy::handle_authentication(Z_APDU *apdu)
1834 if (apdu->which != Z_APDU_initRequest)
1835 return 1; // pass if no init request
1836 Z_InitRequest *req = apdu->u.initRequest;
1838 Yaz_ProxyConfig *cfg = check_reconfigure();
1840 return 1; // pass if no config
1843 if (req->idAuthentication == 0)
1845 ret = cfg->client_authentication(m_default_target, 0, 0, 0);
1847 else if (req->idAuthentication->which == Z_IdAuthentication_idPass)
1849 ret = cfg->client_authentication(m_default_target,
1850 req->idAuthentication->u.idPass->userId,
1851 req->idAuthentication->u.idPass->groupId,
1852 req->idAuthentication->u.idPass->password);
1854 else if (req->idAuthentication->which == Z_IdAuthentication_open)
1856 char user[64], pass[64];
1859 sscanf(req->idAuthentication->u.open, "%63[^/]/%63s", user, pass);
1860 ret = cfg->client_authentication(m_default_target, user, 0, pass);
1863 ret = cfg->client_authentication(m_default_target, 0, 0, 0);
1865 cfg->target_authentication(m_default_target, odr_encode(), req);
1870 Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu)
1872 m_marcxml_mode = none;
1873 if (apdu->which == Z_APDU_searchRequest)
1875 Z_SearchRequest *sr = apdu->u.searchRequest;
1878 Yaz_ProxyConfig *cfg = check_reconfigure();
1880 Z_RecordComposition rc_temp, *rc = 0;
1881 if (sr->smallSetElementSetNames)
1883 rc_temp.which = Z_RecordComp_simple;
1884 rc_temp.u.simple = sr->smallSetElementSetNames;
1888 if (sr->preferredRecordSyntax)
1891 ent = oid_getentbyoid(sr->preferredRecordSyntax);
1892 m_frontend_type = ent->value;
1895 m_frontend_type = VAL_NONE;
1897 char *stylesheet_name = 0;
1899 err = cfg->check_syntax(odr_encode(),
1901 sr->preferredRecordSyntax, rc,
1902 &addinfo, &stylesheet_name, &m_schema,
1903 &m_backend_type, &m_backend_charset,
1904 &m_usemarcon_ini_stage1,
1905 &m_usemarcon_ini_stage2);
1906 if (stylesheet_name)
1908 m_parent->low_socket_close();
1911 if (m_stylesheet_xsp)
1912 xsltFreeStylesheet((xsltStylesheetPtr) m_stylesheet_xsp);
1913 m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*)
1916 m_stylesheet_offset = 0;
1917 xfree(stylesheet_name);
1919 m_parent->low_socket_open();
1923 sr->smallSetElementSetNames = 0;
1924 sr->mediumSetElementSetNames = 0;
1925 m_marcxml_mode = marcxml;
1929 sr->preferredRecordSyntax =
1930 yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN,
1934 sr->preferredRecordSyntax =
1935 yaz_oidval_to_z3950oid(odr_encode(), CLASS_RECSYN,
1940 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1942 new_apdu->u.searchResponse->referenceId = sr->referenceId;
1943 new_apdu->u.searchResponse->records =
1944 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
1945 *new_apdu->u.searchResponse->searchStatus = 0;
1947 send_to_client(new_apdu);
1951 else if (m_backend_type)
1953 sr->preferredRecordSyntax =
1954 yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN, m_backend_type);
1957 else if (apdu->which == Z_APDU_presentRequest)
1959 Z_PresentRequest *pr = apdu->u.presentRequest;
1962 Yaz_ProxyConfig *cfg = check_reconfigure();
1964 if (pr->preferredRecordSyntax)
1967 ent = oid_getentbyoid(pr->preferredRecordSyntax);
1968 m_frontend_type = ent->value;
1971 m_frontend_type = VAL_NONE;
1973 char *stylesheet_name = 0;
1975 err = cfg->check_syntax(odr_encode(), m_default_target,
1976 pr->preferredRecordSyntax,
1977 pr->recordComposition,
1978 &addinfo, &stylesheet_name, &m_schema,
1979 &m_backend_type, &m_backend_charset,
1980 &m_usemarcon_ini_stage1,
1981 &m_usemarcon_ini_stage2
1983 if (stylesheet_name)
1985 m_parent->low_socket_close();
1988 if (m_stylesheet_xsp)
1989 xsltFreeStylesheet((xsltStylesheetPtr) m_stylesheet_xsp);
1990 m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*)
1993 m_stylesheet_offset = 0;
1994 xfree(stylesheet_name);
1996 m_parent->low_socket_open();
2000 pr->recordComposition = 0;
2001 m_marcxml_mode = marcxml;
2005 pr->preferredRecordSyntax =
2006 yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN,
2010 pr->preferredRecordSyntax =
2011 yaz_oidval_to_z3950oid(odr_encode(), CLASS_RECSYN,
2016 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
2018 new_apdu->u.presentResponse->referenceId = pr->referenceId;
2019 new_apdu->u.presentResponse->records =
2020 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
2021 *new_apdu->u.presentResponse->presentStatus =
2022 Z_PresentStatus_failure;
2024 send_to_client(new_apdu);
2028 else if (m_backend_type)
2030 pr->preferredRecordSyntax =
2031 yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN, m_backend_type);
2037 Z_ElementSetNames *Yaz_Proxy::mk_esn_from_schema(ODR o, const char *schema)
2041 Z_ElementSetNames *esn = (Z_ElementSetNames *)
2042 odr_malloc(o, sizeof(Z_ElementSetNames));
2043 esn->which = Z_ElementSetNames_generic;
2044 esn->u.generic = odr_strdup(o, schema);
2048 void Yaz_Proxy::srw_get_client(const char *db, const char **backend_db)
2051 Yaz_ProxyConfig *cfg = check_reconfigure();
2053 t = cfg->get_explain_name(db, backend_db);
2055 if (m_client && m_default_target && t && strcmp(m_default_target, t))
2062 xfree(m_default_target);
2063 m_default_target = xstrdup(t);
2067 int Yaz_Proxy::file_access(Z_HTTP_Request *hreq)
2070 yaz_log(YLOG_LOG, "file_access");
2071 if (strcmp(hreq->method, "GET"))
2073 if (hreq->path[0] != '/')
2075 yaz_log(YLOG_WARN, "Bad path: %s", hreq->path);
2078 const char *cp = hreq->path;
2081 if (*cp == '/' && strchr("/.", cp[1]))
2083 yaz_log(YLOG_WARN, "Bad path: %s", hreq->path);
2088 const char *fname = hreq->path+1;
2089 if (stat(fname, &sbuf))
2091 yaz_log(YLOG_WARN|YLOG_ERRNO, "%s: stat failed", fname);
2094 if ((sbuf.st_mode & S_IFMT) != S_IFREG)
2096 yaz_log(YLOG_WARN, "%s: not a regular file", fname);
2099 if (sbuf.st_size > (off_t) 1000000)
2101 yaz_log(YLOG_WARN, "%s: too large for transfer", fname);
2105 ODR o = odr_encode();
2106 Yaz_ProxyConfig *cfg = check_reconfigure();
2107 const char *ctype = cfg->check_mime_type(fname);
2108 Z_GDU *gdu = z_get_HTTP_Response(o, 200);
2109 Z_HTTP_Response *hres = gdu->u.HTTP_Response;
2111 hres->version = odr_strdup(o, m_http_version);
2112 z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
2113 if (m_http_keepalive)
2114 z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
2118 hres->content_len = sbuf.st_size;
2119 hres->content_buf = (char*) odr_malloc(o, hres->content_len);
2120 FILE *f = fopen(fname, "rb");
2123 fread(hres->content_buf, 1, hres->content_len, f);
2130 if (m_log_mask & PROXY_LOG_REQ_CLIENT)
2132 yaz_log (YLOG_LOG, "%sSending file %s to client", m_session_str,
2136 send_GDU(gdu, &len);
2140 void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
2144 odr_destroy(m_s2z_odr_init);
2147 if (m_s2z_odr_search)
2149 odr_destroy(m_s2z_odr_search);
2150 m_s2z_odr_search = 0;
2153 m_http_keepalive = 0;
2155 if (!strcmp(hreq->version, "1.0"))
2157 const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
2158 if (v && !strcmp(v, "Keep-Alive"))
2159 m_http_keepalive = 1;
2161 m_http_keepalive = 0;
2162 m_http_version = "1.0";
2166 const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
2167 if (v && !strcmp(v, "close"))
2168 m_http_keepalive = 0;
2170 m_http_keepalive = 1;
2171 m_http_version = "1.1";
2174 Z_SRW_PDU *srw_pdu = 0;
2175 Z_SOAP *soap_package = 0;
2177 Z_SRW_diagnostic *diagnostic = 0;
2178 int num_diagnostic = 0;
2180 if (file_access(hreq))
2184 else if (yaz_srw_decode(hreq, &srw_pdu, &soap_package, odr_decode(),
2186 || yaz_sru_decode(hreq, &srw_pdu, &soap_package, odr_decode(),
2187 &charset, &diagnostic, &num_diagnostic) == 0)
2189 m_s2z_odr_init = odr_createmem(ODR_ENCODE);
2190 m_s2z_odr_search = odr_createmem(ODR_ENCODE);
2191 m_soap_ns = odr_strdup(m_s2z_odr_search, soap_package->ns);
2192 m_s2z_init_apdu = 0;
2193 m_s2z_search_apdu = 0;
2194 m_s2z_present_apdu = 0;
2196 m_s2z_stylesheet = 0;
2198 if (srw_pdu->which == Z_SRW_searchRetrieve_request)
2200 Z_SRW_searchRetrieveRequest *srw_req = srw_pdu->u.request;
2202 const char *backend_db = srw_req->database;
2203 srw_get_client(srw_req->database, &backend_db);
2205 m_s2z_database = odr_strdup(m_s2z_odr_init, srw_req->database);
2206 // recordXPath unsupported.
2207 if (srw_req->recordXPath)
2209 yaz_add_srw_diagnostic(odr_decode(),
2210 &diagnostic, &num_diagnostic,
2214 if (srw_req->sort_type != Z_SRW_sort_type_none)
2216 yaz_add_srw_diagnostic(odr_decode(),
2217 &diagnostic, &num_diagnostic,
2221 if (srw_req->stylesheet)
2223 odr_strdup(m_s2z_odr_init, srw_req->stylesheet);
2225 // set packing for response records ..
2226 if (srw_req->recordPacking &&
2227 !strcmp(srw_req->recordPacking, "xml"))
2228 m_s2z_packing = Z_SRW_recordPacking_XML;
2230 m_s2z_packing = Z_SRW_recordPacking_string;
2234 Z_SRW_PDU *srw_pdu =
2235 yaz_srw_get(odr_encode(),
2236 Z_SRW_searchRetrieve_response);
2237 Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
2239 srw_res->diagnostics = diagnostic;
2240 srw_res->num_diagnostics = num_diagnostic;
2241 send_srw_response(srw_pdu);
2245 // prepare search PDU
2246 m_s2z_search_apdu = zget_APDU(m_s2z_odr_search,
2247 Z_APDU_searchRequest);
2248 Z_SearchRequest *z_searchRequest =
2249 m_s2z_search_apdu->u.searchRequest;
2251 z_searchRequest->num_databaseNames = 1;
2252 z_searchRequest->databaseNames = (char**)
2253 odr_malloc(m_s2z_odr_search, sizeof(char *));
2254 z_searchRequest->databaseNames[0] = odr_strdup(m_s2z_odr_search,
2257 // query transformation
2258 Z_Query *query = (Z_Query *)
2259 odr_malloc(m_s2z_odr_search, sizeof(Z_Query));
2260 z_searchRequest->query = query;
2262 if (srw_req->query_type == Z_SRW_query_type_cql)
2264 Z_External *ext = (Z_External *)
2265 odr_malloc(m_s2z_odr_search, sizeof(*ext));
2266 ext->direct_reference =
2267 odr_getoidbystr(m_s2z_odr_search, "1.2.840.10003.16.2");
2268 ext->indirect_reference = 0;
2269 ext->descriptor = 0;
2270 ext->which = Z_External_CQL;
2271 ext->u.cql = srw_req->query.cql;
2273 query->which = Z_Query_type_104;
2274 query->u.type_104 = ext;
2276 else if (srw_req->query_type == Z_SRW_query_type_pqf)
2278 Z_RPNQuery *RPNquery;
2279 YAZ_PQF_Parser pqf_parser;
2281 pqf_parser = yaz_pqf_create ();
2283 RPNquery = yaz_pqf_parse (pqf_parser, m_s2z_odr_search,
2284 srw_req->query.pqf);
2287 const char *pqf_msg;
2289 int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
2290 yaz_log(YLOG_LOG, "%*s^\n", off+4, "");
2291 yaz_log(YLOG_LOG, "Bad PQF: %s (code %d)\n", pqf_msg, code);
2293 send_to_srw_client_error(10, 0);
2296 query->which = Z_Query_type_1;
2297 query->u.type_1 = RPNquery;
2299 yaz_pqf_destroy (pqf_parser);
2303 send_to_srw_client_error(7, "query");
2308 m_s2z_present_apdu = 0;
2310 if (srw_req->maximumRecords)
2311 max = *srw_req->maximumRecords;
2313 if (srw_req->startRecord)
2314 start = *srw_req->startRecord;
2317 // Some backend, such as Voyager doesn't honor piggyback
2318 // So we use present always (0 &&).
2319 if (0 && start <= 1) // Z39.50 piggyback
2321 *z_searchRequest->smallSetUpperBound = max;
2322 *z_searchRequest->mediumSetPresentNumber = max;
2323 *z_searchRequest->largeSetLowerBound = 2000000000; // 2e9
2325 z_searchRequest->preferredRecordSyntax =
2326 yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN,
2328 if (srw_req->recordSchema)
2330 z_searchRequest->smallSetElementSetNames =
2331 z_searchRequest->mediumSetElementSetNames =
2332 mk_esn_from_schema(m_s2z_odr_search,
2333 srw_req->recordSchema);
2336 else // Z39.50 present
2338 m_s2z_present_apdu = zget_APDU(m_s2z_odr_search,
2339 Z_APDU_presentRequest);
2340 Z_PresentRequest *z_presentRequest =
2341 m_s2z_present_apdu->u.presentRequest;
2342 *z_presentRequest->resultSetStartPoint = start;
2343 *z_presentRequest->numberOfRecordsRequested = max;
2344 z_presentRequest->preferredRecordSyntax =
2345 yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN,
2347 if (srw_req->recordSchema)
2349 z_presentRequest->recordComposition =
2350 (Z_RecordComposition *)
2351 odr_malloc(m_s2z_odr_search,
2352 sizeof(Z_RecordComposition));
2353 z_presentRequest->recordComposition->which =
2354 Z_RecordComp_simple;
2355 z_presentRequest->recordComposition->u.simple =
2356 mk_esn_from_schema(m_s2z_odr_search,
2357 srw_req->recordSchema);
2363 m_s2z_init_apdu = zget_APDU(m_s2z_odr_init,
2364 Z_APDU_initRequest);
2366 // prevent m_initRequest_apdu memory from being grabbed
2367 // in Yaz_Proxy::handle_incoming_Z_PDU
2368 m_initRequest_apdu = m_s2z_init_apdu;
2369 handle_incoming_Z_PDU(m_s2z_init_apdu);
2374 handle_incoming_Z_PDU(m_s2z_search_apdu);
2378 else if (srw_pdu->which == Z_SRW_explain_request)
2380 Z_SRW_explainRequest *srw_req = srw_pdu->u.explain_request;
2382 const char *backend_db = srw_req->database;
2383 srw_get_client(srw_req->database, &backend_db);
2385 m_s2z_database = odr_strdup(m_s2z_odr_init, srw_req->database);
2388 if (srw_req->stylesheet)
2390 odr_strdup(m_s2z_odr_init, srw_req->stylesheet);
2392 if (srw_req->recordPacking &&
2393 !strcmp(srw_req->recordPacking, "xml"))
2394 m_s2z_packing = Z_SRW_recordPacking_XML;
2396 m_s2z_packing = Z_SRW_recordPacking_string;
2400 send_srw_explain_response(diagnostic, num_diagnostic);
2406 m_s2z_init_apdu = zget_APDU(m_s2z_odr_init,
2407 Z_APDU_initRequest);
2409 // prevent m_initRequest_apdu memory from being grabbed
2410 // in Yaz_Proxy::handle_incoming_Z_PDU
2411 m_initRequest_apdu = m_s2z_init_apdu;
2412 handle_incoming_Z_PDU(m_s2z_init_apdu);
2415 send_srw_explain_response(0, 0);
2418 else if (srw_pdu->which == Z_SRW_scan_request)
2420 m_s2z_database = odr_strdup(m_s2z_odr_init,
2421 srw_pdu->u.scan_request->database);
2423 yaz_add_srw_diagnostic(odr_decode(),
2424 &diagnostic, &num_diagnostic,
2426 Z_SRW_PDU *srw_pdu =
2427 yaz_srw_get(odr_encode(),
2428 Z_SRW_scan_response);
2429 Z_SRW_scanResponse *srw_res = srw_pdu->u.scan_response;
2431 srw_res->diagnostics = diagnostic;
2432 srw_res->num_diagnostics = num_diagnostic;
2433 send_srw_response(srw_pdu);
2440 send_to_srw_client_error(4, 0);
2443 send_http_response(400);
2446 void Yaz_Proxy::handle_incoming_Z_PDU(Z_APDU *apdu)
2448 Z_ReferenceId **refid = get_referenceIdP(apdu);
2449 nmem_reset(m_referenceId_mem);
2450 if (refid && *refid)
2452 m_referenceId = (Z_ReferenceId *)
2453 nmem_malloc(m_referenceId_mem, sizeof(*m_referenceId));
2454 m_referenceId->len = m_referenceId->size = (*refid)->len;
2455 m_referenceId->buf = (unsigned char *)
2456 nmem_malloc(m_referenceId_mem, (*refid)->len);
2457 memcpy(m_referenceId->buf, (*refid)->buf, (*refid)->len);
2462 if (!m_client && m_invalid_session)
2464 m_apdu_invalid_session = apdu;
2465 m_mem_invalid_session = odr_extract_mem(odr_decode());
2466 apdu = m_initRequest_apdu;
2469 // Determine our client.
2470 Z_OtherInformation **oi;
2471 get_otherInfoAPDU(apdu, &oi);
2472 m_client = get_client(apdu, get_cookie(oi), get_proxy(oi));
2477 send_http_response(404);
2487 m_client->m_server = this;
2489 if (apdu->which == Z_APDU_initRequest)
2491 if (apdu->u.initRequest->implementationId)
2492 yaz_log(YLOG_LOG, "%simplementationId: %s",
2493 m_session_str, apdu->u.initRequest->implementationId);
2494 if (apdu->u.initRequest->implementationName)
2495 yaz_log(YLOG_LOG, "%simplementationName: %s",
2496 m_session_str, apdu->u.initRequest->implementationName);
2497 if (apdu->u.initRequest->implementationVersion)
2498 yaz_log(YLOG_LOG, "%simplementationVersion: %s",
2499 m_session_str, apdu->u.initRequest->implementationVersion);
2500 if (m_initRequest_apdu == 0)
2502 if (m_initRequest_mem)
2503 nmem_destroy(m_initRequest_mem);
2505 m_initRequest_apdu = apdu;
2506 m_initRequest_mem = odr_extract_mem(odr_decode());
2508 m_initRequest_preferredMessageSize = *apdu->u.initRequest->
2509 preferredMessageSize;
2510 *apdu->u.initRequest->preferredMessageSize = 1024*1024;
2511 m_initRequest_maximumRecordSize = *apdu->u.initRequest->
2513 *apdu->u.initRequest->maximumRecordSize = 1024*1024;
2515 // Save proposal charsets and langs.
2516 if (ODR_MASK_GET(apdu->u.initRequest->options,
2517 Z_Options_negotiationModel))
2519 Z_CharSetandLanguageNegotiation *charSetandLangRecord =
2520 yaz_get_charneg_record(*oi);
2522 yaz_get_proposal_charneg(m_referenceId_mem,
2523 charSetandLangRecord,
2524 &m_initRequest_oi_negotiation_charsets,
2525 &m_initRequest_oi_negotiation_num_charsets,
2526 &m_initRequest_oi_negotiation_langs,
2527 &m_initRequest_oi_negotiation_num_langs,
2528 &m_initRequest_oi_negotiation_selected);
2530 for (int i=0; i<m_initRequest_oi_negotiation_num_charsets; i++)
2532 yaz_log(YLOG_LOG, "%scharacters set proposal: %s",
2533 m_session_str,(m_initRequest_oi_negotiation_charsets[i])?
2534 m_initRequest_oi_negotiation_charsets[i]:"none");
2536 for (int i=0; i<m_initRequest_oi_negotiation_num_langs; i++)
2538 yaz_log(YLOG_LOG, "%slanguages proposal: %s",
2539 m_session_str, (m_initRequest_oi_negotiation_langs[i])?
2540 m_initRequest_oi_negotiation_langs[i]:"none");
2542 yaz_log(YLOG_LOG, "%sselected proposal: %d (boolean)",
2543 m_session_str, m_initRequest_oi_negotiation_selected);
2545 // save init options for the response..
2546 m_initRequest_options = apdu->u.initRequest->options;
2548 apdu->u.initRequest->options =
2549 (Odr_bitmask *)nmem_malloc(m_initRequest_mem,
2550 sizeof(Odr_bitmask));
2551 ODR_MASK_ZERO(apdu->u.initRequest->options);
2553 for (i = 0; i<= 24; i++)
2554 ODR_MASK_SET(apdu->u.initRequest->options, i);
2555 // check negotiation option
2556 if (!ODR_MASK_GET(m_initRequest_options,
2557 Z_Options_negotiationModel))
2559 ODR_MASK_CLEAR(apdu->u.initRequest->options,
2560 Z_Options_negotiationModel);
2562 ODR_MASK_CLEAR(apdu->u.initRequest->options,
2563 Z_Options_concurrentOperations);
2565 m_initRequest_version = apdu->u.initRequest->protocolVersion;
2566 apdu->u.initRequest->protocolVersion =
2567 (Odr_bitmask *)nmem_malloc(m_initRequest_mem,
2568 sizeof(Odr_bitmask));
2569 ODR_MASK_ZERO(apdu->u.initRequest->protocolVersion);
2571 for (i = 0; i<= 8; i++)
2572 ODR_MASK_SET(apdu->u.initRequest->protocolVersion, i);
2574 if (m_client->m_init_flag)
2576 if (handle_init_response_for_invalid_session(apdu))
2578 if (m_client->m_initResponse)
2580 Z_APDU *apdu2 = m_client->m_initResponse;
2581 apdu2->u.initResponse->otherInfo = 0;
2582 if (m_client->m_cookie && *m_client->m_cookie)
2583 set_otherInformationString(apdu2, VAL_COOKIE, 1,
2584 m_client->m_cookie);
2585 apdu2->u.initResponse->referenceId =
2586 apdu->u.initRequest->referenceId;
2587 apdu2->u.initResponse->options = m_client->m_initResponse_options;
2588 apdu2->u.initResponse->protocolVersion =
2589 m_client->m_initResponse_version;
2591 send_to_client(apdu2);
2595 m_client->m_init_flag = 1;
2598 if (!handle_authentication(apdu))
2600 Z_APDU *apdu_reject = zget_APDU(odr_encode(), Z_APDU_initResponse);
2601 *apdu_reject->u.initResponse->result = 0;
2602 send_to_client(apdu_reject);
2608 handle_max_record_retrieve(apdu);
2611 apdu = handle_syntax_validation(apdu);
2614 apdu = handle_query_transformation(apdu);
2617 apdu = handle_query_validation(apdu);
2620 apdu = result_set_optimize(apdu);
2623 m_client->timeout(m_target_idletime); // mark it active even
2624 // though we didn't use it
2627 // Add otherInformation entry in APDU if
2628 // negotiatoin in use.
2630 handle_charset_lang_negotiation(apdu);
2632 // delete other info construct completely if 0 elements
2633 get_otherInfoAPDU(apdu, &oi);
2634 if (oi && *oi && (*oi)->num_elements == 0)
2637 if (apdu->which == Z_APDU_presentRequest &&
2638 m_client->m_resultSetStartPoint == 0)
2640 Z_PresentRequest *pr = apdu->u.presentRequest;
2641 m_client->m_resultSetStartPoint = *pr->resultSetStartPoint;
2642 m_client->m_cache.copy_presentRequest(apdu->u.presentRequest);
2644 m_client->m_resultSetStartPoint = 0;
2646 if (m_client->send_to_target(apdu) < 0)
2653 m_client->m_waiting = 1;
2656 void Yaz_Proxy::connectNotify()
2660 void Yaz_Proxy::releaseClient()
2662 xfree(m_proxyTarget);
2664 m_invalid_session = 0;
2665 // only keep if keep_alive flag is set...
2667 m_client->m_pdu_recv < m_keepalive_limit_pdu &&
2668 m_client->m_bytes_recv+m_client->m_bytes_sent < m_keepalive_limit_bw &&
2669 m_client->m_waiting == 0)
2671 yaz_log(YLOG_LOG, "%sShutdown (client to proxy) keepalive %s",
2673 m_client->get_hostname());
2674 yaz_log(YLOG_LOG, "%sbw=%d pdu=%d limit-bw=%d limit-pdu=%d",
2675 m_session_str, m_client->m_pdu_recv,
2676 m_client->m_bytes_sent + m_client->m_bytes_recv,
2677 m_keepalive_limit_bw, m_keepalive_limit_pdu);
2678 assert (m_client->m_waiting != 2);
2679 // Tell client (if any) that no server connection is there..
2680 m_client->m_server = 0;
2685 yaz_log (YLOG_LOG, "%sShutdown (client to proxy) close %s",
2687 m_client->get_hostname());
2688 assert (m_client->m_waiting != 2);
2694 yaz_log (YLOG_LOG, "%sshutdown (client to proxy) bad state",
2700 yaz_log (YLOG_LOG, "%sShutdown (client to proxy)",
2704 m_parent->pre_init();
2707 void Yaz_Proxy::shutdown()
2713 const char *Yaz_ProxyClient::get_session_str()
2717 return m_server->get_session_str();
2720 void Yaz_ProxyClient::shutdown()
2722 yaz_log (YLOG_LOG, "%sShutdown (proxy to target) %s", get_session_str(),
2728 void Yaz_Proxy::failNotify()
2731 yaz_log (YLOG_LOG, "%sConnection closed by client",
2736 void Yaz_ProxyClient::failNotify()
2739 m_server->inc_request_no();
2740 yaz_log (YLOG_LOG, "%sConnection closed by target %s",
2741 get_session_str(), get_hostname());
2745 void Yaz_ProxyClient::connectNotify()
2747 const char *s = get_session_str();
2748 const char *h = get_hostname();
2749 yaz_log (YLOG_LOG, "%sConnection accepted by %s timeout=%d", s, h,
2751 timeout(m_target_idletime);
2756 IYaz_PDU_Observer *Yaz_ProxyClient::sessionNotify(IYaz_PDU_Observable
2757 *the_PDU_Observable, int fd)
2759 return new Yaz_ProxyClient(the_PDU_Observable, 0);
2762 Yaz_ProxyClient::~Yaz_ProxyClient()
2767 m_next->m_prev = m_prev;
2768 m_waiting = 2; // for debugging purposes only.
2769 odr_destroy(m_init_odr);
2770 delete m_last_query;
2771 xfree (m_last_resultSetId);
2775 void Yaz_ProxyClient::pre_init_client()
2777 Z_APDU *apdu = create_Z_PDU(Z_APDU_initRequest);
2778 Z_InitRequest *req = apdu->u.initRequest;
2781 for (i = 0; i<= 24; i++)
2782 ODR_MASK_SET(req->options, i);
2783 ODR_MASK_CLEAR(apdu->u.initRequest->options,
2784 Z_Options_negotiationModel);
2785 ODR_MASK_CLEAR(apdu->u.initRequest->options,
2786 Z_Options_concurrentOperations);
2787 for (i = 0; i<= 10; i++)
2788 ODR_MASK_SET(req->protocolVersion, i);
2790 if (send_to_target(apdu) < 0)
2801 void Yaz_Proxy::pre_init()
2804 const char *name = 0;
2805 const char *zurl_in_use[MAX_ZURL_PLEX];
2806 int limit_bw, limit_pdu, limit_req;
2807 int target_idletime, client_idletime;
2809 int keepalive_limit_bw, keepalive_limit_pdu;
2811 const char *cql2rpn = 0;
2812 const char *authentication = 0;
2813 const char *negotiation_charset = 0;
2814 const char *negotiation_lang = 0;
2816 Yaz_ProxyConfig *cfg = check_reconfigure();
2820 if (m_log_mask & PROXY_LOG_APDU_CLIENT)
2825 for (i = 0; cfg && cfg->get_target_no(i, &name, zurl_in_use,
2826 &limit_bw, &limit_pdu, &limit_req,
2827 &target_idletime, &client_idletime,
2829 &keepalive_limit_bw,
2830 &keepalive_limit_pdu,
2834 &negotiation_charset,
2835 &negotiation_lang) ; i++)
2840 for (j = 0; zurl_in_use[j]; j++)
2844 int spare_waiting = 0;
2847 for (c = m_clientPool; c; c = c->m_next)
2849 if (!strcmp(zurl_in_use[j], c->get_hostname()))
2851 if (c->m_cookie == 0)
2853 if (c->m_server == 0)
2865 yaz_log(YLOG_LOG, "%spre-init %s %s use=%d other=%d spare=%d "
2866 "sparew=%d preinit=%d",m_session_str,
2867 name, zurl_in_use[j], in_use, other,
2868 spare, spare_waiting, pre_init);
2869 if (spare + spare_waiting < pre_init)
2871 c = new Yaz_ProxyClient(m_PDU_Observable->clone(), this);
2872 c->m_next = m_clientPool;
2874 c->m_next->m_prev = &c->m_next;
2876 c->m_prev = &m_clientPool;
2878 if (m_log_mask & PROXY_LOG_APDU_SERVER)
2879 c->set_APDU_yazlog(1);
2881 c->set_APDU_yazlog(0);
2883 if (c->client(zurl_in_use[j]))
2891 c->m_target_idletime = target_idletime;
2892 c->m_seqno = m_seqno++;
2899 void Yaz_Proxy::timeoutNotify()
2905 timeout(m_client_idletime);
2906 Z_GDU *apdu = m_bw_hold_PDU;
2909 if (apdu->which == Z_GDU_Z3950)
2910 handle_incoming_Z_PDU(apdu->u.z3950);
2911 else if (apdu->which == Z_GDU_HTTP_Request)
2912 handle_incoming_HTTP(apdu->u.HTTP_Request);
2914 else if (m_stylesheet_nprl)
2915 convert_xsl_delay();
2920 yaz_log (YLOG_LOG, "%sTimeout (client to proxy)", m_session_str);
2931 void Yaz_Proxy::markInvalid()
2934 m_invalid_session = 1;
2937 void Yaz_ProxyClient::timeoutNotify()
2940 m_server->inc_request_no();
2942 yaz_log (YLOG_LOG, "%sTimeout (proxy to target) %s", get_session_str(),
2946 if (m_server && m_init_flag)
2948 // target timed out in a session that was properly initialized
2949 // server object stay alive but we mark it as invalid so it
2950 // gets initialized again
2951 m_server->markInvalid();
2957 Yaz_ProxyClient::Yaz_ProxyClient(IYaz_PDU_Observable *the_PDU_Observable,
2958 Yaz_Proxy *parent) :
2959 Yaz_Z_Assoc (the_PDU_Observable)
2966 m_last_resultSetId = 0;
2967 m_last_resultCount = 0;
2971 m_init_odr = odr_createmem (ODR_DECODE);
2973 m_initResponse_options = 0;
2974 m_initResponse_version = 0;
2975 m_initResponse_preferredMessageSize = 0;
2976 m_initResponse_maximumRecordSize = 0;
2977 m_resultSetStartPoint = 0;
2978 m_bytes_sent = m_bytes_recv = 0;
2982 m_target_idletime = 600;
2986 const char *Yaz_Proxy::option(const char *name, const char *value)
2988 if (!strcmp (name, "optimize")) {
2991 m_optimize = xstrdup (value);
2998 void Yaz_ProxyClient::recv_HTTP_response(Z_HTTP_Response *apdu, int len)
3003 void Yaz_ProxyClient::recv_GDU(Z_GDU *apdu, int len)
3005 if (apdu->which == Z_GDU_Z3950)
3006 recv_Z_PDU(apdu->u.z3950, len);
3007 else if (apdu->which == Z_GDU_HTTP_Response)
3008 recv_HTTP_response(apdu->u.HTTP_Response, len);
3013 int Yaz_Proxy::handle_init_response_for_invalid_session(Z_APDU *apdu)
3015 if (!m_invalid_session)
3017 m_invalid_session = 0;
3018 handle_incoming_Z_PDU(m_apdu_invalid_session);
3019 assert (m_mem_invalid_session);
3020 nmem_destroy(m_mem_invalid_session);
3021 m_mem_invalid_session = 0;
3025 void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len)
3027 m_bytes_recv += len;
3031 if (m_root->get_log_mask() & PROXY_LOG_REQ_SERVER)
3032 yaz_log (YLOG_LOG, "%sReceiving %s from %s %d bytes", get_session_str(),
3033 apdu_name(apdu), get_hostname(), len);
3034 if (apdu->which == Z_APDU_initResponse)
3036 if (!m_server) // if this is a pre init session , check for more
3038 NMEM nmem = odr_extract_mem (odr_decode());
3039 odr_reset (m_init_odr);
3040 nmem_transfer (m_init_odr->mem, nmem);
3041 m_initResponse = apdu;
3042 m_initResponse_options = apdu->u.initResponse->options;
3043 m_initResponse_version = apdu->u.initResponse->protocolVersion;
3044 m_initResponse_preferredMessageSize =
3045 *apdu->u.initResponse->preferredMessageSize;
3046 m_initResponse_maximumRecordSize =
3047 *apdu->u.initResponse->maximumRecordSize;
3049 Z_InitResponse *ir = apdu->u.initResponse;
3050 char *im0 = ir->implementationName;
3053 odr_malloc(m_init_odr, 20 + (im0 ? strlen(im0) : 0));
3060 strcat(im1, "(YAZ Proxy)");
3061 ir->implementationName = im1;
3063 nmem_destroy (nmem);
3065 if (m_server && m_server->handle_init_response_for_invalid_session(apdu))
3068 if (apdu->which == Z_APDU_searchResponse)
3070 Z_SearchResponse *sr = apdu->u.searchResponse;
3071 m_last_resultCount = *sr->resultCount;
3072 int status = *sr->searchStatus;
3073 if (status && (!sr->records || sr->records->which == Z_Records_DBOSD))
3077 if (sr->records && sr->records->which == Z_Records_DBOSD)
3079 m_cache.add(odr_decode(),
3080 sr->records->u.databaseOrSurDiagnostics, 1,
3085 if (apdu->which == Z_APDU_presentResponse)
3087 Z_PresentResponse *pr = apdu->u.presentResponse;
3091 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
3092 Z_SearchResponse *sr = new_apdu->u.searchResponse;
3093 sr->referenceId = pr->referenceId;
3094 *sr->resultCount = m_last_resultCount;
3095 sr->records = pr->records;
3096 sr->nextResultSetPosition = pr->nextResultSetPosition;
3097 sr->numberOfRecordsReturned = pr->numberOfRecordsReturned;
3101 pr->records->which == Z_Records_DBOSD && m_resultSetStartPoint)
3103 m_cache.add(odr_decode(),
3104 pr->records->u.databaseOrSurDiagnostics,
3105 m_resultSetStartPoint, -1);
3106 m_resultSetStartPoint = 0;
3110 set_otherInformationString (apdu, VAL_COOKIE, 1, m_cookie);
3113 m_server->send_to_client(apdu);
3115 if (apdu->which == Z_APDU_close)
3121 void Yaz_Proxy::low_socket_close()
3126 for (i = 0; i<NO_SPARE_SOLARIS_FD; i++)
3127 if (m_lo_fd[i] >= 0)
3128 ::close(m_lo_fd[i]);
3132 void Yaz_Proxy::low_socket_open()
3137 for (i = 0; i<NO_SPARE_SOLARIS_FD; i++)
3138 m_lo_fd[i] = open("/dev/null", O_RDONLY);
3142 int Yaz_Proxy::server(const char *addr)
3144 int r = Yaz_Z_Assoc::server(addr);
3147 yaz_log(YLOG_LOG, "%sStarted proxy "
3151 " on %s", m_session_str, addr);