Update.
This commit is contained in:
parent
3199654a4b
commit
ede25f9002
14
Makefile
Normal file
14
Makefile
Normal 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
|
46
README.md
46
README.md
@ -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
50
http.hpp
Normal 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
BIN
httpsbench
Executable file
Binary file not shown.
82
main.cpp
Normal file
82
main.cpp
Normal 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
78
threadpool.hpp
Normal 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;
|
||||
};
|
Loading…
Reference in New Issue
Block a user