Skip to content

Commit b8ddd76

Browse files
committed
Improved polling webserver, dynamic buffer allocation for HTTP clients
for lowered memory usage and greater vertical scalability
1 parent d6f35c2 commit b8ddd76

2 files changed

Lines changed: 155 additions & 51 deletions

File tree

res/index.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,7 @@
342342
var imgElem = document.getElementById(base + '_img');
343343
var titleElem = document.getElementById(base + '_title');
344344
if (textElem && imgElem && titleElem) {
345-
var title = imgElem.value ? imgElem.value : textElem.value;
346-
titleElem.innerHTML = title ? " - " + title : "";
345+
titleElem.innerHTML = imgElem.value ? imgElem.value : textElem.value;
347346
}
348347
}
349348

src/server.c

Lines changed: 154 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
#include "server.h"
2+
#include <poll.h>
3+
#include <fcntl.h>
24

3-
#define MAX_CLIENTS 50
4-
#define REQSIZE (512 * 1024)
5+
#define HTTP_MAX_CLIENTS 50
6+
#define HTTP_MIN_BUF_SIZE 4096
7+
#define HTTP_MAX_BUF_SIZE (4 * 1024 * 1024)
58

69
IMPORT_STR(.rodata, "../res/index.html", indexhtml);
710
extern const char indexhtml[];
@@ -21,14 +24,16 @@ typedef struct {
2124
int clntFd;
2225
char *input, *method, *payload, *prot, *query, *uri;
2326
int paysize, total;
27+
size_t buf_size;
28+
int header_len;
2429
} http_request_t;
2530

2631
struct {
2732
int sockFd;
2833
enum StreamType type;
2934
struct Mp4State mp4;
3035
unsigned int nalCnt;
31-
} client_fds[MAX_CLIENTS];
36+
} client_fds[HTTP_MAX_CLIENTS];
3237

