1 /* $Id: connection.c,v 1.9 2007-07-25 13:27:06 adam Exp $
2 Copyright (c) 2006-2007, Index Data.
4 This file is part of Pazpar2.
6 Pazpar2 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 Pazpar2 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 Pazpar2; see the file LICENSE. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 /** \file connection.c
23 \brief Z39.50 connection (low-level client)
31 #include <sys/socket.h>
42 #include <yaz/comstack.h>
43 #include <yaz/tcpip.h>
44 #include "connection.h"
50 #include "parameters.h"
53 /** \brief Represents a physical, reusable connection to a remote Z39.50 host
59 struct client *client;
62 char *authentication; // Empty string or authentication string if set
69 struct connection *next; // next for same host or next in free list
72 static struct connection *connection_freelist = 0;
74 static void remove_connection_from_host(struct connection *con)
76 struct connection **conp = &con->host->connections;
82 *conp = (*conp)->next;
85 conp = &(*conp)->next;
90 // Close connection and recycle structure
91 void connection_destroy(struct connection *co)
96 iochan_destroy(co->iochan);
99 yaz_log(YLOG_DEBUG, "Connection destroy %s", co->host->hostport);
101 remove_connection_from_host(co);
104 client_disconnect(co->client);
106 co->next = connection_freelist;
107 connection_freelist = co;
110 // Creates a new connection for client, associated with the host of
112 struct connection *connection_create(struct client *cl)
114 struct connection *new;
115 struct host *host = client_get_host(cl);
117 if ((new = connection_freelist))
118 connection_freelist = new->next;
121 new = xmalloc(sizeof (struct connection));
126 new->next = new->host->connections;
127 new->host->connections = new;
129 new->authentication = "";
130 client_set_connection(cl, new);
132 new->state = Conn_Resolving;
134 connection_connect(new);
138 static void connection_handler(IOCHAN i, int event)
140 struct connection *co = iochan_getdata(i);
141 struct client *cl = co->client;
142 struct session *se = 0;
145 se = client_get_session(cl);
148 connection_destroy(co);
152 if (event & EVENT_TIMEOUT)
154 if (co->state == Conn_Connecting)
156 yaz_log(YLOG_WARN, "connect timeout %s", client_get_url(cl));
161 yaz_log(YLOG_LOG, "idle timeout %s", client_get_url(cl));
162 connection_destroy(co);
166 if (co->state == Conn_Connecting && event & EVENT_OUTPUT)
169 socklen_t errlen = sizeof(errcode);
171 if (getsockopt(cs_fileno(co->link), SOL_SOCKET, SO_ERROR, &errcode,
172 &errlen) < 0 || errcode != 0)
179 yaz_log(YLOG_DEBUG, "Connect OK");
180 co->state = Conn_Open;
182 client_set_state(cl, Client_Connected);
183 iochan_settimeout(i, global_parameters.z3950_session_timeout);
187 else if (event & EVENT_INPUT)
189 int len = cs_get(co->link, &co->ibuf, &co->ibufsize);
193 yaz_log(YLOG_WARN|YLOG_ERRNO, "Error reading from %s",
195 connection_destroy(co);
200 yaz_log(YLOG_WARN, "EOF reading from %s", client_get_url(cl));
201 connection_destroy(co);
204 else if (len > 1) // We discard input if we have no connection
206 co->state = Conn_Open;
208 if (client_is_our_response(cl))
211 struct session_database *sdb = client_get_database(cl);
212 const char *apdulog = session_setting_oneval(sdb, PZ_APDULOG);
214 odr_reset(global_parameters.odr_in);
215 odr_setbuf(global_parameters.odr_in, co->ibuf, len, 0);
216 if (!z_APDU(global_parameters.odr_in, &a, 0, 0))
222 if (apdulog && *apdulog && *apdulog != '0')
224 ODR p = odr_createmem(ODR_PRINT);
225 yaz_log(YLOG_LOG, "recv APDU %s", client_get_url(cl));
227 odr_setprint(p, yaz_log_file());
229 odr_setprint(p, stderr);
234 case Z_APDU_initResponse:
235 client_init_response(cl, a);
237 case Z_APDU_searchResponse:
238 client_search_response(cl, a);
240 case Z_APDU_presentResponse:
241 client_present_response(cl, a);
244 client_close_response(cl, a);
248 "Unexpected Z39.50 response from %s",
253 // We aren't expecting staggered output from target
254 // if (cs_more(t->link))
255 // iochan_setevent(i, EVENT_INPUT);
257 else // we throw away response and go to idle mode
259 yaz_log(YLOG_DEBUG, "Ignoring result of expired operation");
260 client_set_state(cl, Client_Idle);
263 /* if len==1 we do nothing but wait for more input */
268 // Disassociate connection from client
269 void connection_release(struct connection *co)
271 struct client *cl = co->client;
273 yaz_log(YLOG_DEBUG, "Connection release %s", co->host->hostport);
276 client_set_connection(cl, 0);
280 void connect_resolver_host(struct host *host)
282 struct connection *con = host->connections;
285 if (con->state == Conn_Resolving)
287 if (!host->ipport) /* unresolved */
289 connection_destroy(con);
290 /* start all over .. at some point it will be NULL */
291 con = host->connections;
294 else if (!con->client)
296 connection_destroy(con);
297 /* start all over .. at some point it will be NULL */
298 con = host->connections;
303 connection_connect(con);
308 yaz_log(YLOG_LOG, "connect_resolver_host: state=%d", con->state);
314 int connection_send_apdu(struct connection *co, Z_APDU *a)
319 if (!z_APDU(global_parameters.odr_out, &a, 0, 0))
321 odr_perror(global_parameters.odr_out, "Encoding APDU");
324 buf = odr_getbuf(global_parameters.odr_out, &len, 0);
325 r = cs_put(co->link, buf, len);
328 yaz_log(YLOG_WARN, "cs_put: %s", cs_errmsg(cs_errno(co->link)));
333 fprintf(stderr, "cs_put incomplete (ParaZ does not handle that)\n");
336 odr_reset(global_parameters.odr_out); /* release the APDU structure */
337 co->state = Conn_Waiting;
338 iochan_setflags(co->iochan, EVENT_INPUT);
342 struct host *connection_get_host(struct connection *con)
347 int connection_connect(struct connection *con)
350 struct host *host = connection_get_host(con);
354 struct session_database *sdb = client_get_database(con->client);
355 char *zproxy = session_setting_oneval(sdb, PZ_ZPROXY);
357 assert(host->ipport);
360 if (!(link = cs_create(tcpip_type, 0, PROTO_Z3950)))
362 yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to create comstack");
366 if (!zproxy || 0 == strlen(zproxy)){
367 /* no Z39.50 proxy needed - direct connect */
368 yaz_log(YLOG_DEBUG, "Connection create %s", connection_get_url(con));
370 if (!(addr = cs_straddr(link, host->ipport)))
372 yaz_log(YLOG_WARN|YLOG_ERRNO,
373 "Lookup of IP address %s failed", host->ipport);
378 /* Z39.50 proxy connect */
379 yaz_log(YLOG_DEBUG, "Connection create %s proxy %s",
380 connection_get_url(con), zproxy);
382 if (!(addr = cs_straddr(link, zproxy)))
384 yaz_log(YLOG_WARN|YLOG_ERRNO,
385 "Lookup of ZProxy IP address %s failed",
391 res = cs_connect(link, addr);
394 yaz_log(YLOG_WARN|YLOG_ERRNO, "cs_connect %s",
395 connection_get_url(con));
399 con->state = Conn_Connecting;
400 con->iochan = iochan_create(cs_fileno(link), connection_handler, 0);
401 iochan_settimeout(con->iochan, global_parameters.z3950_connect_timeout);
402 iochan_setdata(con->iochan, con);
403 pazpar2_add_channel(con->iochan);
405 /* this fragment is bad DRY: from client_prep_connection */
406 client_set_state(con->client, Client_Connecting);
407 iochan_setflag(con->iochan, EVENT_OUTPUT);
411 const char *connection_get_url(struct connection *co)
413 return client_get_url(co->client);
416 void connection_set_authentication(struct connection *co, char *auth)
418 co->authentication = auth;
421 // Ensure that client has a connection associated
422 int client_prep_connection(struct client *cl)
424 struct connection *co;
425 struct session *se = client_get_session(cl);
426 struct host *host = client_get_host(cl);
428 co = client_get_connection(cl);
430 yaz_log(YLOG_DEBUG, "Client prep %s", client_get_url(cl));
434 // See if someone else has an idle connection
435 // We should look at timestamps here to select the longest-idle connection
436 for (co = host->connections; co; co = co->next)
437 if (co->state == Conn_Open &&
438 (!co->client || client_get_session(co->client) != se) &&
439 !strcmp(co->authentication,
440 session_setting_oneval(client_get_database(cl),
445 connection_release(co);
446 client_set_connection(cl, co);
450 co = connection_create(cl);
454 if (co->state == Conn_Connecting)
456 client_set_state(cl, Client_Connecting);
457 iochan_setflag(co->iochan, EVENT_OUTPUT);
459 else if (co->state == Conn_Open)
461 if (client_get_state(cl) == Client_Error
462 || client_get_state(cl) == Client_Disconnected)
463 client_set_state(cl, Client_Idle);
464 iochan_setflag(co->iochan, EVENT_OUTPUT);
477 * indent-tabs-mode: nil
479 * vim: shiftwidth=4 tabstop=8 expandtab