/*
- * Copyright (c) 1998-2003, Index Data.
+ * Copyright (c) 1998-2004, Index Data.
* See the file LICENSE for details.
*
- * $Id: yaz-proxy.cpp,v 1.72 2003-12-20 22:44:30 adam Exp $
+ * $Id: yaz-proxy.cpp,v 1.79 2004-01-07 11:37:45 adam Exp $
*/
#include <assert.h>
#include <yaz++/proxy.h>
#include <yaz/pquery.h>
+#if HAVE_XSLT
+#include <libxslt/xsltutils.h>
+#include <libxslt/transform.h>
+#endif
+
static const char *apdu_name(Z_APDU *apdu)
{
switch (apdu->which)
m_invalid_session = 0;
m_config = 0;
m_marcxml_flag = 0;
+ m_stylesheet = 0;
+ m_schema = 0;
m_initRequest_apdu = 0;
m_initRequest_mem = 0;
m_apdu_invalid_session = 0;
m_http_version = 0;
m_soap_ns = 0;
m_s2z_packing = Z_SRW_recordPacking_string;
- m_zeerex_fname = 0;
+ m_time_tv.tv_sec = 0;
+ m_time_tv.tv_usec = 0;
}
Yaz_Proxy::~Yaz_Proxy()
xfree (m_default_target);
xfree (m_proxy_authentication);
xfree (m_optimize);
+ xfree (m_stylesheet);
+ xfree (m_schema);
if (m_s2z_odr_init)
odr_destroy(m_s2z_odr_init);
if (m_s2z_odr_search)
&m_keepalive_limit_bw,
&m_keepalive_limit_pdu,
&pre_init,
- &cql2rpn_fname,
- &m_zeerex_fname);
+ &cql2rpn_fname);
}
if (client_idletime != -1)
{
}
}
+void Yaz_Proxy::convert_xsl(Z_NamePlusRecordList *p)
+{
+ if (!m_stylesheet)
+ return;
+ xmlDocPtr xslt_doc = xmlParseFile(m_stylesheet);
+ xsltStylesheetPtr xsp;
+
+ xsp = xsltParseStylesheetDoc(xslt_doc);
+
+ int i;
+ for (i = 0; i < p->num_records; i++)
+ {
+ Z_NamePlusRecord *npr = p->records[i];
+ if (npr->which == Z_NamePlusRecord_databaseRecord)
+ {
+ Z_External *r = npr->u.databaseRecord;
+ if (r->which == Z_External_octet)
+ {
+ xmlDocPtr res, doc = xmlParseMemory(
+ (char*) r->u.octet_aligned->buf,
+ r->u.octet_aligned->len);
+
+ res = xsltApplyStylesheet(xsp, doc, 0);
+
+ xmlChar *out_buf;
+ int out_len;
+ xmlDocDumpMemory (res, &out_buf, &out_len);
+ p->records[i]->u.databaseRecord =
+ z_ext_record(odr_encode(), VAL_TEXT_XML,
+ (char*) out_buf, out_len);
+ xmlFreeDoc(doc);
+ xmlFreeDoc(res);
+ }
+ }
+ }
+ xsltFreeStylesheet(xsp);
+}
+
void Yaz_Proxy::convert_to_marcxml(Z_NamePlusRecordList *p)
{
int i;
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();
if (m_http_version)
hres->version = odr_strdup(o, m_http_version);
m_http_keepalive = 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;
- return send_GDU(gdu, &len);
+ int r = send_GDU(gdu, &len);
+ logtime();
+ return r;
}
int Yaz_Proxy::send_srw_response(Z_SRW_PDU *srw_pdu)
z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
static Z_SOAP_Handler soap_handlers[2] = {
-#if HAVE_XML2
+#if HAVE_XSLT
{"http://www.loc.gov/zing/srw/", 0,
(Z_SOAP_fun) yaz_srw_codec},
#endif
int ret = z_soap_codec_enc(o, &soap_package,
&hres->content_buf, &hres->content_len,
soap_handlers, 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;
- return send_GDU(gdu, &len);
+ int r = send_GDU(gdu, &len);
+ logtime();
+ return r;
}
int Yaz_Proxy::send_to_srw_client_error(int srw_error)
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;
Z_SRW_PDU *res = yaz_srw_get(odr_encode(), Z_SRW_explain_response);
Z_SRW_explainResponse *er = res->u.explain_response;
- if (m_zeerex_fname)
+ Yaz_ProxyConfig *cfg = check_reconfigure();
+ if (cfg)
{
- FILE *inf = fopen(m_zeerex_fname, "rb");
- if (inf)
+ int len;
+ assert (m_proxyTarget);
+ char *b = cfg->get_explain(odr_encode(), 0 /* target */,
+ 0 /* db */, &len);
+ if (b)
{
- fseek(inf, 0L, SEEK_END);
- long sz = ftell(inf);
- fseek(inf, 0L, SEEK_SET);
- if (sz > 0)
- {
- er->record.recordData_buf =
- (char*) odr_malloc(odr_encode(), sz);
- size_t s = fread(er->record.recordData_buf, 1,sz, inf);
- if (s > 0)
- er->record.recordData_len = s;
- }
- else
- yaz_log(LOG_WARN|LOG_ERRNO, "zeerex file: ftell");
- fclose(inf);
+ er->record.recordData_buf = b;
+ er->record.recordData_len = len;
+ er->record.recordPacking = m_s2z_packing;
}
- else
- yaz_log(LOG_WARN|LOG_ERRNO, "zeerex file: fopen");
}
- else
- yaz_log(LOG_LOG, "zeerex file: not defined");
return send_srw_response(res);
}
}
}
else
- return send_Z_PDU(apdu, len);
+ {
+ 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);
+ logtime();
+ return r;
+ }
return 0;
}
}
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);
+ convert_xsl(p->u.databaseOrSurDiagnostics);
+ }
if (sr->resultCount)
{
yaz_log(LOG_LOG, "%s%d hits", m_session_str,
*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);
+ convert_xsl(p->u.databaseOrSurDiagnostics);
+ }
}
int r = send_PDU_convert(apdu, &len);
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)
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;
}
}
-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;
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();
{
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;
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;
+ }
+
if (cfg)
err = cfg->check_syntax(odr_encode(),
m_default_target,
- sr->preferredRecordSyntax,
- &addinfo);
+ sr->preferredRecordSyntax, rc,
+ &addinfo, &m_stylesheet, &m_schema);
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)
if (cfg)
err = cfg->check_syntax(odr_encode(), m_default_target,
pr->preferredRecordSyntax,
- &addinfo);
+ pr->recordComposition,
+ &addinfo, &m_stylesheet, &m_schema);
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)
return apdu;
}
+Z_ElementSetNames *Yaz_Proxy::mk_esn_from_schema(ODR o, const char *schema)
+{
+ 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;
+}
+
void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
{
- Z_SRW_PDU *srw_pdu = 0;
- char *soap_ns = 0;
+
if (m_s2z_odr_init)
{
odr_destroy(m_s2z_odr_init);
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;
+ if (yaz_srw_decode(hreq, &srw_pdu, &soap_package, odr_decode(),
+ &charset) == 0
+ || yaz_sru_decode(hreq, &srw_pdu, &soap_package, odr_decode(),
+ &charset) == 0)
{
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_ns);
+ 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;
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_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
{
z_presentRequest->preferredRecordSyntax =
yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN,
VAL_TEXT_XML);
+ z_presentRequest->recordComposition =
+ (Z_RecordComposition *)
+ odr_malloc(m_s2z_odr_search,
+ sizeof(Z_RecordComposition));
+ if (srw_req->recordSchema)
+ {
+ 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)
{
- yaz_log(LOG_LOG, "handle_incoming: initRequest");
m_s2z_init_apdu = zget_APDU(m_s2z_odr_init,
Z_APDU_initRequest);
}
else if (srw_pdu->which == Z_SRW_explain_request)
{
+ Z_SRW_explainRequest *srw_req = srw_pdu->u.explain_request;
+
+ 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 (!m_client)
{
yaz_log(LOG_LOG, "handle_incoming: initRequest");
void Yaz_Proxy::failNotify()
{
+ inc_request_no();
yaz_log (LOG_LOG, "%sConnection closed by client",
get_session_str());
shutdown();
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();
&keepalive_limit_bw,
&keepalive_limit_pdu,
&pre_init,
- &cql2rpn,
- &zeerex) ; i++)
+ &cql2rpn) ; i++)
{
if (pre_init)
{
}
else
{
+ inc_request_no();
+
yaz_log (LOG_LOG, "%sTimeout (client to proxy)", m_session_str);
shutdown();
}
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;
int r = Yaz_Z_Assoc::server(addr);
if (!r)
{
- yaz_log(LOG_LOG, "%sStarted listener on %s", m_session_str, addr);
+ yaz_log(LOG_LOG, "%sStarted proxy " VERSION " on %s", m_session_str, addr);
timeout(1);
}
return r;