Send response on quota exceeded
So far, slcl would just close the connection with a client when the Content-Length of an incoming request exceeded the user quota, without any meaningful information given back to the user. Now, slcl responds with a HTML file with meaningful information about the error. Limitations: - While this commits has been successfully tested on ungoogled-chromium, LibreWolf (and I assume Firefox and any other derivates too) does not seem to receive the response from the server. - However, this issue only occurred during local testing, but not on remote instances.
This commit is contained in:
parent
d51b191ab7
commit
d9bb874591
|
@ -112,13 +112,14 @@ static int on_payload(const struct http_payload *const p,
|
|||
}
|
||||
|
||||
static int on_length(const unsigned long long len,
|
||||
const struct http_cookie *const c, void *const user)
|
||||
const struct http_cookie *const c, struct http_response *const r,
|
||||
void *const user)
|
||||
{
|
||||
struct client *const cl = user;
|
||||
struct handler *const h = cl->h;
|
||||
|
||||
if (h->cfg.length)
|
||||
return h->cfg.length(len, c, h->cfg.user);
|
||||
return h->cfg.length(len, c, r, h->cfg.user);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ struct handler_cfg
|
|||
{
|
||||
const char *tmpdir;
|
||||
int (*length)(unsigned long long len, const struct http_cookie *c,
|
||||
void *user);
|
||||
struct http_response *r, void *user);
|
||||
void *user;
|
||||
};
|
||||
|
||||
|
|
33
http.c
33
http.c
|
@ -85,7 +85,7 @@ struct http_ctx
|
|||
|
||||
struct write_ctx
|
||||
{
|
||||
bool pending;
|
||||
bool pending, close;
|
||||
enum state state;
|
||||
struct http_response r;
|
||||
off_t n;
|
||||
|
@ -372,6 +372,8 @@ static int write_header_cr_line(struct http_ctx *const h, bool *const close)
|
|||
return rw_error(res, close);
|
||||
else if ((w->n += res) >= d->len)
|
||||
{
|
||||
const bool close_pending = w->close;
|
||||
|
||||
dynstr_free(d);
|
||||
|
||||
if (w->r.n)
|
||||
|
@ -384,6 +386,8 @@ static int write_header_cr_line(struct http_ctx *const h, bool *const close)
|
|||
fprintf(stderr, "%s: write_ctx_free failed\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
else if (close_pending)
|
||||
*close = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -394,17 +398,24 @@ static int write_body_mem(struct http_ctx *const h, bool *const close)
|
|||
struct write_ctx *const w = &h->wctx;
|
||||
const struct http_response *const r = &w->r;
|
||||
const size_t rem = r->n - w->n;
|
||||
const int res = h->cfg.write((const char *)r->buf.ro + w->n, rem,
|
||||
int res = h->cfg.write((const char *)r->buf.ro + w->n, rem,
|
||||
h->cfg.user);
|
||||
|
||||
if (res <= 0)
|
||||
return rw_error(res, close);
|
||||
else if ((w->n += res) >= r->n)
|
||||
{
|
||||
const bool close_pending = w->close;
|
||||
|
||||
if (h->version == HTTP_1_0)
|
||||
*close = true;
|
||||
|
||||
return write_ctx_free(w);
|
||||
if ((res = write_ctx_free(w)))
|
||||
fprintf(stderr, "%s: write_ctx_free failed\n", __func__);
|
||||
else if (close_pending)
|
||||
*close = true;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -425,16 +436,21 @@ static int write_body_file(struct http_ctx *const h, bool *const close)
|
|||
return -1;
|
||||
}
|
||||
|
||||
const int res = h->cfg.write(buf, rem, h->cfg.user);
|
||||
int res = h->cfg.write(buf, rem, h->cfg.user);
|
||||
|
||||
if (res <= 0)
|
||||
return rw_error(res, close);
|
||||
else if ((w->n += res) >= r->n)
|
||||
{
|
||||
const bool close_pending = w->close;
|
||||
|
||||
if (h->version == HTTP_1_0)
|
||||
*close = true;
|
||||
|
||||
return write_ctx_free(w);
|
||||
if ((res = write_ctx_free(w)))
|
||||
fprintf(stderr, "%s: write_ctx_free failed\n", __func__);
|
||||
else if (close_pending)
|
||||
*close = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -810,7 +826,7 @@ static int check_length(struct http_ctx *const h)
|
|||
.value = c->value
|
||||
};
|
||||
|
||||
return h->cfg.length(c->post.len, &cookie, h->cfg.user);
|
||||
return h->cfg.length(c->post.len, &cookie, &h->wctx.r, h->cfg.user);
|
||||
}
|
||||
|
||||
static int header_cr_line(struct http_ctx *const h)
|
||||
|
@ -834,7 +850,10 @@ static int header_cr_line(struct http_ctx *const h)
|
|||
const int res = check_length(h);
|
||||
|
||||
if (res)
|
||||
return res;
|
||||
{
|
||||
h->wctx.close = true;
|
||||
return start_response(h);
|
||||
}
|
||||
}
|
||||
|
||||
c->state = BODY_LINE;
|
||||
|
|
3
http.h
3
http.h
|
@ -45,6 +45,7 @@ struct http_payload
|
|||
X(UNAUTHORIZED, "Unauthorized", 401) \
|
||||
X(FORBIDDEN, "Forbidden", 403) \
|
||||
X(NOT_FOUND, "Not found", 404) \
|
||||
X(PAYLOAD_TOO_LARGE, "Payload too large", 413) \
|
||||
X(INTERNAL_ERROR, "Internal Server Error", 500)
|
||||
|
||||
struct http_response
|
||||
|
@ -80,7 +81,7 @@ struct http_cfg
|
|||
int (*payload)(const struct http_payload *p, struct http_response *r,
|
||||
void *user);
|
||||
int (*length)(unsigned long long len, const struct http_cookie *c,
|
||||
void *user);
|
||||
struct http_response *r, void *user);
|
||||
const char *tmpdir;
|
||||
void *user;
|
||||
};
|
||||
|
|
14
main.c
14
main.c
|
@ -610,7 +610,8 @@ static int check_quota(const struct auth *const a, const char *const username,
|
|||
}
|
||||
|
||||
static int check_length(const unsigned long long len,
|
||||
const struct http_cookie *const c, void *const user)
|
||||
const struct http_cookie *const c, struct http_response *const r,
|
||||
void *const user)
|
||||
{
|
||||
struct auth *const a = user;
|
||||
const char *const username = c->field;
|
||||
|
@ -623,7 +624,16 @@ static int check_length(const unsigned long long len,
|
|||
return -1;
|
||||
}
|
||||
else if (has_quota)
|
||||
return check_quota(a, username, len, quota);
|
||||
{
|
||||
int res = check_quota(a, username, len, quota);
|
||||
|
||||
if (res < 0)
|
||||
fprintf(stderr, "%s: check_quota failed\n", __func__);
|
||||
else if (res > 0 && page_quota_exceeded(r, len, quota) < 0)
|
||||
return -1;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
90
page.c
90
page.c
|
@ -1311,3 +1311,93 @@ end:
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int page_quota_exceeded(struct http_response *const r,
|
||||
const unsigned long long len, const unsigned long long quota)
|
||||
{
|
||||
int ret = -1;
|
||||
struct dynstr msg, out;
|
||||
char q[sizeof MAXSIZEFMT], l[sizeof q];
|
||||
struct html_node *const html = html_node_alloc("html"),
|
||||
*head, *body;
|
||||
|
||||
dynstr_init(&msg);
|
||||
dynstr_init(&out);
|
||||
|
||||
if (!html)
|
||||
{
|
||||
fprintf(stderr, "%s: html_node_alloc failed\n", __func__);
|
||||
goto end;
|
||||
}
|
||||
else if (!(head = html_node_add_child(html, "head")))
|
||||
{
|
||||
fprintf(stderr, "%s: html_node_add_child head failed\n", __func__);
|
||||
goto end;
|
||||
}
|
||||
else if (!(body = html_node_add_child(html, "body")))
|
||||
{
|
||||
fprintf(stderr, "%s: html_node_add_child body failed\n", __func__);
|
||||
goto end;
|
||||
}
|
||||
else if (common_head(head, NULL))
|
||||
{
|
||||
fprintf(stderr, "%s: common_head failed\n", __func__);
|
||||
goto end;
|
||||
}
|
||||
else if (size_units(quota, q, sizeof q))
|
||||
{
|
||||
fprintf(stderr, "%s: size_units quota failed\n", __func__);
|
||||
goto end;
|
||||
}
|
||||
else if (size_units(len, l, sizeof l))
|
||||
{
|
||||
fprintf(stderr, "%s: size_units len failed\n", __func__);
|
||||
goto end;
|
||||
}
|
||||
else if (dynstr_append(&msg, "Maximum quota exceeded: %s "
|
||||
"(requested size: %s)", q, l))
|
||||
{
|
||||
fprintf(stderr, "%s: dynstr_append msg failed\n", __func__);
|
||||
goto end;
|
||||
}
|
||||
else if (html_node_set_value(body, msg.str))
|
||||
{
|
||||
fprintf(stderr, "%s: html_node_set_value msg failed\n", __func__);
|
||||
goto end;
|
||||
}
|
||||
else if (dynstr_append(&out, DOCTYPE_TAG))
|
||||
{
|
||||
fprintf(stderr, "%s: dynstr_prepend failed\n", __func__);
|
||||
goto end;
|
||||
}
|
||||
else if (html_serialize(html, &out))
|
||||
{
|
||||
fprintf(stderr, "%s: html_serialize failed\n", __func__);
|
||||
goto end;
|
||||
}
|
||||
|
||||
*r = (const struct http_response)
|
||||
{
|
||||
.status = HTTP_STATUS_PAYLOAD_TOO_LARGE,
|
||||
.buf.rw = out.str,
|
||||
.n = out.len,
|
||||
.free = free
|
||||
};
|
||||
|
||||
if (http_response_add_header(r, "Content-Type", "text/html"))
|
||||
{
|
||||
fprintf(stderr, "%s: http_response_add_header failed\n", __func__);
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
end:
|
||||
html_node_free(html);
|
||||
dynstr_free(&msg);
|
||||
|
||||
if (ret)
|
||||
dynstr_free(&out);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
2
page.h
2
page.h
|
@ -17,5 +17,7 @@ int page_resource(struct http_response *r, const char *dir, const char *root,
|
|||
const char *res, const struct page_quota *q);
|
||||
int page_public(struct http_response *r, const char *res);
|
||||
int page_share(struct http_response *r, const char *path);
|
||||
int page_quota_exceeded(struct http_response *r, unsigned long long len,
|
||||
unsigned long long quota);
|
||||
|
||||
#endif /* PAGE_H */
|
||||
|
|
Loading…
Reference in New Issue