This commit is contained in:
retoor 2025-04-08 06:21:02 +02:00
parent 3199654a4b
commit ede25f9002
6 changed files with 268 additions and 2 deletions

14
Makefile Normal file
View File

@ -0,0 +1,14 @@
CC=g++
CFLAGS=-Werror -Wall -Wextra -Ofast -pedantic
all: build
build:
$(CC) main.cpp -o httpsbench -lssl -lcrypto -lcurl -pthread $(CFLAGS)
run: build
@echo "Usage: ./httpsbench -c <ThreadCount> -n <RequestCount> <URL>"
@echo "Example: ./httpsbench -c 10 -n 1000 http://example.com"
./httpsbench $(ARGS)
.PHONY: all build run

View File

@ -1,3 +1,45 @@
# httpsbench
# HTTPS Benchmark Tool
Application for benchmarking https servers
This tool is designed to benchmark HTTP requests using a thread pool for concurrent execution.
## Usage
To use the tool, compile and run it with the following commands:
```bash
make build
./httpsbench -c <ThreadCount> -n <RequestCount> <URL>
```
### Example
```bash
./httpsbench -c 10 -n 1000 http://example.com
```
## Parameters
- `-c <ThreadCount>`: Number of threads to use in the thread pool.
- `-n <RequestCount>`: Number of HTTP requests to make.
- `<URL>`: The URL to benchmark.
## Output
The tool will output the elapsed time in seconds and milliseconds, as well as the requests per second.
## Dependencies
- libcurl
- libssl
- libcrypto
## Files
- `main.cpp`: Main program file.
- `threadpool.hpp`: Thread pool implementation.
- `http.hpp`: HTTP request function declaration.
- `Makefile`: Build script.
## License
This project is licensed under the MIT License - see the LICENSE.md file for details.

50
http.hpp Normal file
View File

@ -0,0 +1,50 @@
#include <curl/curl.h>
#include <stdio.h>
// Callback function for handling data received from the server
size_t write_callback(void *buffer, size_t size, size_t nmemb, void *userp) {
size_t total_size = size * nmemb;
fwrite(buffer, size, nmemb, (FILE *)userp);
return total_size;
}
void http_get(const char * url){
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if (curl) {
// Set the URL for the request
curl_easy_setopt(curl, CURLOPT_URL, url);
// Use HTTPS
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
// Verify the server's SSL certificate
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); // Enable SSL certificate verification
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); // Verify that the certificate matches the hostname
// Optional: Specify CA certificate if the default is not sufficient
// curl_easy_setopt(curl, CURLOPT_CAINFO, "/path/to/cacert.pem");
// Set the write callback function to handle the response
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, stdout);
// Perform the request
res = curl_easy_perform(curl);
// Check for errors
if (res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
}
// Cleanup
curl_easy_cleanup(curl);
}
}

BIN
httpsbench Executable file

Binary file not shown.

82
main.cpp Normal file
View File

@ -0,0 +1,82 @@
#include <iostream>
#include <vector>
#include <thread>
#include <queue>
#include <functional>
#include <condition_variable>
#include "threadpool.hpp"
#include "http.hpp"
#include <chrono>
std::mutex mtx; // Mutex to protect shared data
int threads_working = 0;
// Function to simulate work that requires thread synchronization
void incThreadsWorking() {
mtx.lock(); // Lock the mutex to enter the critical section
threads_working++;
mtx.unlock(); // Unlock the mutex to allow other threads to enter the critical section
}
void decThreadsWorking(){
mtx.lock();
threads_working--;
mtx.unlock();
}
int main(int argc, char* argv[]) {
const char* url = nullptr;
int threadCount = 10; // Default value
int requestCount = 1000; // Default value
// Parse command-line arguments
for (int i = 1; i < argc; ++i) {
if (std::string(argv[i]) == "-c" && i + 1 < argc) {
threadCount = std::stoi(argv[++i]);
} else if (std::string(argv[i]) == "-n" && i + 1 < argc) {
requestCount = std::stoi(argv[++i]);
} else {
url = argv[i]; // Assume the last argument is the URL
}
}
if (url == nullptr) {
std::cerr << "Usage: " << argv[0] << " -c <ThreadCount> -n <RequestCount> <URL>" << std::endl;
return 1;
}
// Initialize libcurl
curl_global_init(CURL_GLOBAL_DEFAULT);
auto start = std::chrono::steady_clock::now();
ThreadPool pool(threadCount); // Create a pool with specified number of threads
// Submit tasks to the thread pool
for (int i = 0; i < requestCount; ++i) {
pool.enqueue([url, i] {
incThreadsWorking();
http_get(url);
decThreadsWorking();
std::cout << "Threads working: " << threads_working << std::endl;
});
}
while(threads_working){
std::this_thread::sleep_for(std::chrono::seconds(1));
}
auto end = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::seconds>(end - start);
std::cout << "Elapsed time in seconds: " << duration.count() << " seconds." << std::endl;
auto duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "Elapsed time in milliseconds: " << duration_ms.count() << " ms." << std::endl;
std::cout << "Requests per second: " << requestCount / duration.count() << "." << std::endl;
// Cleanup global libcurl state
curl_global_cleanup();
return 0;
}

78
threadpool.hpp Normal file
View File

@ -0,0 +1,78 @@
#include <iostream>
#include <vector>
#include <thread>
#include <queue>
#include <functional>
#include <condition_variable>
#include <atomic>
class ThreadPool {
public:
ThreadPool(size_t numThreads) : stop(false) {
for (size_t i = 0; i < numThreads; ++i) {
workers.emplace_back([this] {
while (true) {
std::function<void()> task;
// Lock the queue to retrieve the next task
{
std::unique_lock<std::mutex> lock(queueMutex);
condition.wait(lock, [this] {
return stop || !tasks.empty();
});
if (stop && tasks.empty()) {
return;
}
// Get the next task from the queue
task = std::move(tasks.front());
tasks.pop();
}
// Execute the task
task();
}
});
}
}
// Submit a task to the thread pool
template <typename F>
void enqueue(F&& f) {
{
std::unique_lock<std::mutex> lock(queueMutex);
if (stop) {
throw std::runtime_error("ThreadPool is stopped");
}
tasks.push(std::function<void()>(std::forward<F>(f)));
}
condition.notify_one();
}
// Wait for all threads to finish executing their tasks
void wait() {
// Block until all tasks have been completed
std::unique_lock<std::mutex> lock(queueMutex);
condition.wait(lock, [this] { return tasks.empty(); });
}
// Destructor: join all threads
~ThreadPool() {
{
std::unique_lock<std::mutex> lock(queueMutex);
stop = true;
}
condition.notify_all();
for (std::thread& worker : workers) {
worker.join(); // Ensure each thread finishes before destruction
}
}
private:
std::vector<std::thread> workers;
std::queue<std::function<void()>> tasks;
std::mutex queueMutex;
std::condition_variable condition;
bool stop;
};