vessel/scripts/stresstest.c
2025-06-13 17:58:13 +03:00

169 lines
4.8 KiB
C

#define _POSIX_C_SOURCE 199309L
/*
* A C program for stress-testing web applications.
*
* Uses multi-threaded logic to send out requests simulating ~n req/min
* randomly. Useful when working on Vessel and trying to test whether everything
* works and if it handles connections, especially loads of them, properly
* (including their state and allocated resources). This is a vital part of
* testing and developing of Vessel.
*
* This program is nothing more but a program made to be used for debugging of
* Vessel locally and authorised stress-testing purposes only. For example,
* local stress-testing while working on Vessel as a developer, or, on vessel
* web applications running on servers owned and ran by you (person who is using
* this program). Any other unauthorised stress-testing (also known as a DoS
* (denial of service) attack) is not allowed and is extremely discouraged. You,
* as the person who is using this program, are responsible for any damages you
* cause using this program.
*
* The authors and maintainers of the program are not responsible for anything
* people decide to use this for. This is a part of the free and open source
* project vessel: https://git.ari.lt/ari/vessel and is licensed under
* AGPL-3.0-or-later. Read more about AGPL-3.0 here:
* https://www.gnu.org/licenses/agpl-3.0.en.html
*
* Just putting this out there before some skid comes in and shits their pants
* at the idea of being a 1337 ep1k h4xx0r x-x Let us open source people be. Or,
* in other words, calm your tits.
*/
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/time.h>
#include <arpa/inet.h>
#ifndef NULL
# define NULL ((void *)0)
#endif /* NULL */
#define BUFFER_SIZE 1024
typedef struct {
const char *host;
int port;
int reqpm;
} State;
static void *send_request(void *arg) {
State *state = (State *)arg;
int sock;
struct sockaddr_in server_addr;
char buffer[BUFFER_SIZE];
struct timespec eep = { 0 };
struct timeval timeout;
timeout.tv_sec = 30;
timeout.tv_usec = 0;
while (1) {
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("Socket creation failed");
continue;
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(state->port);
inet_pton(AF_INET, state->host, &server_addr.sin_addr);
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Connection failed");
shutdown(sock, SHUT_RDWR);
close(sock);
continue;
}
/* TODO: Add randomness to headers, paths, http versions, etc. */
snprintf(buffer,
sizeof(buffer),
"GET / HTTP/1.1\r\nHost: %s\r\nConnection: keep-alive\r\n\r\n",
state->host);
send(sock, buffer, strlen(buffer), 0);
ssize_t ret = recv(sock, buffer, BUFFER_SIZE - 1, 0);
if (ret < 0)
ret = 0;
buffer[ret] = 0;
puts(buffer);
shutdown(sock, SHUT_RDWR);
close(sock);
if (state->reqpm > 0) {
/* Add some random deviation */
int64_t sleep_time_ns =
(60000000000ULL / state->reqpm) + (((rand() % 3) - 1) * 100000000ULL);
if (sleep_time_ns < 1)
sleep_time_ns = 0;
eep.tv_sec = sleep_time_ns / 1000000000ULL;
eep.tv_nsec = sleep_time_ns % 1000000000ULL;
nanosleep(&eep, NULL);
}
}
return NULL;
}
int main(const int argc, const char *const argv[]) {
int idx, threads;
State state;
pthread_t *ts;
if (argc < 5) {
fprintf(stderr, "Usage: %s <host> <port> <req/min> <threads>\n", argv[0]);
return 1;
}
state.host = argv[1];
state.port = atoi(argv[2]);
state.reqpm = atoi(argv[3]);
threads = atoi(argv[4]);
srand((unsigned)time(NULL));
if (threads < 1) {
fputs("Threads too low.\n", stderr);
return 1;
}
ts = malloc(threads);
if (!ts) {
fputs("Failed to allocate threads.\n", stderr);
return 1;
}
for (idx = 0; idx < threads; ++idx) {
if (pthread_create(&ts[idx], NULL, send_request, &state) != 0) {
fprintf(stderr, "Failed to create thread %d.\n", idx);
free(ts);
return 1;
}
}
for (idx = 0; idx < threads; ++idx)
if (pthread_join(ts[idx], NULL) != 0)
fprintf(stderr, "Failed to join thread %d.\n", idx);
free(ts);
return 0;
}