3338
typedef struct {
3439
char *name, *value;
@@ -147,7 +152,7 @@ void send_h26x_to_client(char index, hal_vidstream *stream) {
147152
unsigned char *pack_data = pack->data + pack->offset;
148153

149154
pthread_mutex_lock(&client_fds_mutex);
150-
for (unsigned int i = 0; i < MAX_CLIENTS; ++i) {
155+
for (unsigned int i = 0; i < HTTP_MAX_CLIENTS; ++i) {
151156
if (client_fds[i].sockFd < 0) continue;
152157
if (client_fds[i].type != STREAM_H26X) continue;
153158

@@ -212,7 +217,7 @@ void send_mp4_to_client(char index, hal_vidstream *stream, char isH265) {
212217
static enum BufError err;
213218
static char len_buf[50];
214219
pthread_mutex_lock(&client_fds_mutex);
215-
for (unsigned int i = 0; i < MAX_CLIENTS; ++i) {
220+
for (unsigned int i = 0; i < HTTP_MAX_CLIENTS; ++i) {
216221
if (client_fds[i].sockFd < 0) continue;
217222
if (client_fds[i].type != STREAM_MP4) continue;
218223

@@ -269,7 +274,7 @@ void send_mp4_to_client(char index, hal_vidstream *stream, char isH265) {
269274

270275
void send_mp3_to_client(char *buf, ssize_t size) {
271276
pthread_mutex_lock(&client_fds_mutex);
272-
for (unsigned int i = 0; i < MAX_CLIENTS; ++i) {
277+
for (unsigned int i = 0; i < HTTP_MAX_CLIENTS; ++i) {
273278
if (client_fds[i].sockFd < 0) continue;
274279
if (client_fds[i].type != STREAM_MP3) continue;
275280

@@ -287,7 +292,7 @@ void send_mp3_to_client(char *buf, ssize_t size) {
287292

288293
void send_pcm_to_client(hal_audframe *frame) {
289294
pthread_mutex_lock(&client_fds_mutex);
290-
for (unsigned int i = 0; i < MAX_CLIENTS; ++i) {
295+
for (unsigned int i = 0; i < HTTP_MAX_CLIENTS; ++i) {
291296
if (client_fds[i].sockFd < 0) continue;
292297
if (client_fds[i].type != STREAM_PCM) continue;
293298

@@ -313,7 +318,7 @@ void send_mjpeg_to_client(char index, char *buf, ssize_t size) {
313318
buf[size++] = '\n';
314319

315320
pthread_mutex_lock(&client_fds_mutex);
316-
for (unsigned int i = 0; i < MAX_CLIENTS; ++i) {
321+
for (unsigned int i = 0; i < HTTP_MAX_CLIENTS; ++i) {
317322
if (client_fds[i].sockFd < 0) continue;
318323
if (client_fds[i].type != STREAM_MJPEG) continue;
319324

@@ -337,7 +342,7 @@ void send_jpeg_to_client(char index, char *buf, ssize_t size) {
337342
buf[size++] = '\n';
338343

339344
pthread_mutex_lock(&client_fds_mutex);
340-
for (unsigned int i = 0; i < MAX_CLIENTS; ++i) {
345+
for (unsigned int i = 0; i < HTTP_MAX_CLIENTS; ++i) {
341346
if (client_fds[i].sockFd < 0) continue;
342347
if (client_fds[i].type != STREAM_JPEG) continue;
343348

@@ -480,26 +485,24 @@ void parse_request(http_request_t *req) {
480485
if (ip_in_cidr(client_ip, app_config.web_whitelist[i])) goto grant_access;
481486
close_socket_fd(req->clntFd);
482487
req->clntFd = -1;
483-
req->total = 0;
484488
return;
485489
}
486490

487491
grant_access:
488-
req->total = 0;
489-
int received = recv(req->clntFd, req->input, REQSIZE, 0);
490-
if (received < 0)
491-
HAL_WARNING("server", "Reading from client failed!\n");
492-
else if (!received)
493-
HAL_WARNING("server", "Client disconnected unexpectedly!\n");
494-
req->total += received;
495-
496492
if (req->total <= 0) return;
497493

494+
req->input[req->total] = '\0';
498495
char *state = NULL;
499496
req->method = strtok_r(req->input, " \t\r\n", &state);
500497
req->uri = strtok_r(NULL, " \t", &state);
501498
req->prot = strtok_r(NULL, " \t\r\n", &state);
502499

500+
if (!req->method || !req->uri || !req->prot) {
501+
close_socket_fd(req->clntFd);
502+
req->clntFd = -1;
503+
return;
504+
}
505+
503506
HAL_INFO("server", "\x1b[32mNew request: (%s) %s\n"
504507
" Received from: %s\x1b[0m\n",
505508
req->method, req->uri, client_ip);
@@ -510,39 +513,25 @@ void parse_request(http_request_t *req) {
510513
req->query = req->uri - 1;
511514

512515
http_header_t *h = http_headers;
513-
char *l;
514516
while (h < http_headers + 16) {
515517
char *k, *v, *e;
516518
if (!(k = strtok_r(NULL, "\r\n: \t", &state)))
517519
break;
518520
v = strtok_r(NULL, "\r\n", &state);
521+
if (!v) break;
519522
while (*v && *v == ' ' && v++);
520523
h->name = k;
521524
h++->value = v;
522525
#ifdef DEBUG_HTTP
523526
fprintf(stderr, " (H) %s: %s\n", k, v);
524527
#endif
528+
if (state >= req->input + req->header_len) break;
525529
e = v + 1 + strlen(v);
526530
if (e[1] == '\r' && e[2] == '\n')
527531
break;
528532
}
529533

530-
l = request_header("Content-Length");
531-
req->paysize = l ? atol(l) : 0;
532-
533-
while (l && req->total < req->paysize) {
534-
received = recv(req->clntFd, req->input + req->total, REQSIZE - req->total, 0);
535-
if (received < 0) {
536-
HAL_WARNING("server", "Reading from client failed!\n");
537-
break;
538-
} else if (!received) {
539-
HAL_WARNING("server", "Client disconnected unexpectedly!\n");
540-
break;
541-
}
542-
req->total += received;
543-
}
544-
545-
req->payload = strtok_r(NULL, "\r\n", &state);
534+
req->payload = req->input + req->header_len;
546535
}
547536

548537
void respond_request(http_request_t *req) {
@@ -680,7 +669,7 @@ void respond_request(http_request_t *req) {
680669
"Connection: keep-alive\r\n\r\n");
681670
send_to_fd(req->clntFd, response, respLen);
682671
pthread_mutex_lock(&client_fds_mutex);
683-
for (uint32_t i = 0; i < MAX_CLIENTS; ++i)
672+
for (uint32_t i = 0; i < HTTP_MAX_CLIENTS; ++i)
684673
if (client_fds[i].sockFd < 0) {
685674
client_fds[i].sockFd = req->clntFd;
686675
client_fds[i].type = STREAM_MP3;
@@ -698,7 +687,7 @@ void respond_request(http_request_t *req) {
698687
"Connection: keep-alive\r\n\r\n");
699688
send_to_fd(req->clntFd, response, respLen);
700689
pthread_mutex_lock(&client_fds_mutex);
701-
for (uint32_t i = 0; i < MAX_CLIENTS; ++i)
690+
for (uint32_t i = 0; i < HTTP_MAX_CLIENTS; ++i)
702691
if (client_fds[i].sockFd < 0) {
703692
client_fds[i].sockFd = req->clntFd;
704693
client_fds[i].type = STREAM_PCM;
@@ -718,7 +707,7 @@ void respond_request(http_request_t *req) {
718707
"Connection: keep-alive\r\n\r\n");
719708
send_to_fd(req->clntFd, response, respLen);
720709
pthread_mutex_lock(&client_fds_mutex);
721-
for (uint32_t i = 0; i < MAX_CLIENTS; ++i)
710+
for (uint32_t i = 0; i < HTTP_MAX_CLIENTS; ++i)
722711
if (client_fds[i].sockFd < 0) {
723712
client_fds[i].sockFd = req->clntFd;
724713
client_fds[i].type = STREAM_H26X;
@@ -738,7 +727,7 @@ void respond_request(http_request_t *req) {
738727
"Connection: keep-alive\r\n\r\n");
739728
send_to_fd(req->clntFd, response, respLen);
740729
pthread_mutex_lock(&client_fds_mutex);
741-
for (uint32_t i = 0; i < MAX_CLIENTS; ++i)
730+
for (uint32_t i = 0; i < HTTP_MAX_CLIENTS; ++i)
742731
if (client_fds[i].sockFd < 0) {
743732
client_fds[i].sockFd = req->clntFd;
744733
client_fds[i].type = STREAM_MP4;
@@ -758,7 +747,7 @@ void respond_request(http_request_t *req) {
758747
"Content-Type: multipart/x-mixed-replace; boundary=boundarydonotcross\r\n\r\n");
759748
send_to_fd(req->clntFd, response, respLen);
760749
pthread_mutex_lock(&client_fds_mutex);
761-
for (uint32_t i = 0; i < MAX_CLIENTS; ++i)
750+
for (uint32_t i = 0; i < HTTP_MAX_CLIENTS; ++i)
762751
if (client_fds[i].sockFd < 0) {
763752
client_fds[i].sockFd = req->clntFd;
764753
client_fds[i].type = STREAM_MJPEG;
@@ -1464,13 +1453,15 @@ void respond_request(http_request_t *req) {
14641453
}
14651454

14661455
void *server_thread(void *vargp) {
1467-
http_request_t req = {0};
14681456
int ret, server_fd = *((int *)vargp);
14691457
int enable = 1;
14701458
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) {
14711459
HAL_WARNING("server", "setsockopt(SO_REUSEADDR) failed");
14721460
fflush(stdout);
14731461
}
1462+
1463+
fcntl(server_fd, F_SETFL, fcntl(server_fd, F_GETFL, 0) | O_NONBLOCK);
1464+
14741465
struct sockaddr_in client, server = {
14751466
.sin_family = AF_INET,
14761467
.sin_port = htons(app_config.web_port),
@@ -1484,27 +1475,141 @@ void *server_thread(void *vargp) {
14841475
}
14851476
listen(server_fd, 128);
14861477

1487-
req.input = malloc(REQSIZE);
1478+
struct pollfd fds[HTTP_MAX_CLIENTS + 1];
1479+
http_request_t reqs[HTTP_MAX_CLIENTS];
1480+
1481+
for (int i = 0; i < HTTP_MAX_CLIENTS; i++) {
1482+
fds[i + 1].fd = -1;
1483+
reqs[i].clntFd = -1;
1484+
reqs[i].input = NULL;
1485+
}
1486+
1487+
fds[0].fd = server_fd;
1488+
fds[0].events = POLLIN;
14881489

14891490
while (keepRunning) {
1490-
if ((req.clntFd = accept(server_fd, NULL, NULL)) == -1)
1491+
int poll_count = poll(fds, HTTP_MAX_CLIENTS + 1, 1000);
1492+
if (poll_count < 0) {
1493+
if (errno == EINTR) continue;
14911494
break;
1495+
}
1496+
1497+
if (fds[0].revents & POLLIN) {
1498+
struct sockaddr_in client_sock;
1499+
socklen_t client_sock_len = sizeof(client_sock);
1500+
int clntFd = accept(server_fd, (struct sockaddr *)&client_sock, &client_sock_len);
1501+
if (clntFd >= 0) {
1502+
fcntl(clntFd, F_SETFL, fcntl(clntFd, F_GETFL, 0) | O_NONBLOCK);
1503+
int i = 0;
1504+
for (i = 0; i < HTTP_MAX_CLIENTS; i++) {
1505+
if (fds[i+1].fd == -1) {
1506+
fds[i+1].fd = clntFd;
1507+
fds[i+1].events = POLLIN;
1508+
reqs[i].clntFd = clntFd;
1509+
reqs[i].total = 0;
1510+
reqs[i].buf_size = HTTP_MIN_BUF_SIZE;
1511+
reqs[i].input = malloc(reqs[i].buf_size + 1);
1512+
reqs[i].header_len = 0;
1513+
reqs[i].paysize = 0;
1514+
break;
1515+
}
1516+
}
1517+
if (i == HTTP_MAX_CLIENTS) {
1518+
close_socket_fd(clntFd);
1519+
}
1520+
}
1521+
}
1522+
1523+
for (int i = 0; i < HTTP_MAX_CLIENTS; i++) {
1524+
if (fds[i + 1].fd != -1) {
1525+
if (fds[i + 1].revents & (POLLERR | POLLHUP | POLLNVAL)) {
1526+
close_socket_fd(fds[i + 1].fd);
1527+
fds[i + 1].fd = -1;
1528+
free(reqs[i].input);
1529+
reqs[i].input = NULL;
1530+
continue;
1531+
}
1532+
if (fds[i + 1].revents & POLLIN) {
1533+
http_request_t *req = &reqs[i];
1534+
1535+
if (req->total == req->buf_size) {
1536+
req->buf_size *= 2;
1537+
if (req->buf_size > HTTP_MAX_BUF_SIZE) {
1538+
close_socket_fd(req->clntFd);
1539+
fds[i + 1].fd = -1;
1540+
free(req->input);
1541+
req->input = NULL;
1542+
continue;
1543+
}
1544+
req->input = realloc(req->input, req->buf_size + 1);
1545+
}
14921546

1493-
parse_request(&req);
1547+
int received = recv(req->clntFd, req->input + req->total, req->buf_size - req->total, 0);
1548+
if (received < 0) {
1549+
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
1550+
continue;
1551+
close_socket_fd(req->clntFd);
1552+
fds[i + 1].fd = -1;
1553+
free(req->input);
1554+
req->input = NULL;
1555+
continue;
1556+
} else if (received == 0) {
1557+
close_socket_fd(req->clntFd);
1558+
fds[i + 1].fd = -1;
1559+
free(req->input);
1560+
req->input = NULL;
1561+
continue;
1562+
}
1563+
req->total += received;
1564+
1565+
if (req->header_len == 0) {
1566+
char *hdr_end = memstr(req->input, "\r\n\r\n", req->total, 4);
1567+
if (hdr_end) {
1568+
req->header_len = (hdr_end + 4) - req->input;
1569+
1570+
char *cl = req->input;
1571+
req->paysize = 0;
1572+
while (cl < hdr_end) {
1573+
if (!strncasecmp(cl, "Content-Length:", 15)) {
1574+
req->paysize = atoi(cl + 15);
1575+
break;
1576+
}
1577+
cl = strchr(cl, '\n');
1578+
if (!cl) break;
1579+
cl++;
1580+
}
1581+
}
1582+
}
14941583

1495-
respond_request(&req);
1584+
if (req->header_len > 0 && req->total >= req->header_len + req->paysize) {
1585+
parse_request(req);
1586+
if (req->clntFd != -1) {
1587+
fcntl(req->clntFd, F_SETFL, fcntl(req->clntFd, F_GETFL, 0) & ~O_NONBLOCK);
1588+
respond_request(req);
1589+
}
1590+
fds[i + 1].fd = -1;
1591+
free(req->input);
1592+
req->input = NULL;
1593+
}
1594+
}
1595+
}
1596+
}
14961597
}
14971598

1498-
if (req.input)
1499-
free(req.input);
1599+
for (int i = 0; i < HTTP_MAX_CLIENTS; i++) {
1600+
if (fds[i + 1].fd != -1) {
1601+
close_socket_fd(fds[i + 1].fd);
1602+
free(reqs[i].input);
1603+
}
1604+
}
15001605

15011606
close_socket_fd(server_fd);
15021607
HAL_INFO("server", "Thread has exited\n");
15031608
return NULL;
15041609
}
15051610

15061611
int start_server() {
1507-
for (unsigned int i = 0; i < MAX_CLIENTS; i++) {
1612+
for (int i = 0; i < HTTP_MAX_CLIENTS; i++) {
15081613
client_fds[i].sockFd = -1;
15091614
client_fds[i].type = -1;
15101615
}
@@ -1517,7 +1622,7 @@ int start_server() {
15171622
pthread_attr_init(&thread_attr);
15181623
size_t stacksize;
15191624
pthread_attr_getstacksize(&thread_attr, &stacksize);
1520-
size_t new_stacksize = app_config.web_server_thread_stack_size + REQSIZE;
1625+
size_t new_stacksize = app_config.web_server_thread_stack_size;
15211626
if (pthread_attr_setstacksize(&thread_attr, new_stacksize))
15221627
HAL_WARNING("server", "Can't set stack size %zu\n", new_stacksize);
15231628
if (pthread_create(

0 commit comments

Comments
 (0)