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