/* Accept a new command connection */ static void http_accept(IOCHAN i, int event) { struct sockaddr_in addr; int fd = iochan_getfd(i); socklen_t len; int s; IOCHAN c; struct http_channel *ch; struct conf_server *server = iochan_getdata(i); len = sizeof addr; if ((s = accept(fd, (struct sockaddr *) &addr, &len)) < 0) { yaz_log(YLOG_WARN|YLOG_ERRNO, "accept"); return; } enable_nonblock(s); yaz_log(YLOG_DEBUG, "New command connection"); c = iochan_create(s, http_io, EVENT_INPUT | EVENT_EXCEPT, "http_session_socket"); ch = http_channel_create(server->http_server, inet_ntoa(addr.sin_addr), server); ch->iochan = c; iochan_setdata(c, ch); iochan_add(server->iochan_man, c); }
static void connection_handler(IOCHAN iochan, int event) { struct connection *co = iochan_getdata(iochan); struct client *cl; struct host *host = co->host; yaz_mutex_enter(host->mutex); cl = co->client; if (!cl) { /* no client associated with it.. We are probably getting a closed connection from the target.. Or, perhaps, an unexpected package.. We will just close the connection */ yaz_log(YLOG_LOG, "timeout connection %p event=%d", co, event); remove_connection_from_host(co); yaz_mutex_leave(host->mutex); connection_destroy(co); } else if (event & EVENT_TIMEOUT) { if (co->state == Conn_Connecting) { yaz_log(YLOG_WARN, "%p connect timeout %s", co, client_get_id(cl)); client_set_state(cl, Client_Error); remove_connection_from_host(co); yaz_mutex_leave(host->mutex); connection_destroy(co); } else { yaz_log(YLOG_LOG, "%p Connection idle timeout %s", co, client_get_id(cl)); remove_connection_from_host(co); yaz_mutex_leave(host->mutex); connection_destroy(co); } } else { yaz_mutex_leave(host->mutex); client_lock(cl); non_block_events(co); ZOOM_connection_fire_event_socket(co->link, event); non_block_events(co); client_unlock(cl); if (co->link) { iochan_setflags(iochan, ZOOM_connection_get_mask(co->link)); iochan_setfd(iochan, ZOOM_connection_get_socket(co->link)); } } }
void iochan_handler(struct iochan *i, int event) { sel_thread_t p = iochan_getdata(i); if (event & EVENT_INPUT) { struct work *w = sel_thread_result(p); w->host->ipport = w->ipport; connect_resolver_host(w->host, w->iochan_man); xfree(w); } }
// Cleanup channel static void http_channel_destroy(IOCHAN i) { struct http_channel *s = iochan_getdata(i); http_server_t http_server; if (s->proxy) { if (s->proxy->iochan) { #ifdef WIN32 closesocket(iochan_getfd(s->proxy->iochan)); #else close(iochan_getfd(s->proxy->iochan)); #endif iochan_destroy(s->proxy->iochan); } http_buf_destroy_queue(s->http_server, s->proxy->oqueue); xfree(s->proxy); } http_buf_destroy_queue(s->http_server, s->iqueue); http_buf_destroy_queue(s->http_server, s->oqueue); http_fire_observers(s); http_destroy_observers(s); http_server = s->http_server; /* save it for destroy (decref) */ http_server_destroy(http_server); #ifdef WIN32 closesocket(iochan_getfd(i)); #else close(iochan_getfd(i)); #endif iochan_destroy(i); nmem_destroy(s->nmem); wrbuf_destroy(s->wrbuf); xfree(s); }
void iochan_handler(struct iochan *i, int event) { static int number = 0; sel_thread_t p = iochan_getdata(i); if (event & EVENT_INPUT) { struct my_work_data *work; work = sel_thread_result(p); YAZ_CHECK(work); if (work) { YAZ_CHECK_EQ(work->x * 2, work->y); /* stop work after a couple of iterations */ if (work->x > 10) iochan_destroy(i); xfree(work); } } if (event & EVENT_TIMEOUT) { struct my_work_data *work; work = xmalloc(sizeof(*work)); work->x = number; sel_thread_add(p, work); work = xmalloc(sizeof(*work)); work->x = number+1; sel_thread_add(p, work); number += 10; } }
static void http_io(IOCHAN i, int event) { struct http_channel *hc = iochan_getdata(i); while (event) { if (event == EVENT_INPUT) { int res, reqlen; struct http_buf *htbuf; htbuf = http_buf_create(hc->http_server); res = recv(iochan_getfd(i), htbuf->buf, HTTP_BUF_SIZE -1, 0); if (res == -1 && errno == EAGAIN) { http_buf_destroy(hc->http_server, htbuf); return; } if (res <= 0) { #if HAVE_SYS_TIME_H if (hc->http_server->record_file) { struct timeval tv; gettimeofday(&tv, 0); fprintf(hc->http_server->record_file, "%lld %lld %lld 0\n", (long long) tv.tv_sec, (long long) tv.tv_usec, (long long) iochan_getfd(i)); } #endif http_buf_destroy(hc->http_server, htbuf); fflush(hc->http_server->record_file); http_channel_destroy(i); return; } htbuf->buf[res] = '\0'; htbuf->len = res; http_buf_enqueue(&hc->iqueue, htbuf); while (1) { if (hc->state == Http_Busy) return; reqlen = request_check(hc->iqueue); if (reqlen <= 2) return; // we have a complete HTTP request nmem_reset(hc->nmem); #if HAVE_SYS_TIME_H if (hc->http_server->record_file) { struct timeval tv; int sz = 0; struct http_buf *hb; for (hb = hc->iqueue; hb; hb = hb->next) sz += hb->len; gettimeofday(&tv, 0); fprintf(hc->http_server->record_file, "%lld %lld %lld %d\n", (long long) tv.tv_sec, (long long) tv.tv_usec, (long long) iochan_getfd(i), sz); for (hb = hc->iqueue; hb; hb = hb->next) fwrite(hb->buf, 1, hb->len, hc->http_server->record_file); } #endif if (!(hc->request = http_parse_request(hc, &hc->iqueue, reqlen))) { yaz_log(YLOG_WARN, "Failed to parse request"); http_error(hc, 400, "Bad Request"); return; } hc->response = 0; yaz_log(YLOG_LOG, "Request: %s %s%s%s", hc->request->method, hc->request->path, *hc->request->search ? "?" : "", hc->request->search); if (hc->request->content_buf) yaz_log(YLOG_LOG, "%s", hc->request->content_buf); if (http_weshouldproxy(hc->request)) http_proxy(hc->request); else { // Execute our business logic! hc->state = Http_Busy; http_command(hc); } } } else if (event == EVENT_OUTPUT) { event = 0; if (hc->oqueue) { struct http_buf *wb = hc->oqueue; int res; res = send(iochan_getfd(hc->iochan), wb->buf + wb->offset, wb->len, 0); if (res <= 0) { yaz_log(YLOG_WARN|YLOG_ERRNO, "write"); http_channel_destroy(i); return; } if (res == wb->len) { hc->oqueue = hc->oqueue->next; http_buf_destroy(hc->http_server, wb); } else { wb->len -= res; wb->offset += res; } if (!hc->oqueue) { if (!hc->keep_alive) { http_channel_destroy(i); return; } else { iochan_clearflag(i, EVENT_OUTPUT); if (hc->iqueue) event = EVENT_INPUT; } } } if (!hc->oqueue && hc->proxy && !hc->proxy->iochan) http_channel_destroy(i); // Server closed; we're done } else { yaz_log(YLOG_WARN, "Unexpected event on connection"); http_channel_destroy(i); event = 0; } } }
// Handles I/O on a client connection to a backend web server (proxy mode) static void proxy_io(IOCHAN pi, int event) { struct http_proxy *pc = iochan_getdata(pi); struct http_channel *hc = pc->channel; switch (event) { int res; struct http_buf *htbuf; case EVENT_INPUT: htbuf = http_buf_create(hc->http_server); res = recv(iochan_getfd(pi), htbuf->buf, HTTP_BUF_SIZE -1, 0); if (res == 0 || (res < 0 && !is_inprogress())) { if (hc->oqueue) { yaz_log(YLOG_WARN, "Proxy read came up short"); // Close channel and alert client HTTP channel that we're gone http_buf_destroy(hc->http_server, htbuf); #ifdef WIN32 closesocket(iochan_getfd(pi)); #else close(iochan_getfd(pi)); #endif iochan_destroy(pi); pc->iochan = 0; } else { http_channel_destroy(hc->iochan); return; } } else { htbuf->buf[res] = '\0'; htbuf->offset = 0; htbuf->len = res; // Write any remaining payload if (htbuf->len - htbuf->offset > 0) http_buf_enqueue(&hc->oqueue, htbuf); } iochan_setflag(hc->iochan, EVENT_OUTPUT); break; case EVENT_OUTPUT: if (!(htbuf = pc->oqueue)) { iochan_clearflag(pi, EVENT_OUTPUT); return; } res = send(iochan_getfd(pi), htbuf->buf + htbuf->offset, htbuf->len, 0); if (res <= 0) { yaz_log(YLOG_WARN|YLOG_ERRNO, "write"); http_channel_destroy(hc->iochan); return; } if (res == htbuf->len) { struct http_buf *np = htbuf->next; http_buf_destroy(hc->http_server, htbuf); pc->oqueue = np; } else { htbuf->len -= res; htbuf->offset += res; } if (!pc->oqueue) { iochan_setflags(pi, EVENT_INPUT); // Turns off output flag } break; default: yaz_log(YLOG_WARN, "Unexpected event on connection"); http_channel_destroy(hc->iochan); } }
static void session_timeout(IOCHAN i, int event) { struct http_session *s = iochan_getdata(i); http_session_destroy(s); }
/* UNIX listener */ static void listener(IOCHAN h, int event) { COMSTACK line = (COMSTACK) iochan_getdata(h); int res; if (event == EVENT_INPUT) { COMSTACK new_line; if ((res = cs_listen_check(line, 0, 0, control_block.check_ip, control_block.daemon_name)) < 0) { yaz_log(YLOG_WARN|YLOG_ERRNO, "cs_listen failed"); return; } else if (res == 1) { yaz_log(YLOG_WARN, "cs_listen incomplete"); return; } new_line = cs_accept(line); if (!new_line) { yaz_log(YLOG_FATAL, "Accept failed."); iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */ return; } yaz_log(log_sessiondetail, "Connect from %s", cs_addrstr(new_line)); no_sessions++; if (control_block.dynamic) { if ((res = fork()) < 0) { yaz_log(YLOG_FATAL|YLOG_ERRNO, "fork"); iochan_destroy(h); return; } else if (res == 0) /* child */ { char nbuf[100]; IOCHAN pp; for (pp = pListener; pp; pp = iochan_getnext(pp)) { COMSTACK l = (COMSTACK)iochan_getdata(pp); cs_close(l); iochan_destroy(pp); } sprintf(nbuf, "%s(%d)", me, no_sessions); yaz_log_init_prefix(nbuf); /* ensure that bend_stop is not called when each child exits - only for the main process .. */ control_block.bend_stop = 0; } else /* parent */ { cs_close(new_line); return; } } if (control_block.threads) { #if YAZ_POSIX_THREADS pthread_t child_thread; pthread_create(&child_thread, 0, new_session, new_line); pthread_detach(child_thread); #elif YAZ_GNU_THREADS pth_attr_t attr; pth_t child_thread; attr = pth_attr_new(); pth_attr_set(attr, PTH_ATTR_JOINABLE, FALSE); pth_attr_set(attr, PTH_ATTR_STACK_SIZE, 32*1024); pth_attr_set(attr, PTH_ATTR_NAME, "session"); yaz_log(YLOG_DEBUG, "pth_spawn begin"); child_thread = pth_spawn(attr, new_session, new_line); yaz_log(YLOG_DEBUG, "pth_spawn finish"); pth_attr_destroy(attr); #else new_session(new_line); #endif } else new_session(new_line); } else if (event == EVENT_TIMEOUT) { yaz_log(log_server, "Shutting down listener."); iochan_destroy(h); } else { yaz_log(YLOG_FATAL, "Bad event on listener."); iochan_destroy(h); } }
/* WIN32 listener */ static void listener(IOCHAN h, int event) { COMSTACK line = (COMSTACK) iochan_getdata(h); IOCHAN parent_chan = line->user; association *newas; int res; HANDLE newHandle; if (event == EVENT_INPUT) { COMSTACK new_line; IOCHAN new_chan; if ((res = cs_listen(line, 0, 0)) < 0) { yaz_log(YLOG_FATAL|YLOG_ERRNO, "cs_listen failed"); return; } else if (res == 1) return; /* incomplete */ yaz_log(YLOG_DEBUG, "listen ok"); new_line = cs_accept(line); if (!new_line) { yaz_log(YLOG_FATAL, "Accept failed."); return; } yaz_log(YLOG_DEBUG, "Accept ok"); if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session, EVENT_INPUT, parent_chan->chan_id))) { yaz_log(YLOG_FATAL, "Failed to create iochan"); iochan_destroy(h); return; } yaz_log(YLOG_DEBUG, "Creating association"); if (!(newas = create_association(new_chan, new_line, control_block.apdufile))) { yaz_log(YLOG_FATAL, "Failed to create new assoc."); iochan_destroy(h); return; } newas->cs_get_mask = EVENT_INPUT; newas->cs_put_mask = 0; newas->cs_accept_mask = 0; yaz_log(YLOG_DEBUG, "Setting timeout %d", control_block.idle_timeout); iochan_setdata(new_chan, newas); iochan_settimeout(new_chan, 60); /* Now what we need todo is create a new thread with this iochan as the parameter */ newHandle = (HANDLE) _beginthread(event_loop_thread, 0, new_chan); if (newHandle == (HANDLE) -1) { yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to create new thread."); iochan_destroy(h); return; } /* We successfully created the thread, so add it to the list */ statserv_add(newHandle, new_chan); yaz_log(YLOG_DEBUG, "Created new thread, id = %ld iochan %p",(long) newHandle, new_chan); iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */ } else { yaz_log(YLOG_FATAL, "Bad event on listener."); iochan_destroy(h); return; } }