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
20 /** \file connection.c
21 \brief Z39.50 connection (low-level client)
38 #include <sys/socket.h>
42 typedef int socklen_t;
53 #include <yaz/comstack.h>
54 #include <yaz/tcpip.h>
55 #include "connection.h"
61 #include "parameters.h"
64 /** \brief Represents a physical, reusable connection to a remote Z39.50 host
70 struct client *client;
73 char *authentication; // Empty string or authentication string if set
81 struct connection *next; // next for same host or next in free list
84 static struct connection *connection_freelist = 0;
86 static void remove_connection_from_host(struct connection *con)
88 struct connection **conp = &con->host->connections;
94 *conp = (*conp)->next;
97 conp = &(*conp)->next;
102 // Close connection and recycle structure
103 void connection_destroy(struct connection *co)
108 iochan_destroy(co->iochan);
111 yaz_log(YLOG_DEBUG, "Connection destroy %s", co->host->hostport);
113 remove_connection_from_host(co);
116 client_disconnect(co->client);
120 co->next = connection_freelist;
121 connection_freelist = co;
124 // Creates a new connection for client, associated with the host of
126 struct connection *connection_create(struct client *cl)
128 struct connection *new;
129 struct host *host = client_get_host(cl);
131 if ((new = connection_freelist))
132 connection_freelist = new->next;
135 new = xmalloc(sizeof (struct connection));
140 new->next = new->host->connections;
141 new->host->connections = new;
143 new->authentication = "";
145 client_set_connection(cl, new);
147 new->state = Conn_Resolving;
149 connection_connect(new);
153 static void connection_handler(IOCHAN i, int event)
155 struct connection *co = iochan_getdata(i);
156 struct client *cl = co->client;
157 struct session *se = 0;
160 se = client_get_session(cl);
163 connection_destroy(co);
167 if (event & EVENT_TIMEOUT)
169 if (co->state == Conn_Connecting)
171 yaz_log(YLOG_WARN, "connect timeout %s", client_get_url(cl));
176 yaz_log(YLOG_LOG, "idle timeout %s", client_get_url(cl));
177 connection_destroy(co);
181 if (co->state == Conn_Connecting && event & EVENT_OUTPUT)
184 socklen_t errlen = sizeof(errcode);
186 if (getsockopt(cs_fileno(co->link), SOL_SOCKET, SO_ERROR, (char*) &errcode,
187 &errlen) < 0 || errcode != 0)
194 yaz_log(YLOG_DEBUG, "Connect OK");
195 co->state = Conn_Open;
197 client_set_state(cl, Client_Connected);
198 iochan_settimeout(i, global_parameters.z3950_session_timeout);
202 else if (event & EVENT_INPUT)
204 int len = cs_get(co->link, &co->ibuf, &co->ibufsize);
208 yaz_log(YLOG_WARN|YLOG_ERRNO, "Error reading from %s",
210 connection_destroy(co);
215 yaz_log(YLOG_WARN, "EOF reading from %s", client_get_url(cl));
216 connection_destroy(co);
219 else if (len > 1) // We discard input if we have no connection
221 co->state = Conn_Open;
223 if (client_is_our_response(cl))
226 struct session_database *sdb = client_get_database(cl);
227 const char *apdulog = session_setting_oneval(sdb, PZ_APDULOG);
229 odr_reset(global_parameters.odr_in);
230 odr_setbuf(global_parameters.odr_in, co->ibuf, len, 0);
231 if (!z_APDU(global_parameters.odr_in, &a, 0, 0))
237 if (apdulog && *apdulog && *apdulog != '0')
239 ODR p = odr_createmem(ODR_PRINT);
240 yaz_log(YLOG_LOG, "recv APDU %s", client_get_url(cl));
242 odr_setprint(p, yaz_log_file());
244 odr_setprint(p, stderr);
249 case Z_APDU_initResponse:
250 client_init_response(cl, a);
252 case Z_APDU_searchResponse:
253 client_search_response(cl, a);
255 case Z_APDU_presentResponse:
256 client_present_response(cl, a);
259 client_close_response(cl, a);
263 "Unexpected Z39.50 response from %s",
268 // We aren't expecting staggered output from target
269 // if (cs_more(t->link))
270 // iochan_setevent(i, EVENT_INPUT);
272 else // we throw away response and go to idle mode
274 yaz_log(YLOG_DEBUG, "Ignoring result of expired operation");
275 client_set_state(cl, Client_Continue);
278 /* if len==1 we do nothing but wait for more input */
283 // Disassociate connection from client
284 void connection_release(struct connection *co)
286 struct client *cl = co->client;
288 yaz_log(YLOG_DEBUG, "Connection release %s", co->host->hostport);
291 client_set_connection(cl, 0);
295 void connect_resolver_host(struct host *host)
297 struct connection *con = host->connections;
300 if (con->state == Conn_Resolving)
302 if (!host->ipport) /* unresolved */
304 connection_destroy(con);
305 /* start all over .. at some point it will be NULL */
306 con = host->connections;
309 else if (!con->client)
311 connection_destroy(con);
312 /* start all over .. at some point it will be NULL */
313 con = host->connections;
318 connection_connect(con);
323 yaz_log(YLOG_LOG, "connect_resolver_host: state=%d", con->state);
329 int connection_send_apdu(struct connection *co, Z_APDU *a)
334 if (!z_APDU(global_parameters.odr_out, &a, 0, 0))
336 odr_perror(global_parameters.odr_out, "Encoding APDU");
339 buf = odr_getbuf(global_parameters.odr_out, &len, 0);
340 r = cs_put(co->link, buf, len);
343 yaz_log(YLOG_WARN, "cs_put: %s", cs_errmsg(cs_errno(co->link)));
348 fprintf(stderr, "cs_put incomplete (ParaZ does not handle that)\n");
351 odr_reset(global_parameters.odr_out); /* release the APDU structure */
352 co->state = Conn_Waiting;
353 iochan_setflags(co->iochan, EVENT_INPUT);
357 struct host *connection_get_host(struct connection *con)
362 int connection_connect(struct connection *con)
365 struct host *host = connection_get_host(con);
369 struct session_database *sdb = client_get_database(con->client);
370 const char *zproxy = session_setting_oneval(sdb, PZ_ZPROXY);
372 assert(host->ipport);
375 if (!(link = cs_create(tcpip_type, 0, PROTO_Z3950)))
377 yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to create comstack");
381 if (zproxy && *zproxy)
382 con->zproxy = xstrdup(zproxy);
386 /* no Z39.50 proxy needed - direct connect */
387 yaz_log(YLOG_DEBUG, "Connection create %s", connection_get_url(con));
389 if (!(addr = cs_straddr(link, host->ipport)))
391 yaz_log(YLOG_WARN|YLOG_ERRNO,
392 "Lookup of IP address %s failed", host->ipport);
397 /* Z39.50 proxy connect */
398 yaz_log(YLOG_DEBUG, "Connection create %s proxy %s",
399 connection_get_url(con), con->zproxy);
401 if (!(addr = cs_straddr(link, con->zproxy)))
403 yaz_log(YLOG_WARN|YLOG_ERRNO,
404 "Lookup of ZProxy IP address %s failed",
410 res = cs_connect(link, addr);
413 yaz_log(YLOG_WARN|YLOG_ERRNO, "cs_connect %s",
414 connection_get_url(con));
418 con->state = Conn_Connecting;
419 con->iochan = iochan_create(cs_fileno(link), connection_handler, 0);
420 iochan_settimeout(con->iochan, global_parameters.z3950_connect_timeout);
421 iochan_setdata(con->iochan, con);
422 pazpar2_add_channel(con->iochan);
424 /* this fragment is bad DRY: from client_prep_connection */
425 client_set_state(con->client, Client_Connecting);
426 iochan_setflag(con->iochan, EVENT_OUTPUT);
430 const char *connection_get_url(struct connection *co)
432 return client_get_url(co->client);
435 void connection_set_authentication(struct connection *co, char *auth)
437 co->authentication = auth;
440 // Ensure that client has a connection associated
441 int client_prep_connection(struct client *cl)
443 struct connection *co;
444 struct session *se = client_get_session(cl);
445 struct host *host = client_get_host(cl);
446 struct session_database *sdb = client_get_database(cl);
447 const char *zproxy = session_setting_oneval(sdb, PZ_ZPROXY);
449 if (zproxy && zproxy[0] == '\0')
452 co = client_get_connection(cl);
454 yaz_log(YLOG_DEBUG, "Client prep %s", client_get_url(cl));
458 // See if someone else has an idle connection
459 // We should look at timestamps here to select the longest-idle connection
460 for (co = host->connections; co; co = co->next)
461 if (co->state == Conn_Open &&
462 (!co->client || client_get_session(co->client) != se) &&
463 !strcmp(co->authentication,
464 session_setting_oneval(client_get_database(cl),
467 if (zproxy == 0 && co->zproxy == 0)
469 if (zproxy && co->zproxy && !strcmp(zproxy, co->zproxy))
474 connection_release(co);
475 client_set_connection(cl, co);
479 co = connection_create(cl);
483 if (co->state == Conn_Connecting)
485 client_set_state(cl, Client_Connecting);
486 iochan_setflag(co->iochan, EVENT_OUTPUT);
488 else if (co->state == Conn_Open)
490 if (client_get_state(cl) == Client_Error
491 || client_get_state(cl) == Client_Disconnected
492 || client_get_state(cl) == Client_Idle)
493 client_set_state(cl, Client_Continue);
494 iochan_setflag(co->iochan, EVENT_OUTPUT);
507 * indent-tabs-mode: nil
509 * vim: shiftwidth=4 tabstop=8 expandtab