commit b513f0aa40b0888f2c211db07db6c7a19c4a3afd
parent 1eb7b699d4b999e8f3a3b5334e57d8c4ff6c9cdb
Author: hhvn <dev@hhvn.uk>
Date: Mon, 2 May 2022 22:07:35 +0100
Dynamically sized server input buffer
Diffstat:
2 files changed, 33 insertions(+), 13 deletions(-)
diff --git a/src/serv.c b/src/serv.c
@@ -31,6 +31,11 @@
#endif /* TLS */
#include "hirc.h"
+/* 1024 is enough to fit two max-length messages in the buffer at once.
+ * To stress-test this, it is possible to set it to 2 as the lowest value.
+ * (The last byte is reserved for '\0', so a value of 1 will act weird). */
+#define INPUT_BUF_MIN 1024
+
void
serv_free(struct Server *server) {
struct Support *p, *prev;
@@ -79,7 +84,9 @@ serv_create(char *name, char *host, char *port, char *nick, char *username,
server = emalloc(sizeof(struct Server));
server->prev = server->next = NULL;
server->wfd = server->rfd = -1;
- server->inputlen = 0;
+ server->input.size = INPUT_BUF_MIN;
+ server->input.pos = 0;
+ server->input.buf = emalloc(server->input.size);
server->rpollfd = emalloc(sizeof(struct pollfd));
server->rpollfd->fd = -1;
server->rpollfd->events = POLLIN;
@@ -357,7 +364,7 @@ serv_read(struct Server *sp) {
#ifdef TLS
if (sp->tls) {
- switch (ret = tls_read(sp->tls_ctx, &sp->inputbuf[sp->inputlen], SERVER_INPUT_SIZE - sp->inputlen - 1)) {
+ switch (ret = tls_read(sp->tls_ctx, &sp->input.buf[sp->input.pos], sp->input.size - sp->input.pos - 1)) {
case -1:
err = (char *)tls_error(sp->tls_ctx);
len = CONSTLEN("tls_read(): ") + strlen(err) + 1;
@@ -368,19 +375,19 @@ serv_read(struct Server *sp) {
serv_disconnect(sp, 1, "EOF");
hist_format(sp->history, Activity_error, HIST_SHOW,
"SELF_CONNECTLOST %s %s %s :%s",
- sp->name, sp->host, sp->port, reason ? reason : "connection close");
+ sp->name, sp->host, sp->port, reason ? reason : "connection closed");
pfree(&reason);
return;
case TLS_WANT_POLLIN:
case TLS_WANT_POLLOUT:
return;
default:
- sp->inputlen += ret;
+ sp->input.pos += ret;
break;
}
} else {
#endif /* TLS */
- switch (ret = read(sp->rfd, &sp->inputbuf[sp->inputlen], SERVER_INPUT_SIZE - sp->inputlen - 1)) {
+ switch (ret = read(sp->rfd, &sp->input.buf[sp->input.pos], sp->input.size - sp->input.pos - 1)) {
case -1:
err = estrdup(strerror(errno));
len = CONSTLEN("read(): ") + strlen(err) + 1;
@@ -396,23 +403,34 @@ serv_read(struct Server *sp) {
pfree(&reason);
return;
default:
- sp->inputlen += ret;
+ sp->input.pos += ret;
break;
}
#ifdef TLS
}
#endif /* TLS */
- sp->inputbuf[SERVER_INPUT_SIZE - 1] = '\0';
- line = sp->inputbuf;
+ sp->input.buf[sp->input.size - 1] = '\0';
+ line = sp->input.buf;
while ((end = strstr(line, "\r\n"))) {
*end = '\0';
handle(sp, line);
line = end + 2;
}
- sp->inputlen -= line - sp->inputbuf;
- memmove(sp->inputbuf, line, sp->inputlen);
+ sp->input.pos -= line - sp->input.buf;
+ memmove(sp->input.buf, line, sp->input.pos);
+
+ /* Shrink and grow buffer as needed
+ * If we didn't read everything, serv_read will be called
+ * again in the main loop as poll gives another POLLIN. */
+ if (sp->input.pos + ret > sp->input.size / 2) {
+ sp->input.size *= 2;
+ sp->input.buf = erealloc(sp->input.buf, sp->input.size);
+ } else if (sp->input.pos + ret < sp->input.size / 2 && sp->input.size != INPUT_BUF_MIN) {
+ sp->input.size /= 2;
+ sp->input.buf = erealloc(sp->input.buf, sp->input.size);
+ }
}
int
diff --git a/src/struct.h b/src/struct.h
@@ -161,13 +161,15 @@ struct Schedule {
struct Schedule *next;
};
-#define SERVER_INPUT_SIZE 16384
struct Server {
struct Server *prev;
int wfd;
int rfd;
- char inputbuf[SERVER_INPUT_SIZE];
- int inputlen;
+ struct {
+ char *buf;
+ size_t size;
+ size_t pos;
+ } input;
struct pollfd *rpollfd;
enum ConnStatus status;
char *name;