X-Git-Url: http://git.indexdata.com/?a=blobdiff_plain;f=src%2Fyaz-proxy.cpp;h=893cc5d67201d000161b5a8fc006d4d758776625;hb=d3d51bee2100324025f7530d4e3ff3502397fd83;hp=d623c3655699f5e9f97c5be0cd9386331afee2bd;hpb=307a8638d6429b0f77ad762d52309c550e45648c;p=yazpp-moved-to-github.git diff --git a/src/yaz-proxy.cpp b/src/yaz-proxy.cpp index d623c36..893cc5d 100644 --- a/src/yaz-proxy.cpp +++ b/src/yaz-proxy.cpp @@ -1,12 +1,15 @@ /* - * Copyright (c) 1998-2003, Index Data. + * Copyright (c) 1998-2004, Index Data. * See the file LICENSE for details. * - * $Id: yaz-proxy.cpp,v 1.71 2003-12-16 14:17:01 adam Exp $ + * $Id: yaz-proxy.cpp,v 1.109 2004-03-15 22:49:13 adam Exp $ */ +#include #include #include +#include +#include #include #include @@ -90,7 +93,8 @@ Yaz_Proxy::Yaz_Proxy(IYaz_PDU_Observable *the_PDU_Observable, m_optimize = xstrdup ("1"); strcpy(m_session_str, "0 "); m_session_no=0; - m_bytes_sent = m_bytes_recv = 0; + m_bytes_sent = 0; + m_bytes_recv = 0; m_bw_hold_PDU = 0; m_bw_max = 0; m_pdu_max = 0; @@ -99,13 +103,25 @@ Yaz_Proxy::Yaz_Proxy(IYaz_PDU_Observable *the_PDU_Observable, m_config_fname = 0; m_request_no = 0; m_invalid_session = 0; + m_referenceId = 0; + m_referenceId_mem = nmem_create(); m_config = 0; m_marcxml_flag = 0; + m_stylesheet_xsp = 0; + m_stylesheet_nprl = 0; + m_s2z_stylesheet = 0; + m_s2z_database = 0; + m_schema = 0; m_initRequest_apdu = 0; m_initRequest_mem = 0; + m_initRequest_preferredMessageSize = 0; + m_initRequest_maximumRecordSize = 0; + m_initRequest_options = 0; + m_initRequest_version = 0; m_apdu_invalid_session = 0; m_mem_invalid_session = 0; - m_s2z_odr = 0; + m_s2z_odr_init = 0; + m_s2z_odr_search = 0; m_s2z_init_apdu = 0; m_s2z_search_apdu = 0; m_s2z_present_apdu = 0; @@ -113,6 +129,10 @@ Yaz_Proxy::Yaz_Proxy(IYaz_PDU_Observable *the_PDU_Observable, m_http_version = 0; m_soap_ns = 0; m_s2z_packing = Z_SRW_recordPacking_string; + m_time_tv.tv_sec = 0; + m_time_tv.tv_usec = 0; + if (!m_parent) + low_socket_open(); } Yaz_Proxy::~Yaz_Proxy() @@ -121,12 +141,23 @@ Yaz_Proxy::~Yaz_Proxy() m_bytes_sent, m_bytes_recv); nmem_destroy(m_initRequest_mem); nmem_destroy(m_mem_invalid_session); + nmem_destroy(m_referenceId_mem); + xfree (m_proxyTarget); xfree (m_default_target); xfree (m_proxy_authentication); xfree (m_optimize); - if (m_s2z_odr) - odr_destroy(m_s2z_odr); + + if (m_stylesheet_xsp) + xsltFreeStylesheet(m_stylesheet_xsp); + + xfree (m_schema); + if (m_s2z_odr_init) + odr_destroy(m_s2z_odr_init); + if (m_s2z_odr_search) + odr_destroy(m_s2z_odr_search); + if (!m_parent) + low_socket_close(); delete m_config; } @@ -247,30 +278,53 @@ char *Yaz_Proxy::get_proxy(Z_OtherInformation **otherInfo) const char *Yaz_Proxy::load_balance(const char **url) { int zurl_in_use[MAX_ZURL_PLEX]; + int zurl_in_spare[MAX_ZURL_PLEX]; Yaz_ProxyClient *c; int i; for (i = 0; im_clientPool; c; c = c->m_next) { for (i = 0; url[i]; i++) if (!strcmp(url[i], c->get_hostname())) + { zurl_in_use[i]++; + if (c->m_cookie == 0 && c->m_server == 0 && c->m_waiting == 0) + zurl_in_spare[i]++; + } } - int min = 100000; - const char *ret = 0; + int min_use = 100000; + int spare_for_min = 0; + int max_spare = 0; + const char *ret_min = 0; + const char *ret_spare = 0; for (i = 0; url[i]; i++) { - yaz_log(LOG_DEBUG, "%szurl=%s use=%d", - m_session_str, url[i], zurl_in_use[i]); - if (min > zurl_in_use[i]) + yaz_log(LOG_DEBUG, "%szurl=%s use=%d spare=%d", + m_session_str, url[i], zurl_in_use[i], zurl_in_spare[i]); + if (min_use > zurl_in_use[i]) + { + ret_min = url[i]; + min_use = zurl_in_use[i]; + spare_for_min = zurl_in_spare[i]; + } + if (max_spare < zurl_in_spare[i]) { - ret = url[i]; - min = zurl_in_use[i]; + ret_spare = url[i]; + max_spare = zurl_in_spare[i]; } } - return ret; + // use the one with minimum connections if spare is > 3 + if (spare_for_min > 3) + return ret_min; + // use one with most spares (if any) + if (max_spare > 0) + return ret_spare; + return ret_min; } Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, @@ -278,7 +332,6 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, { assert (m_parent); Yaz_Proxy *parent = m_parent; - Z_OtherInformation **oi; Yaz_ProxyClient *c = m_client; if (!m_proxyTarget) @@ -318,10 +371,7 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, timeout(m_client_idletime); } if (cql2rpn_fname) - { - yaz_log(LOG_LOG, "set_pqf_file %s", cql2rpn_fname); m_cql2rpn.set_pqf_file(cql2rpn_fname); - } if (!url[0]) { yaz_log(LOG_LOG, "%sNo default target", m_session_str); @@ -391,8 +441,8 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, !strcmp(m_proxyTarget, c->get_hostname())) { // found it in cache - yaz_log (LOG_LOG, "%sREUSE %s", - m_session_str, c->get_hostname()); + yaz_log (LOG_LOG, "%sREUSE %d %s", + m_session_str, parent->m_seqno, c->get_hostname()); c->m_seqno = parent->m_seqno; assert(c->m_server == 0); @@ -415,7 +465,7 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, { if (apdu->which != Z_APDU_initRequest) { - yaz_log (LOG_LOG, "no first INIT!"); + yaz_log (LOG_LOG, "%sno init request as first PDU", m_session_str); return 0; } Z_InitRequest *initRequest = apdu->u.initRequest; @@ -434,7 +484,6 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, odr_strdup (odr_encode(), m_proxy_authentication); } } - // go through list of clients - and find the lowest/oldest one. Yaz_ProxyClient *c_min = 0; int min_seq = -1; @@ -458,16 +507,16 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, c = c_min; if (c->m_waiting || strcmp(m_proxyTarget, c->get_hostname())) { - yaz_log (LOG_LOG, "%sMAXCLIENTS Destroy %d", - m_session_str, c->m_seqno); + yaz_log (LOG_LOG, "%sMAXCLIENTS %d Destroy %d", + m_session_str, parent->m_max_clients, c->m_seqno); if (c->m_server && c->m_server != this) delete c->m_server; c->m_server = 0; } else { - yaz_log (LOG_LOG, "%sMAXCLIENTS Reuse %d %d %s", - m_session_str, + yaz_log (LOG_LOG, "%sMAXCLIENTS %d Reuse %d %d %s", + m_session_str, parent->m_max_clients, c->m_seqno, parent->m_seqno, c->get_hostname()); xfree (c->m_cookie); c->m_cookie = 0; @@ -570,12 +619,75 @@ void Yaz_Proxy::display_diagrecs(Z_DiagRec **pp, int num) } } +int Yaz_Proxy::convert_xsl(Z_NamePlusRecordList *p, Z_APDU *apdu) +{ + if (!m_stylesheet_xsp || p->num_records <= 0) + return 0; /* no XSLT to be done ... */ + + m_stylesheet_offset = 0; + m_stylesheet_nprl = p; + m_stylesheet_apdu = apdu; + timeout(0); + return 1; +} + +void Yaz_Proxy::convert_xsl_delay() +{ + Z_NamePlusRecord *npr = m_stylesheet_nprl->records[m_stylesheet_offset]; + if (npr->which == Z_NamePlusRecord_databaseRecord) + { + Z_External *r = npr->u.databaseRecord; + if (r->which == Z_External_octet) + { + fwrite((char*) r->u.octet_aligned->buf, 1, r->u.octet_aligned->len, stdout); + xmlDocPtr res, doc = xmlParseMemory( + (char*) r->u.octet_aligned->buf, + r->u.octet_aligned->len); + + + yaz_log(LOG_LOG, "%sXSLT convert %d", + m_session_str, m_stylesheet_offset); + res = xsltApplyStylesheet(m_stylesheet_xsp, doc, 0); + + if (res) + { + xmlChar *out_buf; + int out_len; + xmlDocDumpFormatMemory (res, &out_buf, &out_len, 1); + + m_stylesheet_nprl->records[m_stylesheet_offset]-> + u.databaseRecord = + z_ext_record(odr_encode(), VAL_TEXT_XML, + (char*) out_buf, out_len); + xmlFree(out_buf); + xmlFreeDoc(res); + } + + xmlFreeDoc(doc); + } + } + m_stylesheet_offset++; + if (m_stylesheet_offset == m_stylesheet_nprl->num_records) + { + m_stylesheet_nprl = 0; + if (m_stylesheet_xsp) + xsltFreeStylesheet(m_stylesheet_xsp); + m_stylesheet_xsp = 0; + timeout(m_client_idletime); + send_PDU_convert(m_stylesheet_apdu); + } + else + timeout(0); +} + void Yaz_Proxy::convert_to_marcxml(Z_NamePlusRecordList *p) { int i; + yaz_iconv_t cd = yaz_iconv_open("UTF-8", "MARC-8"); yaz_marc_t mt = yaz_marc_create(); yaz_marc_xml(mt, YAZ_MARC_MARCXML); + yaz_marc_iconv(mt, cd); for (i = 0; i < p->num_records; i++) { Z_NamePlusRecord *npr = p->records[i]; @@ -590,58 +702,56 @@ void Yaz_Proxy::convert_to_marcxml(Z_NamePlusRecordList *p) r->u.octet_aligned->len, &result, &rlen)) { - yaz_iconv_t cd = yaz_iconv_open("UTF-8", "MARC-8"); - WRBUF wrbuf = wrbuf_alloc(); - - char outbuf[120]; - size_t inbytesleft = rlen; - const char *inp = result; - while (cd && inbytesleft) - { - size_t outbytesleft = sizeof(outbuf); - char *outp = outbuf; - size_t r; - - r = yaz_iconv (cd, (char**) &inp, - &inbytesleft, - &outp, &outbytesleft); - if (r == (size_t) (-1)) - { - int e = yaz_iconv_error(cd); - if (e != YAZ_ICONV_E2BIG) - { - yaz_log(LOG_WARN, "conversion failure"); - break; - } - } - wrbuf_write(wrbuf, outbuf, outp - outbuf); - } - if (cd) - yaz_iconv_close(cd); - npr->u.databaseRecord = z_ext_record(odr_encode(), VAL_TEXT_XML, - wrbuf_buf(wrbuf), - wrbuf_len(wrbuf)); - wrbuf_free(wrbuf, 1); + result, rlen); } } } } + if (cd) + yaz_iconv_close(cd); yaz_marc_destroy(mt); } +void Yaz_Proxy::logtime() +{ + if (m_time_tv.tv_sec) + { + struct timeval tv; + gettimeofday(&tv, 0); + long diff = (tv.tv_sec - m_time_tv.tv_sec)*1000000 + + (tv.tv_usec - m_time_tv.tv_usec); + if (diff >= 0) + yaz_log(LOG_LOG, "%sElapsed %ld.%03ld", m_session_str, + diff/1000000, (diff/1000)%1000); + } + m_time_tv.tv_sec = 0; + m_time_tv.tv_usec = 0; +} + int Yaz_Proxy::send_http_response(int code) { ODR o = odr_encode(); - const char *ctype = "text/xml"; Z_GDU *gdu = z_get_HTTP_Response(o, code); Z_HTTP_Response *hres = gdu->u.HTTP_Response; if (m_http_version) hres->version = odr_strdup(o, m_http_version); + if (m_http_keepalive) + z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive"); + else + timeout(0); + + if (m_log_mask & PROXY_LOG_REQ_CLIENT) + { + yaz_log (LOG_LOG, "%sSending %s to client", m_session_str, + gdu_name(gdu)); + } int len; int r = send_GDU(gdu, &len); - delete this; + m_bytes_sent += len; + m_bw_stat.add_bytes(len); + logtime(); return r; } @@ -656,10 +766,12 @@ int Yaz_Proxy::send_srw_response(Z_SRW_PDU *srw_pdu) z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype); if (m_http_keepalive) z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive"); + else + timeout(0); static Z_SOAP_Handler soap_handlers[2] = { -#if HAVE_XML2 - {"http://www.loc.gov/zing/srw/v1.0/", 0, +#if HAVE_XSLT + {"http://www.loc.gov/zing/srw/", 0, (Z_SOAP_fun) yaz_srw_codec}, #endif {0, 0, 0} @@ -673,16 +785,23 @@ int Yaz_Proxy::send_srw_response(Z_SRW_PDU *srw_pdu) soap_package->u.generic->ns = soap_handlers[0].ns; soap_package->u.generic->p = (void *) srw_pdu; soap_package->ns = m_soap_ns; - int ret = z_soap_codec_enc(o, &soap_package, - &hres->content_buf, &hres->content_len, - soap_handlers, 0); + z_soap_codec_enc_xsl(o, &soap_package, + &hres->content_buf, &hres->content_len, + soap_handlers, 0, m_s2z_stylesheet); + if (m_log_mask & PROXY_LOG_REQ_CLIENT) + { + yaz_log (LOG_LOG, "%sSending %s to client", m_session_str, + gdu_name(gdu)); + } int len; int r = send_GDU(gdu, &len); - if (!m_http_keepalive) - delete this; + m_bytes_sent += len; + m_bw_stat.add_bytes(len); + logtime(); + return r; } -int Yaz_Proxy::send_to_srw_client_error(int srw_error) +int Yaz_Proxy::send_to_srw_client_error(int srw_error, const char *add) { ODR o = odr_encode(); Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response); @@ -691,8 +810,7 @@ int Yaz_Proxy::send_to_srw_client_error(int srw_error) srw_res->num_diagnostics = 1; srw_res->diagnostics = (Z_SRW_diagnostic *) odr_malloc(o, sizeof(*srw_res->diagnostics)); - srw_res->diagnostics[0].code = odr_intdup(o, srw_error); - srw_res->diagnostics[0].details = 0; + yaz_mk_std_diagnostic(o, srw_res->diagnostics, srw_error, add); return send_srw_response(srw_pdu); } @@ -705,13 +823,13 @@ int Yaz_Proxy::z_to_srw_diag(ODR o, Z_SRW_searchRetrieveResponse *srw_res, srw_res->num_diagnostics = 1; srw_res->diagnostics = (Z_SRW_diagnostic *) odr_malloc(o, sizeof(*srw_res->diagnostics)); - srw_res->diagnostics[0].code = - odr_intdup(o, yaz_diag_bib1_to_srw(*ddf->condition)); - srw_res->diagnostics[0].details = ddf->u.v2Addinfo; + yaz_mk_std_diagnostic(o, srw_res->diagnostics, + yaz_diag_bib1_to_srw(*ddf->condition), + ddf->u.v2Addinfo); return 0; } -int Yaz_Proxy::send_to_srw_client_ok(int hits, Z_Records *records) +int Yaz_Proxy::send_to_srw_client_ok(int hits, Z_Records *records, int start) { ODR o = odr_encode(); Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response); @@ -734,19 +852,19 @@ int Yaz_Proxy::send_to_srw_client_ok(int hits, Z_Records *records) srw_res->records[i].recordPacking = m_s2z_packing; srw_res->records[i].recordData_buf = "67"; srw_res->records[i].recordData_len = 2; - srw_res->records[i].recordPosition = 0; + srw_res->records[i].recordPosition = odr_intdup(o, i+start); continue; } Z_External *r = npr->u.databaseRecord; oident *ent = oid_getentbyoid(r->direct_reference); if (r->which == Z_External_octet && ent->value == VAL_TEXT_XML) { - srw_res->records[i].recordSchema = "http://www.loc.gov/marcxml/"; + srw_res->records[i].recordSchema = m_schema; srw_res->records[i].recordPacking = m_s2z_packing; srw_res->records[i].recordData_buf = (char*) r->u.octet_aligned->buf; srw_res->records[i].recordData_len = r->u.octet_aligned->len; - srw_res->records[i].recordPosition = 0; + srw_res->records[i].recordPosition = odr_intdup(o, i+start); } else { @@ -754,7 +872,7 @@ int Yaz_Proxy::send_to_srw_client_ok(int hits, Z_Records *records) srw_res->records[i].recordPacking = m_s2z_packing; srw_res->records[i].recordData_buf = "67"; srw_res->records[i].recordData_len = 2; - srw_res->records[i].recordPosition = 0; + srw_res->records[i].recordPosition = odr_intdup(o, i+start); } } } @@ -769,54 +887,116 @@ int Yaz_Proxy::send_to_srw_client_ok(int hits, Z_Records *records) return send_srw_response(srw_pdu); } -int Yaz_Proxy::send_PDU_convert(Z_APDU *apdu, int *len) + +int Yaz_Proxy::send_srw_explain_response(Z_SRW_diagnostic *diagnostics, + int num_diagnostics) { - if (m_s2z_init_apdu && apdu->which == Z_APDU_initResponse) + Yaz_ProxyConfig *cfg = check_reconfigure(); + if (cfg) { - m_s2z_init_apdu = 0; - Z_InitResponse *res = apdu->u.initResponse; - if (*res->result == 0) + int len; + char *b = cfg->get_explain(odr_encode(), 0 /* target */, + m_s2z_database, &len); + if (b) { - send_to_srw_client_error(3); - } - else - { - handle_incoming_Z_PDU(m_s2z_search_apdu); + Z_SRW_PDU *res = yaz_srw_get(odr_encode(), Z_SRW_explain_response); + Z_SRW_explainResponse *er = res->u.explain_response; + + er->record.recordData_buf = b; + er->record.recordData_len = len; + er->record.recordPacking = m_s2z_packing; + er->record.recordSchema = "http://explain.z3950.org/dtd/2.0/"; + + er->diagnostics = diagnostics; + er->num_diagnostics = num_diagnostics; + return send_srw_response(res); } } - else if (m_s2z_search_apdu && apdu->which == Z_APDU_searchResponse) + return send_http_response(404); +} + +int Yaz_Proxy::send_PDU_convert(Z_APDU *apdu) +{ + if (m_http_version) { - m_s2z_search_apdu = 0; - Z_SearchResponse *res = apdu->u.searchResponse; - m_s2z_hit_count = *res->resultCount; - if (res->records && res->records->which == Z_Records_NSD) + if (apdu->which == Z_APDU_initResponse) { - send_to_srw_client_ok(0, res->records); + Z_InitResponse *res = apdu->u.initResponse; + if (*res->result == 0) + { + send_to_srw_client_error(3, 0); + } + else if (!m_s2z_search_apdu) + { + send_srw_explain_response(0, 0); + } + else + { + handle_incoming_Z_PDU(m_s2z_search_apdu); + } } - else if (m_s2z_present_apdu) + else if (m_s2z_search_apdu && apdu->which == Z_APDU_searchResponse) { - handle_incoming_Z_PDU(m_s2z_present_apdu); + m_s2z_search_apdu = 0; + Z_SearchResponse *res = apdu->u.searchResponse; + m_s2z_hit_count = *res->resultCount; + if (res->records && res->records->which == Z_Records_NSD) + { + send_to_srw_client_ok(0, res->records, 1); + } + else if (m_s2z_present_apdu && m_s2z_hit_count > 0) + { + // adjust + Z_PresentRequest *pr = m_s2z_present_apdu->u.presentRequest; + + if (*pr->resultSetStartPoint <= m_s2z_hit_count) + { + if (*pr->numberOfRecordsRequested+ *pr->resultSetStartPoint + > m_s2z_hit_count) + *pr->numberOfRecordsRequested = + 1 + m_s2z_hit_count - *pr->resultSetStartPoint; + } + handle_incoming_Z_PDU(m_s2z_present_apdu); + } + else + { + m_s2z_present_apdu = 0; + send_to_srw_client_ok(m_s2z_hit_count, res->records, 1); + } } - else + else if (m_s2z_present_apdu && apdu->which == Z_APDU_presentResponse) { - send_to_srw_client_ok(m_s2z_hit_count, res->records); + int start = + *m_s2z_present_apdu->u.presentRequest->resultSetStartPoint; + + m_s2z_present_apdu = 0; + Z_PresentResponse *res = apdu->u.presentResponse; + send_to_srw_client_ok(m_s2z_hit_count, res->records, start); } } - else if (m_s2z_present_apdu && apdu->which == Z_APDU_presentResponse) + else { - m_s2z_present_apdu = 0; - Z_PresentResponse *res = apdu->u.presentResponse; - send_to_srw_client_ok(m_s2z_hit_count, res->records); + int len = 0; + if (m_log_mask & PROXY_LOG_REQ_CLIENT) + yaz_log (LOG_LOG, "%sSending %s to client", m_session_str, + apdu_name(apdu)); + int r = send_Z_PDU(apdu, &len); + m_bytes_sent += len; + m_bw_stat.add_bytes(len); + logtime(); + return r; } - else - return send_Z_PDU(apdu, len); return 0; } int Yaz_Proxy::send_to_client(Z_APDU *apdu) { - int len = 0; int kill_session = 0; + Z_ReferenceId **new_id = get_referenceIdP(apdu); + + if (new_id) + *new_id = m_referenceId; + if (apdu->which == Z_APDU_searchResponse) { Z_SearchResponse *sr = apdu->u.searchResponse; @@ -832,8 +1012,14 @@ int Yaz_Proxy::send_to_client(Z_APDU *apdu) } else { - if (m_marcxml_flag && p && p->which == Z_Records_DBOSD) - convert_to_marcxml(p->u.databaseOrSurDiagnostics); + if (p && p->which == Z_Records_DBOSD) + { + if (m_marcxml_flag) + convert_to_marcxml(p->u.databaseOrSurDiagnostics); + if (convert_xsl(p->u.databaseOrSurDiagnostics, apdu)) + return 0; + + } if (sr->resultCount) { yaz_log(LOG_LOG, "%s%d hits", m_session_str, @@ -864,17 +1050,60 @@ int Yaz_Proxy::send_to_client(Z_APDU *apdu) *sr->presentStatus = Z_PresentStatus_failure; display_diagrecs(&dr_p, 1); } - if (m_marcxml_flag && p && p->which == Z_Records_DBOSD) - convert_to_marcxml(p->u.databaseOrSurDiagnostics); + if (p && p->which == Z_Records_DBOSD) + { + if (m_marcxml_flag) + convert_to_marcxml(p->u.databaseOrSurDiagnostics); + if (convert_xsl(p->u.databaseOrSurDiagnostics, apdu)) + return 0; + } + } + else if (apdu->which == Z_APDU_initResponse) + { + if (m_initRequest_options) + { + Z_Options *nopt = + (Odr_bitmask *)odr_malloc(odr_encode(), + sizeof(Odr_bitmask)); + ODR_MASK_ZERO(nopt); + + int i; + for (i = 0; i<24; i++) + if (ODR_MASK_GET(m_initRequest_options, i) && + ODR_MASK_GET(apdu->u.initResponse->options, i)) + ODR_MASK_SET(nopt, i); + apdu->u.initResponse->options = nopt; + } + if (m_initRequest_version) + { + Z_ProtocolVersion *nopt = + (Odr_bitmask *)odr_malloc(odr_encode(), + sizeof(Odr_bitmask)); + ODR_MASK_ZERO(nopt); + + int i; + for (i = 0; i<8; i++) + if (ODR_MASK_GET(m_initRequest_version, i) && + ODR_MASK_GET(apdu->u.initResponse->protocolVersion, i)) + ODR_MASK_SET(nopt, i); + apdu->u.initResponse->protocolVersion = nopt; + } + apdu->u.initResponse->preferredMessageSize = + odr_intdup(odr_encode(), + m_client->m_initResponse_preferredMessageSize > + m_initRequest_preferredMessageSize ? + m_initRequest_preferredMessageSize : + m_client->m_initResponse_preferredMessageSize); + apdu->u.initResponse->maximumRecordSize = + odr_intdup(odr_encode(), + m_client->m_initResponse_maximumRecordSize > + m_initRequest_maximumRecordSize ? + m_initRequest_maximumRecordSize : + m_client->m_initResponse_maximumRecordSize); } - int r = send_PDU_convert(apdu, &len); + int r = send_PDU_convert(apdu); if (r) return r; - if (m_log_mask & PROXY_LOG_APDU_CLIENT) - yaz_log (LOG_DEBUG, "%sSending %s to client %d bytes", m_session_str, - apdu_name(apdu), len); - m_bytes_sent += len; - m_bw_stat.add_bytes(len); if (kill_session) { delete m_client; @@ -887,11 +1116,12 @@ int Yaz_Proxy::send_to_client(Z_APDU *apdu) int Yaz_ProxyClient::send_to_target(Z_APDU *apdu) { int len = 0; + const char *apdu_name_tmp = apdu_name(apdu); int r = send_Z_PDU(apdu, &len); if (m_root->get_log_mask() & PROXY_LOG_REQ_SERVER) yaz_log (LOG_LOG, "%sSending %s to %s %d bytes", get_session_str(), - apdu_name(apdu), get_hostname(), len); + apdu_name_tmp, get_hostname(), len); m_bytes_sent += len; return r; } @@ -1124,12 +1354,17 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu) } -void Yaz_Proxy::recv_GDU(Z_GDU *apdu, int len) +void Yaz_Proxy::inc_request_no() { char *cp = strchr(m_session_str, ' '); m_request_no++; if (cp) sprintf(cp+1, "%d ", m_request_no); +} + +void Yaz_Proxy::recv_GDU(Z_GDU *apdu, int len) +{ + inc_request_no(); m_bytes_recv += len; @@ -1143,11 +1378,11 @@ void Yaz_Proxy::recv_GDU(Z_GDU *apdu, int len) m_bw_stat.add_bytes(len); m_pdu_stat.add_bytes(1); + gettimeofday(&m_time_tv, 0); + int bw_total = m_bw_stat.get_total(); int pdu_total = m_pdu_stat.get_total(); - yaz_log(LOG_LOG, "%sstat bw=%d pdu=%d limit-bw=%d limit-pdu=%d", - m_session_str, bw_total, pdu_total, m_bw_max, m_pdu_max); int reduce = 0; if (m_bw_max) { @@ -1166,7 +1401,10 @@ void Yaz_Proxy::recv_GDU(Z_GDU *apdu, int len) } if (reduce) { - yaz_log(LOG_LOG, "%sLimit delay=%d", m_session_str, reduce); + yaz_log(LOG_LOG, "%sdelay=%d bw=%d pdu=%d limit-bw=%d limit-pdu=%d", + m_session_str, reduce, bw_total, pdu_total, + m_bw_max, m_pdu_max); + m_bw_hold_PDU = apdu; // save PDU and signal "on hold" timeout(reduce); // call us reduce seconds later } @@ -1222,16 +1460,32 @@ Z_APDU *Yaz_Proxy::handle_query_transformation(Z_APDU *apdu) { Z_RPNQuery *rpnquery = 0; Z_SearchRequest *sr = apdu->u.searchRequest; + char *addinfo = 0; yaz_log(LOG_LOG, "%sCQL: %s", m_session_str, sr->query->u.type_104->u.cql); int r = m_cql2rpn.query_transform(sr->query->u.type_104->u.cql, - &rpnquery, odr_encode()); + &rpnquery, odr_encode(), + &addinfo); if (r == -3) yaz_log(LOG_LOG, "%sNo CQL to RPN table", m_session_str); else if (r) + { yaz_log(LOG_LOG, "%sCQL Conversion error %d", m_session_str, r); + Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse); + + new_apdu->u.searchResponse->referenceId = sr->referenceId; + new_apdu->u.searchResponse->records = + create_nonSurrogateDiagnostics(odr_encode(), + yaz_diag_srw_to_bib1(r), + addinfo); + *new_apdu->u.searchResponse->searchStatus = 0; + + send_to_client(new_apdu); + + return 0; + } else { sr->query->which = Z_Query_type_1; @@ -1280,17 +1534,39 @@ Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu) int err = 0; char *addinfo = 0; Yaz_ProxyConfig *cfg = check_reconfigure(); - + + Z_RecordComposition rc_temp, *rc = 0; + if (sr->smallSetElementSetNames) + { + rc_temp.which = Z_RecordComp_simple; + rc_temp.u.simple = sr->smallSetElementSetNames; + rc = &rc_temp; + } + + char *stylesheet_name = 0; if (cfg) err = cfg->check_syntax(odr_encode(), m_default_target, - sr->preferredRecordSyntax, - &addinfo); + sr->preferredRecordSyntax, rc, + &addinfo, &stylesheet_name, &m_schema); + if (stylesheet_name) + { + m_parent->low_socket_close(); + + if (m_stylesheet_xsp) + xsltFreeStylesheet(m_stylesheet_xsp); + + m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*) + stylesheet_name); + m_stylesheet_offset = 0; + xfree(stylesheet_name); + + m_parent->low_socket_open(); + } if (err == -1) { sr->preferredRecordSyntax = - yaz_oidval_to_z3950oid(odr_decode(), CLASS_RECSYN, - VAL_USMARC); + yaz_oidval_to_z3950oid(odr_encode(), CLASS_RECSYN, VAL_USMARC); m_marcxml_flag = 1; } else if (err) @@ -1314,15 +1590,30 @@ Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu) char *addinfo = 0; Yaz_ProxyConfig *cfg = check_reconfigure(); + char *stylesheet_name = 0; if (cfg) err = cfg->check_syntax(odr_encode(), m_default_target, pr->preferredRecordSyntax, - &addinfo); + pr->recordComposition, + &addinfo, &stylesheet_name, &m_schema); + if (stylesheet_name) + { + m_parent->low_socket_close(); + + if (m_stylesheet_xsp) + xsltFreeStylesheet(m_stylesheet_xsp); + + m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*) + stylesheet_name); + m_stylesheet_offset = 0; + xfree(stylesheet_name); + + m_parent->low_socket_open(); + } if (err == -1) { pr->preferredRecordSyntax = - yaz_oidval_to_z3950oid(odr_decode(), CLASS_RECSYN, - VAL_USMARC); + yaz_oidval_to_z3950oid(odr_decode(), CLASS_RECSYN, VAL_USMARC); m_marcxml_flag = 1; } else if (err) @@ -1343,221 +1634,28 @@ Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu) return apdu; } -static int hex_digit (int ch) +Z_ElementSetNames *Yaz_Proxy::mk_esn_from_schema(ODR o, const char *schema) { - if (ch >= '0' && ch <= '9') - return ch - '0'; - else if (ch >= 'a' && ch <= 'f') - return ch - 'a'+10; - else if (ch >= 'A' && ch <= 'F') - return ch - 'A'+10; - return 0; -} - -static char *uri_val(const char *path, const char *name, ODR o) -{ - size_t nlen = strlen(name); - if (*path != '?') - return 0; - path++; - while (path && *path) - { - const char *p1 = strchr(path, '='); - if (!p1) - break; - if ((size_t)(p1 - path) == nlen && !memcmp(path, name, nlen)) - { - size_t i = 0; - char *ret; - - path = p1 + 1; - p1 = strchr(path, '&'); - if (!p1) - p1 = strlen(path) + path; - ret = (char*) odr_malloc(o, p1 - path + 1); - while (*path && *path != '&') - { - if (*path == '+') - { - ret[i++] = ' '; - path++; - } - else if (*path == '%' && path[1] && path[2]) - { - ret[i++] = hex_digit (path[1])*16 + hex_digit (path[2]); - path = path + 3; - } - else - ret[i++] = *path++; - } - ret[i] = '\0'; - return ret; - } - path = strchr(p1, '&'); - if (path) - path++; - } - return 0; -} - -void uri_val_int(const char *path, const char *name, ODR o, int **intp) -{ - const char *v = uri_val(path, name, o); - if (v) - *intp = odr_intdup(o, atoi(v)); -} - -int yaz_check_for_sru(Z_HTTP_Request *hreq, Z_SRW_PDU **srw_pdu, - char **soap_ns, ODR decode) -{ - if (!strcmp(hreq->method, "GET")) - { - char *db = "Default"; - const char *p0 = hreq->path, *p1; -#if HAVE_XML2 - int ret = -1; - char *charset = 0; - Z_SOAP *soap_package = 0; - static Z_SOAP_Handler soap_handlers[2] = { - {"http://www.loc.gov/zing/srw/v1.0/", 0, - (Z_SOAP_fun) yaz_srw_codec}, - {0, 0, 0} - }; -#endif - - if (*p0 == '/') - p0++; - p1 = strchr(p0, '?'); - if (!p1) - p1 = p0 + strlen(p0); - if (p1 != p0) - { - db = (char*) odr_malloc(decode, p1 - p0 + 1); - memcpy (db, p0, p1 - p0); - db[p1 - p0] = '\0'; - } -#if HAVE_XML2 - if (p1 && *p1 == '?' && p1[1]) - { - Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_searchRetrieve_request); - char *query = uri_val(p1, "query", decode); - char *pQuery = uri_val(p1, "pQuery", decode); - char *sortKeys = uri_val(p1, "sortKeys", decode); - - *srw_pdu = sr; - if (query) - { - sr->u.request->query_type = Z_SRW_query_type_cql; - sr->u.request->query.cql = query; - } - if (pQuery) - { - sr->u.request->query_type = Z_SRW_query_type_pqf; - sr->u.request->query.pqf = pQuery; - } - if (sortKeys) - { - sr->u.request->sort_type = Z_SRW_sort_type_sort; - sr->u.request->sort.sortKeys = sortKeys; - } - sr->u.request->recordSchema = uri_val(p1, "recordSchema", decode); - sr->u.request->recordPacking = uri_val(p1, "recordPacking", decode); - if (!sr->u.request->recordPacking) - sr->u.request->recordPacking = "xml"; - uri_val_int(p1, "maximumRecords", decode, - &sr->u.request->maximumRecords); - uri_val_int(p1, "startRecord", decode, - &sr->u.request->startRecord); - if (sr->u.request->startRecord) - yaz_log(LOG_LOG, "startRecord=%d", *sr->u.request->startRecord); - sr->u.request->database = db; - *soap_ns = "SRU"; - return 0; - } -#endif - return 1; - } - return 2; + if (!schema) + return 0; + Z_ElementSetNames *esn = (Z_ElementSetNames *) + odr_malloc(o, sizeof(Z_ElementSetNames)); + esn->which = Z_ElementSetNames_generic; + esn->u.generic = odr_strdup(o, schema); + return esn; } -int yaz_check_for_srw(Z_HTTP_Request *hreq, Z_SRW_PDU **srw_pdu, - char **soap_ns, ODR decode) +void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) { - if (!strcmp(hreq->method, "POST")) + if (m_s2z_odr_init) { - const char *content_type = z_HTTP_header_lookup(hreq->headers, - "Content-Type"); - if (content_type && !yaz_strcmp_del("text/xml", content_type, "; ")) - { - char *db = "Default"; - const char *p0 = hreq->path, *p1; - Z_SOAP *soap_package = 0; - int ret = -1; - int http_code = 500; - const char *charset_p = 0; - char *charset = 0; - - static Z_SOAP_Handler soap_handlers[2] = { -#if HAVE_XML2 - {"http://www.loc.gov/zing/srw/v1.0/", 0, - (Z_SOAP_fun) yaz_srw_codec}, -#endif - {0, 0, 0} - }; - - if (*p0 == '/') - p0++; - p1 = strchr(p0, '?'); - if (!p1) - p1 = p0 + strlen(p0); - if (p1 != p0) - { - db = (char*) odr_malloc(decode, p1 - p0 + 1); - memcpy (db, p0, p1 - p0); - db[p1 - p0] = '\0'; - } - - if ((charset_p = strstr(content_type, "; charset="))) - { - int i = 0; - charset_p += 10; - while (i < 20 && charset_p[i] && - !strchr("; \n\r", charset_p[i])) - i++; - charset = (char*) odr_malloc(decode, i+1); - memcpy(charset, charset_p, i); - charset[i] = '\0'; - yaz_log(LOG_LOG, "SOAP encoding %s", charset); - } - ret = z_soap_codec(decode, &soap_package, - &hreq->content_buf, &hreq->content_len, - soap_handlers); - if (!ret && soap_package->which == Z_SOAP_generic && - soap_package->u.generic->no == 0) - { - *srw_pdu = (Z_SRW_PDU*) soap_package->u.generic->p; - - if ((*srw_pdu)->which == Z_SRW_searchRetrieve_request && - (*srw_pdu)->u.request->database == 0) - (*srw_pdu)->u.request->database = db; - - *soap_ns = odr_strdup(decode, soap_package->ns); - return 0; - } - return 1; - } + odr_destroy(m_s2z_odr_init); + m_s2z_odr_init = 0; } - return 2; -} - -void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) -{ - Z_SRW_PDU *srw_pdu = 0; - char *soap_ns = 0; - if (m_s2z_odr) + if (m_s2z_odr_search) { - odr_destroy(m_s2z_odr); - m_s2z_odr = 0; + odr_destroy(m_s2z_odr_search); + m_s2z_odr_search = 0; } m_http_keepalive = 0; @@ -1581,18 +1679,56 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) m_http_version = "1.1"; } - if (yaz_check_for_srw(hreq, &srw_pdu, &soap_ns, odr_decode()) == 0 - || yaz_check_for_sru(hreq, &srw_pdu, &soap_ns, odr_decode()) == 0) + Z_SRW_PDU *srw_pdu = 0; + Z_SOAP *soap_package = 0; + char *charset = 0; + Z_SRW_diagnostic *diagnostic = 0; + int num_diagnostic = 0; + if (yaz_srw_decode(hreq, &srw_pdu, &soap_package, odr_decode(), + &charset) == 0 + || yaz_sru_decode(hreq, &srw_pdu, &soap_package, odr_decode(), + &charset, &diagnostic, &num_diagnostic) == 0) { - m_s2z_odr = odr_createmem(ODR_ENCODE); - m_soap_ns = odr_strdup(m_s2z_odr, soap_ns); + m_s2z_odr_init = odr_createmem(ODR_ENCODE); + m_s2z_odr_search = odr_createmem(ODR_ENCODE); + m_soap_ns = odr_strdup(m_s2z_odr_search, soap_package->ns); m_s2z_init_apdu = 0; m_s2z_search_apdu = 0; m_s2z_present_apdu = 0; + + m_s2z_stylesheet = 0; + if (srw_pdu->which == Z_SRW_searchRetrieve_request) { Z_SRW_searchRetrieveRequest *srw_req = srw_pdu->u.request; + m_s2z_database = odr_strdup(m_s2z_odr_init, srw_req->database); + // recordXPath unsupported. + if (srw_req->recordXPath) + { + yaz_add_srw_diagnostic(odr_decode(), + &diagnostic, &num_diagnostic, + 72, 0); + } + // must have a query + if (!srw_req->query.cql) + { + yaz_add_srw_diagnostic(odr_decode(), + &diagnostic, &num_diagnostic, + 7, "query"); + } + // sort unsupported + if (srw_req->sort_type != Z_SRW_sort_type_none) + { + yaz_add_srw_diagnostic(odr_decode(), + &diagnostic, &num_diagnostic, + 80, 0); + } + // save stylesheet + if (srw_req->stylesheet) + m_s2z_stylesheet = + odr_strdup(m_s2z_odr_init, srw_req->stylesheet); + // set packing for response records .. if (srw_req->recordPacking && !strcmp(srw_req->recordPacking, "xml")) @@ -1600,28 +1736,42 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) else m_s2z_packing = Z_SRW_recordPacking_string; + if (num_diagnostic) + { + Z_SRW_PDU *srw_pdu = + yaz_srw_get(odr_encode(), + Z_SRW_searchRetrieve_response); + Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response; + + srw_res->diagnostics = diagnostic; + srw_res->num_diagnostics = num_diagnostic; + send_srw_response(srw_pdu); + return; + } + // prepare search PDU - m_s2z_search_apdu = zget_APDU(m_s2z_odr, Z_APDU_searchRequest); + m_s2z_search_apdu = zget_APDU(m_s2z_odr_search, + Z_APDU_searchRequest); Z_SearchRequest *z_searchRequest = m_s2z_search_apdu->u.searchRequest; z_searchRequest->num_databaseNames = 1; z_searchRequest->databaseNames = (char**) - odr_malloc(m_s2z_odr, sizeof(char *)); - z_searchRequest->databaseNames[0] = odr_strdup(m_s2z_odr, + odr_malloc(m_s2z_odr_search, sizeof(char *)); + z_searchRequest->databaseNames[0] = odr_strdup(m_s2z_odr_search, srw_req->database); // query transformation Z_Query *query = (Z_Query *) - odr_malloc(odr_encode(), sizeof(Z_Query)); + odr_malloc(m_s2z_odr_search, sizeof(Z_Query)); z_searchRequest->query = query; if (srw_req->query_type == Z_SRW_query_type_cql) { Z_External *ext = (Z_External *) - odr_malloc(m_s2z_odr, sizeof(*ext)); + odr_malloc(m_s2z_odr_search, sizeof(*ext)); ext->direct_reference = - odr_getoidbystr(m_s2z_odr, "1.2.840.10003.16.2"); + odr_getoidbystr(m_s2z_odr_search, "1.2.840.10003.16.2"); ext->indirect_reference = 0; ext->descriptor = 0; ext->which = Z_External_CQL; @@ -1637,7 +1787,7 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) pqf_parser = yaz_pqf_create (); - RPNquery = yaz_pqf_parse (pqf_parser, m_s2z_odr, + RPNquery = yaz_pqf_parse (pqf_parser, m_s2z_odr_search, srw_req->query.pqf); if (!RPNquery) { @@ -1647,7 +1797,7 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) yaz_log(LOG_LOG, "%*s^\n", off+4, ""); yaz_log(LOG_LOG, "Bad PQF: %s (code %d)\n", pqf_msg, code); - send_to_srw_client_error(10); + send_to_srw_client_error(10, 0); return; } query->which = Z_Query_type_1; @@ -1657,7 +1807,7 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) } else { - send_to_srw_client_error(11); + send_to_srw_client_error(7, "query"); return; } @@ -1671,31 +1821,58 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) start = *srw_req->startRecord; if (max > 0) { - if (start <= 1) // Z39.50 piggyback + // Some backend, such as Voyager doesn't honor piggyback + // So we use present always (0 &&). + if (0 && start <= 1) // Z39.50 piggyback { *z_searchRequest->smallSetUpperBound = max; *z_searchRequest->mediumSetPresentNumber = max; *z_searchRequest->largeSetLowerBound = 2000000000; // 2e9 + z_searchRequest->preferredRecordSyntax = - yaz_oidval_to_z3950oid(m_s2z_odr, CLASS_RECSYN, + yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN, VAL_TEXT_XML); + if (srw_req->recordSchema) + { + z_searchRequest->smallSetElementSetNames = + z_searchRequest->mediumSetElementSetNames = + mk_esn_from_schema(m_s2z_odr_search, + srw_req->recordSchema); + } } else // Z39.50 present { - m_s2z_present_apdu = zget_APDU(m_s2z_odr, + m_s2z_present_apdu = zget_APDU(m_s2z_odr_search, Z_APDU_presentRequest); Z_PresentRequest *z_presentRequest = m_s2z_present_apdu->u.presentRequest; *z_presentRequest->resultSetStartPoint = start; *z_presentRequest->numberOfRecordsRequested = max; z_presentRequest->preferredRecordSyntax = - yaz_oidval_to_z3950oid(m_s2z_odr, CLASS_RECSYN, + yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN, VAL_TEXT_XML); + if (srw_req->recordSchema) + { + z_presentRequest->recordComposition = + (Z_RecordComposition *) + odr_malloc(m_s2z_odr_search, + sizeof(Z_RecordComposition)); + z_presentRequest->recordComposition->which = + Z_RecordComp_simple; + z_presentRequest->recordComposition->u.simple = + mk_esn_from_schema(m_s2z_odr_search, + srw_req->recordSchema); + } } } if (!m_client) { - m_s2z_init_apdu = zget_APDU(m_s2z_odr, Z_APDU_initRequest); + m_s2z_init_apdu = zget_APDU(m_s2z_odr_init, + Z_APDU_initRequest); + + // prevent m_initRequest_apdu memory from being grabbed + // in Yaz_Proxy::handle_incoming_Z_PDU + m_initRequest_apdu = m_s2z_init_apdu; handle_incoming_Z_PDU(m_s2z_init_apdu); return; } @@ -1705,22 +1882,97 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) return; } } + else if (srw_pdu->which == Z_SRW_explain_request) + { + Z_SRW_explainRequest *srw_req = srw_pdu->u.explain_request; + + m_s2z_database = odr_strdup(m_s2z_odr_init, srw_req->database); + + // save stylesheet + if (srw_req->stylesheet) + m_s2z_stylesheet = + odr_strdup(m_s2z_odr_init, srw_req->stylesheet); + + if (srw_req->recordPacking && + !strcmp(srw_req->recordPacking, "xml")) + m_s2z_packing = Z_SRW_recordPacking_XML; + else + m_s2z_packing = Z_SRW_recordPacking_string; + + if (num_diagnostic) + { + send_srw_explain_response(diagnostic, num_diagnostic); + return; + } + + if (!m_client) + { + m_s2z_init_apdu = zget_APDU(m_s2z_odr_init, + Z_APDU_initRequest); + + // prevent m_initRequest_apdu memory from being grabbed + // in Yaz_Proxy::handle_incoming_Z_PDU + m_initRequest_apdu = m_s2z_init_apdu; + handle_incoming_Z_PDU(m_s2z_init_apdu); + } + else + send_srw_explain_response(0, 0); + return; + } + else if (srw_pdu->which == Z_SRW_scan_request) + { + m_s2z_database = odr_strdup(m_s2z_odr_init, + srw_pdu->u.scan_request->database); + + yaz_add_srw_diagnostic(odr_decode(), + &diagnostic, &num_diagnostic, + 4, "scan"); + Z_SRW_PDU *srw_pdu = + yaz_srw_get(odr_encode(), + Z_SRW_scan_response); + Z_SRW_scanResponse *srw_res = srw_pdu->u.scan_response; + + srw_res->diagnostics = diagnostic; + srw_res->num_diagnostics = num_diagnostic; + send_srw_response(srw_pdu); + return; + } + else + { + m_s2z_database = 0; + + send_to_srw_client_error(4, 0); + } } int len = 0; Z_GDU *p = z_get_HTTP_Response(odr_encode(), 400); + timeout(0); send_GDU(p, &len); - timeout(1); } void Yaz_Proxy::handle_incoming_Z_PDU(Z_APDU *apdu) { + Z_ReferenceId **refid = get_referenceIdP(apdu); + nmem_reset(m_referenceId_mem); + if (refid && *refid) + { + m_referenceId = (Z_ReferenceId *) + nmem_malloc(m_referenceId_mem, sizeof(*m_referenceId)); + m_referenceId->len = m_referenceId->size = (*refid)->len; + m_referenceId->buf = (unsigned char *) + nmem_malloc(m_referenceId_mem, (*refid)->len); + memcpy(m_referenceId->buf, (*refid)->buf, (*refid)->len); + } + else + m_referenceId = 0; + if (!m_client && m_invalid_session) { m_apdu_invalid_session = apdu; m_mem_invalid_session = odr_extract_mem(odr_decode()); apdu = m_initRequest_apdu; } - + // Determine our client. Z_OtherInformation **oi; get_otherInfoAPDU(apdu, &oi); @@ -1749,18 +2001,59 @@ void Yaz_Proxy::handle_incoming_Z_PDU(Z_APDU *apdu) nmem_destroy(m_initRequest_mem); m_initRequest_apdu = apdu; m_initRequest_mem = odr_extract_mem(odr_decode()); + + m_initRequest_preferredMessageSize = *apdu->u.initRequest-> + preferredMessageSize; + *apdu->u.initRequest->preferredMessageSize = 1024*1024; + m_initRequest_maximumRecordSize = *apdu->u.initRequest-> + maximumRecordSize; + *apdu->u.initRequest->maximumRecordSize = 1024*1024; + + // save init options for the response.. + m_initRequest_options = apdu->u.initRequest->options; + + apdu->u.initRequest->options = + (Odr_bitmask *)nmem_malloc(m_initRequest_mem, + sizeof(Odr_bitmask)); + ODR_MASK_ZERO(apdu->u.initRequest->options); + int i; + for (i = 0; i<= 24; i++) + ODR_MASK_SET(apdu->u.initRequest->options, i); + ODR_MASK_CLEAR(apdu->u.initRequest->options, + Z_Options_negotiationModel); + ODR_MASK_CLEAR(apdu->u.initRequest->options, + Z_Options_concurrentOperations); + + // make new version + m_initRequest_version = apdu->u.initRequest->protocolVersion; + apdu->u.initRequest->protocolVersion = + (Odr_bitmask *)nmem_malloc(m_initRequest_mem, + sizeof(Odr_bitmask)); + ODR_MASK_ZERO(apdu->u.initRequest->protocolVersion); + + for (i = 0; i<= 8; i++) + ODR_MASK_SET(apdu->u.initRequest->protocolVersion, i); } if (m_client->m_init_flag) { if (handle_init_response_for_invalid_session(apdu)) return; - Z_APDU *apdu = m_client->m_initResponse; - apdu->u.initResponse->otherInfo = 0; - if (m_client->m_cookie && *m_client->m_cookie) - set_otherInformationString(apdu, VAL_COOKIE, 1, - m_client->m_cookie); - send_to_client(apdu); - return; + if (m_client->m_initResponse) + { + Z_APDU *apdu2 = m_client->m_initResponse; + apdu2->u.initResponse->otherInfo = 0; + if (m_client->m_cookie && *m_client->m_cookie) + set_otherInformationString(apdu2, VAL_COOKIE, 1, + m_client->m_cookie); + apdu2->u.initResponse->referenceId = + apdu->u.initRequest->referenceId; + apdu2->u.initResponse->options = m_client->m_initResponse_options; + apdu2->u.initResponse->protocolVersion = + m_client->m_initResponse_version; + + send_to_client(apdu2); + return; + } } m_client->m_init_flag = 1; } @@ -1874,6 +2167,7 @@ void Yaz_ProxyClient::shutdown() void Yaz_Proxy::failNotify() { + inc_request_no(); yaz_log (LOG_LOG, "%sConnection closed by client", get_session_str()); shutdown(); @@ -1881,6 +2175,8 @@ void Yaz_Proxy::failNotify() void Yaz_ProxyClient::failNotify() { + if (m_server) + m_server->inc_request_no(); yaz_log (LOG_LOG, "%sConnection closed by target %s", get_session_str(), get_hostname()); shutdown(); @@ -1921,19 +2217,16 @@ void Yaz_ProxyClient::pre_init_client() Z_APDU *apdu = create_Z_PDU(Z_APDU_initRequest); Z_InitRequest *req = apdu->u.initRequest; - ODR_MASK_SET(req->options, Z_Options_search); - ODR_MASK_SET(req->options, Z_Options_present); - ODR_MASK_SET(req->options, Z_Options_namedResultSets); - ODR_MASK_SET(req->options, Z_Options_triggerResourceCtrl); - ODR_MASK_SET(req->options, Z_Options_scan); - ODR_MASK_SET(req->options, Z_Options_sort); - ODR_MASK_SET(req->options, Z_Options_extendedServices); - ODR_MASK_SET(req->options, Z_Options_delSet); - - ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_1); - ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_2); - ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_3); - + int i; + for (i = 0; i<= 24; i++) + ODR_MASK_SET(req->options, i); + ODR_MASK_CLEAR(apdu->u.initRequest->options, + Z_Options_negotiationModel); + ODR_MASK_CLEAR(apdu->u.initRequest->options, + Z_Options_concurrentOperations); + for (i = 0; i<= 10; i++) + ODR_MASK_SET(req->protocolVersion, i); + if (send_to_target(apdu) < 0) { delete this; @@ -1982,6 +2275,7 @@ void Yaz_Proxy::pre_init() { Yaz_ProxyClient *c; int spare = 0; + int spare_waiting = 0; int in_use = 0; int other = 0; for (c = m_clientPool; c; c = c->m_next) @@ -1991,7 +2285,10 @@ void Yaz_Proxy::pre_init() if (c->m_cookie == 0) { if (c->m_server == 0) - spare++; + if (c->m_waiting) + spare_waiting++; + else + spare++; else in_use++; } @@ -2000,9 +2297,10 @@ void Yaz_Proxy::pre_init() } } yaz_log(LOG_LOG, "%spre-init %s %s use=%d other=%d spare=%d " - "preinit=%d",m_session_str, - name, zurl_in_use[j], in_use, other, spare, pre_init); - if (spare < pre_init) + "sparew=%d preinit=%d",m_session_str, + name, zurl_in_use[j], in_use, other, + spare, spare_waiting, pre_init); + if (spare + spare_waiting < pre_init) { c = new Yaz_ProxyClient(m_PDU_Observable->clone(), this); c->m_next = m_clientPool; @@ -2047,8 +2345,12 @@ void Yaz_Proxy::timeoutNotify() else if (apdu->which == Z_GDU_HTTP_Request) handle_incoming_HTTP(apdu->u.HTTP_Request); } + else if (m_stylesheet_nprl) + convert_xsl_delay(); else { + inc_request_no(); + yaz_log (LOG_LOG, "%sTimeout (client to proxy)", m_session_str); shutdown(); } @@ -2060,12 +2362,29 @@ void Yaz_Proxy::timeoutNotify() } } +void Yaz_Proxy::markInvalid() +{ + m_client = 0; + m_invalid_session = 1; +} + void Yaz_ProxyClient::timeoutNotify() { + if (m_server) + m_server->inc_request_no(); + yaz_log (LOG_LOG, "%sTimeout (proxy to target) %s", get_session_str(), get_hostname()); m_waiting = 1; m_root->pre_init(); + if (m_server && m_init_flag) + { + // target timed out in a session that was properly initialized + // server object stay alive but we mark it as invalid so it + // gets initialized again + m_server->markInvalid(); + m_server = 0; + } shutdown(); } @@ -2085,6 +2404,10 @@ Yaz_ProxyClient::Yaz_ProxyClient(IYaz_PDU_Observable *the_PDU_Observable, m_waiting = 0; m_init_odr = odr_createmem (ODR_DECODE); m_initResponse = 0; + m_initResponse_options = 0; + m_initResponse_version = 0; + m_initResponse_preferredMessageSize = 0; + m_initResponse_maximumRecordSize = 0; m_resultSetStartPoint = 0; m_bytes_sent = m_bytes_recv = 0; m_pdu_recv = 0; @@ -2136,6 +2459,7 @@ int Yaz_Proxy::handle_init_response_for_invalid_session(Z_APDU *apdu) void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len) { m_bytes_recv += len; + m_pdu_recv++; m_waiting = 0; if (m_root->get_log_mask() & PROXY_LOG_REQ_SERVER) @@ -2149,6 +2473,12 @@ void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len) odr_reset (m_init_odr); nmem_transfer (m_init_odr->mem, nmem); m_initResponse = apdu; + m_initResponse_options = apdu->u.initResponse->options; + m_initResponse_version = apdu->u.initResponse->protocolVersion; + m_initResponse_preferredMessageSize = + *apdu->u.initResponse->preferredMessageSize; + m_initResponse_maximumRecordSize = + *apdu->u.initResponse->maximumRecordSize; Z_InitResponse *ir = apdu->u.initResponse; char *im0 = ir->implementationName; @@ -2222,12 +2552,27 @@ void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len) } } +void Yaz_Proxy::low_socket_close() +{ + int i; + for (i = 0; i= 0) + ::close(m_lo_fd[i]); +} + +void Yaz_Proxy::low_socket_open() +{ + int i; + for (i = 0; i