mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-02-17 02:56:51 +00:00
MEDIUM: proto_htx: Convert all HTTP error messages into HTX
During startup, after the configuration parsing, all HTTP error messages (errorloc, errorfile or default messages) are converted into HTX messages and stored in dedicated buffers. We use it to return errors in the HTX analyzers instead of using ugly OOB blocks.
This commit is contained in:
parent
99daf28a76
commit
a7b677cd0d
@ -119,6 +119,7 @@ struct http_method_desc {
|
||||
};
|
||||
|
||||
extern const int http_err_codes[HTTP_ERR_SIZE];
|
||||
extern const char *http_err_msgs[HTTP_ERR_SIZE];
|
||||
extern struct buffer http_err_chunks[HTTP_ERR_SIZE];
|
||||
const struct ist http_known_methods[HTTP_METH_OTHER];
|
||||
extern const uint8_t http_char_classes[256];
|
||||
|
@ -24,10 +24,13 @@
|
||||
#define _PROTO_HTTP_HTX_H
|
||||
|
||||
#include <common/buf.h>
|
||||
#include <common/ist.h>
|
||||
|
||||
#include <types/h1.h>
|
||||
#include <types/http_htx.h>
|
||||
|
||||
extern struct buffer htx_err_chunks[HTTP_ERR_SIZE];
|
||||
|
||||
struct htx_sl *http_find_stline(struct htx *htx);
|
||||
int http_find_header(const struct htx *htx, const struct ist name, struct http_hdr_ctx *ctx, int full);
|
||||
int http_add_header(struct htx *htx, const struct ist n, const struct ist v);
|
||||
|
@ -78,7 +78,7 @@ int htx_send_name_header(struct stream *s, struct proxy *be, const char *srv_nam
|
||||
void htx_perform_server_redirect(struct stream *s, struct stream_interface *si);
|
||||
void htx_server_error(struct stream *s, struct stream_interface *si, int err, int finst, const struct buffer *msg);
|
||||
void htx_reply_and_close(struct stream *s, short status, struct buffer *msg);
|
||||
|
||||
struct buffer *htx_error_message(struct stream *s);
|
||||
|
||||
void debug_hdr(const char *dir, struct stream *s, const char *start, const char *end);
|
||||
int apply_filter_to_req_headers(struct stream *s, struct channel *req, struct hdr_exp *exp);
|
||||
|
@ -230,7 +230,7 @@ const int http_err_codes[HTTP_ERR_SIZE] = {
|
||||
[HTTP_ERR_504] = 504,
|
||||
};
|
||||
|
||||
static const char *http_err_msgs[HTTP_ERR_SIZE] = {
|
||||
const char *http_err_msgs[HTTP_ERR_SIZE] = {
|
||||
[HTTP_ERR_200] =
|
||||
"HTTP/1.0 200 OK\r\n"
|
||||
"Cache-Control: no-cache\r\n"
|
||||
|
118
src/http_htx.c
118
src/http_htx.c
@ -11,11 +11,15 @@
|
||||
*/
|
||||
|
||||
#include <common/config.h>
|
||||
#include <common/cfgparse.h>
|
||||
#include <common/http.h>
|
||||
|
||||
#include <proto/h1.h>
|
||||
#include <proto/http_htx.h>
|
||||
#include <proto/htx.h>
|
||||
|
||||
struct buffer htx_err_chunks[HTTP_ERR_SIZE];
|
||||
|
||||
/* Finds the start line in the HTX message stopping at the first
|
||||
* end-of-message. It returns NULL when not found, otherwise, it returns the
|
||||
* pointer on the htx_sl structure. The HTX message may be updated if the
|
||||
@ -614,3 +618,117 @@ unsigned int http_get_htx_fhdr(const struct htx *htx, const struct ist hdr,
|
||||
*vlen = val_hist[hist_idx].len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct htx *http_str_to_htx(struct buffer *buf, struct ist raw)
|
||||
{
|
||||
struct htx *htx;
|
||||
struct htx_sl *sl;
|
||||
struct h1m h1m;
|
||||
struct http_hdr hdrs[MAX_HTTP_HDR];
|
||||
union h1_sl h1sl;
|
||||
unsigned int flags = HTX_SL_F_IS_RESP;
|
||||
int ret = 0;
|
||||
|
||||
buf->size = global.tune.bufsize;
|
||||
buf->area = (char *)malloc(buf->size);
|
||||
if (!buf->area)
|
||||
goto error;
|
||||
b_reset(buf);
|
||||
|
||||
h1m_init_res(&h1m);
|
||||
h1m.flags |= H1_MF_NO_PHDR;
|
||||
ret = h1_headers_to_hdr_list(raw.ptr, raw.ptr + raw.len,
|
||||
hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &h1m, &h1sl);
|
||||
if (ret <= 0)
|
||||
goto error;
|
||||
|
||||
if (unlikely(h1sl.st.v.len != 8))
|
||||
goto error;
|
||||
if ((*(h1sl.st.v.ptr + 5) > '1') ||
|
||||
((*(h1sl.st.v.ptr + 5) == '1') && (*(h1sl.st.v.ptr + 7) >= '1')))
|
||||
h1m.flags |= H1_MF_VER_11;
|
||||
|
||||
if (h1m.flags & H1_MF_VER_11)
|
||||
flags |= HTX_SL_F_VER_11;
|
||||
if (h1m.flags & H1_MF_XFER_ENC)
|
||||
flags |= HTX_SL_F_XFER_ENC;
|
||||
if (h1m.flags & H1_MF_XFER_LEN) {
|
||||
flags |= HTX_SL_F_XFER_LEN;
|
||||
if (h1m.flags & H1_MF_CHNK)
|
||||
goto error; /* Unsupported because there is no body parsing */
|
||||
else if (h1m.flags & H1_MF_CLEN) {
|
||||
flags |= HTX_SL_F_CLEN;
|
||||
if (h1m.body_len == 0)
|
||||
flags |= HTX_SL_F_BODYLESS;
|
||||
}
|
||||
}
|
||||
|
||||
htx = htx_from_buf(buf);
|
||||
sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, h1sl.st.v, h1sl.st.c, h1sl.st.r);
|
||||
if (!sl || !htx_add_all_headers(htx, hdrs))
|
||||
goto error;
|
||||
sl->info.res.status = h1sl.st.status;
|
||||
|
||||
if (raw.len > ret) {
|
||||
if (!htx_add_data(htx, ist2(raw.ptr + ret, raw.len - ret)))
|
||||
goto error;
|
||||
}
|
||||
if (!htx_add_endof(htx, HTX_BLK_EOM))
|
||||
goto error;
|
||||
|
||||
b_set_data(buf, b_size(buf));
|
||||
return htx;
|
||||
|
||||
error:
|
||||
if (buf->size)
|
||||
free(buf->area);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int http_htx_init(void)
|
||||
{
|
||||
struct proxy *px;
|
||||
struct buffer chk;
|
||||
struct ist raw;
|
||||
int rc;
|
||||
int err_code = 0;
|
||||
|
||||
for (px = proxies_list; px; px = px->next) {
|
||||
if (!(px->options2 & PR_O2_USE_HTX))
|
||||
continue;
|
||||
|
||||
for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
|
||||
if (!b_data(&px->errmsg[rc]))
|
||||
continue;
|
||||
|
||||
raw = ist2(b_head(&px->errmsg[rc]), b_data(&px->errmsg[rc]));
|
||||
if (!http_str_to_htx(&chk, raw)) {
|
||||
ha_alert("config: %s '%s': Unable to convert message in HTX for HTTP return code %d.\n",
|
||||
proxy_type_str(px), px->id, http_err_codes[rc]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
}
|
||||
chunk_destroy(&px->errmsg[rc]);
|
||||
px->errmsg[rc] = chk;
|
||||
}
|
||||
}
|
||||
|
||||
for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
|
||||
if (!http_err_msgs[rc]) {
|
||||
ha_alert("Internal error: no message defined for HTTP return code %d", rc);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
continue;
|
||||
}
|
||||
|
||||
raw = ist2(http_err_msgs[rc], strlen(http_err_msgs[rc]));
|
||||
if (!http_str_to_htx(&chk, raw)) {
|
||||
ha_alert("Internal error: Unable to convert message in HTX for HTTP return code %d.\n",
|
||||
http_err_codes[rc]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
}
|
||||
htx_err_chunks[rc] = chk;
|
||||
}
|
||||
end:
|
||||
return err_code;
|
||||
}
|
||||
|
||||
REGISTER_CONFIG_POSTPARSER("http_htx", http_htx_init);
|
||||
|
@ -413,6 +413,9 @@ struct buffer *http_error_message(struct stream *s)
|
||||
{
|
||||
const int msgnum = http_get_status_idx(s->txn->status);
|
||||
|
||||
if (IS_HTX_STRM(s))
|
||||
return htx_error_message(s);
|
||||
|
||||
if (s->be->errmsg[msgnum].area)
|
||||
return &s->be->errmsg[msgnum];
|
||||
else if (strm_fe(s)->errmsg[msgnum].area)
|
||||
|
@ -185,7 +185,7 @@ int htx_wait_for_request(struct stream *s, struct channel *req, int an_bit)
|
||||
txn->status = 408;
|
||||
msg->err_state = msg->msg_state;
|
||||
msg->msg_state = HTTP_MSG_ERROR;
|
||||
htx_reply_and_close(s, txn->status, http_error_message(s));
|
||||
htx_reply_and_close(s, txn->status, htx_error_message(s));
|
||||
req->analysers &= AN_REQ_FLT_END;
|
||||
|
||||
if (!(s->flags & SF_FINST_MASK))
|
||||
@ -214,7 +214,7 @@ int htx_wait_for_request(struct stream *s, struct channel *req, int an_bit)
|
||||
txn->status = 400;
|
||||
msg->err_state = msg->msg_state;
|
||||
msg->msg_state = HTTP_MSG_ERROR;
|
||||
htx_reply_and_close(s, txn->status, http_error_message(s));
|
||||
htx_reply_and_close(s, txn->status, htx_error_message(s));
|
||||
req->analysers &= AN_REQ_FLT_END;
|
||||
|
||||
if (!(s->flags & SF_FINST_MASK))
|
||||
@ -347,7 +347,7 @@ int htx_wait_for_request(struct stream *s, struct channel *req, int an_bit)
|
||||
if (ret) {
|
||||
/* we fail this request, let's return 503 service unavail */
|
||||
txn->status = 503;
|
||||
htx_reply_and_close(s, txn->status, http_error_message(s));
|
||||
htx_reply_and_close(s, txn->status, htx_error_message(s));
|
||||
if (!(s->flags & SF_ERR_MASK))
|
||||
s->flags |= SF_ERR_LOCAL; /* we don't want a real error here */
|
||||
goto return_prx_cond;
|
||||
@ -356,7 +356,7 @@ int htx_wait_for_request(struct stream *s, struct channel *req, int an_bit)
|
||||
|
||||
/* nothing to fail, let's reply normaly */
|
||||
txn->status = 200;
|
||||
htx_reply_and_close(s, txn->status, http_error_message(s));
|
||||
htx_reply_and_close(s, txn->status, htx_error_message(s));
|
||||
if (!(s->flags & SF_ERR_MASK))
|
||||
s->flags |= SF_ERR_LOCAL; /* we don't want a real error here */
|
||||
goto return_prx_cond;
|
||||
@ -446,7 +446,7 @@ int htx_wait_for_request(struct stream *s, struct channel *req, int an_bit)
|
||||
txn->status = 400;
|
||||
txn->req.err_state = txn->req.msg_state;
|
||||
txn->req.msg_state = HTTP_MSG_ERROR;
|
||||
htx_reply_and_close(s, txn->status, http_error_message(s));
|
||||
htx_reply_and_close(s, txn->status, htx_error_message(s));
|
||||
HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_req, 1);
|
||||
if (sess->listener->counters)
|
||||
HA_ATOMIC_ADD(&sess->listener->counters->failed_req, 1);
|
||||
@ -551,7 +551,7 @@ int htx_process_req_common(struct stream *s, struct channel *req, int an_bit, st
|
||||
if (unlikely(!stream_int_register_handler(&s->si[1], objt_applet(s->target)))) {
|
||||
txn->status = 500;
|
||||
s->logs.tv_request = now;
|
||||
htx_reply_and_close(s, txn->status, http_error_message(s));
|
||||
htx_reply_and_close(s, txn->status, htx_error_message(s));
|
||||
|
||||
if (!(s->flags & SF_ERR_MASK))
|
||||
s->flags |= SF_ERR_RESOURCE;
|
||||
@ -703,7 +703,7 @@ int htx_process_req_common(struct stream *s, struct channel *req, int an_bit, st
|
||||
txn->flags |= TX_CLDENY;
|
||||
txn->status = http_err_codes[deny_status];
|
||||
s->logs.tv_request = now;
|
||||
htx_reply_and_close(s, txn->status, http_error_message(s));
|
||||
htx_reply_and_close(s, txn->status, htx_error_message(s));
|
||||
stream_inc_http_err_ctr(s);
|
||||
HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
|
||||
if (sess->fe != s->be)
|
||||
@ -716,7 +716,7 @@ int htx_process_req_common(struct stream *s, struct channel *req, int an_bit, st
|
||||
txn->req.err_state = txn->req.msg_state;
|
||||
txn->req.msg_state = HTTP_MSG_ERROR;
|
||||
txn->status = 400;
|
||||
htx_reply_and_close(s, txn->status, http_error_message(s));
|
||||
htx_reply_and_close(s, txn->status, htx_error_message(s));
|
||||
|
||||
HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_req, 1);
|
||||
if (sess->listener->counters)
|
||||
@ -789,7 +789,7 @@ int htx_process_request(struct stream *s, struct channel *req, int an_bit)
|
||||
txn->req.msg_state = HTTP_MSG_ERROR;
|
||||
txn->status = 500;
|
||||
req->analysers &= AN_REQ_FLT_END;
|
||||
htx_reply_and_close(s, txn->status, http_error_message(s));
|
||||
htx_reply_and_close(s, txn->status, htx_error_message(s));
|
||||
|
||||
if (!(s->flags & SF_ERR_MASK))
|
||||
s->flags |= SF_ERR_RESOURCE;
|
||||
@ -979,7 +979,7 @@ int htx_process_request(struct stream *s, struct channel *req, int an_bit)
|
||||
txn->req.msg_state = HTTP_MSG_ERROR;
|
||||
txn->status = 400;
|
||||
req->analysers &= AN_REQ_FLT_END;
|
||||
htx_reply_and_close(s, txn->status, http_error_message(s));
|
||||
htx_reply_and_close(s, txn->status, htx_error_message(s));
|
||||
|
||||
HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_req, 1);
|
||||
if (sess->listener->counters)
|
||||
@ -1019,7 +1019,7 @@ int htx_process_tarpit(struct stream *s, struct channel *req, int an_bit)
|
||||
s->logs.t_queue = tv_ms_elapsed(&s->logs.tv_accept, &now);
|
||||
|
||||
if (!(req->flags & CF_READ_ERROR))
|
||||
htx_reply_and_close(s, txn->status, http_error_message(s));
|
||||
htx_reply_and_close(s, txn->status, htx_error_message(s));
|
||||
|
||||
req->analysers &= AN_REQ_FLT_END;
|
||||
req->analyse_exp = TICK_ETERNITY;
|
||||
@ -1102,7 +1102,7 @@ int htx_wait_for_request_body(struct stream *s, struct channel *req, int an_bit)
|
||||
|
||||
if ((req->flags & CF_READ_TIMEOUT) || tick_is_expired(req->analyse_exp, now_ms)) {
|
||||
txn->status = 408;
|
||||
htx_reply_and_close(s, txn->status, http_error_message(s));
|
||||
htx_reply_and_close(s, txn->status, htx_error_message(s));
|
||||
|
||||
if (!(s->flags & SF_ERR_MASK))
|
||||
s->flags |= SF_ERR_CLITO;
|
||||
@ -1136,7 +1136,7 @@ int htx_wait_for_request_body(struct stream *s, struct channel *req, int an_bit)
|
||||
txn->req.err_state = txn->req.msg_state;
|
||||
txn->req.msg_state = HTTP_MSG_ERROR;
|
||||
txn->status = 400;
|
||||
htx_reply_and_close(s, txn->status, http_error_message(s));
|
||||
htx_reply_and_close(s, txn->status, htx_error_message(s));
|
||||
|
||||
if (!(s->flags & SF_ERR_MASK))
|
||||
s->flags |= SF_ERR_PRXCOND;
|
||||
@ -1354,7 +1354,7 @@ int htx_request_forward_body(struct stream *s, struct channel *req, int an_bit)
|
||||
htx_reply_and_close(s, txn->status, NULL);
|
||||
} else {
|
||||
txn->status = 400;
|
||||
htx_reply_and_close(s, txn->status, http_error_message(s));
|
||||
htx_reply_and_close(s, txn->status, htx_error_message(s));
|
||||
}
|
||||
req->analysers &= AN_REQ_FLT_END;
|
||||
s->res.analysers &= AN_RES_FLT_END; /* we're in data phase, we want to abort both directions */
|
||||
@ -1377,7 +1377,7 @@ int htx_request_forward_body(struct stream *s, struct channel *req, int an_bit)
|
||||
htx_reply_and_close(s, txn->status, NULL);
|
||||
} else {
|
||||
txn->status = 502;
|
||||
htx_reply_and_close(s, txn->status, http_error_message(s));
|
||||
htx_reply_and_close(s, txn->status, htx_error_message(s));
|
||||
}
|
||||
req->analysers &= AN_REQ_FLT_END;
|
||||
s->res.analysers &= AN_RES_FLT_END; /* we're in data phase, we want to abort both directions */
|
||||
@ -1477,7 +1477,7 @@ int htx_wait_for_response(struct stream *s, struct channel *rep, int an_bit)
|
||||
}
|
||||
|
||||
s->si[1].flags |= SI_FL_NOLINGER;
|
||||
htx_reply_and_close(s, txn->status, http_error_message(s));
|
||||
htx_reply_and_close(s, txn->status, htx_error_message(s));
|
||||
|
||||
if (!(s->flags & SF_ERR_MASK))
|
||||
s->flags |= SF_ERR_SRVCL;
|
||||
@ -1497,7 +1497,7 @@ int htx_wait_for_response(struct stream *s, struct channel *rep, int an_bit)
|
||||
rep->analysers &= AN_RES_FLT_END;
|
||||
txn->status = 504;
|
||||
s->si[1].flags |= SI_FL_NOLINGER;
|
||||
htx_reply_and_close(s, txn->status, http_error_message(s));
|
||||
htx_reply_and_close(s, txn->status, htx_error_message(s));
|
||||
|
||||
if (!(s->flags & SF_ERR_MASK))
|
||||
s->flags |= SF_ERR_SRVTO;
|
||||
@ -1515,7 +1515,7 @@ int htx_wait_for_response(struct stream *s, struct channel *rep, int an_bit)
|
||||
|
||||
rep->analysers &= AN_RES_FLT_END;
|
||||
txn->status = 400;
|
||||
htx_reply_and_close(s, txn->status, http_error_message(s));
|
||||
htx_reply_and_close(s, txn->status, htx_error_message(s));
|
||||
|
||||
if (!(s->flags & SF_ERR_MASK))
|
||||
s->flags |= SF_ERR_CLICL;
|
||||
@ -1540,7 +1540,7 @@ int htx_wait_for_response(struct stream *s, struct channel *rep, int an_bit)
|
||||
rep->analysers &= AN_RES_FLT_END;
|
||||
txn->status = 502;
|
||||
s->si[1].flags |= SI_FL_NOLINGER;
|
||||
htx_reply_and_close(s, txn->status, http_error_message(s));
|
||||
htx_reply_and_close(s, txn->status, htx_error_message(s));
|
||||
|
||||
if (!(s->flags & SF_ERR_MASK))
|
||||
s->flags |= SF_ERR_SRVCL;
|
||||
@ -1750,7 +1750,7 @@ int htx_wait_for_response(struct stream *s, struct channel *rep, int an_bit)
|
||||
}
|
||||
txn->status = 502;
|
||||
s->si[1].flags |= SI_FL_NOLINGER;
|
||||
htx_reply_and_close(s, txn->status, http_error_message(s));
|
||||
htx_reply_and_close(s, txn->status, htx_error_message(s));
|
||||
rep->analysers &= AN_RES_FLT_END;
|
||||
|
||||
if (!(s->flags & SF_ERR_MASK))
|
||||
@ -2063,7 +2063,7 @@ int htx_process_res_common(struct stream *s, struct channel *rep, int an_bit, st
|
||||
txn->status = 502;
|
||||
s->logs.t_data = -1; /* was not a valid response */
|
||||
s->si[1].flags |= SI_FL_NOLINGER;
|
||||
htx_reply_and_close(s, txn->status, http_error_message(s));
|
||||
htx_reply_and_close(s, txn->status, htx_error_message(s));
|
||||
if (!(s->flags & SF_ERR_MASK))
|
||||
s->flags |= SF_ERR_PRXCOND;
|
||||
if (!(s->flags & SF_FINST_MASK))
|
||||
@ -5283,13 +5283,17 @@ void htx_server_error(struct stream *s, struct stream_interface *si, int err,
|
||||
channel_erase(si_oc(si));
|
||||
channel_auto_close(si_ic(si));
|
||||
channel_auto_read(si_ic(si));
|
||||
|
||||
/* <msg> is an HTX structure. So we copy it in the response's
|
||||
* channel */
|
||||
if (msg) {
|
||||
struct channel *chn = si_ic(si);
|
||||
struct htx *htx;
|
||||
|
||||
htx = htx_from_buf(&chn->buf);
|
||||
htx_add_oob(htx, ist2(msg->area, msg->data));
|
||||
//FLT_STRM_CB(s, flt_htx_reply(s, s->txn->status, htx));
|
||||
chn->buf.data = msg->data;
|
||||
memcpy(chn->buf.area, msg->area, msg->data);
|
||||
htx = htx_from_buf(&chn->buf);
|
||||
b_set_data(&chn->buf, b_size(&chn->buf));
|
||||
c_adv(chn, htx->data);
|
||||
chn->total += htx->data;
|
||||
@ -5309,13 +5313,18 @@ void htx_reply_and_close(struct stream *s, short status, struct buffer *msg)
|
||||
channel_truncate(&s->res);
|
||||
|
||||
s->txn->flags &= ~TX_WAIT_NEXT_RQ;
|
||||
|
||||
/* <msg> is an HTX structure. So we copy it in the response's
|
||||
* channel */
|
||||
/* FIXME: It is a problem for now if there is some outgoing data */
|
||||
if (msg) {
|
||||
struct channel *chn = &s->res;
|
||||
struct htx *htx;
|
||||
|
||||
htx = htx_from_buf(&chn->buf);
|
||||
htx_add_oob(htx, ist2(msg->area, msg->data));
|
||||
//FLT_STRM_CB(s, flt_htx_reply(s, s->txn->status, htx));
|
||||
chn->buf.data = msg->data;
|
||||
memcpy(chn->buf.area, msg->area, msg->data);
|
||||
htx = htx_from_buf(&chn->buf);
|
||||
b_set_data(&chn->buf, b_size(&chn->buf));
|
||||
c_adv(chn, htx->data);
|
||||
chn->total += htx->data;
|
||||
@ -5327,6 +5336,19 @@ void htx_reply_and_close(struct stream *s, short status, struct buffer *msg)
|
||||
channel_shutr_now(&s->res);
|
||||
}
|
||||
|
||||
struct buffer *htx_error_message(struct stream *s)
|
||||
{
|
||||
const int msgnum = http_get_status_idx(s->txn->status);
|
||||
|
||||
if (s->be->errmsg[msgnum].area)
|
||||
return &s->be->errmsg[msgnum];
|
||||
else if (strm_fe(s)->errmsg[msgnum].area)
|
||||
return &strm_fe(s)->errmsg[msgnum];
|
||||
else
|
||||
return &htx_err_chunks[msgnum];
|
||||
}
|
||||
|
||||
|
||||
/* Send a 100-Continue response to the client. It returns 0 on success and -1
|
||||
* on error. The response channel is updated accordingly.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user