1 /* This file is part of Pazpar2.
2 Copyright (C) 2006-2008 Index Data
4 Pazpar2 is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
9 Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
38 #include <sys/socket.h>
47 #include <yaz/marcdisp.h>
48 #include <yaz/comstack.h>
49 #include <yaz/tcpip.h>
50 #include <yaz/proto.h>
51 #include <yaz/readconf.h>
52 #include <yaz/pquery.h>
53 #include <yaz/otherinfo.h>
54 #include <yaz/yaz-util.h>
56 #include <yaz/query-charset.h>
57 #include <yaz/querytowrbuf.h>
58 #include <yaz/oid_db.h>
59 #include <yaz/diagbib1.h>
63 #include <yaz/timing.h>
67 #include <netinet/in.h>
73 #include "connection.h"
76 /** \brief Represents client state for a connection to one search target */
78 struct session_database *database;
79 struct connection *connection;
80 struct session *session;
81 char *pquery; // Current search
85 int requestid; // ID of current outstanding request
87 enum client_state state;
88 struct show_raw *show_raw;
89 struct client *next; // next client in session or next in free list
93 int active; // whether this request has been sent to the server
98 void (*error_handler)(void *data, const char *addinfo);
99 void (*record_handler)(void *data, const char *buf, size_t sz);
101 struct show_raw *next;
104 static const char *client_states[] = {
108 "Client_Initializing",
113 "Client_Disconnected",
118 static struct client *client_freelist = 0;
120 const char *client_get_state_str(struct client *cl)
122 return client_states[cl->state];
125 enum client_state client_get_state(struct client *cl)
130 void client_set_state(struct client *cl, enum client_state st)
135 int no_active = session_active_clients(cl->session);
137 session_alert_watch(cl->session, SESSION_WATCH_SHOW);
141 static void client_show_raw_error(struct client *cl, const char *addinfo);
143 // Close connection and set state to error
144 void client_fatal(struct client *cl)
146 //client_show_raw_error(cl, "client connection failure");
147 yaz_log(YLOG_WARN, "Fatal error from %s", client_get_url(cl));
148 connection_destroy(cl->connection);
149 client_set_state(cl, Client_Error);
152 struct connection *client_get_connection(struct client *cl)
154 return cl->connection;
157 struct session_database *client_get_database(struct client *cl)
162 struct session *client_get_session(struct client *cl)
167 const char *client_get_pquery(struct client *cl)
172 void client_set_requestid(struct client *cl, int id)
178 static void client_send_raw_present(struct client *cl);
180 int client_show_raw_begin(struct client *cl, int position,
181 const char *syntax, const char *esn,
183 void (*error_handler)(void *data, const char *addinfo),
184 void (*record_handler)(void *data, const char *buf,
189 struct show_raw *rr, **rrp;
191 { /* the client has no connection */
194 rr = xmalloc(sizeof(*rr));
196 rr->position = position;
199 rr->error_handler = error_handler;
200 rr->record_handler = record_handler;
203 rr->syntax = xstrdup(syntax);
207 rr->esn = xstrdup(esn);
212 for (rrp = &cl->show_raw; *rrp; rrp = &(*rrp)->next)
216 if (cl->state == Client_Failed)
218 client_show_raw_error(cl, "client failed");
220 else if (cl->state == Client_Disconnected)
222 client_show_raw_error(cl, "client disconnected");
226 client_send_raw_present(cl);
231 void client_show_raw_remove(struct client *cl, void *data)
233 struct show_raw *rr = data;
234 struct show_raw **rrp = &cl->show_raw;
244 void client_show_raw_dequeue(struct client *cl)
246 struct show_raw *rr = cl->show_raw;
248 cl->show_raw = rr->next;
252 static void client_show_raw_error(struct client *cl, const char *addinfo)
256 cl->show_raw->error_handler(cl->show_raw->data, addinfo);
257 client_show_raw_dequeue(cl);
261 static void client_show_raw_cancel(struct client *cl)
265 cl->show_raw->error_handler(cl->show_raw->data, "cancel");
266 client_show_raw_dequeue(cl);
270 static void client_send_raw_present(struct client *cl)
272 struct session_database *sdb = client_get_database(cl);
273 struct connection *co = client_get_connection(cl);
274 ZOOM_resultset set = connection_get_resultset(co);
276 int offset = cl->show_raw->position;
277 const char *syntax = 0;
278 const char *elements = 0;
280 assert(cl->show_raw);
283 yaz_log(YLOG_DEBUG, "%s: trying to present %d record(s) from %d",
284 client_get_url(cl), 1, offset);
286 if (cl->show_raw->syntax)
287 syntax = cl->show_raw->syntax;
289 syntax = session_setting_oneval(sdb, PZ_REQUESTSYNTAX);
290 ZOOM_resultset_option_set(set, "preferredRecordSyntax", syntax);
292 if (cl->show_raw->esn)
293 elements = cl->show_raw->esn;
295 elements = session_setting_oneval(sdb, PZ_ELEMENTS);
296 ZOOM_resultset_option_set(set, "elementSetName", elements);
298 ZOOM_resultset_records(set, 0, offset, 1);
299 cl->show_raw->active = 1;
300 ZOOM_connection_process(connection_get_link(co));
305 static void ingest_raw_records(struct client *cl, Z_Records *r)
307 Z_NamePlusRecordList *rlist;
308 Z_NamePlusRecord *npr;
312 if (r->which != Z_Records_DBOSD)
314 client_show_raw_error(cl, "non-surrogate diagnostics");
318 rlist = r->u.databaseOrSurDiagnostics;
319 if (rlist->num_records != 1 || !rlist->records || !rlist->records[0])
321 client_show_raw_error(cl, "no records");
324 npr = rlist->records[0];
325 if (npr->which != Z_NamePlusRecord_databaseRecord)
327 client_show_raw_error(cl, "surrogate diagnostic");
331 if (cl->show_raw && cl->show_raw->binary)
333 Z_External *rec = npr->u.databaseRecord;
334 if (rec->which == Z_External_octet)
336 cl->show_raw->record_handler(cl->show_raw->data,
338 rec->u.octet_aligned->buf,
339 rec->u.octet_aligned->len);
340 client_show_raw_dequeue(cl);
343 client_show_raw_error(cl, "no records");
346 doc = record_to_xml(client_get_database(cl), npr->u.databaseRecord);
349 client_show_raw_error(cl, "unable to convert record to xml");
353 xmlDocDumpMemory(doc, &buf_out, &len_out);
358 cl->show_raw->record_handler(cl->show_raw->data,
359 (const char *) buf_out, len_out);
360 client_show_raw_dequeue(cl);
365 #endif // RETIRED show raw
367 void client_search_response(struct client *cl)
369 struct connection *co = cl->connection;
370 struct session *se = cl->session;
371 ZOOM_connection link = connection_get_link(co);
372 ZOOM_resultset resultset = connection_get_resultset(co);
373 const char *error, *addinfo;
375 if (ZOOM_connection_error(link, &error, &addinfo))
378 cl->state = Client_Error;
379 yaz_log(YLOG_WARN, "Search error %s (%s): %s",
380 error, addinfo, client_get_url(cl));
384 cl->hits = ZOOM_resultset_size(resultset);
385 se->total_hits += cl->hits;
389 void client_record_response(struct client *cl)
391 struct connection *co = cl->connection;
392 ZOOM_connection link = connection_get_link(co);
393 ZOOM_resultset resultset = connection_get_resultset(co);
394 const char *error, *addinfo;
396 if (ZOOM_connection_error(link, &error, &addinfo))
398 cl->state = Client_Error;
399 yaz_log(YLOG_WARN, "Search error %s (%s): %s",
400 error, addinfo, client_get_url(cl));
405 int offset = cl->records;
406 const char *msg, *addinfo;
408 if ((rec = ZOOM_resultset_record(resultset, offset)))
410 yaz_log(YLOG_LOG, "Record with offset %d", offset);
412 if (ZOOM_record_error(rec, &msg, &addinfo, 0))
413 yaz_log(YLOG_WARN, "Record error %s (%s): %s (rec #%d)",
414 error, addinfo, client_get_url(cl), cl->records);
417 struct session_database *sdb = client_get_database(cl);
419 char type[128] = "xml";
420 const char *nativesyntax =
421 session_setting_oneval(sdb, PZ_NATIVESYNTAX);
424 if (*nativesyntax && (cset = strchr(nativesyntax, ';')))
425 sprintf(type, "xml; charset=%s", cset + 1);
427 if ((xmlrec = ZOOM_record_get(rec, type, NULL)))
429 if (ingest_record(cl, xmlrec, cl->records))
431 session_alert_watch(cl->session, SESSION_WATCH_SHOW);
432 session_alert_watch(cl->session, SESSION_WATCH_RECORD);
435 yaz_log(YLOG_WARN, "Failed to ingest");
438 yaz_log(YLOG_WARN, "Failed to extract ZOOM record");
443 yaz_log(YLOG_WARN, "Expected record, but got NULL");
449 void client_present_response(struct client *cl, Z_APDU *a)
451 Z_PresentResponse *r = a->u.presentResponse;
452 Z_Records *recs = r->records;
454 if (recs && recs->which == Z_Records_NSD)
456 WRBUF w = wrbuf_alloc();
458 Z_DiagRec dr, *dr_p = &dr;
459 dr.which = Z_DiagRec_defaultFormat;
460 dr.u.defaultFormat = recs->u.nonSurrogateDiagnostic;
462 wrbuf_printf(w, "Present response NSD %s: ",
463 cl->database->database->url);
465 cl->diagnostic = diag_to_wrbuf(&dr_p, 1, w);
467 yaz_log(YLOG_WARN, "%s", wrbuf_cstr(w));
469 cl->state = Client_Error;
472 client_show_raw_error(cl, "non surrogate diagnostics");
474 else if (recs && recs->which == Z_Records_multipleNSD)
476 WRBUF w = wrbuf_alloc();
478 wrbuf_printf(w, "Present response multipleNSD %s: ",
479 cl->database->database->url);
481 diag_to_wrbuf(recs->u.multipleNonSurDiagnostics->diagRecs,
482 recs->u.multipleNonSurDiagnostics->num_diagRecs,
484 yaz_log(YLOG_WARN, "%s", wrbuf_cstr(w));
485 cl->state = Client_Error;
488 else if (recs && !*r->presentStatus && cl->state != Client_Error)
490 yaz_log(YLOG_DEBUG, "Good Present response %s",
491 cl->database->database->url);
493 // we can mix show raw and normal show ..
494 if (cl->show_raw && cl->show_raw->active)
496 cl->show_raw->active = 0; // no longer active
497 ingest_raw_records(cl, recs);
500 ingest_records(cl, recs);
501 cl->state = Client_Continue;
503 else if (*r->presentStatus)
505 yaz_log(YLOG_WARN, "Bad Present response %s",
506 cl->database->database->url);
507 cl->state = Client_Error;
508 client_show_raw_error(cl, "bad present response");
512 void client_close_response(struct client *cl, Z_APDU *a)
514 struct connection *co = cl->connection;
515 /* Z_Close *r = a->u.close; */
517 yaz_log(YLOG_WARN, "Close response %s", cl->database->database->url);
519 cl->state = Client_Failed;
520 connection_destroy(co);
523 #endif // RETIRED show raw
526 int client_is_our_response(struct client *cl)
528 struct session *se = client_get_session(cl);
530 if (cl && (cl->requestid == se->requestid ||
531 cl->state == Client_Initializing))
537 void client_start_search(struct client *cl)
539 struct session_database *sdb = client_get_database(cl);
540 struct connection *co = client_get_connection(cl);
541 ZOOM_connection link = connection_get_link(co);
543 char *databaseName = sdb->database->databases[0];
544 const char *opt_piggyback = session_setting_oneval(sdb, PZ_PIGGYBACK);
545 const char *opt_queryenc = session_setting_oneval(sdb, PZ_QUERYENCODING);
546 const char *opt_elements = session_setting_oneval(sdb, PZ_ELEMENTS);
547 const char *opt_requestsyn = session_setting_oneval(sdb, PZ_REQUESTSYNTAX);
548 const char *opt_maxrecs = session_setting_oneval(sdb, PZ_MAXRECS);
557 ZOOM_connection_option_set(link, "piggyback", opt_piggyback);
559 ZOOM_connection_option_set(link, "piggyback", "1");
561 ZOOM_connection_option_set(link, "rpnCharset", opt_queryenc);
563 ZOOM_connection_option_set(link, "elementSetName", opt_elements);
565 ZOOM_connection_option_set(link, "preferredRecordSyntax", opt_requestsyn);
567 ZOOM_connection_option_set(link, "count", opt_maxrecs);
571 sprintf(n, "%d", global_parameters.toget);
572 ZOOM_connection_option_set(link, "count", n);
574 if (!databaseName || !*databaseName)
575 databaseName = "Default";
576 ZOOM_connection_option_set(link, "databaseName", databaseName);
578 ZOOM_connection_option_set(link, "presentChunk", "20");
580 rs = ZOOM_connection_search_pqf(link, cl->pquery);
581 connection_set_resultset(co, rs);
582 ZOOM_connection_process(link);
585 struct client *client_create(void)
591 client_freelist = client_freelist->next;
594 r = xmalloc(sizeof(struct client));
604 r->state = Client_Disconnected;
610 void client_destroy(struct client *c)
612 struct session *se = c->session;
613 if (c == se->clients)
614 se->clients = c->next;
618 for (cc = se->clients; cc && cc->next != c; cc = cc->next)
626 connection_release(c->connection);
627 c->next = client_freelist;
631 void client_set_connection(struct client *cl, struct connection *con)
633 cl->connection = con;
636 void client_disconnect(struct client *cl)
638 if (cl->state != Client_Idle)
639 client_set_state(cl, Client_Disconnected);
640 client_set_connection(cl, 0);
643 // Extract terms from query into null-terminated termlist
644 static void extract_terms(NMEM nmem, struct ccl_rpn_node *query, char **termlist)
648 pull_terms(nmem, query, termlist, &num);
652 // Initialize CCL map for a target
653 static CCL_bibset prepare_cclmap(struct client *cl)
655 struct session_database *sdb = client_get_database(cl);
662 for (s = sdb->settings[PZ_CCLMAP]; s; s = s->next)
664 char *p = strchr(s->name + 3, ':');
667 yaz_log(YLOG_WARN, "Malformed cclmap name: %s", s->name);
672 ccl_qual_fitem(res, s->value, p);
677 // Parse the query given the settings specific to this client
678 int client_parse_query(struct client *cl, const char *query)
680 struct session *se = client_get_session(cl);
681 struct ccl_rpn_node *cn;
683 CCL_bibset ccl_map = prepare_cclmap(cl);
688 cn = ccl_find_str(ccl_map, query, &cerror, &cpos);
689 ccl_qual_rm(&ccl_map);
692 cl->state = Client_Error;
693 yaz_log(YLOG_WARN, "Failed to parse query for %s",
694 client_get_database(cl)->database->url);
697 wrbuf_rewind(se->wrbuf);
698 ccl_pquery(se->wrbuf, cn);
700 cl->pquery = xstrdup(wrbuf_cstr(se->wrbuf));
704 // Initialize relevance structure with query terms
706 extract_terms(se->nmem, cn, p);
707 se->relevance = relevance_create(
708 global_parameters.server->relevance_pct,
709 se->nmem, (const char **) p,
710 se->expected_maxrecs);
717 void client_set_session(struct client *cl, struct session *se)
720 cl->next = se->clients;
724 int client_is_active(struct client *cl)
726 if (cl->connection && (cl->state == Client_Continue ||
727 cl->state == Client_Connecting ||
728 cl->state == Client_Working))
733 struct client *client_next_in_session(struct client *cl)
741 int client_get_hits(struct client *cl)
746 int client_get_num_records(struct client *cl)
751 int client_get_diagnostic(struct client *cl)
753 return cl->diagnostic;
756 void client_set_database(struct client *cl, struct session_database *db)
761 struct host *client_get_host(struct client *cl)
763 return client_get_database(cl)->database->host;
766 const char *client_get_url(struct client *cl)
768 return client_get_database(cl)->database->url;
774 * indent-tabs-mode: nil
776 * vim: shiftwidth=4 tabstop=8 expandtab