1 /* $Id: connection.c,v 1.1 2007-04-23 21:05:23 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"
49 #include "parameters.h"
52 // Represents a physical, reusable connection to a remote Z39.50 host
57 struct client *client;
66 struct connection *next;
69 static struct connection *connection_freelist = 0;
71 void host_remove_connection(struct host *h, struct connection *con)
73 struct connection **conp = &h->connections;
79 *conp = (*conp)->next;
82 conp = &(*conp)->next;
87 // Close connection and recycle structure
88 void connection_destroy(struct connection *co)
90 struct host *h = co->host;
95 iochan_destroy(co->iochan);
98 yaz_log(YLOG_DEBUG, "Connection destroy %s", co->host->hostport);
100 host_remove_connection(h, co);
103 client_disconnect(co->client);
105 co->next = connection_freelist;
106 connection_freelist = co;
109 // Creates a new connection for client, associated with the host of
111 struct connection *connection_create(struct client *cl)
113 struct connection *new;
114 struct host *host = client_get_host(cl);
116 if ((new = connection_freelist))
117 connection_freelist = new->next;
120 new = xmalloc(sizeof (struct connection));
125 new->next = new->host->connections;
126 new->host->connections = new;
128 client_set_connection(cl, new);
130 new->state = Conn_Resolving;
132 connection_connect(new);
136 static void connection_handler(IOCHAN i, int event)
138 struct connection *co = iochan_getdata(i);
139 struct client *cl = co->client;
140 struct session *se = 0;
143 se = client_get_session(cl);
146 yaz_log(YLOG_WARN, "Destroying orphan connection");
147 connection_destroy(co);
151 if (co->state == Conn_Connecting && event & EVENT_OUTPUT)
154 socklen_t errlen = sizeof(errcode);
156 if (getsockopt(cs_fileno(co->link), SOL_SOCKET, SO_ERROR, &errcode,
157 &errlen) < 0 || errcode != 0)
164 yaz_log(YLOG_DEBUG, "Connect OK");
165 co->state = Conn_Open;
167 client_set_state(cl, Client_Connected);
171 else if (event & EVENT_INPUT)
173 int len = cs_get(co->link, &co->ibuf, &co->ibufsize);
177 yaz_log(YLOG_WARN|YLOG_ERRNO, "Error reading from %s",
179 connection_destroy(co);
184 yaz_log(YLOG_WARN, "EOF reading from %s", client_get_url(cl));
185 connection_destroy(co);
188 else if (len > 1) // We discard input if we have no connection
190 co->state = Conn_Open;
192 if (client_is_our_response(cl))
196 odr_reset(global_parameters.odr_in);
197 odr_setbuf(global_parameters.odr_in, co->ibuf, len, 0);
198 if (!z_APDU(global_parameters.odr_in, &a, 0, 0))
205 case Z_APDU_initResponse:
206 client_init_response(cl, a);
208 case Z_APDU_searchResponse:
209 client_search_response(cl, a);
211 case Z_APDU_presentResponse:
212 client_present_response(cl, a);
215 client_close_response(cl, a);
219 "Unexpected Z39.50 response from %s",
224 // We aren't expecting staggered output from target
225 // if (cs_more(t->link))
226 // iochan_setevent(i, EVENT_INPUT);
228 else // we throw away response and go to idle mode
230 yaz_log(YLOG_DEBUG, "Ignoring result of expired operation");
231 client_set_state(cl, Client_Idle);
234 /* if len==1 we do nothing but wait for more input */
239 // Disassociate connection from client
240 void connection_release(struct connection *co)
242 struct client *cl = co->client;
244 yaz_log(YLOG_DEBUG, "Connection release %s", co->host->hostport);
247 client_set_connection(cl, 0);
251 void connect_resolver_host(struct host *host)
253 struct connection *con = host->connections;
256 if (con->state == Conn_Resolving)
258 if (!host->ipport) /* unresolved */
260 connection_destroy(con);
261 /* start all over .. at some point it will be NULL */
262 con = host->connections;
264 else if (!con->client)
266 yaz_log(YLOG_WARN, "connect_unresolved_host : ophan client");
267 connection_destroy(con);
268 /* start all over .. at some point it will be NULL */
269 con = host->connections;
273 connection_connect(con);
280 int connection_send_apdu(struct connection *co, Z_APDU *a)
285 if (!z_APDU(global_parameters.odr_out, &a, 0, 0))
287 odr_perror(global_parameters.odr_out, "Encoding APDU");
290 buf = odr_getbuf(global_parameters.odr_out, &len, 0);
291 r = cs_put(co->link, buf, len);
294 yaz_log(YLOG_WARN, "cs_put: %s", cs_errmsg(cs_errno(co->link)));
299 fprintf(stderr, "cs_put incomplete (ParaZ does not handle that)\n");
302 odr_reset(global_parameters.odr_out); /* release the APDU structure */
303 co->state = Conn_Waiting;
304 iochan_setflags(co->iochan, EVENT_INPUT);
308 struct host *connection_get_host(struct connection *con)
313 int connection_connect(struct connection *con)
316 struct host *host = connection_get_host(con);
320 assert(host->ipport);
323 if (!(link = cs_create(tcpip_type, 0, PROTO_Z3950)))
325 yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to create comstack");
329 if (0 == strlen(global_parameters.zproxy_override)){
330 /* no Z39.50 proxy needed - direct connect */
331 yaz_log(YLOG_DEBUG, "Connection create %s", connection_get_url(con));
333 if (!(addr = cs_straddr(link, host->ipport)))
335 yaz_log(YLOG_WARN|YLOG_ERRNO,
336 "Lookup of IP address %s failed", host->ipport);
341 /* Z39.50 proxy connect */
342 yaz_log(YLOG_DEBUG, "Connection create %s proxy %s",
343 connection_get_url(con), global_parameters.zproxy_override);
345 if (!(addr = cs_straddr(link, global_parameters.zproxy_override)))
347 yaz_log(YLOG_WARN|YLOG_ERRNO,
348 "Lookup of IP address %s failed",
349 global_parameters.zproxy_override);
354 res = cs_connect(link, addr);
357 yaz_log(YLOG_WARN|YLOG_ERRNO, "cs_connect %s",
358 connection_get_url(con));
362 con->state = Conn_Connecting;
363 con->iochan = iochan_create(cs_fileno(link), connection_handler, 0);
364 iochan_setdata(con->iochan, con);
365 pazpar2_add_channel(con->iochan);
367 /* this fragment is bad DRY: from client_prep_connection */
368 client_set_state(con->client, Client_Connecting);
369 iochan_setflag(con->iochan, EVENT_OUTPUT);
373 const char *connection_get_url(struct connection *co)
375 return client_get_url(co->client);
378 // Ensure that client has a connection associated
379 int client_prep_connection(struct client *cl)
381 struct connection *co;
382 struct session *se = client_get_session(cl);
383 struct host *host = client_get_host(cl);
385 co = client_get_connection(cl);
387 yaz_log(YLOG_DEBUG, "Client prep %s", client_get_url(cl));
391 // See if someone else has an idle connection
392 // We should look at timestamps here to select the longest-idle connection
393 for (co = host->connections; co; co = co->next)
394 if (co->state == Conn_Open && (!co->client || client_get_session(co->client) != se))
398 connection_release(co);
399 client_set_connection(cl, co);
403 co = connection_create(cl);
407 if (co->state == Conn_Connecting)
409 client_set_state(cl, Client_Connecting);
410 iochan_setflag(co->iochan, EVENT_OUTPUT);
412 else if (co->state == Conn_Open)
414 if (client_get_state(cl) == Client_Error
415 || client_get_state(cl) == Client_Disconnected)
416 client_set_state(cl, Client_Idle);
417 iochan_setflag(co->iochan, EVENT_OUTPUT);
430 * indent-tabs-mode: nil
432 * vim: shiftwidth=4 tabstop=8 expandtab