[MEDIUM] rename process_request to http_process_request
Now the function only does HTTP request and nothing else. Also pass
the request buffer to it.
diff --git a/src/proto_http.c b/src/proto_http.c
index 4fdb21b..e24b01b 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -1563,812 +1563,793 @@
* called after XXX bytes have been received (or transfered), and the min of
* all's wishes will be used to ring back (unless a special condition occurs).
*/
-int process_request(struct session *t)
+int http_process_request(struct session *s, struct buffer *req)
{
- struct buffer *req = t->req;
DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n",
now_ms, __FUNCTION__,
- t,
+ s,
req,
req->rex, req->wex,
req->flags,
req->l,
req->analysers);
- if (req->analysers & AN_REQ_HTTP_HDR) {
- /*
- * Now parse the partial (or complete) lines.
- * We will check the request syntax, and also join multi-line
- * headers. An index of all the lines will be elaborated while
- * parsing.
- *
- * For the parsing, we use a 28 states FSM.
- *
- * Here is the information we currently have :
- * req->data + req->som = beginning of request
- * req->data + req->eoh = end of processed headers / start of current one
- * req->data + req->eol = end of current header or line (LF or CRLF)
- * req->lr = first non-visited byte
- * req->r = end of data
- */
+ /*
+ * We will parse the partial (or complete) lines.
+ * We will check the request syntax, and also join multi-line
+ * headers. An index of all the lines will be elaborated while
+ * parsing.
+ *
+ * For the parsing, we use a 28 states FSM.
+ *
+ * Here is the information we currently have :
+ * req->data + req->som = beginning of request
+ * req->data + req->eoh = end of processed headers / start of current one
+ * req->data + req->eol = end of current header or line (LF or CRLF)
+ * req->lr = first non-visited byte
+ * req->r = end of data
+ */
- int cur_idx;
- struct http_txn *txn = &t->txn;
- struct http_msg *msg = &txn->req;
- struct proxy *cur_proxy;
+ int cur_idx;
+ struct http_txn *txn = &s->txn;
+ struct http_msg *msg = &txn->req;
+ struct proxy *cur_proxy;
- if (likely(req->lr < req->r))
- http_msg_analyzer(req, msg, &txn->hdr_idx);
+ if (likely(req->lr < req->r))
+ http_msg_analyzer(req, msg, &txn->hdr_idx);
- /* 1: we might have to print this header in debug mode */
- if (unlikely((global.mode & MODE_DEBUG) &&
- (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) &&
- (msg->msg_state == HTTP_MSG_BODY || msg->msg_state == HTTP_MSG_ERROR))) {
- char *eol, *sol;
+ /* 1: we might have to print this header in debug mode */
+ if (unlikely((global.mode & MODE_DEBUG) &&
+ (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) &&
+ (msg->msg_state == HTTP_MSG_BODY || msg->msg_state == HTTP_MSG_ERROR))) {
+ char *eol, *sol;
- sol = req->data + msg->som;
- eol = sol + msg->sl.rq.l;
- debug_hdr("clireq", t, sol, eol);
+ sol = req->data + msg->som;
+ eol = sol + msg->sl.rq.l;
+ debug_hdr("clireq", s, sol, eol);
- sol += hdr_idx_first_pos(&txn->hdr_idx);
- cur_idx = hdr_idx_first_idx(&txn->hdr_idx);
+ sol += hdr_idx_first_pos(&txn->hdr_idx);
+ cur_idx = hdr_idx_first_idx(&txn->hdr_idx);
- while (cur_idx) {
- eol = sol + txn->hdr_idx.v[cur_idx].len;
- debug_hdr("clihdr", t, sol, eol);
- sol = eol + txn->hdr_idx.v[cur_idx].cr + 1;
- cur_idx = txn->hdr_idx.v[cur_idx].next;
- }
+ while (cur_idx) {
+ eol = sol + txn->hdr_idx.v[cur_idx].len;
+ debug_hdr("clihdr", s, sol, eol);
+ sol = eol + txn->hdr_idx.v[cur_idx].cr + 1;
+ cur_idx = txn->hdr_idx.v[cur_idx].next;
}
+ }
+
+ /*
+ * Now we quickly check if we have found a full valid request.
+ * If not so, we check the FD and buffer states before leaving.
+ * A full request is indicated by the fact that we have seen
+ * the double LF/CRLF, so the state is HTTP_MSG_BODY. Invalid
+ * requests are checked first.
+ *
+ */
+ if (unlikely(msg->msg_state != HTTP_MSG_BODY)) {
/*
- * Now we quickly check if we have found a full valid request.
- * If not so, we check the FD and buffer states before leaving.
- * A full request is indicated by the fact that we have seen
- * the double LF/CRLF, so the state is HTTP_MSG_BODY. Invalid
- * requests are checked first.
- *
+ * First, let's catch bad requests.
*/
+ if (unlikely(msg->msg_state == HTTP_MSG_ERROR))
+ goto return_bad_req;
- if (unlikely(msg->msg_state != HTTP_MSG_BODY)) {
- /*
- * First, let's catch bad requests.
- */
- if (unlikely(msg->msg_state == HTTP_MSG_ERROR))
- goto return_bad_req;
-
- /* 1: Since we are in header mode, if there's no space
- * left for headers, we won't be able to free more
- * later, so the session will never terminate. We
- * must terminate it now.
+ /* 1: Since we are in header mode, if there's no space
+ * left for headers, we won't be able to free more
+ * later, so the session will never terminate. We
+ * must terminate it now.
+ */
+ if (unlikely(req->flags & BF_FULL)) {
+ /* FIXME: check if URI is set and return Status
+ * 414 Request URI too long instead.
*/
- if (unlikely(req->flags & BF_FULL)) {
- /* FIXME: check if URI is set and return Status
- * 414 Request URI too long instead.
- */
- goto return_bad_req;
- }
-
- /* 2: have we encountered a read error ? */
- else if (req->flags & BF_READ_ERROR) {
- /* we cannot return any message on error */
- msg->msg_state = HTTP_MSG_ERROR;
- req->analysers = 0;
- //t->fe->failed_req++;
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_CLICL;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_R;
- return 0;
- }
-
- /* 3: has the read timeout expired ? */
- else if (req->flags & BF_READ_TIMEOUT || tick_is_expired(req->analyse_exp, now_ms)) {
- /* read timeout : give up with an error message. */
- txn->status = 408;
- stream_int_retnclose(req->prod, error_message(t, HTTP_ERR_408));
- msg->msg_state = HTTP_MSG_ERROR;
- req->analysers = 0;
- t->fe->failed_req++;
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_CLITO;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_R;
- return 0;
- }
+ goto return_bad_req;
+ }
- /* 4: have we encountered a close ? */
- else if (req->flags & BF_SHUTR) {
- txn->status = 400;
- stream_int_retnclose(req->prod, error_message(t, HTTP_ERR_400));
- msg->msg_state = HTTP_MSG_ERROR;
- req->analysers = 0;
- t->fe->failed_req++;
+ /* 2: have we encountered a read error ? */
+ else if (req->flags & BF_READ_ERROR) {
+ /* we cannot return any message on error */
+ msg->msg_state = HTTP_MSG_ERROR;
+ req->analysers = 0;
+ s->fe->failed_req++;
+ if (!(s->flags & SN_ERR_MASK))
+ s->flags |= SN_ERR_CLICL;
+ if (!(s->flags & SN_FINST_MASK))
+ s->flags |= SN_FINST_R;
+ return 0;
+ }
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_CLICL;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_R;
- return 0;
- }
+ /* 3: has the read timeout expired ? */
+ else if (req->flags & BF_READ_TIMEOUT || tick_is_expired(req->analyse_exp, now_ms)) {
+ /* read timeout : give up with an error message. */
+ txn->status = 408;
+ stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_408));
+ msg->msg_state = HTTP_MSG_ERROR;
+ req->analysers = 0;
+ s->fe->failed_req++;
+ if (!(s->flags & SN_ERR_MASK))
+ s->flags |= SN_ERR_CLITO;
+ if (!(s->flags & SN_FINST_MASK))
+ s->flags |= SN_FINST_R;
+ return 0;
+ }
- buffer_write_dis(req);
- /* just set the request timeout once at the beginning of the request */
- if (!tick_isset(req->analyse_exp))
- req->analyse_exp = tick_add_ifset(now_ms, t->fe->timeout.httpreq);
+ /* 4: have we encountered a close ? */
+ else if (req->flags & BF_SHUTR) {
+ txn->status = 400;
+ stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_400));
+ msg->msg_state = HTTP_MSG_ERROR;
+ req->analysers = 0;
+ s->fe->failed_req++;
- /* we're not ready yet */
+ if (!(s->flags & SN_ERR_MASK))
+ s->flags |= SN_ERR_CLICL;
+ if (!(s->flags & SN_FINST_MASK))
+ s->flags |= SN_FINST_R;
return 0;
}
+ buffer_write_dis(req);
+ /* just set the request timeout once at the beginning of the request */
+ if (!tick_isset(req->analyse_exp))
+ req->analyse_exp = tick_add_ifset(now_ms, s->fe->timeout.httpreq);
- /****************************************************************
- * More interesting part now : we know that we have a complete *
- * request which at least looks like HTTP. We have an indicator *
- * of each header's length, so we can parse them quickly. *
- ****************************************************************/
+ /* we're not ready yet */
+ return 0;
+ }
- req->analysers &= ~AN_REQ_HTTP_HDR;
- req->analyse_exp = TICK_ETERNITY;
- /* ensure we keep this pointer to the beginning of the message */
- msg->sol = req->data + msg->som;
+ /****************************************************************
+ * More interesting part now : we know that we have a complete *
+ * request which at least looks like HTTP. We have an indicator *
+ * of each header's length, so we can parse them quickly. *
+ ****************************************************************/
- /*
- * 1: identify the method
- */
- txn->meth = find_http_meth(&req->data[msg->som], msg->sl.rq.m_l);
+ req->analysers &= ~AN_REQ_HTTP_HDR;
+ req->analyse_exp = TICK_ETERNITY;
+
+ /* ensure we keep this pointer to the beginning of the message */
+ msg->sol = req->data + msg->som;
- /* we can make use of server redirect on GET and HEAD */
- if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
- t->flags |= SN_REDIRECTABLE;
+ /*
+ * 1: identify the method
+ */
+ txn->meth = find_http_meth(&req->data[msg->som], msg->sl.rq.m_l);
+
+ /* we can make use of server redirect on GET and HEAD */
+ if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
+ s->flags |= SN_REDIRECTABLE;
+ /*
+ * 2: check if the URI matches the monitor_uri.
+ * We have to do this for every request which gets in, because
+ * the monitor-uri is defined by the frontend.
+ */
+ if (unlikely((s->fe->monitor_uri_len != 0) &&
+ (s->fe->monitor_uri_len == msg->sl.rq.u_l) &&
+ !memcmp(&req->data[msg->sl.rq.u],
+ s->fe->monitor_uri,
+ s->fe->monitor_uri_len))) {
/*
- * 2: check if the URI matches the monitor_uri.
- * We have to do this for every request which gets in, because
- * the monitor-uri is defined by the frontend.
+ * We have found the monitor URI
*/
- if (unlikely((t->fe->monitor_uri_len != 0) &&
- (t->fe->monitor_uri_len == msg->sl.rq.u_l) &&
- !memcmp(&req->data[msg->sl.rq.u],
- t->fe->monitor_uri,
- t->fe->monitor_uri_len))) {
- /*
- * We have found the monitor URI
- */
- struct acl_cond *cond;
- cur_proxy = t->fe;
+ struct acl_cond *cond;
+ cur_proxy = s->fe;
- t->flags |= SN_MONITOR;
+ s->flags |= SN_MONITOR;
- /* Check if we want to fail this monitor request or not */
- list_for_each_entry(cond, &cur_proxy->mon_fail_cond, list) {
- int ret = acl_exec_cond(cond, cur_proxy, t, txn, ACL_DIR_REQ);
+ /* Check if we want to fail this monitor request or not */
+ list_for_each_entry(cond, &cur_proxy->mon_fail_cond, list) {
+ int ret = acl_exec_cond(cond, cur_proxy, s, txn, ACL_DIR_REQ);
- ret = acl_pass(ret);
- if (cond->pol == ACL_COND_UNLESS)
- ret = !ret;
+ ret = acl_pass(ret);
+ if (cond->pol == ACL_COND_UNLESS)
+ ret = !ret;
- if (ret) {
- /* we fail this request, let's return 503 service unavail */
- txn->status = 503;
- stream_int_retnclose(req->prod, error_message(t, HTTP_ERR_503));
- goto return_prx_cond;
- }
+ if (ret) {
+ /* we fail this request, let's return 503 service unavail */
+ txn->status = 503;
+ stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_503));
+ goto return_prx_cond;
}
-
- /* nothing to fail, let's reply normaly */
- txn->status = 200;
- stream_int_retnclose(req->prod, &http_200_chunk);
- goto return_prx_cond;
}
- /*
- * 3: Maybe we have to copy the original REQURI for the logs ?
- * Note: we cannot log anymore if the request has been
- * classified as invalid.
- */
- if (unlikely(t->logs.logwait & LW_REQ)) {
- /* we have a complete HTTP request that we must log */
- if ((txn->uri = pool_alloc2(pool2_requri)) != NULL) {
- int urilen = msg->sl.rq.l;
+ /* nothing to fail, let's reply normaly */
+ txn->status = 200;
+ stream_int_retnclose(req->prod, &http_200_chunk);
+ goto return_prx_cond;
+ }
+
+ /*
+ * 3: Maybe we have to copy the original REQURI for the logs ?
+ * Note: we cannot log anymore if the request has been
+ * classified as invalid.
+ */
+ if (unlikely(s->logs.logwait & LW_REQ)) {
+ /* we have a complete HTTP request that we must log */
+ if ((txn->uri = pool_alloc2(pool2_requri)) != NULL) {
+ int urilen = msg->sl.rq.l;
- if (urilen >= REQURI_LEN)
- urilen = REQURI_LEN - 1;
- memcpy(txn->uri, &req->data[msg->som], urilen);
- txn->uri[urilen] = 0;
+ if (urilen >= REQURI_LEN)
+ urilen = REQURI_LEN - 1;
+ memcpy(txn->uri, &req->data[msg->som], urilen);
+ txn->uri[urilen] = 0;
- if (!(t->logs.logwait &= ~LW_REQ))
- t->do_log(t);
- } else {
- Alert("HTTP logging : out of memory.\n");
- }
+ if (!(s->logs.logwait &= ~LW_REQ))
+ s->do_log(s);
+ } else {
+ Alert("HTTP logging : out of memory.\n");
}
-
+ }
- /* 4. We may have to convert HTTP/0.9 requests to HTTP/1.0 */
- if (unlikely(msg->sl.rq.v_l == 0)) {
- int delta;
- char *cur_end;
- msg->sol = req->data + msg->som;
- cur_end = msg->sol + msg->sl.rq.l;
- delta = 0;
+ /* 4. We may have to convert HTTP/0.9 requests to HTTP/1.0 */
+ if (unlikely(msg->sl.rq.v_l == 0)) {
+ int delta;
+ char *cur_end;
+ msg->sol = req->data + msg->som;
+ cur_end = msg->sol + msg->sl.rq.l;
+ delta = 0;
- if (msg->sl.rq.u_l == 0) {
- /* if no URI was set, add "/" */
- delta = buffer_replace2(req, cur_end, cur_end, " /", 2);
- cur_end += delta;
- msg->eoh += delta;
- }
- /* add HTTP version */
- delta = buffer_replace2(req, cur_end, cur_end, " HTTP/1.0\r\n", 11);
- msg->eoh += delta;
+ if (msg->sl.rq.u_l == 0) {
+ /* if no URI was set, add "/" */
+ delta = buffer_replace2(req, cur_end, cur_end, " /", 2);
cur_end += delta;
- cur_end = (char *)http_parse_reqline(msg, req->data,
- HTTP_MSG_RQMETH,
- msg->sol, cur_end + 1,
- NULL, NULL);
- if (unlikely(!cur_end))
- goto return_bad_req;
-
- /* we have a full HTTP/1.0 request now and we know that
- * we have either a CR or an LF at <ptr>.
- */
- hdr_idx_set_start(&txn->hdr_idx, msg->sl.rq.l, *cur_end == '\r');
+ msg->eoh += delta;
}
-
+ /* add HTTP version */
+ delta = buffer_replace2(req, cur_end, cur_end, " HTTP/1.0\r\n", 11);
+ msg->eoh += delta;
+ cur_end += delta;
+ cur_end = (char *)http_parse_reqline(msg, req->data,
+ HTTP_MSG_RQMETH,
+ msg->sol, cur_end + 1,
+ NULL, NULL);
+ if (unlikely(!cur_end))
+ goto return_bad_req;
- /* 5: we may need to capture headers */
- if (unlikely((t->logs.logwait & LW_REQHDR) && t->fe->req_cap))
- capture_headers(req->data + msg->som, &txn->hdr_idx,
- txn->req.cap, t->fe->req_cap);
-
- /*
- * 6: we will have to evaluate the filters.
- * As opposed to version 1.2, now they will be evaluated in the
- * filters order and not in the header order. This means that
- * each filter has to be validated among all headers.
- *
- * We can now check whether we want to switch to another
- * backend, in which case we will re-check the backend's
- * filters and various options. In order to support 3-level
- * switching, here's how we should proceed :
- *
- * a) run be.
- * if (switch) then switch ->be to the new backend.
- * b) run be if (be != fe).
- * There cannot be any switch from there, so ->be cannot be
- * changed anymore.
- *
- * => filters always apply to ->be, then ->be may change.
- *
- * The response path will be able to apply either ->be, or
- * ->be then ->fe filters in order to match the reverse of
- * the forward sequence.
+ /* we have a full HTTP/1.0 request now and we know that
+ * we have either a CR or an LF at <ptr>.
*/
+ hdr_idx_set_start(&txn->hdr_idx, msg->sl.rq.l, *cur_end == '\r');
+ }
- do {
- struct acl_cond *cond;
- struct redirect_rule *rule;
- struct proxy *rule_set = t->be;
- cur_proxy = t->be;
- /* first check whether we have some ACLs set to redirect this request */
- list_for_each_entry(rule, &cur_proxy->redirect_rules, list) {
- int ret = acl_exec_cond(rule->cond, cur_proxy, t, txn, ACL_DIR_REQ);
+ /* 5: we may need to capture headers */
+ if (unlikely((s->logs.logwait & LW_REQHDR) && s->fe->req_cap))
+ capture_headers(req->data + msg->som, &txn->hdr_idx,
+ txn->req.cap, s->fe->req_cap);
- ret = acl_pass(ret);
- if (rule->cond->pol == ACL_COND_UNLESS)
- ret = !ret;
+ /*
+ * 6: we will have to evaluate the filters.
+ * As opposed to version 1.2, now they will be evaluated in the
+ * filters order and not in the header order. This means that
+ * each filter has to be validated among all headers.
+ *
+ * We can now check whether we want to switch to another
+ * backend, in which case we will re-check the backend's
+ * filters and various options. In order to support 3-level
+ * switching, here's how we should proceed :
+ *
+ * a) run be.
+ * if (switch) then switch ->be to the new backend.
+ * b) run be if (be != fe).
+ * There cannot be any switch from there, so ->be cannot be
+ * changed anymore.
+ *
+ * => filters always apply to ->be, then ->be may change.
+ *
+ * The response path will be able to apply either ->be, or
+ * ->be then ->fe filters in order to match the reverse of
+ * the forward sequence.
+ */
- if (ret) {
- struct chunk rdr = { trash, 0 };
- const char *msg_fmt;
+ do {
+ struct acl_cond *cond;
+ struct redirect_rule *rule;
+ struct proxy *rule_set = s->be;
+ cur_proxy = s->be;
- /* build redirect message */
- switch(rule->code) {
- case 303:
- rdr.len = strlen(HTTP_303);
- msg_fmt = HTTP_303;
- break;
- case 301:
- rdr.len = strlen(HTTP_301);
- msg_fmt = HTTP_301;
- break;
- case 302:
- default:
- rdr.len = strlen(HTTP_302);
- msg_fmt = HTTP_302;
- break;
- }
+ /* first check whether we have some ACLs set to redirect this request */
+ list_for_each_entry(rule, &cur_proxy->redirect_rules, list) {
+ int ret = acl_exec_cond(rule->cond, cur_proxy, s, txn, ACL_DIR_REQ);
- if (unlikely(rdr.len > sizeof(trash)))
- goto return_bad_req;
- memcpy(rdr.str, msg_fmt, rdr.len);
+ ret = acl_pass(ret);
+ if (rule->cond->pol == ACL_COND_UNLESS)
+ ret = !ret;
- switch(rule->type) {
- case REDIRECT_TYPE_PREFIX: {
- const char *path;
- int pathlen;
+ if (ret) {
+ struct chunk rdr = { trash, 0 };
+ const char *msg_fmt;
- path = http_get_path(txn);
- /* build message using path */
- if (path) {
- pathlen = txn->req.sl.rq.u_l + (txn->req.sol+txn->req.sl.rq.u) - path;
- } else {
- path = "/";
- pathlen = 1;
- }
+ /* build redirect message */
+ switch(rule->code) {
+ case 303:
+ rdr.len = strlen(HTTP_303);
+ msg_fmt = HTTP_303;
+ break;
+ case 301:
+ rdr.len = strlen(HTTP_301);
+ msg_fmt = HTTP_301;
+ break;
+ case 302:
+ default:
+ rdr.len = strlen(HTTP_302);
+ msg_fmt = HTTP_302;
+ break;
+ }
- if (rdr.len + rule->rdr_len + pathlen > sizeof(trash) - 4)
- goto return_bad_req;
+ if (unlikely(rdr.len > sizeof(trash)))
+ goto return_bad_req;
+ memcpy(rdr.str, msg_fmt, rdr.len);
- /* add prefix */
- memcpy(rdr.str + rdr.len, rule->rdr_str, rule->rdr_len);
- rdr.len += rule->rdr_len;
+ switch(rule->type) {
+ case REDIRECT_TYPE_PREFIX: {
+ const char *path;
+ int pathlen;
- /* add path */
- memcpy(rdr.str + rdr.len, path, pathlen);
- rdr.len += pathlen;
- break;
- }
- case REDIRECT_TYPE_LOCATION:
- default:
- if (rdr.len + rule->rdr_len > sizeof(trash) - 4)
- goto return_bad_req;
-
- /* add location */
- memcpy(rdr.str + rdr.len, rule->rdr_str, rule->rdr_len);
- rdr.len += rule->rdr_len;
- break;
+ path = http_get_path(txn);
+ /* build message using path */
+ if (path) {
+ pathlen = txn->req.sl.rq.u_l + (txn->req.sol+txn->req.sl.rq.u) - path;
+ } else {
+ path = "/";
+ pathlen = 1;
}
- /* add end of headers */
- memcpy(rdr.str + rdr.len, "\r\n\r\n", 4);
- rdr.len += 4;
+ if (rdr.len + rule->rdr_len + pathlen > sizeof(trash) - 4)
+ goto return_bad_req;
+
+ /* add prefix */
+ memcpy(rdr.str + rdr.len, rule->rdr_str, rule->rdr_len);
+ rdr.len += rule->rdr_len;
- txn->status = rule->code;
- /* let's log the request time */
- t->logs.tv_request = now;
- stream_int_retnclose(req->prod, &rdr);
- goto return_prx_cond;
+ /* add path */
+ memcpy(rdr.str + rdr.len, path, pathlen);
+ rdr.len += pathlen;
+ break;
}
- }
+ case REDIRECT_TYPE_LOCATION:
+ default:
+ if (rdr.len + rule->rdr_len > sizeof(trash) - 4)
+ goto return_bad_req;
- /* first check whether we have some ACLs set to block this request */
- list_for_each_entry(cond, &cur_proxy->block_cond, list) {
- int ret = acl_exec_cond(cond, cur_proxy, t, txn, ACL_DIR_REQ);
+ /* add location */
+ memcpy(rdr.str + rdr.len, rule->rdr_str, rule->rdr_len);
+ rdr.len += rule->rdr_len;
+ break;
+ }
- ret = acl_pass(ret);
- if (cond->pol == ACL_COND_UNLESS)
- ret = !ret;
+ /* add end of headers */
+ memcpy(rdr.str + rdr.len, "\r\n\r\n", 4);
+ rdr.len += 4;
- if (ret) {
- txn->status = 403;
- /* let's log the request time */
- t->logs.tv_request = now;
- stream_int_retnclose(req->prod, error_message(t, HTTP_ERR_403));
- goto return_prx_cond;
- }
+ txn->status = rule->code;
+ /* let's log the request time */
+ s->logs.tv_request = now;
+ stream_int_retnclose(req->prod, &rdr);
+ goto return_prx_cond;
}
+ }
- /* try headers filters */
- if (rule_set->req_exp != NULL) {
- if (apply_filters_to_request(t, req, rule_set->req_exp) < 0)
- goto return_bad_req;
- }
+ /* first check whether we have some ACLs set to block this request */
+ list_for_each_entry(cond, &cur_proxy->block_cond, list) {
+ int ret = acl_exec_cond(cond, cur_proxy, s, txn, ACL_DIR_REQ);
- if (!(t->flags & SN_BE_ASSIGNED) && (t->be != cur_proxy)) {
- /* to ensure correct connection accounting on
- * the backend, we count the connection for the
- * one managing the queue.
- */
- t->be->beconn++;
- if (t->be->beconn > t->be->beconn_max)
- t->be->beconn_max = t->be->beconn;
- t->be->cum_beconn++;
- t->flags |= SN_BE_ASSIGNED;
- }
+ ret = acl_pass(ret);
+ if (cond->pol == ACL_COND_UNLESS)
+ ret = !ret;
- /* has the request been denied ? */
- if (txn->flags & TX_CLDENY) {
- /* no need to go further */
+ if (ret) {
txn->status = 403;
/* let's log the request time */
- t->logs.tv_request = now;
- stream_int_retnclose(req->prod, error_message(t, HTTP_ERR_403));
+ s->logs.tv_request = now;
+ stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_403));
goto return_prx_cond;
}
+ }
+
+ /* try headers filters */
+ if (rule_set->req_exp != NULL) {
+ if (apply_filters_to_request(s, req, rule_set->req_exp) < 0)
+ goto return_bad_req;
+ }
+
+ if (!(s->flags & SN_BE_ASSIGNED) && (s->be != cur_proxy)) {
+ /* to ensure correct connection accounting on
+ * the backend, we count the connection for the
+ * one managing the queue.
+ */
+ s->be->beconn++;
+ if (s->be->beconn > s->be->beconn_max)
+ s->be->beconn_max = s->be->beconn;
+ s->be->cum_beconn++;
+ s->flags |= SN_BE_ASSIGNED;
+ }
- /* We might have to check for "Connection:" */
- if (((t->fe->options | t->be->options) & (PR_O_HTTP_CLOSE|PR_O_FORCE_CLO)) &&
- !(t->flags & SN_CONN_CLOSED)) {
- char *cur_ptr, *cur_end, *cur_next;
- int cur_idx, old_idx, delta, val;
- struct hdr_idx_elem *cur_hdr;
+ /* has the request been denied ? */
+ if (txn->flags & TX_CLDENY) {
+ /* no need to go further */
+ txn->status = 403;
+ /* let's log the request time */
+ s->logs.tv_request = now;
+ stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_403));
+ goto return_prx_cond;
+ }
- cur_next = req->data + txn->req.som + hdr_idx_first_pos(&txn->hdr_idx);
- old_idx = 0;
+ /* We might have to check for "Connection:" */
+ if (((s->fe->options | s->be->options) & (PR_O_HTTP_CLOSE|PR_O_FORCE_CLO)) &&
+ !(s->flags & SN_CONN_CLOSED)) {
+ char *cur_ptr, *cur_end, *cur_next;
+ int cur_idx, old_idx, delta, val;
+ struct hdr_idx_elem *cur_hdr;
- while ((cur_idx = txn->hdr_idx.v[old_idx].next)) {
- cur_hdr = &txn->hdr_idx.v[cur_idx];
- cur_ptr = cur_next;
- cur_end = cur_ptr + cur_hdr->len;
- cur_next = cur_end + cur_hdr->cr + 1;
+ cur_next = req->data + txn->req.som + hdr_idx_first_pos(&txn->hdr_idx);
+ old_idx = 0;
- val = http_header_match2(cur_ptr, cur_end, "Connection", 10);
- if (val) {
- /* 3 possibilities :
- * - we have already set Connection: close,
- * so we remove this line.
- * - we have not yet set Connection: close,
- * but this line indicates close. We leave
- * it untouched and set the flag.
- * - we have not yet set Connection: close,
- * and this line indicates non-close. We
- * replace it.
- */
- if (t->flags & SN_CONN_CLOSED) {
- delta = buffer_replace2(req, cur_ptr, cur_next, NULL, 0);
- txn->req.eoh += delta;
+ while ((cur_idx = txn->hdr_idx.v[old_idx].next)) {
+ cur_hdr = &txn->hdr_idx.v[cur_idx];
+ cur_ptr = cur_next;
+ cur_end = cur_ptr + cur_hdr->len;
+ cur_next = cur_end + cur_hdr->cr + 1;
+
+ val = http_header_match2(cur_ptr, cur_end, "Connection", 10);
+ if (val) {
+ /* 3 possibilities :
+ * - we have already set Connection: close,
+ * so we remove this line.
+ * - we have not yet set Connection: close,
+ * but this line indicates close. We leave
+ * it untouched and set the flag.
+ * - we have not yet set Connection: close,
+ * and this line indicates non-close. We
+ * replace it.
+ */
+ if (s->flags & SN_CONN_CLOSED) {
+ delta = buffer_replace2(req, cur_ptr, cur_next, NULL, 0);
+ txn->req.eoh += delta;
+ cur_next += delta;
+ txn->hdr_idx.v[old_idx].next = cur_hdr->next;
+ txn->hdr_idx.used--;
+ cur_hdr->len = 0;
+ } else {
+ if (strncasecmp(cur_ptr + val, "close", 5) != 0) {
+ delta = buffer_replace2(req, cur_ptr + val, cur_end,
+ "close", 5);
cur_next += delta;
- txn->hdr_idx.v[old_idx].next = cur_hdr->next;
- txn->hdr_idx.used--;
- cur_hdr->len = 0;
- } else {
- if (strncasecmp(cur_ptr + val, "close", 5) != 0) {
- delta = buffer_replace2(req, cur_ptr + val, cur_end,
- "close", 5);
- cur_next += delta;
- cur_hdr->len += delta;
- txn->req.eoh += delta;
- }
- t->flags |= SN_CONN_CLOSED;
+ cur_hdr->len += delta;
+ txn->req.eoh += delta;
}
+ s->flags |= SN_CONN_CLOSED;
}
- old_idx = cur_idx;
}
- }
- /* add request headers from the rule sets in the same order */
- for (cur_idx = 0; cur_idx < rule_set->nb_reqadd; cur_idx++) {
- if (unlikely(http_header_add_tail(req,
- &txn->req,
- &txn->hdr_idx,
- rule_set->req_add[cur_idx])) < 0)
- goto return_bad_req;
+ old_idx = cur_idx;
}
+ }
+ /* add request headers from the rule sets in the same order */
+ for (cur_idx = 0; cur_idx < rule_set->nb_reqadd; cur_idx++) {
+ if (unlikely(http_header_add_tail(req,
+ &txn->req,
+ &txn->hdr_idx,
+ rule_set->req_add[cur_idx])) < 0)
+ goto return_bad_req;
+ }
- /* check if stats URI was requested, and if an auth is needed */
- if (rule_set->uri_auth != NULL &&
- (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)) {
- /* we have to check the URI and auth for this request.
- * FIXME!!! that one is rather dangerous, we want to
- * make it follow standard rules (eg: clear req->analysers).
- */
- if (stats_check_uri_auth(t, rule_set)) {
- req->analysers = 0;
- return 0;
- }
+ /* check if stats URI was requested, and if an auth is needed */
+ if (rule_set->uri_auth != NULL &&
+ (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)) {
+ /* we have to check the URI and auth for this request.
+ * FIXME!!! that one is rather dangerous, we want to
+ * make it follow standard rules (eg: clear req->analysers).
+ */
+ if (stats_check_uri_auth(s, rule_set)) {
+ req->analysers = 0;
+ return 0;
}
+ }
- /* now check whether we have some switching rules for this request */
- if (!(t->flags & SN_BE_ASSIGNED)) {
- struct switching_rule *rule;
+ /* now check whether we have some switching rules for this request */
+ if (!(s->flags & SN_BE_ASSIGNED)) {
+ struct switching_rule *rule;
- list_for_each_entry(rule, &cur_proxy->switching_rules, list) {
- int ret;
+ list_for_each_entry(rule, &cur_proxy->switching_rules, list) {
+ int ret;
- ret = acl_exec_cond(rule->cond, cur_proxy, t, txn, ACL_DIR_REQ);
+ ret = acl_exec_cond(rule->cond, cur_proxy, s, txn, ACL_DIR_REQ);
- ret = acl_pass(ret);
- if (rule->cond->pol == ACL_COND_UNLESS)
- ret = !ret;
+ ret = acl_pass(ret);
+ if (rule->cond->pol == ACL_COND_UNLESS)
+ ret = !ret;
- if (ret) {
- t->be = rule->be.backend;
- t->be->beconn++;
- if (t->be->beconn > t->be->beconn_max)
- t->be->beconn_max = t->be->beconn;
- t->be->cum_beconn++;
+ if (ret) {
+ s->be = rule->be.backend;
+ s->be->beconn++;
+ if (s->be->beconn > s->be->beconn_max)
+ s->be->beconn_max = s->be->beconn;
+ s->be->cum_beconn++;
- /* assign new parameters to the session from the new backend */
- t->rep->rto = t->req->wto = t->be->timeout.server;
- t->req->cto = t->be->timeout.connect;
- t->conn_retries = t->be->conn_retries;
- t->flags |= SN_BE_ASSIGNED;
- break;
- }
+ /* assign new parameters to the session from the new backend */
+ s->rep->rto = s->req->wto = s->be->timeout.server;
+ s->req->cto = s->be->timeout.connect;
+ s->conn_retries = s->be->conn_retries;
+ s->flags |= SN_BE_ASSIGNED;
+ break;
}
}
-
- if (!(t->flags & SN_BE_ASSIGNED) && cur_proxy->defbe.be) {
- /* No backend was set, but there was a default
- * backend set in the frontend, so we use it and
- * loop again.
- */
- t->be = cur_proxy->defbe.be;
- t->be->beconn++;
- if (t->be->beconn > t->be->beconn_max)
- t->be->beconn_max = t->be->beconn;
- t->be->cum_beconn++;
-
- /* assign new parameters to the session from the new backend */
- t->rep->rto = t->req->wto = t->be->timeout.server;
- t->req->cto = t->be->timeout.connect;
- t->conn_retries = t->be->conn_retries;
- t->flags |= SN_BE_ASSIGNED;
- }
- } while (t->be != cur_proxy); /* we loop only if t->be has changed */
-
- if (!(t->flags & SN_BE_ASSIGNED)) {
- /* To ensure correct connection accounting on
- * the backend, we count the connection for the
- * one managing the queue.
- */
- t->be->beconn++;
- if (t->be->beconn > t->be->beconn_max)
- t->be->beconn_max = t->be->beconn;
- t->be->cum_beconn++;
- t->flags |= SN_BE_ASSIGNED;
}
- /*
- * Right now, we know that we have processed the entire headers
- * and that unwanted requests have been filtered out. We can do
- * whatever we want with the remaining request. Also, now we
- * may have separate values for ->fe, ->be.
- */
+ if (!(s->flags & SN_BE_ASSIGNED) && cur_proxy->defbe.be) {
+ /* No backend was set, but there was a default
+ * backend set in the frontend, so we use it and
+ * loop again.
+ */
+ s->be = cur_proxy->defbe.be;
+ s->be->beconn++;
+ if (s->be->beconn > s->be->beconn_max)
+ s->be->beconn_max = s->be->beconn;
+ s->be->cum_beconn++;
- /*
- * If HTTP PROXY is set we simply get remote server address
- * parsing incoming request.
- */
- if ((t->be->options & PR_O_HTTP_PROXY) && !(t->flags & SN_ADDR_SET)) {
- url2sa(req->data + msg->sl.rq.u, msg->sl.rq.u_l, &t->srv_addr);
+ /* assign new parameters to the session from the new backend */
+ s->rep->rto = s->req->wto = s->be->timeout.server;
+ s->req->cto = s->be->timeout.connect;
+ s->conn_retries = s->be->conn_retries;
+ s->flags |= SN_BE_ASSIGNED;
}
+ } while (s->be != cur_proxy); /* we loop only if s->be has changed */
- /*
- * 7: the appsession cookie was looked up very early in 1.2,
- * so let's do the same now.
+ if (!(s->flags & SN_BE_ASSIGNED)) {
+ /* To ensure correct connection accounting on
+ * the backend, we count the connection for the
+ * one managing the queue.
*/
-
- /* It needs to look into the URI */
- if (t->be->appsession_name) {
- get_srv_from_appsession(t, &req->data[msg->som], msg->sl.rq.l);
- }
+ s->be->beconn++;
+ if (s->be->beconn > s->be->beconn_max)
+ s->be->beconn_max = s->be->beconn;
+ s->be->cum_beconn++;
+ s->flags |= SN_BE_ASSIGNED;
+ }
+ /*
+ * Right now, we know that we have processed the entire headers
+ * and that unwanted requests have been filtered out. We can do
+ * whatever we want with the remaining request. Also, now we
+ * may have separate values for ->fe, ->be.
+ */
- /*
- * 8: Now we can work with the cookies.
- * Note that doing so might move headers in the request, but
- * the fields will stay coherent and the URI will not move.
- * This should only be performed in the backend.
- */
- if ((t->be->cookie_name || t->be->appsession_name || t->be->capture_name)
- && !(txn->flags & (TX_CLDENY|TX_CLTARPIT)))
- manage_client_side_cookies(t, req);
+ /*
+ * If HTTP PROXY is set we simply get remote server address
+ * parsing incoming request.
+ */
+ if ((s->be->options & PR_O_HTTP_PROXY) && !(s->flags & SN_ADDR_SET)) {
+ url2sa(req->data + msg->sl.rq.u, msg->sl.rq.u_l, &s->srv_addr);
+ }
+ /*
+ * 7: the appsession cookie was looked up very early in 1.2,
+ * so let's do the same now.
+ */
- /*
- * 9: add X-Forwarded-For if either the frontend or the backend
- * asks for it.
- */
- if ((t->fe->options | t->be->options) & PR_O_FWDFOR) {
- if (t->cli_addr.ss_family == AF_INET) {
- /* Add an X-Forwarded-For header unless the source IP is
- * in the 'except' network range.
- */
- if ((!t->fe->except_mask.s_addr ||
- (((struct sockaddr_in *)&t->cli_addr)->sin_addr.s_addr & t->fe->except_mask.s_addr)
- != t->fe->except_net.s_addr) &&
- (!t->be->except_mask.s_addr ||
- (((struct sockaddr_in *)&t->cli_addr)->sin_addr.s_addr & t->be->except_mask.s_addr)
- != t->be->except_net.s_addr)) {
- int len;
- unsigned char *pn;
- pn = (unsigned char *)&((struct sockaddr_in *)&t->cli_addr)->sin_addr;
+ /* It needs to look into the URI */
+ if (s->be->appsession_name) {
+ get_srv_from_appsession(s, &req->data[msg->som], msg->sl.rq.l);
+ }
- /* Note: we rely on the backend to get the header name to be used for
- * x-forwarded-for, because the header is really meant for the backends.
- * However, if the backend did not specify any option, we have to rely
- * on the frontend's header name.
- */
- if (t->be->fwdfor_hdr_len) {
- len = t->be->fwdfor_hdr_len;
- memcpy(trash, t->be->fwdfor_hdr_name, len);
- } else {
- len = t->fe->fwdfor_hdr_len;
- memcpy(trash, t->fe->fwdfor_hdr_name, len);
- }
- len += sprintf(trash + len, ": %d.%d.%d.%d", pn[0], pn[1], pn[2], pn[3]);
+ /*
+ * 8: Now we can work with the cookies.
+ * Note that doing so might move headers in the request, but
+ * the fields will stay coherent and the URI will not move.
+ * This should only be performed in the backend.
+ */
+ if ((s->be->cookie_name || s->be->appsession_name || s->be->capture_name)
+ && !(txn->flags & (TX_CLDENY|TX_CLTARPIT)))
+ manage_client_side_cookies(s, req);
- if (unlikely(http_header_add_tail2(req, &txn->req,
- &txn->hdr_idx, trash, len)) < 0)
- goto return_bad_req;
- }
- }
- else if (t->cli_addr.ss_family == AF_INET6) {
- /* FIXME: for the sake of completeness, we should also support
- * 'except' here, although it is mostly useless in this case.
- */
+ /*
+ * 9: add X-Forwarded-For if either the frontend or the backend
+ * asks for it.
+ */
+ if ((s->fe->options | s->be->options) & PR_O_FWDFOR) {
+ if (s->cli_addr.ss_family == AF_INET) {
+ /* Add an X-Forwarded-For header unless the source IP is
+ * in the 'except' network range.
+ */
+ if ((!s->fe->except_mask.s_addr ||
+ (((struct sockaddr_in *)&s->cli_addr)->sin_addr.s_addr & s->fe->except_mask.s_addr)
+ != s->fe->except_net.s_addr) &&
+ (!s->be->except_mask.s_addr ||
+ (((struct sockaddr_in *)&s->cli_addr)->sin_addr.s_addr & s->be->except_mask.s_addr)
+ != s->be->except_net.s_addr)) {
int len;
- char pn[INET6_ADDRSTRLEN];
- inet_ntop(AF_INET6,
- (const void *)&((struct sockaddr_in6 *)(&t->cli_addr))->sin6_addr,
- pn, sizeof(pn));
+ unsigned char *pn;
+ pn = (unsigned char *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr;
/* Note: we rely on the backend to get the header name to be used for
* x-forwarded-for, because the header is really meant for the backends.
* However, if the backend did not specify any option, we have to rely
* on the frontend's header name.
*/
- if (t->be->fwdfor_hdr_len) {
- len = t->be->fwdfor_hdr_len;
- memcpy(trash, t->be->fwdfor_hdr_name, len);
+ if (s->be->fwdfor_hdr_len) {
+ len = s->be->fwdfor_hdr_len;
+ memcpy(trash, s->be->fwdfor_hdr_name, len);
} else {
- len = t->fe->fwdfor_hdr_len;
- memcpy(trash, t->fe->fwdfor_hdr_name, len);
- }
- len += sprintf(trash + len, ": %s", pn);
+ len = s->fe->fwdfor_hdr_len;
+ memcpy(trash, s->fe->fwdfor_hdr_name, len);
+ }
+ len += sprintf(trash + len, ": %d.%d.%d.%d", pn[0], pn[1], pn[2], pn[3]);
if (unlikely(http_header_add_tail2(req, &txn->req,
&txn->hdr_idx, trash, len)) < 0)
goto return_bad_req;
}
}
-
- /*
- * 10: add "Connection: close" if needed and not yet set.
- * Note that we do not need to add it in case of HTTP/1.0.
- */
- if (!(t->flags & SN_CONN_CLOSED) &&
- ((t->fe->options | t->be->options) & (PR_O_HTTP_CLOSE|PR_O_FORCE_CLO))) {
- if ((unlikely(msg->sl.rq.v_l != 8) ||
- unlikely(req->data[msg->som + msg->sl.rq.v + 7] != '0')) &&
- unlikely(http_header_add_tail2(req, &txn->req, &txn->hdr_idx,
- "Connection: close", 17)) < 0)
- goto return_bad_req;
- t->flags |= SN_CONN_CLOSED;
- }
- /* Before we switch to data, was assignment set in manage_client_side_cookie?
- * If not assigned, perhaps we are balancing on url_param, but this is a
- * POST; and the parameters are in the body, maybe scan there to find our server.
- * (unless headers overflowed the buffer?)
- */
- if (!(t->flags & (SN_ASSIGNED|SN_DIRECT)) &&
- t->txn.meth == HTTP_METH_POST && t->be->url_param_name != NULL &&
- t->be->url_param_post_limit != 0 && !(req->flags & BF_FULL) &&
- memchr(msg->sol + msg->sl.rq.u, '?', msg->sl.rq.u_l) == NULL) {
- /* are there enough bytes here? total == l || r || rlim ?
- * len is unsigned, but eoh is int,
- * how many bytes of body have we received?
- * eoh is the first empty line of the header
+ else if (s->cli_addr.ss_family == AF_INET6) {
+ /* FIXME: for the sake of completeness, we should also support
+ * 'except' here, although it is mostly useless in this case.
*/
- /* already established CRLF or LF at eoh, move to start of message, find message length in buffer */
- unsigned long len = req->l - (msg->sol[msg->eoh] == '\r' ? msg->eoh + 2 : msg->eoh + 1);
+ int len;
+ char pn[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6,
+ (const void *)&((struct sockaddr_in6 *)(&s->cli_addr))->sin6_addr,
+ pn, sizeof(pn));
- /* If we have HTTP/1.1 and Expect: 100-continue, then abort.
- * We can't assume responsibility for the server's decision,
- * on this URI and header set. See rfc2616: 14.20, 8.2.3,
- * We also can't change our mind later, about which server to choose, so round robin.
+ /* Note: we rely on the backend to get the header name to be used for
+ * x-forwarded-for, because the header is really meant for the backends.
+ * However, if the backend did not specify any option, we have to rely
+ * on the frontend's header name.
*/
- if ((likely(msg->sl.rq.v_l == 8) && req->data[msg->som + msg->sl.rq.v + 7] == '1')) {
- struct hdr_ctx ctx;
- ctx.idx = 0;
- /* Expect is allowed in 1.1, look for it */
- http_find_header2("Expect", 6, msg->sol, &txn->hdr_idx, &ctx);
- if (ctx.idx != 0 &&
- unlikely(ctx.vlen == 12 && strncasecmp(ctx.line+ctx.val, "100-continue", 12) == 0))
- /* We can't reliablly stall and wait for data, because of
- * .NET clients that don't conform to rfc2616; so, no need for
- * the next block to check length expectations.
- * We could send 100 status back to the client, but then we need to
- * re-write headers, and send the message. And this isn't the right
- * place for that action.
- * TODO: support Expect elsewhere and delete this block.
- */
- goto end_check_maybe_wait_for_body;
+ if (s->be->fwdfor_hdr_len) {
+ len = s->be->fwdfor_hdr_len;
+ memcpy(trash, s->be->fwdfor_hdr_name, len);
+ } else {
+ len = s->fe->fwdfor_hdr_len;
+ memcpy(trash, s->fe->fwdfor_hdr_name, len);
}
+ len += sprintf(trash + len, ": %s", pn);
- if (likely(len > t->be->url_param_post_limit)) {
- /* nothing to do, we got enough */
- } else {
- /* limit implies we are supposed to need this many bytes
- * to find the parameter. Let's see how many bytes we can wait for.
+ if (unlikely(http_header_add_tail2(req, &txn->req,
+ &txn->hdr_idx, trash, len)) < 0)
+ goto return_bad_req;
+ }
+ }
+
+ /*
+ * 10: add "Connection: close" if needed and not yet set.
+ * Note that we do not need to add it in case of HTTP/1.0.
+ */
+ if (!(s->flags & SN_CONN_CLOSED) &&
+ ((s->fe->options | s->be->options) & (PR_O_HTTP_CLOSE|PR_O_FORCE_CLO))) {
+ if ((unlikely(msg->sl.rq.v_l != 8) ||
+ unlikely(req->data[msg->som + msg->sl.rq.v + 7] != '0')) &&
+ unlikely(http_header_add_tail2(req, &txn->req, &txn->hdr_idx,
+ "Connection: close", 17)) < 0)
+ goto return_bad_req;
+ s->flags |= SN_CONN_CLOSED;
+ }
+ /* Before we switch to data, was assignment set in manage_client_side_cookie?
+ * If not assigned, perhaps we are balancing on url_param, but this is a
+ * POST; and the parameters are in the body, maybe scan there to find our server.
+ * (unless headers overflowed the buffer?)
+ */
+ if (!(s->flags & (SN_ASSIGNED|SN_DIRECT)) &&
+ s->txn.meth == HTTP_METH_POST && s->be->url_param_name != NULL &&
+ s->be->url_param_post_limit != 0 && !(req->flags & BF_FULL) &&
+ memchr(msg->sol + msg->sl.rq.u, '?', msg->sl.rq.u_l) == NULL) {
+ /* are there enough bytes here? total == l || r || rlim ?
+ * len is unsigned, but eoh is int,
+ * how many bytes of body have we received?
+ * eoh is the first empty line of the header
+ */
+ /* already established CRLF or LF at eoh, move to start of message, find message length in buffer */
+ unsigned long len = req->l - (msg->sol[msg->eoh] == '\r' ? msg->eoh + 2 : msg->eoh + 1);
+
+ /* If we have HTTP/1.1 and Expect: 100-continue, then abort.
+ * We can't assume responsibility for the server's decision,
+ * on this URI and header set. See rfc2616: 14.20, 8.2.3,
+ * We also can't change our mind later, about which server to choose, so round robin.
+ */
+ if ((likely(msg->sl.rq.v_l == 8) && req->data[msg->som + msg->sl.rq.v + 7] == '1')) {
+ struct hdr_ctx ctx;
+ ctx.idx = 0;
+ /* Expect is allowed in 1.1, look for it */
+ http_find_header2("Expect", 6, msg->sol, &txn->hdr_idx, &ctx);
+ if (ctx.idx != 0 &&
+ unlikely(ctx.vlen == 12 && strncasecmp(ctx.line+ctx.val, "100-continue", 12) == 0))
+ /* We can't reliablly stall and wait for data, because of
+ * .NET clients that don't conform to rfc2616; so, no need for
+ * the next block to check length expectations.
+ * We could send 100 status back to the client, but then we need to
+ * re-write headers, and send the message. And this isn't the right
+ * place for that action.
+ * TODO: support Expect elsewhere and delete this block.
*/
- long long hint = len;
- struct hdr_ctx ctx;
+ goto end_check_maybe_wait_for_body;
+ }
+
+ if (likely(len > s->be->url_param_post_limit)) {
+ /* nothing to do, we got enough */
+ } else {
+ /* limit implies we are supposed to need this many bytes
+ * to find the parameter. Let's see how many bytes we can wait for.
+ */
+ long long hint = len;
+ struct hdr_ctx ctx;
+ ctx.idx = 0;
+ http_find_header2("Transfer-Encoding", 17, msg->sol, &txn->hdr_idx, &ctx);
+ if (ctx.idx && ctx.vlen >= 7 && strncasecmp(ctx.line+ctx.val, "chunked", 7) == 0) {
+ buffer_write_dis(req);
+ req->analysers |= AN_REQ_HTTP_BODY;
+ }
+ else {
ctx.idx = 0;
- http_find_header2("Transfer-Encoding", 17, msg->sol, &txn->hdr_idx, &ctx);
- if (ctx.idx && ctx.vlen >= 7 && strncasecmp(ctx.line+ctx.val, "chunked", 7) == 0) {
+ http_find_header2("Content-Length", 14, msg->sol, &txn->hdr_idx, &ctx);
+ /* now if we have a length, we'll take the hint */
+ if (ctx.idx) {
+ /* We have Content-Length */
+ if (strl2llrc(ctx.line+ctx.val,ctx.vlen, &hint))
+ hint = 0; /* parse failure, untrusted client */
+ else {
+ if (hint > 0)
+ msg->hdr_content_len = hint;
+ else
+ hint = 0; /* bad client, sent negative length */
+ }
+ }
+ /* but limited to what we care about, maybe we don't expect any entity data (hint == 0) */
+ if (s->be->url_param_post_limit < hint)
+ hint = s->be->url_param_post_limit;
+ /* now do we really need to buffer more data? */
+ if (len < hint) {
buffer_write_dis(req);
req->analysers |= AN_REQ_HTTP_BODY;
}
- else {
- ctx.idx = 0;
- http_find_header2("Content-Length", 14, msg->sol, &txn->hdr_idx, &ctx);
- /* now if we have a length, we'll take the hint */
- if (ctx.idx) {
- /* We have Content-Length */
- if (strl2llrc(ctx.line+ctx.val,ctx.vlen, &hint))
- hint = 0; /* parse failure, untrusted client */
- else {
- if (hint > 0)
- msg->hdr_content_len = hint;
- else
- hint = 0; /* bad client, sent negative length */
- }
- }
- /* but limited to what we care about, maybe we don't expect any entity data (hint == 0) */
- if (t->be->url_param_post_limit < hint)
- hint = t->be->url_param_post_limit;
- /* now do we really need to buffer more data? */
- if (len < hint) {
- buffer_write_dis(req);
- req->analysers |= AN_REQ_HTTP_BODY;
- }
- /* else... There are no body bytes to wait for */
- }
+ /* else... There are no body bytes to wait for */
}
}
- end_check_maybe_wait_for_body:
+ }
+ end_check_maybe_wait_for_body:
- /*************************************************************
- * OK, that's finished for the headers. We have done what we *
- * could. Let's switch to the DATA state. *
- ************************************************************/
+ /*************************************************************
+ * OK, that's finished for the headers. We have done what we *
+ * could. Let's switch to the DATA state. *
+ ************************************************************/
- buffer_set_rlim(req, BUFSIZE); /* no more rewrite needed */
- t->logs.tv_request = now;
+ buffer_set_rlim(req, BUFSIZE); /* no more rewrite needed */
+ s->logs.tv_request = now;
- /* When a connection is tarpitted, we use the tarpit timeout,
- * which may be the same as the connect timeout if unspecified.
- * If unset, then set it to zero because we really want it to
- * eventually expire. We build the tarpit as an analyser.
+ /* When a connection is tarpitted, we use the tarpit timeout,
+ * which may be the same as the connect timeout if unspecified.
+ * If unset, then set it to zero because we really want it to
+ * eventually expire. We build the tarpit as an analyser.
+ */
+ if (txn->flags & TX_CLTARPIT) {
+ buffer_flush(s->req);
+ /* flush the request so that we can drop the connection early
+ * if the client closes first.
*/
- if (txn->flags & TX_CLTARPIT) {
- buffer_flush(t->req);
- /* flush the request so that we can drop the connection early
- * if the client closes first.
- */
- buffer_write_dis(req);
- req->analysers |= AN_REQ_HTTP_TARPIT;
- req->analyse_exp = tick_add_ifset(now_ms, t->be->timeout.tarpit);
- if (!req->analyse_exp)
- req->analyse_exp = now_ms;
- }
+ buffer_write_dis(req);
+ req->analysers |= AN_REQ_HTTP_TARPIT;
+ req->analyse_exp = tick_add_ifset(now_ms, s->be->timeout.tarpit);
+ if (!req->analyse_exp)
+ req->analyse_exp = now_ms;
+ }
- /* OK let's go on with the BODY now */
- goto end_of_headers;
+ /* OK let's go on with the BODY now */
+ return 1;
- return_bad_req: /* let's centralize all bad requests */
- txn->req.msg_state = HTTP_MSG_ERROR;
- txn->status = 400;
- req->analysers = 0;
- stream_int_retnclose(req->prod, error_message(t, HTTP_ERR_400));
- t->fe->failed_req++;
- return_prx_cond:
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_PRXCOND;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_R;
- return 0;
- end_of_headers:
- ; // to keep gcc happy
- }
+ return_bad_req: /* let's centralize all bad requests */
+ txn->req.msg_state = HTTP_MSG_ERROR;
+ txn->status = 400;
+ req->analysers = 0;
+ stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_400));
+ s->fe->failed_req++;
- /* Note: eventhough nobody should set an unknown flag, clearing them right now will
- * probably reduce one day's debugging session.
- */
-#ifdef DEBUG_DEV
- if (req->analysers & ~(AN_REQ_INSPECT | AN_REQ_HTTP_HDR | AN_REQ_HTTP_TARPIT | AN_REQ_HTTP_BODY)) {
- fprintf(stderr, "FIXME !!!! unknown analysers flags %s:%d = 0x%08X\n",
- __FILE__, __LINE__, req->analysers);
- ABORT_NOW();
- }
-#endif
+ return_prx_cond:
+ if (!(s->flags & SN_ERR_MASK))
+ s->flags |= SN_ERR_PRXCOND;
+ if (!(s->flags & SN_FINST_MASK))
+ s->flags |= SN_FINST_R;
return 0;
}
diff --git a/src/session.c b/src/session.c
index 9a9e469..f49814a 100644
--- a/src/session.c
+++ b/src/session.c
@@ -705,8 +705,8 @@
if (!tcp_inspect_request(s, s->req))
break;
- if (s->req->analysers)
- if (!process_request(s))
+ if (s->req->analysers & AN_REQ_HTTP_HDR)
+ if (!http_process_request(s, s->req))
break;
if (s->req->analysers & AN_REQ_HTTP_TARPIT)