From: Willy Tarreau Date: Wed, 27 Aug 2008 19:41:35 +0000 (+0200) Subject: [MEDIUM] stream_sock_process_data moved to stream_sock.c X-Git-Tag: v1.3.16-rc1~171 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2d2127989c3097c08beeb1b100f52f59b58f642f;p=thirdparty%2Fhaproxy.git [MEDIUM] stream_sock_process_data moved to stream_sock.c The old temporary process_srv_data function moved to stream_sock.c. --- diff --git a/include/proto/stream_sock.h b/include/proto/stream_sock.h index d57ddf5b64..dc2b763ca8 100644 --- a/include/proto/stream_sock.h +++ b/include/proto/stream_sock.h @@ -33,6 +33,7 @@ /* main event functions used to move data between sockets and buffers */ int stream_sock_read(int fd); int stream_sock_write(int fd); +int stream_sock_process_data(int fd); /* This either returns the sockname or the original destination address. Code diff --git a/src/proto_http.c b/src/proto_http.c index b53bb6c818..4581813398 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #ifdef CONFIG_HAP_TCPSPLICE @@ -756,7 +757,7 @@ void process_session(struct task *t, int *next) buffer_shutw_now(s->req); } - if (process_srv_data(s)) + if (stream_sock_process_data(s->req->cons->fd)) resync |= PROCESS_SRV; /* Count server-side errors (but not timeouts). */ @@ -3863,916 +3864,6 @@ int process_srv_conn(struct session *t) return 0; } -/* - * Manages the server FSM and its socket during the DATA phase. It must not be - * called when a file descriptor is not attached to the buffer. It must only be - * called during SI_ST_EST. It normally returns zero, but may return 1 if it - * absolutely wants to be called again. - */ -int process_srv_data(struct session *t) -{ - struct buffer *req = t->req; - struct buffer *rep = t->rep; - int fd = req->cons->fd; - - DPRINTF(stderr,"[%u] %s: c=%s exp(r,w)=%u,%u req=%08x rep=%08x rql=%d rpl=%d\n", - now_ms, __FUNCTION__, - cli_stnames[t->cli_state], - rep->rex, req->wex, - req->flags, rep->flags, - req->l, rep->l); - - /* Read or write error on the file descriptor */ - if (fdtab[fd].state == FD_STERROR) { - trace_term(t, TT_HTTP_SRV_6); - if (!req->cons->err_type) { - req->cons->err_loc = t->srv; - req->cons->err_type = SI_ET_DATA_ERR; - } - buffer_shutw(req); - req->flags |= BF_WRITE_ERROR; - buffer_shutr(rep); - rep->flags |= BF_READ_ERROR; - - do_close_and_return: - fd_delete(fd); - req->cons->state = SI_ST_CLO; - return 0; - } - - /* Check if we need to close the read side */ - if (!(rep->flags & BF_SHUTR)) { - /* Last read, forced read-shutdown, or other end closed */ - if (rep->flags & (BF_READ_NULL|BF_SHUTR_NOW|BF_SHUTW)) { - trace_term(t, TT_HTTP_SRV_10); - do_close_read: - buffer_shutr(rep); - if (req->flags & BF_SHUTW) - goto do_close_and_return; - - EV_FD_CLR(fd, DIR_RD); - } - /* Read timeout */ - else if (unlikely(!(rep->flags & BF_READ_TIMEOUT) && tick_is_expired(rep->rex, now_ms))) { - trace_term(t, TT_HTTP_SRV_12); - rep->flags |= BF_READ_TIMEOUT; - if (!req->cons->err_type) { - req->cons->err_loc = t->srv; - req->cons->err_type = SI_ET_DATA_TO; - } - goto do_close_read; - } - /* Read not closed, update FD status and timeout for reads */ - else if (rep->flags & (BF_FULL|BF_HIJACK)) { - /* stop reading */ - EV_FD_COND_C(fd, DIR_RD); - rep->rex = TICK_ETERNITY; - } - else { - /* (re)start reading and update timeout. Note: we don't recompute the timeout - * everytime we get here, otherwise it would risk never to expire. We only - * update it if is was not yet set, or if we already got some read status. - */ - EV_FD_COND_S(fd, DIR_RD); - if (!tick_isset(rep->rex) || rep->flags & BF_READ_STATUS) - rep->rex = tick_add_ifset(now_ms, rep->rto); - } - } - - /* Check if we need to close the write side */ - if (!(req->flags & BF_SHUTW)) { - /* Forced write-shutdown or other end closed with empty buffer. */ - if ((req->flags & BF_SHUTW_NOW) || - (req->flags & (BF_EMPTY|BF_MAY_FORWARD|BF_SHUTR)) == (BF_EMPTY|BF_MAY_FORWARD|BF_SHUTR)) { - trace_term(t, TT_HTTP_SRV_11); - do_close_write: - buffer_shutw(req); - if (rep->flags & BF_SHUTR) - goto do_close_and_return; - - EV_FD_CLR(fd, DIR_WR); - shutdown(fd, SHUT_WR); - } - /* Write timeout */ - else if (unlikely(!(req->flags & BF_WRITE_TIMEOUT) && tick_is_expired(req->wex, now_ms))) { - trace_term(t, TT_HTTP_SRV_13); - req->flags |= BF_WRITE_TIMEOUT; - if (!req->cons->err_type) { - req->cons->err_loc = t->srv; - req->cons->err_type = SI_ET_DATA_TO; - } - goto do_close_write; - } - /* Write not closed, update FD status and timeout for writes */ - else if ((req->flags & (BF_EMPTY|BF_MAY_FORWARD)) != BF_MAY_FORWARD) { - /* stop writing */ - EV_FD_COND_C(fd, DIR_WR); - req->wex = TICK_ETERNITY; - } - else { - /* (re)start writing and update timeout. Note: we don't recompute the timeout - * everytime we get here, otherwise it would risk never to expire. We only - * update it if is was not yet set, or if we already got some write status. - */ - EV_FD_COND_S(fd, DIR_WR); - if (!tick_isset(req->wex) || req->flags & BF_WRITE_STATUS) { - req->wex = tick_add_ifset(now_ms, req->wto); - if (tick_isset(req->wex) && !(rep->flags & BF_SHUTR) && tick_isset(rep->rex)) { - /* Note: depending on the protocol, we don't know if we're waiting - * for incoming data or not. So in order to prevent the socket from - * expiring read timeouts during writes, we refresh the read timeout, - * except if it was already infinite. - */ - rep->rex = req->wex; - } - } - } - } - return 0; /* other cases change nothing */ -} - - -///* -// * Manages the client FSM and its socket. It normally returns zero, but may -// * return 1 if it absolutely wants to be called again. -// * -// * Note: process_cli is the ONLY function allowed to set cli_state to anything -// * but CL_STCLOSE. -// */ -//int process_cli(struct session *t) -//{ -// struct buffer *req = t->req; -// struct buffer *rep = t->rep; -// -// DPRINTF(stderr,"[%u] %s: c=%s set(r,w)=%d,%d exp(r,w)=%u,%u req=%08x rep=%08x rql=%d rpl=%d\n", -// now_ms, __FUNCTION__, -// cli_stnames[t->cli_state], -// t->cli_fd >= 0 && fdtab[t->cli_fd].state != FD_STCLOSE ? EV_FD_ISSET(t->cli_fd, DIR_RD) : 0, -// t->cli_fd >= 0 && fdtab[t->cli_fd].state != FD_STCLOSE ? EV_FD_ISSET(t->cli_fd, DIR_WR) : 0, -// req->rex, rep->wex, -// req->flags, rep->flags, -// req->l, rep->l); -// -// update_state: -// /* FIXME: we still have to check for CL_STSHUTR because client_retnclose -// * still set this state (and will do until unix sockets are converted). -// */ -// if (t->cli_state == CL_STDATA || t->cli_state == CL_STSHUTR) { -// /* we can skip most of the tests at once if some conditions are not met */ -// if (!((req->flags & (BF_READ_TIMEOUT|BF_READ_ERROR)) || -// (rep->flags & (BF_WRITE_TIMEOUT|BF_WRITE_ERROR)) || -// (!(req->flags & BF_SHUTR) && req->flags & (BF_READ_NULL|BF_SHUTW)) || -// (!(rep->flags & BF_SHUTW) && -// (rep->flags & (BF_EMPTY|BF_MAY_FORWARD|BF_SHUTR)) == (BF_EMPTY|BF_MAY_FORWARD|BF_SHUTR)))) -// goto update_timeouts; -// -// /* read or write error */ -// if (rep->flags & BF_WRITE_ERROR || req->flags & BF_READ_ERROR) { -// buffer_shutr(req); -// buffer_shutw(rep); -// fd_delete(t->cli_fd); -// t->cli_state = CL_STCLOSE; -// trace_term(t, TT_HTTP_CLI_1); -// if (!req->analysers) { -// if (!(t->flags & SN_ERR_MASK)) -// t->flags |= SN_ERR_CLICL; -// if (!(t->flags & SN_FINST_MASK)) { -// if (t->pend_pos) -// t->flags |= SN_FINST_Q; -// else if (!(req->flags & BF_CONNECTED)) -// t->flags |= SN_FINST_C; -// else -// t->flags |= SN_FINST_D; -// } -// } -// goto update_state; -// } -// /* last read, or end of server write */ -// else if (!(req->flags & BF_SHUTR) && /* not already done */ -// req->flags & (BF_READ_NULL | BF_SHUTW)) { -// buffer_shutr(req); -// if (!(rep->flags & BF_SHUTW)) { -// EV_FD_CLR(t->cli_fd, DIR_RD); -// trace_term(t, TT_HTTP_CLI_2); -// } else { -// /* output was already closed */ -// fd_delete(t->cli_fd); -// t->cli_state = CL_STCLOSE; -// trace_term(t, TT_HTTP_CLI_3); -// } -// goto update_state; -// } -// /* last server read and buffer empty : we only check them when we're -// * allowed to forward the data. -// */ -// else if (!(rep->flags & BF_SHUTW) && /* not already done */ -// rep->flags & BF_EMPTY && rep->flags & BF_MAY_FORWARD && -// rep->flags & BF_SHUTR && !(t->flags & SN_SELF_GEN)) { -// buffer_shutw(rep); -// if (!(req->flags & BF_SHUTR)) { -// EV_FD_CLR(t->cli_fd, DIR_WR); -// shutdown(t->cli_fd, SHUT_WR); -// /* We must ensure that the read part is still alive when switching to shutw */ -// /* FIXME: is this still true ? */ -// EV_FD_SET(t->cli_fd, DIR_RD); -// req->rex = tick_add_ifset(now_ms, t->fe->timeout.client); -// trace_term(t, TT_HTTP_CLI_4); -// } else { -// fd_delete(t->cli_fd); -// t->cli_state = CL_STCLOSE; -// trace_term(t, TT_HTTP_CLI_5); -// } -// goto update_state; -// } -// /* read timeout */ -// else if ((req->flags & (BF_SHUTR|BF_READ_TIMEOUT)) == BF_READ_TIMEOUT) { -// buffer_shutr(req); -// if (!(rep->flags & BF_SHUTW)) { -// EV_FD_CLR(t->cli_fd, DIR_RD); -// trace_term(t, TT_HTTP_CLI_6); -// } else { -// /* output was already closed */ -// fd_delete(t->cli_fd); -// t->cli_state = CL_STCLOSE; -// trace_term(t, TT_HTTP_CLI_7); -// } -// if (!req->analysers) { -// if (!(t->flags & SN_ERR_MASK)) -// t->flags |= SN_ERR_CLITO; -// if (!(t->flags & SN_FINST_MASK)) { -// if (t->pend_pos) -// t->flags |= SN_FINST_Q; -// else if (!(req->flags & BF_CONNECTED)) -// t->flags |= SN_FINST_C; -// else -// t->flags |= SN_FINST_D; -// } -// } -// goto update_state; -// } -// /* write timeout */ -// else if ((rep->flags & (BF_SHUTW|BF_WRITE_TIMEOUT)) == BF_WRITE_TIMEOUT) { -// buffer_shutw(rep); -// if (!(req->flags & BF_SHUTR)) { -// EV_FD_CLR(t->cli_fd, DIR_WR); -// shutdown(t->cli_fd, SHUT_WR); -// /* We must ensure that the read part is still alive when switching to shutw */ -// /* FIXME: is this still true ? */ -// EV_FD_SET(t->cli_fd, DIR_RD); -// req->rex = tick_add_ifset(now_ms, t->fe->timeout.client); -// trace_term(t, TT_HTTP_CLI_8); -// } else { -// fd_delete(t->cli_fd); -// t->cli_state = CL_STCLOSE; -// trace_term(t, TT_HTTP_CLI_9); -// } -// if (!req->analysers) { -// if (!(t->flags & SN_ERR_MASK)) -// t->flags |= SN_ERR_CLITO; -// if (!(t->flags & SN_FINST_MASK)) { -// if (t->pend_pos) -// t->flags |= SN_FINST_Q; -// else if (!(req->flags & BF_CONNECTED)) -// t->flags |= SN_FINST_C; -// else -// t->flags |= SN_FINST_D; -// } -// } -// goto update_state; -// } -// -// update_timeouts: -// /* manage read timeout */ -// if (!(req->flags & BF_SHUTR)) { -// if (req->flags & BF_FULL) { -// /* no room to read more data */ -// if (EV_FD_COND_C(t->cli_fd, DIR_RD)) { -// /* stop reading until we get some space */ -// req->rex = TICK_ETERNITY; -// } -// } else { -// EV_FD_COND_S(t->cli_fd, DIR_RD); -// req->rex = tick_add_ifset(now_ms, t->fe->timeout.client); -// } -// } -// -// /* manage write timeout */ -// if (!(rep->flags & BF_SHUTW)) { -// /* first, we may have to produce data (eg: stats). -// * right now, this is limited to the SHUTR state. -// */ -// if (req->flags & BF_SHUTR && t->flags & SN_SELF_GEN) { -// produce_content(t); -// if (rep->flags & BF_EMPTY) { -// buffer_shutw(rep); -// fd_delete(t->cli_fd); -// t->cli_state = CL_STCLOSE; -// trace_term(t, TT_HTTP_CLI_10); -// goto update_state; -// } -// } -// -// /* we don't enable client write if the buffer is empty, nor if the server has to analyze it */ -// if ((rep->flags & BF_EMPTY) || !(rep->flags & BF_MAY_FORWARD)) { -// if (EV_FD_COND_C(t->cli_fd, DIR_WR)) { -// /* stop writing */ -// rep->wex = TICK_ETERNITY; -// } -// } else { -// /* buffer not empty */ -// EV_FD_COND_S(t->cli_fd, DIR_WR); -// if (!tick_isset(rep->wex)) { -// /* restart writing */ -// rep->wex = tick_add_ifset(now_ms, t->fe->timeout.client); -// if (!(req->flags & BF_SHUTR) && tick_isset(rep->wex) && tick_isset(req->rex)) { -// /* FIXME: to prevent the client from expiring read timeouts during writes, -// * we refresh it, except if it was already infinite. */ -// req->rex = rep->wex; -// } -// } -// } -// } -// return 0; /* other cases change nothing */ -// } -// else if (t->cli_state == CL_STCLOSE) { /* CL_STCLOSE: nothing to do */ -// if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) { -// int len; -// len = sprintf(trash, "%08x:%s.clicls[%04x:%04x]\n", t->uniq_id, t->be->id, (unsigned short)t->cli_fd, (unsigned short)req->cons->fd); -// write(1, trash, len); -// } -// return 0; -// } -//#ifdef DEBUG_DEV -// fprintf(stderr, "FIXME !!!! impossible state at %s:%d = %d\n", __FILE__, __LINE__, t->cli_state); -// ABORT_NOW(); -//#endif -// return 0; -//} -// -// -///* Return 1 if we could get a new connection for session t, otherwise zero */ -//int tcp_get_connection(struct session *t) -//{ -// struct http_txn *txn = &t->txn; -// struct buffer *req = t->req; -// struct buffer *rep = t->rep; -// -// DPRINTF(stderr,"[%u] %s: c=%s exp(r,w)=%u,%u req=%08x rep=%08x rql=%d rpl=%d\n", -// now_ms, __FUNCTION__, -// cli_stnames[t->cli_state], -// rep->rex, req->wex, -// req->flags, rep->flags, -// req->l, rep->l); -// -// -// if ((rep->flags & BF_SHUTW) || -// ((req->flags & BF_SHUTR) && -// (req->flags & BF_EMPTY || t->be->options & PR_O_ABRT_CLOSE))) { /* give up */ -// req->wex = TICK_ETERNITY; -// if (t->pend_pos) -// t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now); -// /* note that this must not return any error because it would be able to -// * overwrite the client_retnclose() output. -// */ -// if (txn->flags & TX_CLTARPIT) -// srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_T, 0, NULL); -// else -// srv_close_with_err(t, SN_ERR_CLICL, t->pend_pos ? SN_FINST_Q : SN_FINST_C, 0, NULL); -// -// trace_term(t, TT_HTTP_SRV_1); -// return 0; -// } -// -// /* stop here if we're not allowed to connect */ -// if (!(req->flags & BF_MAY_FORWARD)) -// return 0; -// -// /* the client allows the server to connect */ -// if (txn->flags & TX_CLTARPIT) { -// /* This connection is being tarpitted. The CLIENT side has -// * already set the connect expiration date to the right -// * timeout. We just have to check that it has not expired. -// */ -// if (!(req->flags & BF_WRITE_TIMEOUT)) -// return 0; -// -// /* We will set the queue timer to the time spent, just for -// * logging purposes. We fake a 500 server error, so that the -// * attacker will not suspect his connection has been tarpitted. -// * It will not cause trouble to the logs because we can exclude -// * the tarpitted connections by filtering on the 'PT' status flags. -// */ -// req->wex = TICK_ETERNITY; -// t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now); -// srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_T, -// 500, error_message(t, HTTP_ERR_500)); -// trace_term(t, TT_HTTP_SRV_2); -// return 0; -// } -// -// /* Right now, we will need to create a connection to the server. -// * We might already have tried, and got a connection pending, in -// * which case we will not do anything till it's pending. It's up -// * to any other session to release it and wake us up again. -// */ -// if (t->pend_pos) { -// if (!(req->flags & BF_WRITE_TIMEOUT)) { -// return 0; -// } else { -// /* we've been waiting too long here */ -// req->wex = TICK_ETERNITY; -// t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now); -// srv_close_with_err(t, SN_ERR_SRVTO, SN_FINST_Q, -// 503, error_message(t, HTTP_ERR_503)); -// trace_term(t, TT_HTTP_SRV_3); -// if (t->srv) -// t->srv->failed_conns++; -// t->be->failed_conns++; -// return 0; -// } -// } -// -// do { -// if (srv_redispatch_connect(t) != 0) -// return 0; -// -// if (t->srv && t->srv->rdr_len && t->flags & SN_REDIRECTABLE) { -// /* Server supporting redirection and it is possible. -// * Invalid requests are reported as such. It concerns all -// * the largest ones. -// */ -// struct chunk rdr; -// char *path; -// int len; -// -// /* 1: create the response header */ -// rdr.len = strlen(HTTP_302); -// rdr.str = trash; -// memcpy(rdr.str, HTTP_302, rdr.len); -// -// /* 2: add the server's prefix */ -// if (rdr.len + t->srv->rdr_len > sizeof(trash)) -// goto cancel_redir; -// -// memcpy(rdr.str + rdr.len, t->srv->rdr_pfx, t->srv->rdr_len); -// rdr.len += t->srv->rdr_len; -// -// /* 3: add the request URI */ -// path = http_get_path(txn); -// if (!path) -// goto cancel_redir; -// len = txn->req.sl.rq.u_l + (txn->req.sol+txn->req.sl.rq.u) - path; -// if (rdr.len + len > sizeof(trash) - 4) /* 4 for CRLF-CRLF */ -// goto cancel_redir; -// -// memcpy(rdr.str + rdr.len, path, len); -// rdr.len += len; -// memcpy(rdr.str + rdr.len, "\r\n\r\n", 4); -// rdr.len += 4; -// -// srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_C, 302, &rdr); -// trace_term(t, TT_HTTP_SRV_3); -// -// /* FIXME: we should increase a counter of redirects per server and per backend. */ -// if (t->srv) -// t->srv->cum_sess++; -// return 0; -// cancel_redir: -// txn->status = 400; -// t->fe->failed_req++; -// srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_C, -// 400, error_message(t, HTTP_ERR_400)); -// trace_term(t, TT_HTTP_SRV_4); -// return 0; -// } -// -// /* try to (re-)connect to the server, and fail if we expire the -// * number of retries. -// */ -// if (srv_retryable_connect(t)) { -// t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now); -// if (!(req->cons.flags & BC_KNOWN)) -// return 0; -// /* We got an FD */ -// return 1; -// } -// } while (1); -//} -// -// -///* Return 1 if the pending connection has failed and should be retried, -// * otherwise zero. -// */ -//int tcp_connection_failed(struct session *t) -//{ -// struct buffer *req = t->req; -// struct buffer *rep = t->rep; -// int conn_err; -// -// DPRINTF(stderr,"[%u] %s: c=%s exp(r,w)=%u,%u req=%08x rep=%08x rql=%d rpl=%d\n", -// now_ms, __FUNCTION__, -// cli_stnames[t->cli_state], -// rep->rex, req->wex, -// req->flags, rep->flags, -// req->l, rep->l); -// -// if ((rep->flags & BF_SHUTW) || -// ((req->flags & BF_SHUTR) && -// ((req->flags & BF_EMPTY && !(req->flags & BF_WRITE_STATUS)) || -// t->be->options & PR_O_ABRT_CLOSE))) { /* give up */ -// req->wex = TICK_ETERNITY; -// if (!(t->flags & SN_CONN_TAR)) { -// /* if we are in turn-around, we have already closed the FD */ -// fd_delete(req->cons->fd); -// req->cons->state = SI_ST_CLO; -// if (t->srv) { -// t->srv->cur_sess--; -// sess_change_server(t, NULL); -// } -// } -// -// /* note that this must not return any error because it would be able to -// * overwrite the client_retnclose() output. -// */ -// srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_C, 0, NULL); -// trace_term(t, TT_HTTP_SRV_5); -// return 0; -// } -// -// if (!(req->flags & (BF_WRITE_STATUS | BF_WRITE_TIMEOUT))) -// return 0; /* nothing changed */ -// -// if (!(req->flags & BF_WRITE_STATUS) || (req->flags & BF_WRITE_ERROR)) { -// /* timeout, asynchronous connect error or first write error */ -// if (t->flags & SN_CONN_TAR) { -// /* We are doing a turn-around waiting for a new connection attempt. */ -// if (!(req->flags & BF_WRITE_TIMEOUT)) -// return 0; -// t->flags &= ~SN_CONN_TAR; -// } -// else { -// fd_delete(req->cons->fd); -// req->cons->state = SI_ST_CLO; -// if (t->srv) { -// t->srv->cur_sess--; -// sess_change_server(t, NULL); -// } -// -// if (!(req->flags & BF_WRITE_STATUS)) -// conn_err = SN_ERR_SRVTO; // it was a connect timeout. -// else -// conn_err = SN_ERR_SRVCL; // it was an asynchronous connect error. -// -// /* ensure that we have enough retries left */ -// if (srv_count_retry_down(t, conn_err)) -// return 0; -// -// if (req->flags & BF_WRITE_ERROR) { -// /* we encountered an immediate connection error, and we -// * will have to retry connecting to the same server, most -// * likely leading to the same result. To avoid this, we -// * fake a connection timeout to retry after a turn-around -// * time of 1 second. We will wait in the previous if block. -// */ -// t->flags |= SN_CONN_TAR; -// req->wex = tick_add(now_ms, MS_TO_TICKS(1000)); -// return 0; -// } -// } -// -// if (t->srv && t->conn_retries == 0 && t->be->options & PR_O_REDISP) { -// /* We're on our last chance, and the REDISP option was specified. -// * We will ignore cookie and force to balance or use the dispatcher. -// */ -// /* let's try to offer this slot to anybody */ -// if (may_dequeue_tasks(t->srv, t->be)) -// process_srv_queue(t->srv); -// -// /* it's left to the dispatcher to choose a server */ -// t->flags &= ~(SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET); -// t->prev_srv = t->srv; -// -// /* first, get a connection */ -// if (srv_redispatch_connect(t)) { -// if (req->cons.flags & BC_KNOWN) -// return 0; -// /* we need to get a connection */ -// return 1; -// } -// } else { -// if (t->srv) -// t->srv->retries++; -// t->be->retries++; -// } -// -// do { -// /* Now we will try to either reconnect to the same server or -// * connect to another server. If the connection gets queued -// * because all servers are saturated, then we will go back to -// * the idle state where the buffer's consumer is marked as -// * unknown. -// */ -// if (srv_retryable_connect(t)) { -// t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now); -// if (req->cons.flags & BC_KNOWN) -// return 0; -// /* we did not get a connection */ -// return 1; -// } -// -// /* we need to redispatch the connection to another server */ -// if (srv_redispatch_connect(t)) { -// if (req->cons.flags & BC_KNOWN) -// return 0; -// /* we need to get a connection */ -// return 1; -// } -// } while (1); -// } -// else { /* no error and write OK */ -// t->logs.t_connect = tv_ms_elapsed(&t->logs.tv_accept, &now); -// -// if (req->flags & BF_EMPTY) { -// EV_FD_CLR(req->cons->fd, DIR_WR); -// req->wex = TICK_ETERNITY; -// } else { -// EV_FD_SET(req->cons->fd, DIR_WR); -// req->wex = tick_add_ifset(now_ms, t->be->timeout.server); -// if (tick_isset(req->wex)) { -// /* FIXME: to prevent the server from expiring read timeouts during writes, -// * we refresh it. */ -// rep->rex = req->wex; -// } -// } -// -// if (t->be->mode == PR_MODE_TCP) { /* let's allow immediate data connection in this case */ -// EV_FD_SET(req->cons->fd, DIR_RD); -// rep->rex = tick_add_ifset(now_ms, t->be->timeout.server); -// buffer_set_rlim(rep, BUFSIZE); /* no rewrite needed */ -// -// /* if the user wants to log as soon as possible, without counting -// bytes from the server, then this is the right moment. */ -// if (t->fe->to_log && !(t->logs.logwait & LW_BYTES)) { -// t->logs.t_close = t->logs.t_connect; /* to get a valid end date */ -// tcp_sess_log(t); -// } -//#ifdef CONFIG_HAP_TCPSPLICE -// if ((t->fe->options & t->be->options) & PR_O_TCPSPLICE) { -// /* TCP splicing supported by both FE and BE */ -// tcp_splice_splicefd(t->cli_fd, req->cons->fd, 0); -// } -//#endif -// } -// else { -// rep->analysers |= AN_RTR_HTTP_HDR; -// buffer_set_rlim(rep, BUFSIZE - MAXREWRITE); /* rewrite needed */ -// t->txn.rsp.msg_state = HTTP_MSG_RPBEFORE; -// /* reset hdr_idx which was already initialized by the request. -// * right now, the http parser does it. -// * hdr_idx_init(&t->txn.hdr_idx); -// */ -// } -// -// req->flags |= BF_CONNECTED; -// if (!rep->analysers) -// t->rep->flags |= BF_MAY_FORWARD; -// req->wex = TICK_ETERNITY; -// return 0; -// } -//} -// -// -///* -// * Tries to establish a connection to the server and associate it to the -// * request buffer's consumer side. It normally returns zero, but may return 1 -// * if it absolutely wants to be called again. -// */ -//int process_srv_conn(struct session *t) -//{ -// DPRINTF(stderr,"[%u] %s: c=%s exp(r,w)=%u,%u req=%08x rep=%08x rql=%d rpl=%d\n", -// now_ms, __FUNCTION__, -// cli_stnames[t->cli_state], -// t->rep->rex, t->req->wex, -// t->req->flags, t->rep->flags, -// t->req->l, t->rep->l); -// -// while (!(t->req->flags & BF_CONNECTED)) { -// if (!(t->req->cons.flags & BC_KNOWN)) { -// /* no connection in progress, get a new one */ -// if (!tcp_get_connection(t)) -// break; -// } else { -// /* connection in progress or just completed */ -// if (!tcp_connection_failed(t)) -// break; -// } -// } -// return 0; -//} -// -// -///* -// * Manages the server FSM and its socket during the DATA phase. It must not -// * be called when a file descriptor is not attached to the buffer. It normally -// * returns zero, but may return 1 if it absolutely wants to be called again. -// */ -//int process_srv_data(struct session *t) -//{ -// struct buffer *req = t->req; -// struct buffer *rep = t->rep; -// -// DPRINTF(stderr,"[%u] %s: c=%s exp(r,w)=%u,%u req=%08x rep=%08x rql=%d rpl=%d\n", -// now_ms, __FUNCTION__, -// cli_stnames[t->cli_state], -// rep->rex, req->wex, -// req->flags, rep->flags, -// req->l, rep->l); -// -// /* we can skip most of the tests at once if some conditions are not met */ -// if (!((req->flags & (BF_WRITE_TIMEOUT|BF_WRITE_ERROR)) || -// (!(req->flags & BF_SHUTW) && -// (req->flags & (BF_EMPTY|BF_MAY_FORWARD)) == (BF_EMPTY|BF_MAY_FORWARD)) || -// (rep->flags & (BF_READ_TIMEOUT|BF_READ_ERROR)) || -// (!(rep->flags & BF_SHUTR) && rep->flags & (BF_READ_NULL|BF_SHUTW)))) -// goto update_timeouts; -// -// /* read or write error */ -// /* FIXME: what happens when we have to deal with HTTP ??? */ -// if (req->flags & BF_WRITE_ERROR || rep->flags & BF_READ_ERROR) { -// buffer_shutr(rep); -// buffer_shutw(req); -// fd_delete(req->cons->fd); -// req->cons->state = SI_ST_CLO; -// if (t->srv) { -// t->srv->cur_sess--; -// t->srv->failed_resp++; -// sess_change_server(t, NULL); -// } -// t->be->failed_resp++; -// trace_term(t, TT_HTTP_SRV_6); -// if (!rep->analysers) { -// if (!(t->flags & SN_ERR_MASK)) -// t->flags |= SN_ERR_SRVCL; -// if (!(t->flags & SN_FINST_MASK)) -// t->flags |= SN_FINST_D; -// } -// if (may_dequeue_tasks(t->srv, t->be)) -// process_srv_queue(t->srv); -// -// return 0; -// } -// -// /* last read, or end of client write */ -// if (!(rep->flags & BF_SHUTR) && /* not already done */ -// rep->flags & (BF_READ_NULL | BF_SHUTW)) { -// buffer_shutr(rep); -// if (!(req->flags & BF_SHUTW)) { -// EV_FD_CLR(req->cons->fd, DIR_RD); -// trace_term(t, TT_HTTP_SRV_7); -// } else { -// /* output was already closed */ -// fd_delete(req->cons->fd); -// req->cons->state = SI_ST_CLO; -// if (t->srv) { -// t->srv->cur_sess--; -// sess_change_server(t, NULL); -// } -// trace_term(t, TT_HTTP_SRV_8); -// -// if (may_dequeue_tasks(t->srv, t->be)) -// process_srv_queue(t->srv); -// return 0; -// } -// } -// /* end of client read and no more data to send. We can forward -// * the close when we're allowed to forward data (anytime right -// * now). If we're using option forceclose, then we may also -// * shutdown the outgoing write channel once the response starts -// * coming from the server. -// */ -// if (!(req->flags & BF_SHUTW) && /* not already done */ -// req->flags & BF_EMPTY && req->flags & BF_MAY_FORWARD && -// (req->flags & BF_SHUTR || -// (t->be->options & PR_O_FORCE_CLO && rep->flags & BF_READ_STATUS))) { -// buffer_shutw(req); -// if (!(rep->flags & BF_SHUTR)) { -// EV_FD_CLR(req->cons->fd, DIR_WR); -// shutdown(req->cons->fd, SHUT_WR); -// trace_term(t, TT_HTTP_SRV_9); -// /* We must ensure that the read part is still alive when switching to shutw */ -// /* FIXME: is this still true ? */ -// EV_FD_SET(req->cons->fd, DIR_RD); -// rep->rex = tick_add_ifset(now_ms, t->be->timeout.server); -// } else { -// fd_delete(req->cons->fd); -// req->cons->state = SI_ST_CLO; -// if (t->srv) { -// t->srv->cur_sess--; -// sess_change_server(t, NULL); -// } -// trace_term(t, TT_HTTP_SRV_10); -// -// if (may_dequeue_tasks(t->srv, t->be)) -// process_srv_queue(t->srv); -// return 0; -// } -// } -// -// /* read timeout */ -// if ((rep->flags & (BF_SHUTR|BF_READ_TIMEOUT)) == BF_READ_TIMEOUT) { -// if (!rep->analysers) { -// if (!(t->flags & SN_ERR_MASK)) -// t->flags |= SN_ERR_SRVTO; -// if (!(t->flags & SN_FINST_MASK)) -// t->flags |= SN_FINST_D; -// } -// buffer_shutr(rep); -// if (!(req->flags & BF_SHUTW)) { -// EV_FD_CLR(req->cons->fd, DIR_RD); -// trace_term(t, TT_HTTP_SRV_11); -// } else { -// fd_delete(req->cons->fd); -// req->cons->state = SI_ST_CLO; -// if (t->srv) { -// t->srv->cur_sess--; -// sess_change_server(t, NULL); -// } -// trace_term(t, TT_HTTP_SRV_12); -// -// if (may_dequeue_tasks(t->srv, t->be)) -// process_srv_queue(t->srv); -// return 0; -// } -// } -// -// /* write timeout */ -// if ((req->flags & (BF_SHUTW|BF_WRITE_TIMEOUT)) == BF_WRITE_TIMEOUT) { -// if (!rep->analysers) { -// if (!(t->flags & SN_ERR_MASK)) -// t->flags |= SN_ERR_SRVTO; -// if (!(t->flags & SN_FINST_MASK)) -// t->flags |= SN_FINST_D; -// } -// buffer_shutw(req); -// if (!(rep->flags & BF_SHUTR)) { -// EV_FD_CLR(req->cons->fd, DIR_WR); -// shutdown(req->cons->fd, SHUT_WR); -// /* We must ensure that the read part is still alive when switching to shutw */ -// /* FIXME: is this still needed ? */ -// EV_FD_SET(req->cons->fd, DIR_RD); -// rep->rex = tick_add_ifset(now_ms, t->be->timeout.server); -// trace_term(t, TT_HTTP_SRV_13); -// } else { -// fd_delete(req->cons->fd); -// req->cons->state = SI_ST_CLO; -// if (t->srv) { -// t->srv->cur_sess--; -// sess_change_server(t, NULL); -// } -// trace_term(t, TT_HTTP_SRV_14); -// -// if (may_dequeue_tasks(t->srv, t->be)) -// process_srv_queue(t->srv); -// return 0; -// } -// } -// -// update_timeouts: -// /* manage read timeout */ -// if (!(rep->flags & BF_SHUTR)) { -// if (rep->flags & BF_FULL) { -// if (EV_FD_COND_C(req->cons->fd, DIR_RD)) -// rep->rex = TICK_ETERNITY; -// } else { -// EV_FD_COND_S(req->cons->fd, DIR_RD); -// rep->rex = tick_add_ifset(now_ms, t->be->timeout.server); -// } -// } -// -// /* manage write timeout */ -// if (!(req->flags & BF_SHUTW)) { -// if (req->flags & BF_EMPTY || !(req->flags & BF_MAY_FORWARD)) { -// /* stop writing */ -// if (EV_FD_COND_C(req->cons->fd, DIR_WR)) -// req->wex = TICK_ETERNITY; -// } else { -// /* buffer not empty, there are still data to be transferred */ -// EV_FD_COND_S(req->cons->fd, DIR_WR); -// if (!tick_isset(req->wex)) { -// /* restart writing */ -// req->wex = tick_add_ifset(now_ms, t->be->timeout.server); -// if (!(rep->flags & BF_SHUTR) && tick_isset(req->wex) && tick_isset(rep->rex)) { -// /* FIXME: to prevent the server from expiring read timeouts during writes, -// * we refresh it, except if it was already infinite. -// */ -// rep->rex = req->wex; -// } -// } -// } -// } -// return 0; /* other cases change nothing */ -//} -// /* * Produces data for the session depending on its source. Expects to be diff --git a/src/stream_sock.c b/src/stream_sock.c index cc33b815d8..a08bf9b0e4 100644 --- a/src/stream_sock.c +++ b/src/stream_sock.c @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -415,6 +416,134 @@ int stream_sock_write(int fd) { } +/* + * Manages a stream_sock connection during its data phase. The file descriptor + * status is checked, and the read and write timeouts are controlled. The + * buffers are examined for special shutdown cases and finally the timeouts, + * file descriptor and buffers' flags are updated accordingly. + */ +int stream_sock_process_data(int fd) +{ + struct buffer *ib = fdtab[fd].cb[DIR_RD].b; + struct buffer *ob = fdtab[fd].cb[DIR_WR].b; + + DPRINTF(stderr,"[%u] %s: fd=%d owner=%p ib=%p, ob=%p, exp(r,w)=%u,%u ibf=%08x obf=%08x ibl=%d obl=%d\n", + now_ms, __FUNCTION__, + fd, fdtab[fd].owner, + ib, ob, + ib->rex, ob->wex, + ib->flags, ob->flags, + ib->l, ob->l); + + /* Read or write error on the file descriptor */ + if (fdtab[fd].state == FD_STERROR) { + //trace_term(t, TT_HTTP_SRV_6); + if (!ob->cons->err_type) { + //ob->cons->err_loc = t->srv; + ob->cons->err_type = SI_ET_DATA_ERR; + } + buffer_shutw(ob); + ob->flags |= BF_WRITE_ERROR; + buffer_shutr(ib); + ib->flags |= BF_READ_ERROR; + + do_close_and_return: + fd_delete(fd); + ob->cons->state = SI_ST_CLO; + return 0; + } + + /* Check if we need to close the read side */ + if (!(ib->flags & BF_SHUTR)) { + /* Last read, forced read-shutdown, or other end closed */ + if (ib->flags & (BF_READ_NULL|BF_SHUTR_NOW|BF_SHUTW)) { + //trace_term(t, TT_HTTP_SRV_10); + do_close_read: + buffer_shutr(ib); + if (ob->flags & BF_SHUTW) + goto do_close_and_return; + + EV_FD_CLR(fd, DIR_RD); + } + /* Read timeout */ + else if (unlikely(!(ib->flags & BF_READ_TIMEOUT) && tick_is_expired(ib->rex, now_ms))) { + //trace_term(t, TT_HTTP_SRV_12); + ib->flags |= BF_READ_TIMEOUT; + if (!ob->cons->err_type) { + //ob->cons->err_loc = t->srv; + ob->cons->err_type = SI_ET_DATA_TO; + } + goto do_close_read; + } + /* Read not closed, update FD status and timeout for reads */ + else if (ib->flags & (BF_FULL|BF_HIJACK)) { + /* stop reading */ + EV_FD_COND_C(fd, DIR_RD); + ib->rex = TICK_ETERNITY; + } + else { + /* (re)start reading and update timeout. Note: we don't recompute the timeout + * everytime we get here, otherwise it would risk never to expire. We only + * update it if is was not yet set, or if we already got some read status. + */ + EV_FD_COND_S(fd, DIR_RD); + if (!tick_isset(ib->rex) || ib->flags & BF_READ_STATUS) + ib->rex = tick_add_ifset(now_ms, ib->rto); + } + } + + /* Check if we need to close the write side */ + if (!(ob->flags & BF_SHUTW)) { + /* Forced write-shutdown or other end closed with empty buffer. */ + if ((ob->flags & BF_SHUTW_NOW) || + (ob->flags & (BF_EMPTY|BF_MAY_FORWARD|BF_SHUTR)) == (BF_EMPTY|BF_MAY_FORWARD|BF_SHUTR)) { + //trace_term(t, TT_HTTP_SRV_11); + do_close_write: + buffer_shutw(ob); + if (ib->flags & BF_SHUTR) + goto do_close_and_return; + + EV_FD_CLR(fd, DIR_WR); + shutdown(fd, SHUT_WR); + } + /* Write timeout */ + else if (unlikely(!(ob->flags & BF_WRITE_TIMEOUT) && tick_is_expired(ob->wex, now_ms))) { + //trace_term(t, TT_HTTP_SRV_13); + ob->flags |= BF_WRITE_TIMEOUT; + if (!ob->cons->err_type) { + //ob->cons->err_loc = t->srv; + ob->cons->err_type = SI_ET_DATA_TO; + } + goto do_close_write; + } + /* Write not closed, update FD status and timeout for writes */ + else if ((ob->flags & (BF_EMPTY|BF_MAY_FORWARD)) != BF_MAY_FORWARD) { + /* stop writing */ + EV_FD_COND_C(fd, DIR_WR); + ob->wex = TICK_ETERNITY; + } + else { + /* (re)start writing and update timeout. Note: we don't recompute the timeout + * everytime we get here, otherwise it would risk never to expire. We only + * update it if is was not yet set, or if we already got some write status. + */ + EV_FD_COND_S(fd, DIR_WR); + if (!tick_isset(ob->wex) || ob->flags & BF_WRITE_STATUS) { + ob->wex = tick_add_ifset(now_ms, ob->wto); + if (tick_isset(ob->wex) && !(ib->flags & BF_SHUTR) && tick_isset(ib->rex)) { + /* Note: depending on the protocol, we don't know if we're waiting + * for incoming data or not. So in order to prevent the socket from + * expiring read timeouts during writes, we refresh the read timeout, + * except if it was already infinite. + */ + ib->rex = ob->wex; + } + } + } + } + return 0; /* other cases change nothing */ +} + /* * Local variables: