*/
if (unlikely(msg->msg_state != HTTP_MSG_BODY)) {
-
/* Invalid response */
if (unlikely(msg->msg_state == HTTP_MSG_ERROR)) {
hdr_response_bad:
t->be->failed_resp++;
t->srv_state = SV_STCLOSE;
t->analysis &= ~AN_RTR_ANY;
- rep->flags |= BF_MAY_FORWARD;
txn->status = 502;
client_return(t, error_message(t, HTTP_ERR_502));
if (!(t->flags & SN_ERR_MASK))
t->flags |= SN_ERR_PRXCOND;
if (!(t->flags & SN_FINST_MASK))
t->flags |= SN_FINST_H;
- /* We used to have a free connection slot. Since we'll never use it,
- * we have to inform the server that it may be used by another session.
- */
+
if (t->srv && may_dequeue_tasks(t->srv, t->be))
process_srv_queue(t->srv);
return 1;
}
-
/* write error to client, read error or close from server */
if (req->flags & BF_WRITE_ERROR ||
rep->flags & (BF_READ_ERROR | BF_READ_NULL | BF_SHUTW_STATUS)) {
t->be->failed_resp++;
t->srv_state = SV_STCLOSE;
t->analysis &= ~AN_RTR_ANY;
- rep->flags |= BF_MAY_FORWARD;
txn->status = 502;
client_return(t, error_message(t, HTTP_ERR_502));
if (!(t->flags & SN_ERR_MASK))
t->flags |= SN_ERR_SRVCL;
if (!(t->flags & SN_FINST_MASK))
t->flags |= SN_FINST_H;
- /* We used to have a free connection slot. Since we'll never use it,
- * we have to inform the server that it may be used by another session.
- */
+
if (t->srv && may_dequeue_tasks(t->srv, t->be))
process_srv_queue(t->srv);
return 1;
}
-
/* too large response does not fit in buffer. */
- else if (rep->l >= rep->rlim - rep->data)
+ else if (rep->l >= rep->rlim - rep->data) {
goto hdr_response_bad;
-
+ }
/* read timeout : return a 504 to the client. */
else if (rep->flags & BF_READ_TIMEOUT) {
buffer_shutr_done(rep);
t->be->failed_resp++;
t->srv_state = SV_STCLOSE;
t->analysis &= ~AN_RTR_ANY;
- rep->flags |= BF_MAY_FORWARD;
txn->status = 504;
client_return(t, error_message(t, HTTP_ERR_504));
if (!(t->flags & SN_ERR_MASK))
t->flags |= SN_ERR_SRVTO;
if (!(t->flags & SN_FINST_MASK))
t->flags |= SN_FINST_H;
- /* We used to have a free connection slot. Since we'll never use it,
- * we have to inform the server that it may be used by another session.
- */
+
if (t->srv && may_dequeue_tasks(t->srv, t->be))
process_srv_queue(t->srv);
return 1;
}
-
- ///* last client read and buffer empty */
- //else if (unlikely(req->flags & BF_SHUTR_STATUS && (req->l == 0))) {
- // EV_FD_CLR(t->srv_fd, DIR_WR);
- // buffer_shutw_done(req);
- //
- // /* We must ensure that the read part is still
- // * alive when switching to shutw */
- // EV_FD_SET(t->srv_fd, DIR_RD);
- // rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
- //
- // shutdown(t->srv_fd, SHUT_WR);
- // t->srv_state = SV_STSHUTW;
- // t->analysis &= ~AN_RTR_ANY;
- // return 1;
- //}
-
- /* write timeout */
- /* FIXME!!! here, we don't want to switch to SHUTW if the
- * client shuts read too early, because we may still have
- * some work to do on the headers.
- */
- //else if (unlikely(EV_FD_ISSET(t->srv_fd, DIR_WR) &&
- // tick_is_expired(req->wex, now_ms))) {
- // EV_FD_CLR(t->srv_fd, DIR_WR);
- // buffer_shutw_done(req);
- // shutdown(t->srv_fd, SHUT_WR);
- // /* We must ensure that the read part is still alive
- // * when switching to shutw */
- // EV_FD_SET(t->srv_fd, DIR_RD);
- // rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
- //
- // t->srv_state = SV_STSHUTW;
- // t->analysis &= ~AN_RTR_ANY;
- // if (!(t->flags & SN_ERR_MASK))
- // t->flags |= SN_ERR_SRVTO;
- // if (!(t->flags & SN_FINST_MASK))
- // t->flags |= SN_FINST_H;
- // return 1;
- //}
-
- /*
- * And now the non-error cases.
- */
-
- /* Data remaining in the request buffer.
- * This happens during the first pass here, and during
- * long posts.
- */
- //else if (likely(req->l)) {
- // if (!tick_isset(req->wex)) {
- // EV_FD_COND_S(t->srv_fd, DIR_WR);
- // /* restart writing */
- // 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;
- // }
- // }
- //}
- //
- ///* nothing left in the request buffer */
- //else {
- // if (EV_FD_COND_C(t->srv_fd, DIR_WR)) {
- // /* stop writing */
- // req->wex = TICK_ETERNITY;
- // }
- //}
- //
- ///* return 0 if nothing changed */
- //return !(t->analysis & AN_RTR_ANY);
return 0;
}
fd_delete(t->srv_fd);
t->srv_state = SV_STCLOSE;
t->analysis &= ~AN_RTR_ANY;
- rep->flags |= BF_MAY_FORWARD;
txn->status = 502;
client_return(t, error_message(t, HTTP_ERR_502));
if (!(t->flags & SN_ERR_MASK))
t->flags |= SN_CONN_CLOSED;
}
-
/*************************************************************
* OK, that's finished for the headers. We have done what we *
* could. Let's switch to the DATA state. *
t->srv_state = SV_STDATA;
t->analysis &= ~AN_RTR_ANY;
- rep->flags |= BF_MAY_FORWARD;
rep->rlim = rep->data + BUFSIZE; /* no more rewrite needed */
t->logs.t_data = tv_ms_elapsed(&t->logs.tv_accept, &now);
- /* client connection already closed or option 'forceclose' required :
- * we close the server's outgoing connection right now.
- */
- //if ((req->l == 0) &&
- // (req->flags & BF_SHUTR_STATUS || t->be->options & PR_O_FORCE_CLO)) {
- // EV_FD_CLR(t->srv_fd, DIR_WR);
- // buffer_shutw_done(req);
- //
- // /* We must ensure that the read part is still alive when switching
- // * to shutw */
- // EV_FD_SET(t->srv_fd, DIR_RD);
- // rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
- //
- // shutdown(t->srv_fd, SHUT_WR);
- // t->srv_state = SV_STSHUTW;
- // t->analysis &= ~AN_RTR_ANY;
- //}
-
#ifdef CONFIG_HAP_TCPSPLICE
if ((t->fe->options & t->be->options) & PR_O_TCPSPLICE) {
/* TCP splicing supported by both FE and BE */
req->flags, rep->flags,
req->l, rep->l);
+ /* if no analysis remains, we have to let the data pass */
+ if (!(t->analysis & AN_RTR_ANY) && !(rep->flags & BF_MAY_FORWARD))
+ rep->flags |= BF_MAY_FORWARD;
+
if (s == SV_STIDLE) {
if ((rep->flags & BF_SHUTW_STATUS) ||
((req->flags & BF_SHUTR_STATUS) &&
}
else if (!(req->flags & BF_WRITE_STATUS) || (req->flags & BF_WRITE_ERROR)) {
/* timeout, asynchronous connect error or first write error */
- //fprintf(stderr,"2: c=%d, s=%d\n", c, s);
-
if (t->flags & SN_CONN_TAR) {
/* We are doing a turn-around waiting for a new connection attempt. */
if (!tick_is_expired(req->cex, now_ms))
else { /* no error or write 0 */
t->logs.t_connect = tv_ms_elapsed(&t->logs.tv_accept, &now);
- //fprintf(stderr,"3: c=%d, s=%d\n", c, s);
if (req->l == 0) /* nothing to write */ {
EV_FD_CLR(t->srv_fd, DIR_WR);
req->wex = TICK_ETERNITY;
EV_FD_SET(t->srv_fd, DIR_RD);
rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
t->srv_state = SV_STDATA;
- rep->flags |= BF_MAY_FORWARD;
rep->rlim = rep->data + BUFSIZE; /* no rewrite needed */
/* if the user wants to log as soon as possible, without counting
return 1;
}
}
- else if (s == SV_STDATA) {
+ else if (s == SV_STDATA || s == SV_STSHUTR || s == SV_STSHUTW) {
/* 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_done(rep);
buffer_shutw_done(req);
}
t->be->failed_resp++;
t->srv_state = SV_STCLOSE;
- rep->flags |= BF_MAY_FORWARD;
if (!(t->flags & SN_ERR_MASK))
t->flags |= SN_ERR_SRVCL;
if (!(t->flags & SN_FINST_MASK))
t->flags |= SN_FINST_D;
- /* We used to have a free connection slot. Since we'll never use it,
- * we have to inform the server that it may be used by another session.
- */
+
if (may_dequeue_tasks(t->srv, t->be))
process_srv_queue(t->srv);
return 1;
}
/* last read, or end of client write */
- else if (rep->flags & (BF_READ_NULL | BF_SHUTW_STATUS)) {
- EV_FD_CLR(t->srv_fd, DIR_RD);
+ else if (!(rep->flags & BF_SHUTR_STATUS) && /* not already done */
+ rep->flags & (BF_READ_NULL | BF_SHUTW_STATUS)) {
buffer_shutr(rep);
- t->srv_state = SV_STSHUTR;
- //fprintf(stderr,"%p:%s(%d), c=%d, s=%d\n", t, __FUNCTION__, __LINE__, t->cli_state, t->cli_state);
+ if (!(req->flags & BF_SHUTW_STATUS)) {
+ EV_FD_CLR(t->srv_fd, DIR_RD);
+ t->srv_state = SV_STSHUTR;
+ } else {
+ /* output was already closed */
+ fd_delete(t->srv_fd);
+ if (t->srv) {
+ t->srv->cur_sess--;
+ sess_change_server(t, NULL);
+ }
+ t->srv_state = SV_STCLOSE;
+
+ if (may_dequeue_tasks(t->srv, t->be))
+ process_srv_queue(t->srv);
+ }
return 1;
}
- /* end of client read and no more data to send */
- else if (req->flags & BF_SHUTR_STATUS && (req->l == 0)) {
- EV_FD_CLR(t->srv_fd, DIR_WR);
+ /* 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.
+ */
+ else if (!(req->flags & BF_SHUTW_STATUS) && /* not already done */
+ req->l == 0 && req->flags & BF_MAY_FORWARD &&
+ (req->flags & BF_SHUTR_STATUS ||
+ (t->be->options & PR_O_FORCE_CLO && rep->flags & BF_READ_STATUS))) {
buffer_shutw_done(req);
- shutdown(t->srv_fd, SHUT_WR);
- /* We must ensure that the read part is still alive when switching
- * to shutw */
- EV_FD_SET(t->srv_fd, DIR_RD);
- rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
+ if (!(rep->flags & BF_SHUTR_STATUS)) {
+ EV_FD_CLR(t->srv_fd, DIR_WR);
+ shutdown(t->srv_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->srv_fd, DIR_RD);
+ rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
+ t->srv_state = SV_STSHUTW;
+ } else {
+ fd_delete(t->srv_fd);
+ if (t->srv) {
+ t->srv->cur_sess--;
+ sess_change_server(t, NULL);
+ }
+ t->srv_state = SV_STCLOSE;
- t->srv_state = SV_STSHUTW;
+ if (may_dequeue_tasks(t->srv, t->be))
+ process_srv_queue(t->srv);
+ }
return 1;
}
/* read timeout */
else if (tick_is_expired(rep->rex, now_ms)) {
- EV_FD_CLR(t->srv_fd, DIR_RD);
- buffer_shutr(rep);
- t->srv_state = SV_STSHUTR;
+ buffer_shutr_done(rep);
+ rep->flags |= BF_READ_TIMEOUT;
+ if (!(req->flags & BF_SHUTW_STATUS)) {
+ EV_FD_CLR(t->srv_fd, DIR_RD);
+ t->srv_state = SV_STSHUTR;
+ } else {
+ fd_delete(t->srv_fd);
+ if (t->srv) {
+ t->srv->cur_sess--;
+ sess_change_server(t, NULL);
+ }
+ t->srv_state = SV_STCLOSE;
+
+ if (may_dequeue_tasks(t->srv, t->be))
+ process_srv_queue(t->srv);
+ }
if (!(t->flags & SN_ERR_MASK))
t->flags |= SN_ERR_SRVTO;
if (!(t->flags & SN_FINST_MASK))
}
/* write timeout */
else if (tick_is_expired(req->wex, now_ms)) {
- EV_FD_CLR(t->srv_fd, DIR_WR);
buffer_shutw_done(req);
- shutdown(t->srv_fd, SHUT_WR);
- /* We must ensure that the read part is still alive when switching
- * to shutw */
- EV_FD_SET(t->srv_fd, DIR_RD);
- rep->cex = tick_add_ifset(now_ms, t->be->timeout.server);
- t->srv_state = SV_STSHUTW;
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_SRVTO;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_D;
- return 1;
- }
-
- /* recompute request time-outs */
- if (req->l == 0) {
- if (EV_FD_COND_C(t->srv_fd, DIR_WR)) {
- /* stop writing */
- req->wex = TICK_ETERNITY;
- }
- }
- else { /* buffer not empty, there are still data to be transferred */
- EV_FD_COND_S(t->srv_fd, DIR_WR);
- if (!tick_isset(req->wex)) {
- /* restart writing */
- 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;
+ req->flags |= BF_WRITE_TIMEOUT;
+ if (!(rep->flags & BF_SHUTR_STATUS)) {
+ EV_FD_CLR(t->srv_fd, DIR_WR);
+ shutdown(t->srv_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(t->srv_fd, DIR_RD);
+ rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
+ t->srv_state = SV_STSHUTW;
+ } else {
+ fd_delete(t->srv_fd);
+ if (t->srv) {
+ t->srv->cur_sess--;
+ sess_change_server(t, NULL);
}
- }
- }
-
- /* recompute response time-outs */
- if (rep->l == BUFSIZE) { /* no room to read more data */
- if (EV_FD_COND_C(t->srv_fd, DIR_RD)) {
- rep->rex = TICK_ETERNITY;
- }
- }
- else {
- EV_FD_COND_S(t->srv_fd, DIR_RD);
- rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
- }
-
- return 0; /* other cases change nothing */
- }
- else if (s == SV_STSHUTR) {
- if (req->flags & BF_WRITE_ERROR) {
- //EV_FD_CLR(t->srv_fd, DIR_WR);
- buffer_shutw_done(req);
- fd_delete(t->srv_fd);
- if (t->srv) {
- t->srv->cur_sess--;
- t->srv->failed_resp++;
- sess_change_server(t, NULL);
- }
- t->be->failed_resp++;
- //close(t->srv_fd);
- t->srv_state = SV_STCLOSE;
- rep->flags |= BF_MAY_FORWARD;
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_SRVCL;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_D;
- /* We used to have a free connection slot. Since we'll never use it,
- * we have to inform the server that it may be used by another session.
- */
- if (may_dequeue_tasks(t->srv, t->be))
- process_srv_queue(t->srv);
-
- return 1;
- }
- else if (req->flags & BF_SHUTR_STATUS && (req->l == 0)) {
- //EV_FD_CLR(t->srv_fd, DIR_WR);
- buffer_shutw_done(req);
- fd_delete(t->srv_fd);
- if (t->srv) {
- t->srv->cur_sess--;
- sess_change_server(t, NULL);
- }
- //close(t->srv_fd);
- t->srv_state = SV_STCLOSE;
- rep->flags |= BF_MAY_FORWARD;
- /* We used to have a free connection slot. Since we'll never use it,
- * we have to inform the server that it may be used by another session.
- */
- if (may_dequeue_tasks(t->srv, t->be))
- process_srv_queue(t->srv);
+ t->srv_state = SV_STCLOSE;
- return 1;
- }
- else if (tick_is_expired(req->wex, now_ms)) {
- //EV_FD_CLR(t->srv_fd, DIR_WR);
- buffer_shutw_done(req);
- fd_delete(t->srv_fd);
- if (t->srv) {
- t->srv->cur_sess--;
- sess_change_server(t, NULL);
+ if (may_dequeue_tasks(t->srv, t->be))
+ process_srv_queue(t->srv);
}
- //close(t->srv_fd);
- t->srv_state = SV_STCLOSE;
- rep->flags |= BF_MAY_FORWARD;
if (!(t->flags & SN_ERR_MASK))
t->flags |= SN_ERR_SRVTO;
if (!(t->flags & SN_FINST_MASK))
t->flags |= SN_FINST_D;
- /* We used to have a free connection slot. Since we'll never use it,
- * we have to inform the server that it may be used by another session.
- */
- if (may_dequeue_tasks(t->srv, t->be))
- process_srv_queue(t->srv);
-
return 1;
}
- else if (req->l == 0) {
- if (EV_FD_COND_C(t->srv_fd, DIR_WR)) {
- /* stop writing */
- req->wex = TICK_ETERNITY;
- }
- }
- else { /* buffer not empty */
- if (!tick_isset(req->wex)) {
- EV_FD_COND_S(t->srv_fd, DIR_WR);
- /* restart writing */
- req->wex = tick_add_ifset(now_ms, t->be->timeout.server);
- }
- }
- return 0;
- }
- else if (s == SV_STSHUTW) {
- if (rep->flags & BF_READ_ERROR) {
- //EV_FD_CLR(t->srv_fd, DIR_RD);
- buffer_shutr_done(rep);
- fd_delete(t->srv_fd);
- if (t->srv) {
- t->srv->cur_sess--;
- t->srv->failed_resp++;
- sess_change_server(t, NULL);
- }
- t->be->failed_resp++;
- //close(t->srv_fd);
- t->srv_state = SV_STCLOSE;
- rep->flags |= BF_MAY_FORWARD;
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_SRVCL;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_D;
- /* We used to have a free connection slot. Since we'll never use it,
- * we have to inform the server that it may be used by another session.
- */
- if (may_dequeue_tasks(t->srv, t->be))
- process_srv_queue(t->srv);
- return 1;
- }
- else if (rep->flags & (BF_READ_NULL | BF_SHUTW_STATUS)) {
- //EV_FD_CLR(t->srv_fd, DIR_RD);
- buffer_shutr_done(rep);
- fd_delete(t->srv_fd);
- if (t->srv) {
- t->srv->cur_sess--;
- sess_change_server(t, NULL);
+ /* manage read timeout */
+ if (!(rep->flags & BF_SHUTR_STATUS)) {
+ if (rep->l >= rep->rlim - rep->data) {
+ if (EV_FD_COND_C(t->srv_fd, DIR_RD))
+ rep->rex = TICK_ETERNITY;
+ } else {
+ EV_FD_COND_S(t->srv_fd, DIR_RD);
+ rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
}
- //close(t->srv_fd);
- t->srv_state = SV_STCLOSE;
- rep->flags |= BF_MAY_FORWARD;
- /* We used to have a free connection slot. Since we'll never use it,
- * we have to inform the server that it may be used by another session.
- */
- if (may_dequeue_tasks(t->srv, t->be))
- process_srv_queue(t->srv);
-
- return 1;
}
- else if (tick_is_expired(rep->rex, now_ms)) {
- //EV_FD_CLR(t->srv_fd, DIR_RD);
- buffer_shutr_done(rep);
- fd_delete(t->srv_fd);
- if (t->srv) {
- t->srv->cur_sess--;
- sess_change_server(t, NULL);
- }
- //close(t->srv_fd);
- t->srv_state = SV_STCLOSE;
- rep->flags |= BF_MAY_FORWARD;
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_SRVTO;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_D;
- /* We used to have a free connection slot. Since we'll never use it,
- * we have to inform the server that it may be used by another session.
- */
- if (may_dequeue_tasks(t->srv, t->be))
- process_srv_queue(t->srv);
- return 1;
- }
- else if (rep->l == BUFSIZE) { /* no room to read more data */
- if (EV_FD_COND_C(t->srv_fd, DIR_RD)) {
- rep->rex = TICK_ETERNITY;
- }
- }
- else {
- if (!tick_isset(rep->rex)) {
- EV_FD_COND_S(t->srv_fd, DIR_RD);
- rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
+ /* manage write timeout */
+ if (!(req->flags & BF_SHUTW_STATUS)) {
+ if (req->l == 0 || !(req->flags & BF_MAY_FORWARD)) {
+ /* stop writing */
+ if (EV_FD_COND_C(t->srv_fd, DIR_WR))
+ req->wex = TICK_ETERNITY;
+ } else {
+ /* buffer not empty, there are still data to be transferred */
+ EV_FD_COND_S(t->srv_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_STATUS) && 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;
+ return 0; /* other cases change nothing */
}
else { /* SV_STCLOSE : nothing to do */
if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {