From 09f5ad52b5ae914f6b8f5d16afcb14e6fc33ea45 Mon Sep 17 00:00:00 2001 From: Adam Dickmeiss Date: Mon, 4 Dec 2006 14:56:54 +0000 Subject: [PATCH] Added GFS utility function bend_assoc_is_alive which returns 1 if association is still alive (client is connected); 0 otherwise (client closed connection). This allows busy servers to stop working for impatient clients. --- NEWS | 5 ++ include/yaz/backend.h | 3 +- src/eventl.c | 27 ++++++- src/eventl.h | 4 +- src/seshigh.c | 202 +++++++++++++++++++++++++++---------------------- src/session.h | 4 +- ztest/ztest.c | 14 +++- 7 files changed, 163 insertions(+), 96 deletions(-) diff --git a/NEWS b/NEWS index 34d01f1..e64545b 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,8 @@ +Added GFS utility function bend_assoc_is_alive which returns 1 if +association is still alive (client is connected); 0 otherwise (client +closed connection). This allows busy servers to stop working for +impatient clients. + Fixed bug #740: Handle SRU records referring to xmlns's outside recordData. --- 2.1.40 2006/11/27 diff --git a/include/yaz/backend.h b/include/yaz/backend.h index 39c4c67..ff0b15d 100644 --- a/include/yaz/backend.h +++ b/include/yaz/backend.h @@ -24,7 +24,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* $Id: backend.h,v 1.38 2006-10-09 21:02:41 adam Exp $ */ +/* $Id: backend.h,v 1.39 2006-12-04 14:56:54 adam Exp $ */ /** * \file backend.h @@ -329,6 +329,7 @@ YAZ_EXPORT statserv_options_block *statserv_getcontrol(void); YAZ_EXPORT void statserv_setcontrol(statserv_options_block *block); YAZ_EXPORT int check_ip_tcpd(void *cd, const char *addr, int len, int type); +YAZ_EXPORT int bend_assoc_is_alive(bend_association assoc); YAZ_END_CDECL diff --git a/src/eventl.c b/src/eventl.c index 2392cc8..2fcac05 100644 --- a/src/eventl.c +++ b/src/eventl.c @@ -2,7 +2,7 @@ * Copyright (C) 1995-2005, Index Data ApS * See the file LICENSE for details. * - * $Id: eventl.c,v 1.9 2005-06-25 15:46:04 adam Exp $ + * $Id: eventl.c,v 1.10 2006-12-04 14:56:55 adam Exp $ */ /** @@ -78,6 +78,31 @@ IOCHAN iochan_create(int fd, IOC_CALLBACK cb, int flags, int chan_id) return new_iochan; } +int iochan_is_alive(IOCHAN chan) +{ + static struct timeval to; + fd_set in, out, except; + int res, max; + + to.tv_sec = 0; + to.tv_usec = 0; + + FD_ZERO(&in); + FD_ZERO(&out); + FD_ZERO(&except); + + FD_SET(chan->fd, &in); + + max = chan->fd + 1; + + res = YAZ_EV_SELECT(max + 1, &in, 0, 0, &to); + if (res == 0) + return 1; + if (!ir_read(chan, EVENT_INPUT)) + return 0; + return 1; +} + int event_loop(IOCHAN *iochans) { do /* loop as long as there are active associations to process */ diff --git a/src/eventl.h b/src/eventl.h index a11fe47..099d764 100644 --- a/src/eventl.h +++ b/src/eventl.h @@ -2,7 +2,7 @@ * Copyright (C) 1995-2005, Index Data ApS * See the file LICENSE for details. * - * $Id: eventl.h,v 1.6 2005-06-25 15:46:04 adam Exp $ + * $Id: eventl.h,v 1.7 2006-12-04 14:56:55 adam Exp $ */ /** @@ -30,7 +30,6 @@ typedef struct iochan #define EVENT_OUTPUT 0x02 #define EVENT_EXCEPT 0x04 #define EVENT_TIMEOUT 0x08 -#define EVENT_WORK 0x10 int force_event; IOC_CALLBACK fun; void *data; @@ -59,6 +58,7 @@ int force_event; #define iochan_settimeout(i, t) ((i)->max_idle = (t), (i)->last_event = time(0)) IOCHAN iochan_create(int fd, IOC_CALLBACK cb, int flags, int port); +int iochan_is_alive(IOCHAN chan); int event_loop(IOCHAN *iochans); void statserv_remove (IOCHAN pIOChannel); #endif diff --git a/src/seshigh.c b/src/seshigh.c index 28830c2..e29973e 100644 --- a/src/seshigh.c +++ b/src/seshigh.c @@ -1,8 +1,8 @@ /* - * Copyright (C) 1995-2005, Index Data ApS + * Copyright (C) 1995-2006, Index Data ApS * See the file LICENSE for details. * - * $Id: seshigh.c,v 1.104 2006-11-14 08:37:38 adam Exp $ + * $Id: seshigh.c,v 1.105 2006-12-04 14:56:55 adam Exp $ */ /** * \file seshigh.c @@ -264,83 +264,31 @@ static void do_close(association *a, int reason, char *message) do_close_req (a, reason, message, req); } -/* - * This is where PDUs from the client are read and the further - * processing is initiated. Flow of control moves down through the - * various process_* functions below, until the encoded result comes back up - * to the output handler in here. - * - * h : the I/O channel that has an outstanding event. - * event : the current outstanding event. - */ -void ir_session(IOCHAN h, int event) + +int ir_read(IOCHAN h, int event) { - int res; association *assoc = (association *)iochan_getdata(h); COMSTACK conn = assoc->client_link; request *req; - - assert(h && conn && assoc); - if (event == EVENT_TIMEOUT) + + if ((assoc->cs_put_mask & EVENT_INPUT) == 0 && (event & assoc->cs_get_mask)) { - if (assoc->state != ASSOC_UP) + yaz_log(YLOG_DEBUG, "ir_session (input)"); + /* We aren't speaking to this fellow */ + if (assoc->state == ASSOC_DEAD) { - yaz_log(YLOG_DEBUG, "Final timeout - closing connection."); - /* do we need to lod this at all */ + yaz_log(log_sessiondetail, "Connection closed - end of session"); cs_close(conn); destroy_association(assoc); iochan_destroy(h); + return 0; } - else - { - yaz_log(log_sessiondetail, - "Session idle too long. Sending close."); - do_close(assoc, Z_Close_lackOfActivity, 0); - } - return; - } - if (event & assoc->cs_accept_mask) - { - if (!cs_accept (conn)) - { - yaz_log (YLOG_WARN, "accept failed"); - destroy_association(assoc); - iochan_destroy(h); - } - iochan_clearflag (h, EVENT_OUTPUT); - if (conn->io_pending) - { /* cs_accept didn't complete */ - assoc->cs_accept_mask = - ((conn->io_pending & CS_WANT_WRITE) ? EVENT_OUTPUT : 0) | - ((conn->io_pending & CS_WANT_READ) ? EVENT_INPUT : 0); + assoc->cs_get_mask = EVENT_INPUT; - iochan_setflag (h, assoc->cs_accept_mask); - } - else - { /* cs_accept completed. Prepare for reading (cs_get) */ - assoc->cs_accept_mask = 0; - assoc->cs_get_mask = EVENT_INPUT; - iochan_setflag (h, assoc->cs_get_mask); - } - return; - } - if ((event & assoc->cs_get_mask) || (event & EVENT_WORK)) /* input */ - { - if ((assoc->cs_put_mask & EVENT_INPUT) == 0 && (event & assoc->cs_get_mask)) + do { - yaz_log(YLOG_DEBUG, "ir_session (input)"); - /* We aren't speaking to this fellow */ - if (assoc->state == ASSOC_DEAD) - { - yaz_log(log_sessiondetail, "Connection closed - end of session"); - cs_close(conn); - destroy_association(assoc); - iochan_destroy(h); - return; - } - assoc->cs_get_mask = EVENT_INPUT; - res = cs_get(conn, &assoc->input_buffer, - &assoc->input_buffer_len); + int res = cs_get(conn, &assoc->input_buffer, + &assoc->input_buffer_len); if (res < 0 && cs_errno(conn) == CSBUFSIZE) { yaz_log(log_session, "Connection error: %s res=%d", @@ -348,31 +296,26 @@ void ir_session(IOCHAN h, int event) req = request_get(&assoc->incoming); /* get a new request */ do_close_req(assoc, Z_Close_protocolError, "Incoming package too large", req); - return; + return 0; } else if (res <= 0) { - yaz_log(log_sessiondetail, "Connection closed by client"); - cs_close(conn); - destroy_association(assoc); - iochan_destroy(h); - return; + yaz_log(log_session, "Connection closed by client"); + assoc->state = ASSOC_DEAD; + return 0; } else if (res == 1) /* incomplete read - wait for more */ { if (conn->io_pending & CS_WANT_WRITE) assoc->cs_get_mask |= EVENT_OUTPUT; iochan_setflag(h, assoc->cs_get_mask); - return; + return 1; } - if (cs_more(conn)) /* more stuff - call us again later, please */ - iochan_setevent(h, EVENT_INPUT); - /* we got a complete PDU. Let's decode it */ yaz_log(YLOG_DEBUG, "Got PDU, %d bytes: lead=%02X %02X %02X", res, - assoc->input_buffer[0] & 0xff, - assoc->input_buffer[1] & 0xff, - assoc->input_buffer[2] & 0xff); + assoc->input_buffer[0] & 0xff, + assoc->input_buffer[1] & 0xff, + assoc->input_buffer[2] & 0xff); req = request_get(&assoc->incoming); /* get a new request */ odr_reset(assoc->decode); odr_setbuf(assoc->decode, assoc->input_buffer, res, 0); @@ -396,20 +339,87 @@ void ir_session(IOCHAN h, int event) assoc->state = ASSOC_DEAD; process_gdu_response(assoc, req, p); } - return; + return 0; } req->request_mem = odr_extract_mem(assoc->decode); if (assoc->print) { if (!z_GDU(assoc->print, &req->gdu_request, 0, 0)) yaz_log(YLOG_WARN, "ODR print error: %s", - odr_errmsg(odr_geterror(assoc->print))); + odr_errmsg(odr_geterror(assoc->print))); odr_reset(assoc->print); } request_enq(&assoc->incoming, req); } + while (cs_more(conn)); + } + return 1; +} + +/* + * This is where PDUs from the client are read and the further + * processing is initiated. Flow of control moves down through the + * various process_* functions below, until the encoded result comes back up + * to the output handler in here. + * + * h : the I/O channel that has an outstanding event. + * event : the current outstanding event. + */ +void ir_session(IOCHAN h, int event) +{ + int res; + association *assoc = (association *)iochan_getdata(h); + COMSTACK conn = assoc->client_link; + request *req; + + assert(h && conn && assoc); + if (event == EVENT_TIMEOUT) + { + if (assoc->state != ASSOC_UP) + { + yaz_log(YLOG_DEBUG, "Final timeout - closing connection."); + /* do we need to lod this at all */ + cs_close(conn); + destroy_association(assoc); + iochan_destroy(h); + } + else + { + yaz_log(log_sessiondetail, + "Session idle too long. Sending close."); + do_close(assoc, Z_Close_lackOfActivity, 0); + } + return; + } + if (event & assoc->cs_accept_mask) + { + if (!cs_accept (conn)) + { + yaz_log (YLOG_WARN, "accept failed"); + destroy_association(assoc); + iochan_destroy(h); + } + iochan_clearflag (h, EVENT_OUTPUT); + if (conn->io_pending) + { /* cs_accept didn't complete */ + assoc->cs_accept_mask = + ((conn->io_pending & CS_WANT_WRITE) ? EVENT_OUTPUT : 0) | + ((conn->io_pending & CS_WANT_READ) ? EVENT_INPUT : 0); - /* can we do something yet? */ + iochan_setflag (h, assoc->cs_accept_mask); + } + else + { /* cs_accept completed. Prepare for reading (cs_get) */ + assoc->cs_accept_mask = 0; + assoc->cs_get_mask = EVENT_INPUT; + iochan_setflag (h, assoc->cs_get_mask); + } + return; + } + if (event & assoc->cs_get_mask) /* input */ + { + if (!ir_read(h, event)) + return; req = request_head(&assoc->incoming); if (req->state == REQUEST_IDLE) { @@ -2116,13 +2126,18 @@ static int process_gdu_response(association *assoc, request *req, Z_GDU *res) iochan_setflag(assoc->client_chan, EVENT_OUTPUT); assoc->cs_put_mask = EVENT_OUTPUT; /* Is there more work to be done? give that to the input handler too */ -#if 1 - if (request_head(&assoc->incoming)) + for (;;) { - yaz_log (YLOG_DEBUG, "more work to be done"); - iochan_setevent(assoc->client_chan, EVENT_WORK); + req = request_head(&assoc->incoming); + if (req && req->state == REQUEST_IDLE) + { + yaz_log(YLOG_LOG, "process next request 3"); + request_deq(&assoc->incoming); + process_gdu_request(assoc, req); + } + else + break; } -#endif return 0; } @@ -2334,7 +2349,7 @@ static Z_APDU *process_initRequest(association *assoc, request *reqb) assoc->init->implementation_name, odr_prepend(assoc->encode, "GFS", resp->implementationName)); - version = odr_strdup(assoc->encode, "$Revision: 1.104 $"); + version = odr_strdup(assoc->encode, "$Revision: 1.105 $"); if (strlen(version) > 10) /* check for unexpanded CVS strings */ version[strlen(version)-2] = '\0'; resp->implementationVersion = odr_prepend(assoc->encode, @@ -3510,6 +3525,15 @@ static Z_APDU *process_ESRequest(association *assoc, request *reqb, int *fd) return apdu; } +int bend_assoc_is_alive(bend_association assoc) +{ + if (assoc->state == ASSOC_DEAD) + return 0; /* already marked as dead. Don't check I/O chan anymore */ + + return iochan_is_alive(assoc->client_chan); +} + + /* * Local variables: * c-basic-offset: 4 diff --git a/src/session.h b/src/session.h index 6ac5299..b63d2cf 100644 --- a/src/session.h +++ b/src/session.h @@ -2,7 +2,7 @@ * Copyright (C) 1995-2006, Index Data ApS * See the file LICENSE for details. * - * $Id: session.h,v 1.11 2006-05-07 14:48:25 adam Exp $ + * $Id: session.h,v 1.12 2006-12-04 14:56:55 adam Exp $ */ /** * \file session.h @@ -133,6 +133,8 @@ int statserv_must_terminate(void); int control_association(association *assoc, const char *host, int force); +int ir_read(IOCHAN h, int event); + #endif /* * Local variables: diff --git a/ztest/ztest.c b/ztest/ztest.c index 51ead41..515d1ac 100644 --- a/ztest/ztest.c +++ b/ztest/ztest.c @@ -2,7 +2,7 @@ * Copyright (C) 1995-2005, Index Data ApS * See the file LICENSE for details. * - * $Id: ztest.c,v 1.81 2006-05-06 00:52:15 quinn Exp $ + * $Id: ztest.c,v 1.82 2006-12-04 14:56:55 adam Exp $ */ /* @@ -54,7 +54,17 @@ int ztest_search(void *handle, bend_search_rr *rr) else if(!yaz_matchstr (rr->basenames[0], "Slow")) { #if HAVE_UNISTD_H - sleep(3); + /* wait up to 3 seconds and check if connection is still alive */ + int i; + for (i = 0; i<3; i++) + { + if (!bend_assoc_is_alive(rr->association)) + { + yaz_log(YLOG_LOG, "search aborted"); + break; + } + sleep(1); + } #endif ; } -- 1.7.10.4